blob: 8d0af0eb3b77d7ff7fd44b659db80411168ea2a4 [file] [log] [blame]
[email protected]b6b72222012-02-11 02:04:131// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]d24c4012009-07-28 01:57:312// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
pmonette32a5cfb42016-04-11 22:04:445#include "chrome/browser/shell_integration_win.h"
[email protected]d24c4012009-07-28 01:57:316
7#include <windows.h>
[email protected]caa05352014-03-01 00:43:058#include <shlwapi.h>
[email protected]d24c4012009-07-28 01:57:319#include <shobjidl.h>
thestig18dfb7a52014-08-26 10:44:0410#include <propkey.h> // Needs to come after shobjidl.h.
avi664c07b2015-12-26 02:18:3111#include <stddef.h>
12#include <stdint.h>
[email protected]d24c4012009-07-28 01:57:3113
pmonettea98315242016-11-23 21:39:1814#include <memory>
15#include <utility>
pmonette32a5cfb42016-04-11 22:04:4416#include <vector>
17
[email protected]3a3e72c2011-11-29 02:59:3818#include "base/bind.h"
pmonette32a5cfb42016-04-11 22:04:4419#include "base/callback.h"
[email protected]d24c4012009-07-28 01:57:3120#include "base/command_line.h"
[email protected]25a4c1c2013-06-08 04:53:3621#include "base/files/file_enumerator.h"
thestig18dfb7a52014-08-26 10:44:0422#include "base/files/file_util.h"
avi664c07b2015-12-26 02:18:3123#include "base/macros.h"
dcheng4af48582016-04-19 00:29:3524#include "base/memory/ptr_util.h"
pmonette32a5cfb42016-04-11 22:04:4425#include "base/memory/weak_ptr.h"
[email protected]fa1e0e12013-07-18 00:10:1426#include "base/message_loop/message_loop.h"
pmonette32a5cfb42016-04-11 22:04:4427#include "base/metrics/histogram_macros.h"
grtc291eea2016-05-26 15:38:4828#include "base/metrics/user_metrics.h"
29#include "base/metrics/user_metrics_action.h"
[email protected]d24c4012009-07-28 01:57:3130#include "base/path_service.h"
[email protected]24a555b62013-06-10 22:01:1731#include "base/strings/string_util.h"
32#include "base/strings/stringprintf.h"
[email protected]e309f312013-06-07 21:50:0833#include "base/strings/utf_string_conversions.h"
pmonette32a5cfb42016-04-11 22:04:4434#include "base/time/time.h"
35#include "base/timer/timer.h"
[email protected]2d6503982010-10-17 04:41:5436#include "base/win/registry.h"
[email protected]8ee65ba2011-04-12 20:53:2337#include "base/win/scoped_comptr.h"
[email protected]07983302013-01-21 19:41:4438#include "base/win/scoped_propvariant.h"
[email protected]f1024e22012-09-12 07:14:5539#include "base/win/shortcut.h"
[email protected]935aa542010-10-15 01:59:1540#include "base/win/windows_version.h"
[email protected]89d43832013-06-29 20:25:2041#include "chrome/browser/policy/policy_path_parser.h"
pmonette32a5cfb42016-04-11 22:04:4442#include "chrome/browser/shell_integration.h"
[email protected]c9bb06f42010-01-13 23:53:4843#include "chrome/browser/web_applications/web_app.h"
grtc291eea2016-05-26 15:38:4844#include "chrome/browser/win/settings_app_monitor.h"
[email protected]d24c4012009-07-28 01:57:3145#include "chrome/common/chrome_constants.h"
[email protected]12f520c2010-01-06 18:11:1546#include "chrome/common/chrome_paths_internal.h"
[email protected]c9bb06f42010-01-13 23:53:4847#include "chrome/common/chrome_switches.h"
pmonette5057ca3b2016-07-04 16:48:4048#include "chrome/common/shell_handler_win.mojom.h"
49#include "chrome/grit/generated_resources.h"
[email protected]4468a5b2011-05-26 07:48:0250#include "chrome/installer/setup/setup_util.h"
[email protected]d24c4012009-07-28 01:57:3151#include "chrome/installer/util/browser_distribution.h"
52#include "chrome/installer/util/create_reg_key_work_item.h"
[email protected]3f69d6e612012-08-03 18:52:2753#include "chrome/installer/util/install_util.h"
pmonette0c40087a2016-04-21 00:05:1654#include "chrome/installer/util/scoped_user_protocol_entry.h"
[email protected]d24c4012009-07-28 01:57:3155#include "chrome/installer/util/set_reg_value_work_item.h"
56#include "chrome/installer/util/shell_util.h"
57#include "chrome/installer/util/util_constants.h"
58#include "chrome/installer/util/work_item.h"
59#include "chrome/installer/util/work_item_list.h"
pmonette2b1dbee2016-01-08 20:18:5860#include "components/variations/variations_associated_data.h"
[email protected]c38831a12011-10-28 12:44:4961#include "content/public/browser/browser_thread.h"
pmonette5057ca3b2016-07-04 16:48:4062#include "content/public/browser/utility_process_mojo_client.h"
63#include "ui/base/l10n/l10n_util.h"
[email protected]d24c4012009-07-28 01:57:3164
[email protected]631bb742011-11-02 11:29:3965using content::BrowserThread;
66
pmonette9fa59e882016-02-10 00:12:1967namespace shell_integration {
68
[email protected]12f520c2010-01-06 18:11:1569namespace {
70
pmonette9fa59e882016-02-10 00:12:1971// Helper function for GetAppId to generates profile id
[email protected]2f1c09d2011-01-14 14:58:1472// from profile path. "profile_id" is composed of sanitized basenames of
[email protected]12f520c2010-01-06 18:11:1573// user data dir and profile dir joined by a ".".
[email protected]6a72a632013-12-12 22:22:0074base::string16 GetProfileIdFromPath(const base::FilePath& profile_path) {
[email protected]12f520c2010-01-06 18:11:1575 // Return empty string if profile_path is empty
76 if (profile_path.empty())
[email protected]0085863a2013-12-06 21:19:0377 return base::string16();
[email protected]12f520c2010-01-06 18:11:1578
[email protected]650b2d52013-02-10 03:41:4579 base::FilePath default_user_data_dir;
[email protected]12f520c2010-01-06 18:11:1580 // Return empty string if profile_path is in default user data
81 // dir and is the default profile.
82 if (chrome::GetDefaultUserDataDirectory(&default_user_data_dir) &&
83 profile_path.DirName() == default_user_data_dir &&
[email protected]162b5992011-03-15 19:40:4884 profile_path.BaseName().value() ==
[email protected]f911df52013-12-24 23:24:2385 base::ASCIIToUTF16(chrome::kInitialProfile)) {
[email protected]0085863a2013-12-06 21:19:0386 return base::string16();
[email protected]162b5992011-03-15 19:40:4887 }
[email protected]12f520c2010-01-06 18:11:1588
89 // Get joined basenames of user data dir and profile.
[email protected]0085863a2013-12-06 21:19:0390 base::string16 basenames = profile_path.DirName().BaseName().value() +
[email protected]12f520c2010-01-06 18:11:1591 L"." + profile_path.BaseName().value();
92
[email protected]0085863a2013-12-06 21:19:0393 base::string16 profile_id;
[email protected]12f520c2010-01-06 18:11:1594 profile_id.reserve(basenames.size());
95
96 // Generate profile_id from sanitized basenames.
97 for (size_t i = 0; i < basenames.length(); ++i) {
brettwb3413062015-06-24 00:39:0298 if (base::IsAsciiAlpha(basenames[i]) ||
99 base::IsAsciiDigit(basenames[i]) ||
[email protected]12f520c2010-01-06 18:11:15100 basenames[i] == L'.')
101 profile_id += basenames[i];
102 }
103
104 return profile_id;
105}
106
taptedc2c690ab2016-05-25 02:49:03107base::string16 GetAppListAppName() {
108 static const base::char16 kAppListAppNameSuffix[] = L"AppList";
109 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
110 base::string16 app_name(dist->GetBaseAppId());
111 app_name.append(kAppListAppNameSuffix);
112 return app_name;
113}
114
[email protected]8ea8f1ef2013-01-06 18:39:03115// Gets expected app id for given Chrome (based on |command_line| and
116// |is_per_user_install|).
avi556c05022014-12-22 23:31:43117base::string16 GetExpectedAppId(const base::CommandLine& command_line,
[email protected]6a72a632013-12-12 22:22:00118 bool is_per_user_install) {
[email protected]b39e32e2013-04-24 08:55:54119 base::FilePath user_data_dir;
120 if (command_line.HasSwitch(switches::kUserDataDir))
121 user_data_dir = command_line.GetSwitchValuePath(switches::kUserDataDir);
122 else
123 chrome::GetDefaultUserDataDirectory(&user_data_dir);
[email protected]89d43832013-06-29 20:25:20124 // Adjust with any policy that overrides any other way to set the path.
125 policy::path_parser::CheckUserDataDirPolicy(&user_data_dir);
[email protected]b39e32e2013-04-24 08:55:54126 DCHECK(!user_data_dir.empty());
[email protected]c9bb06f42010-01-13 23:53:48127
[email protected]b39e32e2013-04-24 08:55:54128 base::FilePath profile_subdir;
129 if (command_line.HasSwitch(switches::kProfileDirectory)) {
130 profile_subdir =
131 command_line.GetSwitchValuePath(switches::kProfileDirectory);
132 } else {
[email protected]f911df52013-12-24 23:24:23133 profile_subdir =
134 base::FilePath(base::ASCIIToUTF16(chrome::kInitialProfile));
[email protected]b39e32e2013-04-24 08:55:54135 }
136 DCHECK(!profile_subdir.empty());
137
138 base::FilePath profile_path = user_data_dir.Append(profile_subdir);
[email protected]0085863a2013-12-06 21:19:03139 base::string16 app_name;
[email protected]c9bb06f42010-01-13 23:53:48140 if (command_line.HasSwitch(switches::kApp)) {
[email protected]f911df52013-12-24 23:24:23141 app_name = base::UTF8ToUTF16(web_app::GenerateApplicationNameFromURL(
[email protected]57ecc4b2010-08-11 03:02:51142 GURL(command_line.GetSwitchValueASCII(switches::kApp))));
[email protected]2f1c09d2011-01-14 14:58:14143 } else if (command_line.HasSwitch(switches::kAppId)) {
[email protected]f911df52013-12-24 23:24:23144 app_name = base::UTF8ToUTF16(
145 web_app::GenerateApplicationNameFromExtensionId(
146 command_line.GetSwitchValueASCII(switches::kAppId)));
taptedc2c690ab2016-05-25 02:49:03147 } else if (command_line.HasSwitch(switches::kShowAppList)) {
148 app_name = GetAppListAppName();
[email protected]c9bb06f42010-01-13 23:53:48149 } else {
[email protected]a0448002012-06-19 04:32:10150 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
[email protected]8ea8f1ef2013-01-06 18:39:03151 app_name = ShellUtil::GetBrowserModelId(dist, is_per_user_install);
[email protected]c9bb06f42010-01-13 23:53:48152 }
[email protected]b39e32e2013-04-24 08:55:54153 DCHECK(!app_name.empty());
[email protected]c9bb06f42010-01-13 23:53:48154
pmonette9e4c1a82016-04-14 18:15:30155 return win::GetAppModelIdForProfile(app_name, profile_path);
[email protected]c9bb06f42010-01-13 23:53:48156}
157
gab88257b62016-01-15 03:19:19158void MigrateTaskbarPinsCallback() {
[email protected]3a3e72c2011-11-29 02:59:38159 // This should run on the file thread.
thestig00844cea2015-09-08 21:44:52160 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
[email protected]3a3e72c2011-11-29 02:59:38161
162 // Get full path of chrome.
[email protected]650b2d52013-02-10 03:41:45163 base::FilePath chrome_exe;
[email protected]3a3e72c2011-11-29 02:59:38164 if (!PathService::Get(base::FILE_EXE, &chrome_exe))
165 return;
166
gab88257b62016-01-15 03:19:19167 base::FilePath pins_path;
168 if (!PathService::Get(base::DIR_TASKBAR_PINS, &pins_path)) {
169 NOTREACHED();
170 return;
[email protected]3a3e72c2011-11-29 02:59:38171 }
gab88257b62016-01-15 03:19:19172
pmonette9e4c1a82016-04-14 18:15:30173 win::MigrateShortcutsInPathInternal(chrome_exe, pins_path);
[email protected]3a3e72c2011-11-29 02:59:38174}
175
[email protected]caa05352014-03-01 00:43:05176// Windows 8 introduced a new protocol->executable binding system which cannot
177// be retrieved in the HKCR registry subkey method implemented below. We call
178// AssocQueryString with the new Win8-only flag ASSOCF_IS_PROTOCOL instead.
179base::string16 GetAppForProtocolUsingAssocQuery(const GURL& url) {
thestig11bf74d2014-11-24 20:14:42180 base::string16 url_scheme = base::ASCIIToUTF16(url.scheme());
[email protected]caa05352014-03-01 00:43:05181 // Don't attempt to query protocol association on an empty string.
[email protected]6e84d372014-05-29 23:36:39182 if (url_scheme.empty())
[email protected]caa05352014-03-01 00:43:05183 return base::string16();
184
[email protected]caa05352014-03-01 00:43:05185 // Query AssocQueryString for a human-readable description of the program
186 // that will be invoked given the provided URL spec. This is used only to
187 // populate the external protocol dialog box the user sees when invoking
188 // an unknown external protocol.
189 wchar_t out_buffer[1024];
190 DWORD buffer_size = arraysize(out_buffer);
191 HRESULT hr = AssocQueryString(ASSOCF_IS_PROTOCOL,
192 ASSOCSTR_FRIENDLYAPPNAME,
[email protected]6e84d372014-05-29 23:36:39193 url_scheme.c_str(),
[email protected]caa05352014-03-01 00:43:05194 NULL,
195 out_buffer,
196 &buffer_size);
197 if (FAILED(hr)) {
198 DLOG(WARNING) << "AssocQueryString failed!";
199 return base::string16();
200 }
201 return base::string16(out_buffer);
202}
203
204base::string16 GetAppForProtocolUsingRegistry(const GURL& url) {
dominickn0c9a5062016-10-06 20:49:00205 base::string16 command_to_launch;
206
207 // First, try and extract the application's display name.
208 base::string16 cmd_key_path = base::ASCIIToUTF16(url.scheme());
209 base::win::RegKey cmd_key_name(HKEY_CLASSES_ROOT, cmd_key_path.c_str(),
210 KEY_READ);
211 if (cmd_key_name.ReadValue(NULL, &command_to_launch) == ERROR_SUCCESS &&
212 !command_to_launch.empty()) {
213 return command_to_launch;
[email protected]caa05352014-03-01 00:43:05214 }
dominickn0c9a5062016-10-06 20:49:00215
216 // Otherwise, parse the command line in the registry, and return the basename
217 // of the program path if it exists.
218 cmd_key_path = base::ASCIIToUTF16(url.scheme() + "\\shell\\open\\command");
219 base::win::RegKey cmd_key_exe(HKEY_CLASSES_ROOT, cmd_key_path.c_str(),
220 KEY_READ);
221 if (cmd_key_exe.ReadValue(NULL, &command_to_launch) == ERROR_SUCCESS) {
222 base::CommandLine command_line(
223 base::CommandLine::FromString(command_to_launch));
224 return command_line.GetProgram().BaseName().value();
225 }
226
[email protected]caa05352014-03-01 00:43:05227 return base::string16();
228}
229
pmonette9fa59e882016-02-10 00:12:19230DefaultWebClientState GetDefaultWebClientStateFromShellUtilDefaultState(
231 ShellUtil::DefaultState default_state) {
[email protected]eb63da72012-10-15 20:39:48232 switch (default_state) {
233 case ShellUtil::NOT_DEFAULT:
pmonette9fa59e882016-02-10 00:12:19234 return DefaultWebClientState::NOT_DEFAULT;
[email protected]eb63da72012-10-15 20:39:48235 case ShellUtil::IS_DEFAULT:
pmonette9fa59e882016-02-10 00:12:19236 return DefaultWebClientState::IS_DEFAULT;
[email protected]eb63da72012-10-15 20:39:48237 default:
238 DCHECK_EQ(ShellUtil::UNKNOWN_DEFAULT, default_state);
pmonette9fa59e882016-02-10 00:12:19239 return DefaultWebClientState::UNKNOWN_DEFAULT;
[email protected]eb63da72012-10-15 20:39:48240 }
241}
242
grtc291eea2016-05-26 15:38:48243// A recorder of user actions in the Windows Settings app.
244class DefaultBrowserActionRecorder : public win::SettingsAppMonitor::Delegate {
245 public:
246 // Creates the recorder and the monitor that drives it. |continuation| will be
247 // run once the monitor's initialization completes (regardless of success or
248 // failure).
249 explicit DefaultBrowserActionRecorder(base::Closure continuation)
250 : continuation_(std::move(continuation)), settings_app_monitor_(this) {}
251
252 private:
253 // win::SettingsAppMonitor::Delegate:
254 void OnInitialized(HRESULT result) override {
255 UMA_HISTOGRAM_BOOLEAN("SettingsAppMonitor.InitializationResult",
256 SUCCEEDED(result));
257 if (SUCCEEDED(result)) {
258 base::RecordAction(
259 base::UserMetricsAction("SettingsAppMonitor.Initialized"));
260 }
261 continuation_.Run();
262 continuation_ = base::Closure();
263 }
264
265 void OnAppFocused() override {
266 base::RecordAction(
267 base::UserMetricsAction("SettingsAppMonitor.AppFocused"));
268 }
269
270 void OnChooserInvoked() override {
271 base::RecordAction(
272 base::UserMetricsAction("SettingsAppMonitor.ChooserInvoked"));
273 }
274
275 void OnBrowserChosen(const base::string16& browser_name) override {
276 if (browser_name ==
277 BrowserDistribution::GetDistribution()->GetDisplayName()) {
278 base::RecordAction(
279 base::UserMetricsAction("SettingsAppMonitor.ChromeBrowserChosen"));
280 } else {
281 base::RecordAction(
282 base::UserMetricsAction("SettingsAppMonitor.OtherBrowserChosen"));
283 }
284 }
285
286 // A closure to be run once initialization completes.
287 base::Closure continuation_;
288
289 // Monitors user interaction with the Windows Settings app for the sake of
290 // reporting user actions.
291 win::SettingsAppMonitor settings_app_monitor_;
292
293 DISALLOW_COPY_AND_ASSIGN(DefaultBrowserActionRecorder);
294};
295
296// A function bound up in a callback with a DefaultBrowserActionRecorder and
297// a closure to keep the former alive until the time comes to run the latter.
298void OnSettingsAppFinished(
299 std::unique_ptr<DefaultBrowserActionRecorder> recorder,
300 const base::Closure& on_finished_callback) {
301 recorder.reset();
302 on_finished_callback.Run();
303}
304
pmonette32a5cfb42016-04-11 22:04:44305// There is no way to make sure the user is done with the system settings, but a
306// signal that the interaction is finished is needed for UMA. A timer of 2
307// minutes is used as a substitute. The registry keys for the protocol
308// association with an app are also monitored to signal the end of the
309// interaction early when it is clear that the user made a choice (e.g. http
310// and https for default browser).
311//
312// This helper class manages both the timer and the registry watchers and makes
313// sure the callback for the end of the settings interaction is only run once.
314// This class also manages its own lifetime.
315class OpenSystemSettingsHelper {
316 public:
317 // Begin the monitoring and will call |on_finished_callback| when done.
318 // Takes in a null-terminated array of |protocols| whose registry keys must be
pmonette0c40087a2016-04-21 00:05:16319 // watched. The array must contain at least one element.
pmonette32a5cfb42016-04-11 22:04:44320 static void Begin(const wchar_t* const protocols[],
321 const base::Closure& on_finished_callback) {
pmonettec541d942016-06-15 22:08:32322 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
323
324 delete instance_;
325 instance_ = new OpenSystemSettingsHelper(protocols, on_finished_callback);
pmonette32a5cfb42016-04-11 22:04:44326 }
327
328 private:
329 // The reason the settings interaction concluded. Do not modify the ordering
330 // because it is used for UMA.
331 enum ConcludeReason { REGISTRY_WATCHER, TIMEOUT, NUM_CONCLUDE_REASON_TYPES };
332
333 OpenSystemSettingsHelper(const wchar_t* const protocols[],
334 const base::Closure& on_finished_callback)
pmonette0c40087a2016-04-21 00:05:16335 : scoped_user_protocol_entry_(protocols[0]),
336 on_finished_callback_(on_finished_callback),
337 weak_ptr_factory_(this) {
pmonette32a5cfb42016-04-11 22:04:44338 static const wchar_t kUrlAssociationFormat[] =
339 L"SOFTWARE\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\"
340 L"%ls\\UserChoice";
341
342 // Remember the start time.
343 start_time_ = base::TimeTicks::Now();
344
345 for (const wchar_t* const* scan = &protocols[0]; *scan != nullptr; ++scan) {
346 AddRegistryKeyWatcher(
347 base::StringPrintf(kUrlAssociationFormat, *scan).c_str());
348 }
349 // Only the watchers that were succesfully initialized are counted.
350 registry_watcher_count_ = registry_key_watchers_.size();
351
352 timer_.Start(
353 FROM_HERE, base::TimeDelta::FromMinutes(2),
354 base::Bind(&OpenSystemSettingsHelper::ConcludeInteraction,
355 weak_ptr_factory_.GetWeakPtr(), ConcludeReason::TIMEOUT));
356 }
357
358 // Called when a change is detected on one of the registry keys being watched.
359 // Note: All types of modification to the registry key will trigger this
360 // function even if the value change is the only one that matters. This
361 // is good enough for now.
362 void OnRegistryKeyChanged() {
363 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
364
365 // Make sure all the registry watchers have fired.
366 if (--registry_watcher_count_ == 0) {
367 UMA_HISTOGRAM_MEDIUM_TIMES(
368 "DefaultBrowser.SettingsInteraction.RegistryWatcherDuration",
369 base::TimeTicks::Now() - start_time_);
370
371 ConcludeInteraction(ConcludeReason::REGISTRY_WATCHER);
372 }
373 }
374
375 // Ends the monitoring with the system settings. Will call
376 // |on_finished_callback_| and then dispose of this class instance to make
377 // sure the callback won't get called subsequently.
378 void ConcludeInteraction(ConcludeReason conclude_reason) {
379 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
380
381 UMA_HISTOGRAM_ENUMERATION(
382 "DefaultBrowser.SettingsInteraction.ConcludeReason", conclude_reason,
383 NUM_CONCLUDE_REASON_TYPES);
384 on_finished_callback_.Run();
pmonettec541d942016-06-15 22:08:32385 delete instance_;
386 instance_ = nullptr;
pmonette32a5cfb42016-04-11 22:04:44387 }
388
389 // Helper function to create a registry watcher for a given |key_path|. Do
390 // nothing on initialization failure.
391 void AddRegistryKeyWatcher(const wchar_t* key_path) {
ricea86fa1dd2016-09-13 05:59:45392 auto reg_key = base::MakeUnique<base::win::RegKey>(HKEY_CURRENT_USER,
393 key_path, KEY_NOTIFY);
pmonette32a5cfb42016-04-11 22:04:44394
395 if (reg_key->Valid() &&
396 reg_key->StartWatching(
397 base::Bind(&OpenSystemSettingsHelper::OnRegistryKeyChanged,
398 weak_ptr_factory_.GetWeakPtr()))) {
399 registry_key_watchers_.push_back(std::move(reg_key));
400 }
401 }
402
pmonettec541d942016-06-15 22:08:32403 // Used to make sure only one instance is alive at the same time.
404 static OpenSystemSettingsHelper* instance_;
405
pmonette0c40087a2016-04-21 00:05:16406 // This is needed to make sure that Windows displays an entry for the protocol
407 // inside the "Choose default apps by protocol" settings page.
408 ScopedUserProtocolEntry scoped_user_protocol_entry_;
409
pmonette32a5cfb42016-04-11 22:04:44410 // The function to call when the interaction with the system settings is
411 // finished.
412 base::Closure on_finished_callback_;
413
414 // The number of time the registry key watchers must fire.
415 int registry_watcher_count_ = 0;
416
417 // There can be multiple registry key watchers as some settings modify
418 // multiple protocol associations. e.g. Changing the default browser modifies
419 // the http and https associations.
dcheng4af48582016-04-19 00:29:35420 std::vector<std::unique_ptr<base::win::RegKey>> registry_key_watchers_;
pmonette32a5cfb42016-04-11 22:04:44421
422 base::OneShotTimer timer_;
423
424 // Records the time it takes for the final registry watcher to get signaled.
425 base::TimeTicks start_time_;
426
427 // Weak ptrs are used to bind this class to the callbacks of the timer and the
428 // registry watcher. This makes it possible to self-delete after one of the
429 // callbacks is executed to cancel the remaining ones.
430 base::WeakPtrFactory<OpenSystemSettingsHelper> weak_ptr_factory_;
431
432 DISALLOW_COPY_AND_ASSIGN(OpenSystemSettingsHelper);
433};
434
pmonettec541d942016-06-15 22:08:32435OpenSystemSettingsHelper* OpenSystemSettingsHelper::instance_ = nullptr;
436
pmonettea98315242016-11-23 21:39:18437// Helper class to determine if Chrome is pinned to the taskbar. Hides the
438// complexity of managing the lifetime of a UtilityProcessMojoClient.
439class IsPinnedToTaskbarHelper {
440 public:
441 using ResultCallback = win::IsPinnedToTaskbarCallback;
442 using ErrorCallback = win::ConnectionErrorCallback;
443 static void GetState(const ErrorCallback& error_callback,
444 const ResultCallback& result_callback);
445
446 private:
447 IsPinnedToTaskbarHelper(const ErrorCallback& error_callback,
448 const ResultCallback& result_callback);
449
450 void OnConnectionError();
451 void OnIsPinnedToTaskbarResult(bool succeeded, bool is_pinned_to_taskbar);
452
453 content::UtilityProcessMojoClient<chrome::mojom::ShellHandler> shell_handler_;
454
455 ErrorCallback error_callback_;
456 ResultCallback result_callback_;
457
458 DISALLOW_COPY_AND_ASSIGN(IsPinnedToTaskbarHelper);
459};
460
461// static
462void IsPinnedToTaskbarHelper::GetState(const ErrorCallback& error_callback,
463 const ResultCallback& result_callback) {
464 // Self-deleting when the ShellHandler completes.
465 new IsPinnedToTaskbarHelper(error_callback, result_callback);
pmonette5057ca3b2016-07-04 16:48:40466}
467
pmonettea98315242016-11-23 21:39:18468IsPinnedToTaskbarHelper::IsPinnedToTaskbarHelper(
469 const ErrorCallback& error_callback,
470 const ResultCallback& result_callback)
471 : shell_handler_(
472 l10n_util::GetStringUTF16(IDS_UTILITY_PROCESS_SHELL_HANDLER_NAME)),
473 error_callback_(error_callback),
474 result_callback_(result_callback) {
475 // |shell_handler_| owns the callbacks and is guaranteed to be destroyed
476 // before |this|, therefore making base::Unretained() safe to use.
477 shell_handler_.set_error_callback(base::Bind(
478 &IsPinnedToTaskbarHelper::OnConnectionError, base::Unretained(this)));
479 shell_handler_.set_disable_sandbox();
480 shell_handler_.Start();
481
482 shell_handler_.service()->IsPinnedToTaskbar(
483 base::Bind(&IsPinnedToTaskbarHelper::OnIsPinnedToTaskbarResult,
484 base::Unretained(this)));
485}
486
487void IsPinnedToTaskbarHelper::OnConnectionError() {
488 error_callback_.Run();
489 delete this;
490}
491
492void IsPinnedToTaskbarHelper::OnIsPinnedToTaskbarResult(
pmonette5057ca3b2016-07-04 16:48:40493 bool succeeded,
494 bool is_pinned_to_taskbar) {
pmonettea98315242016-11-23 21:39:18495 result_callback_.Run(succeeded, is_pinned_to_taskbar);
496 delete this;
pmonette5057ca3b2016-07-04 16:48:40497}
498
[email protected]3a3e72c2011-11-29 02:59:38499} // namespace
[email protected]12f520c2010-01-06 18:11:15500
pmonette9fa59e882016-02-10 00:12:19501bool SetAsDefaultBrowser() {
[email protected]650b2d52013-02-10 03:41:45502 base::FilePath chrome_exe;
[email protected]d24c4012009-07-28 01:57:31503 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
504 LOG(ERROR) << "Error getting app exe path";
505 return false;
506 }
507
508 // From UI currently we only allow setting default browser for current user.
[email protected]bf6117c7e2010-12-01 06:00:25509 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
grte76ca2852014-12-05 16:42:10510 if (!ShellUtil::MakeChromeDefault(dist, ShellUtil::CURRENT_USER, chrome_exe,
511 true /* elevate_if_not_admin */)) {
[email protected]d24c4012009-07-28 01:57:31512 LOG(ERROR) << "Chrome could not be set as default browser.";
513 return false;
514 }
515
[email protected]8e96e502010-10-21 20:57:12516 VLOG(1) << "Chrome registered as default browser.";
[email protected]d24c4012009-07-28 01:57:31517 return true;
518}
519
pmonette9fa59e882016-02-10 00:12:19520bool SetAsDefaultProtocolClient(const std::string& protocol) {
[email protected]4468a5b2011-05-26 07:48:02521 if (protocol.empty())
522 return false;
523
[email protected]650b2d52013-02-10 03:41:45524 base::FilePath chrome_exe;
[email protected]4468a5b2011-05-26 07:48:02525 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
526 LOG(ERROR) << "Error getting app exe path";
527 return false;
528 }
529
[email protected]f911df52013-12-24 23:24:23530 base::string16 wprotocol(base::UTF8ToUTF16(protocol));
[email protected]4468a5b2011-05-26 07:48:02531 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
grte76ca2852014-12-05 16:42:10532 if (!ShellUtil::MakeChromeDefaultProtocolClient(dist, chrome_exe,
533 wprotocol)) {
[email protected]4468a5b2011-05-26 07:48:02534 LOG(ERROR) << "Chrome could not be set as default handler for "
535 << protocol << ".";
536 return false;
537 }
538
539 VLOG(1) << "Chrome registered as default handler for " << protocol << ".";
540 return true;
541}
542
pmonette32a5cfb42016-04-11 22:04:44543DefaultWebClientSetPermission GetDefaultWebClientSetPermission() {
pmonette034a03d92015-10-02 21:04:27544 BrowserDistribution* distribution = BrowserDistribution::GetDistribution();
545 if (distribution->GetDefaultBrowserControlPolicy() !=
546 BrowserDistribution::DEFAULT_BROWSER_FULL_CONTROL)
547 return SET_DEFAULT_NOT_ALLOWED;
pmonette034a03d92015-10-02 21:04:27548 if (ShellUtil::CanMakeChromeDefaultUnattended())
549 return SET_DEFAULT_UNATTENDED;
pmonette32a5cfb42016-04-11 22:04:44550 // Windows 8 and 10 both introduced a new way to set the default web client
551 // which require user interaction.
pmonettef89ac7c72015-10-06 03:22:01552 return SET_DEFAULT_INTERACTIVE;
pmonette034a03d92015-10-02 21:04:27553}
554
pmonette9fa59e882016-02-10 00:12:19555bool IsElevationNeededForSettingDefaultProtocolClient() {
pmonette868ca642015-09-02 14:34:02556 return base::win::GetVersion() < base::win::VERSION_WIN8;
557}
558
pmonette9fa59e882016-02-10 00:12:19559base::string16 GetApplicationNameForProtocol(const GURL& url) {
benwellsd0c67aa2016-07-28 02:57:20560 base::string16 application_name;
561 // Windows 8 or above has a new protocol association query.
562 if (base::win::GetVersion() >= base::win::VERSION_WIN8) {
563 application_name = GetAppForProtocolUsingAssocQuery(url);
564 if (!application_name.empty())
565 return application_name;
566 }
567
568 return GetAppForProtocolUsingRegistry(url);
[email protected]42dc9402013-01-30 07:54:20569}
570
pmonette9fa59e882016-02-10 00:12:19571DefaultWebClientState GetDefaultBrowser() {
pmonette034a03d92015-10-02 21:04:27572 return GetDefaultWebClientStateFromShellUtilDefaultState(
573 ShellUtil::GetChromeDefaultState());
574}
575
[email protected]d24c4012009-07-28 01:57:31576// There is no reliable way to say which browser is default on a machine (each
577// browser can have some of the protocols/shortcuts). So we look for only HTTP
578// protocol handler. Even this handler is located at different places in
579// registry on XP and Vista:
580// - HKCR\http\shell\open\command (XP)
581// - HKCU\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\
582// http\UserChoice (Vista)
thakise4a02ff32016-09-20 17:00:42583// This method checks if Firefox is default browser by checking these
[email protected]d24c4012009-07-28 01:57:31584// locations and returns true if Firefox traces are found there. In case of
585// error (or if Firefox is not found)it returns the default value which
586// is false.
pmonette9fa59e882016-02-10 00:12:19587bool IsFirefoxDefaultBrowser() {
[email protected]d24c4012009-07-28 01:57:31588 bool ff_default = false;
[email protected]935aa542010-10-15 01:59:15589 if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
[email protected]0085863a2013-12-06 21:19:03590 base::string16 app_cmd;
[email protected]2d6503982010-10-17 04:41:54591 base::win::RegKey key(HKEY_CURRENT_USER,
592 ShellUtil::kRegVistaUrlPrefs, KEY_READ);
[email protected]e06f4d52011-01-19 07:28:46593 if (key.Valid() && (key.ReadValue(L"Progid", &app_cmd) == ERROR_SUCCESS) &&
[email protected]d24c4012009-07-28 01:57:31594 app_cmd == L"FirefoxURL")
595 ff_default = true;
596 } else {
[email protected]0085863a2013-12-06 21:19:03597 base::string16 key_path(L"http");
[email protected]d24c4012009-07-28 01:57:31598 key_path.append(ShellUtil::kRegShellOpen);
[email protected]2d6503982010-10-17 04:41:54599 base::win::RegKey key(HKEY_CLASSES_ROOT, key_path.c_str(), KEY_READ);
[email protected]0085863a2013-12-06 21:19:03600 base::string16 app_cmd;
[email protected]e06f4d52011-01-19 07:28:46601 if (key.Valid() && (key.ReadValue(L"", &app_cmd) == ERROR_SUCCESS) &&
[email protected]cb1f4ac2014-08-07 16:55:42602 base::string16::npos !=
brettwfce8d192015-08-10 19:07:51603 base::ToLowerASCII(app_cmd).find(L"firefox"))
[email protected]d24c4012009-07-28 01:57:31604 ff_default = true;
605 }
606 return ff_default;
607}
[email protected]12f520c2010-01-06 18:11:15608
pmonette9fa59e882016-02-10 00:12:19609DefaultWebClientState IsDefaultProtocolClient(const std::string& protocol) {
pmonette034a03d92015-10-02 21:04:27610 return GetDefaultWebClientStateFromShellUtilDefaultState(
611 ShellUtil::GetChromeDefaultProtocolClientState(
612 base::UTF8ToUTF16(protocol)));
613}
614
pmonette9e4c1a82016-04-14 18:15:30615namespace win {
616
617bool SetAsDefaultBrowserUsingIntentPicker() {
618 base::FilePath chrome_exe;
619 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
620 NOTREACHED() << "Error getting app exe path";
621 return false;
622 }
623
624 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
625 if (!ShellUtil::ShowMakeChromeDefaultSystemUI(dist, chrome_exe)) {
626 LOG(ERROR) << "Failed to launch the set-default-browser Windows UI.";
627 return false;
628 }
629
630 VLOG(1) << "Set-default-browser Windows UI completed.";
631 return true;
632}
633
634void SetAsDefaultBrowserUsingSystemSettings(
635 const base::Closure& on_finished_callback) {
636 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
637
638 base::FilePath chrome_exe;
639 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
640 NOTREACHED() << "Error getting app exe path";
641 on_finished_callback.Run();
642 return;
643 }
644
grtc291eea2016-05-26 15:38:48645 // Create an action recorder that will open the settings app once it has
646 // initialized.
647 std::unique_ptr<DefaultBrowserActionRecorder> recorder(
648 new DefaultBrowserActionRecorder(base::Bind(
649 base::IgnoreResult(&ShellUtil::ShowMakeChromeDefaultSystemUI),
650 base::Unretained(BrowserDistribution::GetDistribution()),
651 chrome_exe)));
pmonette9e4c1a82016-04-14 18:15:30652
grtc291eea2016-05-26 15:38:48653 // The helper manages its own lifetime. Bind the action recorder
654 // into the finished callback to keep it alive throughout the
655 // interaction.
656 static const wchar_t* const kProtocols[] = {L"http", L"https", nullptr};
657 OpenSystemSettingsHelper::Begin(
658 kProtocols, base::Bind(&OnSettingsAppFinished, base::Passed(&recorder),
659 on_finished_callback));
pmonette9e4c1a82016-04-14 18:15:30660}
661
662bool SetAsDefaultProtocolClientUsingIntentPicker(const std::string& protocol) {
663 base::FilePath chrome_exe;
664 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
665 NOTREACHED() << "Error getting app exe path";
666 return false;
667 }
668
669 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
670 base::string16 wprotocol(base::UTF8ToUTF16(protocol));
671 if (!ShellUtil::ShowMakeChromeDefaultProtocolClientSystemUI(dist, chrome_exe,
672 wprotocol)) {
673 LOG(ERROR) << "Failed to launch the set-default-client Windows UI.";
674 return false;
675 }
676
677 VLOG(1) << "Set-default-client Windows UI completed.";
678 return true;
679}
680
pmonette0c40087a2016-04-21 00:05:16681void SetAsDefaultProtocolClientUsingSystemSettings(
682 const std::string& protocol,
683 const base::Closure& on_finished_callback) {
684 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
685
686 base::FilePath chrome_exe;
687 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
688 NOTREACHED() << "Error getting app exe path";
689 on_finished_callback.Run();
690 return;
691 }
692
693 // The helper manages its own lifetime.
694 base::string16 wprotocol(base::UTF8ToUTF16(protocol));
695 const wchar_t* const kProtocols[] = {wprotocol.c_str(), nullptr};
696 OpenSystemSettingsHelper::Begin(kProtocols, on_finished_callback);
697
698 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
699 ShellUtil::ShowMakeChromeDefaultProtocolClientSystemUI(dist, chrome_exe,
700 wprotocol);
701}
702
pmonette9fa59e882016-02-10 00:12:19703base::string16 GetAppModelIdForProfile(const base::string16& app_name,
704 const base::FilePath& profile_path) {
[email protected]d2065e062013-12-12 23:49:52705 std::vector<base::string16> components;
[email protected]a0448002012-06-19 04:32:10706 components.push_back(app_name);
[email protected]0085863a2013-12-06 21:19:03707 const base::string16 profile_id(GetProfileIdFromPath(profile_path));
[email protected]a0448002012-06-19 04:32:10708 if (!profile_id.empty())
709 components.push_back(profile_id);
710 return ShellUtil::BuildAppModelId(components);
[email protected]12f520c2010-01-06 18:11:15711}
712
pmonette9fa59e882016-02-10 00:12:19713base::string16 GetChromiumModelIdForProfile(
[email protected]650b2d52013-02-10 03:41:45714 const base::FilePath& profile_path) {
[email protected]a0448002012-06-19 04:32:10715 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
[email protected]650b2d52013-02-10 03:41:45716 base::FilePath chrome_exe;
[email protected]a0448002012-06-19 04:32:10717 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
718 NOTREACHED();
719 return dist->GetBaseAppId();
720 }
721 return GetAppModelIdForProfile(
grte76ca2852014-12-05 16:42:10722 ShellUtil::GetBrowserModelId(dist,
723 InstallUtil::IsPerUserInstall(chrome_exe)),
[email protected]786799692012-09-26 14:16:48724 profile_path);
[email protected]12f520c2010-01-06 18:11:15725}
[email protected]c9bb06f42010-01-13 23:53:48726
pmonette9fa59e882016-02-10 00:12:19727void MigrateTaskbarPins() {
[email protected]935aa542010-10-15 01:59:15728 if (base::win::GetVersion() < base::win::VERSION_WIN7)
[email protected]c9bb06f42010-01-13 23:53:48729 return;
730
[email protected]81fcf952012-11-26 22:25:14731 // This needs to happen eventually (e.g. so that the appid is fixed and the
732 // run-time Chrome icon is merged with the taskbar shortcut), but this is not
733 // urgent and shouldn't delay Chrome startup.
gab88257b62016-01-15 03:19:19734 static const int64_t kMigrateTaskbarPinsDelaySeconds = 15;
[email protected]81fcf952012-11-26 22:25:14735 BrowserThread::PostDelayedTask(
[email protected]3a3e72c2011-11-29 02:59:38736 BrowserThread::FILE, FROM_HERE,
gab88257b62016-01-15 03:19:19737 base::Bind(&MigrateTaskbarPinsCallback),
738 base::TimeDelta::FromSeconds(kMigrateTaskbarPinsDelaySeconds));
[email protected]c9bb06f42010-01-13 23:53:48739}
[email protected]43903b82012-06-01 05:26:23740
pmonettea98315242016-11-23 21:39:18741void GetIsPinnedToTaskbarState(
742 const ConnectionErrorCallback& on_error_callback,
743 const IsPinnedToTaskbarCallback& result_callback) {
744 IsPinnedToTaskbarHelper::GetState(on_error_callback, result_callback);
pmonette5057ca3b2016-07-04 16:48:40745}
746
pmonette9fa59e882016-02-10 00:12:19747int MigrateShortcutsInPathInternal(const base::FilePath& chrome_exe,
748 const base::FilePath& path) {
[email protected]8ea8f1ef2013-01-06 18:39:03749 DCHECK(base::win::GetVersion() >= base::win::VERSION_WIN7);
750
751 // Enumerate all pinned shortcuts in the given path directly.
[email protected]25a4c1c2013-06-08 04:53:36752 base::FileEnumerator shortcuts_enum(
[email protected]8ea8f1ef2013-01-06 18:39:03753 path, false, // not recursive
[email protected]25a4c1c2013-06-08 04:53:36754 base::FileEnumerator::FILES, FILE_PATH_LITERAL("*.lnk"));
[email protected]8ea8f1ef2013-01-06 18:39:03755
grte76ca2852014-12-05 16:42:10756 bool is_per_user_install = InstallUtil::IsPerUserInstall(chrome_exe);
[email protected]8ea8f1ef2013-01-06 18:39:03757
758 int shortcuts_migrated = 0;
[email protected]650b2d52013-02-10 03:41:45759 base::FilePath target_path;
[email protected]0085863a2013-12-06 21:19:03760 base::string16 arguments;
[email protected]07983302013-01-21 19:41:44761 base::win::ScopedPropVariant propvariant;
[email protected]650b2d52013-02-10 03:41:45762 for (base::FilePath shortcut = shortcuts_enum.Next(); !shortcut.empty();
[email protected]8ea8f1ef2013-01-06 18:39:03763 shortcut = shortcuts_enum.Next()) {
764 // TODO(gab): Use ProgramCompare instead of comparing FilePaths below once
765 // it is fixed to work with FilePaths with spaces.
766 if (!base::win::ResolveShortcut(shortcut, &target_path, &arguments) ||
767 chrome_exe != target_path) {
768 continue;
769 }
avi556c05022014-12-22 23:31:43770 base::CommandLine command_line(
771 base::CommandLine::FromString(base::StringPrintf(
772 L"\"%ls\" %ls", target_path.value().c_str(), arguments.c_str())));
[email protected]8ea8f1ef2013-01-06 18:39:03773
774 // Get the expected AppId for this Chrome shortcut.
[email protected]0085863a2013-12-06 21:19:03775 base::string16 expected_app_id(
[email protected]8ea8f1ef2013-01-06 18:39:03776 GetExpectedAppId(command_line, is_per_user_install));
777 if (expected_app_id.empty())
778 continue;
779
780 // Load the shortcut.
781 base::win::ScopedComPtr<IShellLink> shell_link;
782 base::win::ScopedComPtr<IPersistFile> persist_file;
783 if (FAILED(shell_link.CreateInstance(CLSID_ShellLink, NULL,
784 CLSCTX_INPROC_SERVER)) ||
dcheng81762e02014-11-21 21:22:28785 FAILED(persist_file.QueryFrom(shell_link.get())) ||
[email protected]8ea8f1ef2013-01-06 18:39:03786 FAILED(persist_file->Load(shortcut.value().c_str(), STGM_READ))) {
787 DLOG(WARNING) << "Failed loading shortcut at " << shortcut.value();
788 continue;
789 }
790
791 // Any properties that need to be updated on the shortcut will be stored in
792 // |updated_properties|.
793 base::win::ShortcutProperties updated_properties;
794
795 // Validate the existing app id for the shortcut.
796 base::win::ScopedComPtr<IPropertyStore> property_store;
[email protected]07983302013-01-21 19:41:44797 propvariant.Reset();
dcheng81762e02014-11-21 21:22:28798 if (FAILED(property_store.QueryFrom(shell_link.get())) ||
799 property_store->GetValue(PKEY_AppUserModel_ID, propvariant.Receive()) !=
800 S_OK) {
[email protected]8ea8f1ef2013-01-06 18:39:03801 // When in doubt, prefer not updating the shortcut.
802 NOTREACHED();
803 continue;
[email protected]8ea8f1ef2013-01-06 18:39:03804 } else {
[email protected]07983302013-01-21 19:41:44805 switch (propvariant.get().vt) {
806 case VT_EMPTY:
807 // If there is no app_id set, set our app_id if one is expected.
808 if (!expected_app_id.empty())
809 updated_properties.set_app_id(expected_app_id);
810 break;
811 case VT_LPWSTR:
[email protected]0085863a2013-12-06 21:19:03812 if (expected_app_id != base::string16(propvariant.get().pwszVal))
[email protected]07983302013-01-21 19:41:44813 updated_properties.set_app_id(expected_app_id);
814 break;
815 default:
816 NOTREACHED();
817 continue;
[email protected]8ea8f1ef2013-01-06 18:39:03818 }
819 }
820
gab88257b62016-01-15 03:19:19821 // Clear dual_mode property from any shortcuts that previously had it (it
822 // was only ever installed on shortcuts with the
823 // |default_chromium_model_id|).
[email protected]b39e32e2013-04-24 08:55:54824 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
[email protected]0085863a2013-12-06 21:19:03825 base::string16 default_chromium_model_id(
[email protected]b39e32e2013-04-24 08:55:54826 ShellUtil::GetBrowserModelId(dist, is_per_user_install));
gab88257b62016-01-15 03:19:19827 if (expected_app_id == default_chromium_model_id) {
[email protected]07983302013-01-21 19:41:44828 propvariant.Reset();
[email protected]8ea8f1ef2013-01-06 18:39:03829 if (property_store->GetValue(PKEY_AppUserModel_IsDualMode,
[email protected]07983302013-01-21 19:41:44830 propvariant.Receive()) != S_OK) {
[email protected]8ea8f1ef2013-01-06 18:39:03831 // When in doubt, prefer to not update the shortcut.
832 NOTREACHED();
833 continue;
gab38d242d4d2016-01-14 20:38:12834 }
835 if (propvariant.get().vt == VT_BOOL &&
836 !!propvariant.get().boolVal) {
837 updated_properties.set_dual_mode(false);
[email protected]8ea8f1ef2013-01-06 18:39:03838 }
839 }
840
841 persist_file.Release();
842 shell_link.Release();
843
844 // Update the shortcut if some of its properties need to be updated.
845 if (updated_properties.options &&
846 base::win::CreateOrUpdateShortcutLink(
847 shortcut, updated_properties,
848 base::win::SHORTCUT_UPDATE_EXISTING)) {
849 ++shortcuts_migrated;
850 }
851 }
852 return shortcuts_migrated;
853}
854
pmonette9fa59e882016-02-10 00:12:19855base::FilePath GetStartMenuShortcut(const base::FilePath& chrome_exe) {
[email protected]3f69d6e612012-08-03 18:52:27856 static const int kFolderIds[] = {
857 base::DIR_COMMON_START_MENU,
858 base::DIR_START_MENU,
859 };
860 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
taptedd7ef82ae2016-05-13 01:10:19861 const base::string16 shortcut_name(dist->GetShortcutName() +
862 installer::kLnkExt);
grt2be0cd082015-11-12 15:32:21863 base::FilePath programs_folder;
[email protected]650b2d52013-02-10 03:41:45864 base::FilePath shortcut;
[email protected]3f69d6e612012-08-03 18:52:27865
866 // Check both the common and the per-user Start Menu folders for system-level
867 // installs.
grte76ca2852014-12-05 16:42:10868 size_t folder = InstallUtil::IsPerUserInstall(chrome_exe) ? 1 : 0;
[email protected]3f69d6e612012-08-03 18:52:27869 for (; folder < arraysize(kFolderIds); ++folder) {
grt2be0cd082015-11-12 15:32:21870 if (!PathService::Get(kFolderIds[folder], &programs_folder)) {
[email protected]3f69d6e612012-08-03 18:52:27871 NOTREACHED();
872 continue;
873 }
874
grt2be0cd082015-11-12 15:32:21875 shortcut = programs_folder.Append(shortcut_name);
876 if (base::PathExists(shortcut))
877 return shortcut;
[email protected]3f69d6e612012-08-03 18:52:27878 }
879
[email protected]650b2d52013-02-10 03:41:45880 return base::FilePath();
[email protected]3f69d6e612012-08-03 18:52:27881}
pmonettef89ac7c72015-10-06 03:22:01882
pmonette32a5cfb42016-04-11 22:04:44883} // namespace win
884
pmonette9fa59e882016-02-10 00:12:19885} // namespace shell_integration