1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/shell_integration_win.h"
11 #include <propkey.h> // Needs to come after shobjidl.h.
14 #include <wrl/client.h>
20 #include "base/command_line.h"
21 #include "base/files/file_enumerator.h"
22 #include "base/files/file_util.h"
23 #include "base/functional/bind.h"
24 #include "base/functional/callback.h"
25 #include "base/memory/weak_ptr.h"
26 #include "base/metrics/user_metrics.h"
27 #include "base/metrics/user_metrics_action.h"
28 #include "base/path_service.h"
29 #include "base/strings/strcat_win.h"
30 #include "base/strings/string_util.h"
31 #include "base/strings/utf_string_conversions.h"
32 #include "base/task/thread_pool.h"
33 #include "base/threading/platform_thread.h"
34 #include "base/threading/scoped_blocking_call.h"
35 #include "base/time/time.h"
36 #include "base/timer/timer.h"
37 #include "base/win/registry.h"
38 #include "base/win/scoped_propvariant.h"
39 #include "base/win/shlwapi.h"
40 #include "base/win/shortcut.h"
41 #include "chrome/browser/policy/policy_path_parser.h"
42 #include "chrome/browser/shell_integration.h"
43 #include "chrome/browser/web_applications/os_integration/web_app_shortcut_win.h"
44 #include "chrome/browser/web_applications/web_app_helpers.h"
45 #include "chrome/browser/win/settings_app_monitor.h"
46 #include "chrome/browser/win/util_win_service.h"
47 #include "chrome/common/chrome_constants.h"
48 #include "chrome/common/chrome_paths_internal.h"
49 #include "chrome/common/chrome_switches.h"
50 #include "chrome/install_static/install_util.h"
51 #include "chrome/installer/util/install_util.h"
52 #include "chrome/installer/util/shell_util.h"
53 #include "chrome/services/util_win/public/mojom/util_win.mojom.h"
54 #include "components/variations/variations_associated_data.h"
55 #include "mojo/public/cpp/bindings/remote.h"
57 namespace shell_integration {
61 // Helper function for GetAppId to generates profile id
62 // from profile path. "profile_id" is composed of sanitized basenames of
63 // user data dir and profile dir joined by a ".".
64 std::wstring GetProfileIdFromPath(const base::FilePath& profile_path) {
65 // Return empty string if profile_path is empty
66 if (profile_path.empty())
67 return std::wstring();
69 base::FilePath default_user_data_dir;
70 // Return empty string if profile_path is in default user data
71 // dir and is the default profile.
72 if (chrome::GetDefaultUserDataDirectory(&default_user_data_dir) &&
73 profile_path.DirName() == default_user_data_dir &&
74 profile_path.BaseName().value() ==
75 base::ASCIIToWide(chrome::kInitialProfile)) {
76 return std::wstring();
79 // Get joined basenames of user data dir and profile.
80 std::wstring basenames = profile_path.DirName().BaseName().value() + L"." +
81 profile_path.BaseName().value();
83 std::wstring profile_id;
84 profile_id.reserve(basenames.size());
86 // Generate profile_id from sanitized basenames.
87 for (size_t i = 0; i < basenames.length(); ++i) {
88 if (base::IsAsciiAlpha(basenames[i]) ||
89 base::IsAsciiDigit(basenames[i]) ||
91 profile_id += basenames[i];
97 std::wstring GetAppUserModelIdImpl(const std::wstring& prefix,
98 const std::wstring& app_name,
99 const base::FilePath& profile_path) {
100 std::vector<std::wstring> components;
102 components.push_back(prefix);
103 components.push_back(app_name);
104 const std::wstring profile_id(GetProfileIdFromPath(profile_path));
105 if (!profile_id.empty())
106 components.push_back(profile_id);
107 return ShellUtil::BuildAppUserModelId(components);
110 // Gets expected app id for given Chrome (based on |command_line| and
111 // |is_per_user_install|).
112 std::wstring GetExpectedAppId(const base::CommandLine& command_line,
113 bool is_per_user_install) {
114 base::FilePath user_data_dir;
115 if (command_line.HasSwitch(switches::kUserDataDir))
116 user_data_dir = command_line.GetSwitchValuePath(switches::kUserDataDir);
117 // Adjust with any policy that overrides any other way to set the path.
118 policy::path_parser::CheckUserDataDirPolicy(&user_data_dir);
119 if (user_data_dir.empty())
120 chrome::GetDefaultUserDataDirectory(&user_data_dir);
121 DCHECK(!user_data_dir.empty());
123 base::FilePath profile_subdir;
124 if (command_line.HasSwitch(switches::kProfileDirectory)) {
126 command_line.GetSwitchValuePath(switches::kProfileDirectory);
128 profile_subdir = base::FilePath(base::ASCIIToWide(chrome::kInitialProfile));
130 DCHECK(!profile_subdir.empty());
132 base::FilePath profile_path = user_data_dir.Append(profile_subdir);
134 std::wstring app_name;
135 if (command_line.HasSwitch(switches::kApp)) {
136 app_name = base::UTF8ToWide(web_app::GenerateApplicationNameFromURL(
137 GURL(command_line.GetSwitchValueASCII(switches::kApp))));
138 prefix = install_static::GetBaseAppId();
139 } else if (command_line.HasSwitch(switches::kAppId)) {
140 app_name = base::UTF8ToWide(web_app::GenerateApplicationNameFromAppId(
141 command_line.GetSwitchValueASCII(switches::kAppId)));
142 prefix = install_static::GetBaseAppId();
144 app_name = ShellUtil::GetBrowserModelId(is_per_user_install);
146 DCHECK(!app_name.empty());
148 return GetAppUserModelIdImpl(prefix, app_name, profile_path);
151 // Windows treats a given scheme as an Internet scheme only if its registry
152 // entry has a "URL Protocol" key. Check this, otherwise we allow ProgIDs to be
153 // used as custom scheme which leads to security bugs.
154 bool IsValidCustomScheme(const std::wstring& scheme) {
157 base::win::RegKey cmd_key(HKEY_CLASSES_ROOT, scheme.c_str(), KEY_QUERY_VALUE);
158 return cmd_key.Valid() && cmd_key.HasValue(L"URL Protocol");
161 // Windows 8 introduced a new scheme->executable binding system which cannot
162 // be retrieved in the HKCR registry subkey method implemented below. We call
163 // AssocQueryString with the new Win8-only flag ASSOCF_IS_PROTOCOL instead.
164 std::u16string GetAppForSchemeUsingAssocQuery(const GURL& url) {
165 const std::wstring url_scheme = base::ASCIIToWide(url.scheme());
166 if (!IsValidCustomScheme(url_scheme)) {
167 return std::u16string();
170 // Query AssocQueryString for a human-readable description of the program
171 // that will be invoked given the provided URL spec. This is used only to
172 // populate the external scheme dialog box the user sees when invoking
173 // an unknown external scheme.
174 wchar_t out_buffer[1024];
175 DWORD buffer_size = std::size(out_buffer);
177 AssocQueryString(ASSOCF_IS_PROTOCOL, ASSOCSTR_FRIENDLYAPPNAME,
178 url_scheme.c_str(), NULL, out_buffer, &buffer_size);
180 DLOG(WARNING) << "AssocQueryString failed!";
181 return std::u16string();
183 return base::AsString16(std::wstring(out_buffer));
186 std::u16string GetAppForSchemeUsingRegistry(const GURL& url) {
187 const std::wstring url_scheme = base::ASCIIToWide(url.scheme());
188 if (!IsValidCustomScheme(url_scheme)) {
189 return std::u16string();
192 // First, try and extract the application's display name.
193 std::wstring command_to_launch;
194 base::win::RegKey cmd_key_name(HKEY_CLASSES_ROOT, url_scheme.c_str(),
196 if (cmd_key_name.ReadValue(NULL, &command_to_launch) == ERROR_SUCCESS &&
197 !command_to_launch.empty()) {
198 return base::AsString16(command_to_launch);
201 // Otherwise, parse the command line in the registry, and return the basename
202 // of the program path if it exists.
203 const std::wstring cmd_key_path = url_scheme + L"\\shell\\open\\command";
204 base::win::RegKey cmd_key_exe(HKEY_CLASSES_ROOT, cmd_key_path.c_str(),
206 if (cmd_key_exe.ReadValue(NULL, &command_to_launch) == ERROR_SUCCESS) {
207 base::CommandLine command_line(
208 base::CommandLine::FromString(command_to_launch));
209 return command_line.GetProgram().BaseName().AsUTF16Unsafe();
212 return std::u16string();
215 DefaultWebClientState GetDefaultWebClientStateFromShellUtilDefaultState(
216 ShellUtil::DefaultState default_state) {
217 switch (default_state) {
218 case ShellUtil::UNKNOWN_DEFAULT:
219 return DefaultWebClientState::UNKNOWN_DEFAULT;
220 case ShellUtil::NOT_DEFAULT:
221 return DefaultWebClientState::NOT_DEFAULT;
222 case ShellUtil::IS_DEFAULT:
223 return DefaultWebClientState::IS_DEFAULT;
224 case ShellUtil::OTHER_MODE_IS_DEFAULT:
225 return DefaultWebClientState::OTHER_MODE_IS_DEFAULT;
228 return DefaultWebClientState::UNKNOWN_DEFAULT;
231 // A recorder of user actions in the Windows Settings app.
232 class DefaultBrowserActionRecorder : public SettingsAppMonitor::Delegate {
234 // Creates the recorder and the monitor that drives it. |continuation| will be
235 // run once the monitor's initialization completes (regardless of success or
237 explicit DefaultBrowserActionRecorder(base::OnceClosure continuation)
238 : continuation_(std::move(continuation)), settings_app_monitor_(this) {}
240 DefaultBrowserActionRecorder(const DefaultBrowserActionRecorder&) = delete;
241 DefaultBrowserActionRecorder& operator=(const DefaultBrowserActionRecorder&) =
245 // win::SettingsAppMonitor::Delegate:
246 void OnInitialized(HRESULT result) override {
247 // UMA indicates that this succeeds > 99.98% of the time.
248 if (SUCCEEDED(result)) {
250 base::UserMetricsAction("SettingsAppMonitor.Initialized"));
252 std::move(continuation_).Run();
255 void OnAppFocused() override {
257 base::UserMetricsAction("SettingsAppMonitor.AppFocused"));
260 void OnChooserInvoked() override {
262 base::UserMetricsAction("SettingsAppMonitor.ChooserInvoked"));
265 void OnBrowserChosen(const std::wstring& browser_name) override {
266 if (browser_name == InstallUtil::GetDisplayName()) {
268 base::UserMetricsAction("SettingsAppMonitor.ChromeBrowserChosen"));
271 base::UserMetricsAction("SettingsAppMonitor.OtherBrowserChosen"));
275 void OnPromoFocused() override {
277 base::UserMetricsAction("SettingsAppMonitor.PromoFocused"));
280 void OnPromoChoiceMade(bool accept_promo) override {
283 base::UserMetricsAction("SettingsAppMonitor.CheckItOut"));
286 base::UserMetricsAction("SettingsAppMonitor.SwitchAnyway"));
290 // A closure to be run once initialization completes.
291 base::OnceClosure continuation_;
293 // Monitors user interaction with the Windows Settings app for the sake of
294 // reporting user actions.
295 SettingsAppMonitor settings_app_monitor_;
298 // A function bound up in a callback with a DefaultBrowserActionRecorder and
299 // a closure to keep the former alive until the time comes to run the latter.
300 void OnSettingsAppFinished(
301 std::unique_ptr<DefaultBrowserActionRecorder> recorder,
302 base::OnceClosure on_finished_callback) {
304 std::move(on_finished_callback).Run();
307 // There is no way to make sure the user is done with the system settings, but a
308 // signal that the interaction is finished is needed for UMA. A timer of 2
309 // minutes is used as a substitute. The registry keys for the scheme
310 // association with an app are also monitored to signal the end of the
311 // interaction early when it is clear that the user made a choice (e.g. http
312 // and https for default browser).
314 // This helper class manages both the timer and the registry watchers and makes
315 // sure the callback for the end of the settings interaction is only run once.
316 // This class also manages its own lifetime.
317 class OpenSystemSettingsHelper {
319 OpenSystemSettingsHelper(const OpenSystemSettingsHelper&) = delete;
320 OpenSystemSettingsHelper& operator=(const OpenSystemSettingsHelper&) = delete;
322 // Begin the monitoring and will call |on_finished_callback| when done.
323 // Takes in a null-terminated array of |schemes| whose registry keys must be
324 // watched. The array must contain at least one element.
325 static void Begin(const wchar_t* const schemes[],
326 base::OnceClosure on_finished_callback) {
329 new OpenSystemSettingsHelper(schemes, std::move(on_finished_callback));
333 OpenSystemSettingsHelper(const wchar_t* const schemes[],
334 base::OnceClosure on_finished_callback)
335 : on_finished_callback_(std::move(on_finished_callback)) {
336 for (const wchar_t* const* scan = &schemes[0]; *scan != nullptr; ++scan) {
337 AddRegistryKeyWatcher(base::StrCat({L"SOFTWARE\\Microsoft\\Windows\\Shell"
338 L"\\Associations\\UrlAssociations\\",
339 *scan, L"\\UserChoice"})
342 // Only the watchers that were succesfully initialized are counted.
343 registry_watcher_count_ = registry_key_watchers_.size();
345 timer_.Start(FROM_HERE, base::Minutes(2),
346 base::BindOnce(&OpenSystemSettingsHelper::ConcludeInteraction,
347 weak_ptr_factory_.GetWeakPtr()));
350 ~OpenSystemSettingsHelper() {
351 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
354 // Called when a change is detected on one of the registry keys being watched.
355 // Note: All types of modification to the registry key will trigger this
356 // function even if the value change is the only one that matters. This
357 // is good enough for now.
358 void OnRegistryKeyChanged() {
359 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
360 // Make sure all the registry watchers have fired.
361 if (--registry_watcher_count_ == 0) {
362 ConcludeInteraction();
366 // Ends the monitoring with the system settings. Will call
367 // |on_finished_callback_| and then dispose of this class instance to make
368 // sure the callback won't get called subsequently.
369 void ConcludeInteraction() {
370 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
372 std::move(on_finished_callback_).Run();
377 // Helper function to create a registry watcher for a given |key_path|. Do
378 // nothing on initialization failure.
379 void AddRegistryKeyWatcher(const wchar_t* key_path) {
380 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
382 auto reg_key = std::make_unique<base::win::RegKey>(HKEY_CURRENT_USER,
383 key_path, KEY_NOTIFY);
385 if (reg_key->Valid() && reg_key->StartWatching(base::BindOnce(
386 &OpenSystemSettingsHelper::OnRegistryKeyChanged,
387 weak_ptr_factory_.GetWeakPtr()))) {
388 registry_key_watchers_.push_back(std::move(reg_key));
392 // Used to make sure only one instance is alive at the same time.
393 static OpenSystemSettingsHelper* instance_;
395 // The function to call when the interaction with the system settings is
397 base::OnceClosure on_finished_callback_;
399 // The number of time the registry key watchers must fire.
400 int registry_watcher_count_ = 0;
402 // There can be multiple registry key watchers as some settings modify
403 // multiple scheme associations. e.g. Changing the default browser modifies
404 // the http and https associations.
405 std::vector<std::unique_ptr<base::win::RegKey>> registry_key_watchers_;
407 base::OneShotTimer timer_;
409 SEQUENCE_CHECKER(sequence_checker_);
411 // Weak ptrs are used to bind this class to the callbacks of the timer and the
412 // registry watcher. This makes it possible to self-delete after one of the
413 // callbacks is executed to cancel the remaining ones.
414 base::WeakPtrFactory<OpenSystemSettingsHelper> weak_ptr_factory_{this};
417 OpenSystemSettingsHelper* OpenSystemSettingsHelper::instance_ = nullptr;
419 // Helper class to determine if Chrome is pinned to the taskbar. Hides the
420 // complexity of managing the lifetime of the connection to the ChromeWinUtil
422 class IsPinnedToTaskbarHelper {
424 using ResultCallback = win::IsPinnedToTaskbarCallback;
425 using ErrorCallback = win::ConnectionErrorCallback;
427 IsPinnedToTaskbarHelper(const IsPinnedToTaskbarHelper&) = delete;
428 IsPinnedToTaskbarHelper& operator=(const IsPinnedToTaskbarHelper&) = delete;
430 static void GetState(ResultCallback result_callback);
433 explicit IsPinnedToTaskbarHelper(ResultCallback result_callback);
435 void OnConnectionError();
436 void OnIsPinnedToTaskbarResult(bool succeeded, bool is_pinned_to_taskbar);
438 mojo::Remote<chrome::mojom::UtilWin> remote_util_win_;
440 ResultCallback result_callback_;
442 SEQUENCE_CHECKER(sequence_checker_);
446 void IsPinnedToTaskbarHelper::GetState(ResultCallback result_callback) {
447 // Self-deleting when the ShellHandler completes.
448 new IsPinnedToTaskbarHelper(std::move(result_callback));
451 IsPinnedToTaskbarHelper::IsPinnedToTaskbarHelper(ResultCallback result_callback)
452 : remote_util_win_(LaunchUtilWinServiceInstance()),
453 result_callback_(std::move(result_callback)) {
454 DCHECK(result_callback_);
456 // |remote_util_win_| owns the callbacks and is guaranteed to be destroyed
457 // before |this|, therefore making base::Unretained() safe to use.
458 remote_util_win_.set_disconnect_handler(base::BindOnce(
459 &IsPinnedToTaskbarHelper::OnConnectionError, base::Unretained(this)));
460 remote_util_win_->IsPinnedToTaskbar(
461 base::BindOnce(&IsPinnedToTaskbarHelper::OnIsPinnedToTaskbarResult,
462 base::Unretained(this)));
465 void IsPinnedToTaskbarHelper::OnConnectionError() {
466 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
470 void IsPinnedToTaskbarHelper::OnIsPinnedToTaskbarResult(
472 bool is_pinned_to_taskbar) {
473 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
475 std::move(result_callback_).Run(succeeded, is_pinned_to_taskbar);
479 // Helper class to unpin shortcuts from the taskbar. Hides the complexity of
480 // managing the lifetime of the connection to the Windows utility service.
481 class UnpinShortcutsHelper {
483 UnpinShortcutsHelper(const UnpinShortcutsHelper&) = delete;
484 UnpinShortcutsHelper& operator=(const UnpinShortcutsHelper&) = delete;
486 static void DoUnpin(const std::vector<base::FilePath>& shortcuts,
487 base::OnceClosure completion_callback);
490 UnpinShortcutsHelper(const std::vector<base::FilePath>& shortcuts,
491 base::OnceClosure completion_callback);
493 void OnConnectionError();
494 void OnUnpinShortcutResult();
496 mojo::Remote<chrome::mojom::UtilWin> remote_util_win_;
498 base::OnceClosure completion_callback_;
500 SEQUENCE_CHECKER(sequence_checker_);
504 void UnpinShortcutsHelper::DoUnpin(const std::vector<base::FilePath>& shortcuts,
505 base::OnceClosure completion_callback) {
506 // Self-deleting when the ShellHandler completes.
507 new UnpinShortcutsHelper(shortcuts, std::move(completion_callback));
510 UnpinShortcutsHelper::UnpinShortcutsHelper(
511 const std::vector<base::FilePath>& shortcuts,
512 base::OnceClosure completion_callback)
513 : remote_util_win_(LaunchUtilWinServiceInstance()),
514 completion_callback_(std::move(completion_callback)) {
515 DCHECK(completion_callback_);
517 // |remote_util_win_| owns the callbacks and is guaranteed to be destroyed
518 // before |this|, therefore making base::Unretained() safe to use.
519 remote_util_win_.set_disconnect_handler(base::BindOnce(
520 &UnpinShortcutsHelper::OnConnectionError, base::Unretained(this)));
521 remote_util_win_->UnpinShortcuts(
522 shortcuts, base::BindOnce(&UnpinShortcutsHelper::OnUnpinShortcutResult,
523 base::Unretained(this)));
526 void UnpinShortcutsHelper::OnConnectionError() {
527 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
528 std::move(completion_callback_).Run();
532 void UnpinShortcutsHelper::OnUnpinShortcutResult() {
533 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
535 std::move(completion_callback_).Run();
539 // Helper class to create or update desktop shortcuts Hides the complexity of
540 // managing the lifetime of the connection to the Windows utility service.
541 class CreateOrUpdateShortcutsHelper {
543 CreateOrUpdateShortcutsHelper(const CreateOrUpdateShortcutsHelper&) = delete;
544 CreateOrUpdateShortcutsHelper& operator=(
545 const CreateOrUpdateShortcutsHelper&) = delete;
547 static void DoCreateOrUpdateShortcuts(
548 const std::vector<base::FilePath>& shortcuts,
549 const std::vector<base::win::ShortcutProperties>& properties,
550 base::win::ShortcutOperation operation,
551 win::CreateOrUpdateShortcutsResultCallback);
554 CreateOrUpdateShortcutsHelper(
555 const std::vector<base::FilePath>& shortcuts,
556 const std::vector<base::win::ShortcutProperties>& properties,
557 base::win::ShortcutOperation operation,
558 win::CreateOrUpdateShortcutsResultCallback completion_callback);
560 void OnConnectionError();
561 void OnCreateOrUpdateShortcutResult(bool succeeded);
563 mojo::Remote<chrome::mojom::UtilWin> remote_util_win_;
565 win::CreateOrUpdateShortcutsResultCallback completion_callback_;
567 SEQUENCE_CHECKER(sequence_checker_);
571 void CreateOrUpdateShortcutsHelper::DoCreateOrUpdateShortcuts(
572 const std::vector<base::FilePath>& shortcuts,
573 const std::vector<base::win::ShortcutProperties>& properties,
574 base::win::ShortcutOperation operation,
575 win::CreateOrUpdateShortcutsResultCallback completion_callback) {
576 // Self-deleting when the ShellHandler completes.
577 new CreateOrUpdateShortcutsHelper(shortcuts, properties, operation,
578 std::move(completion_callback));
581 CreateOrUpdateShortcutsHelper::CreateOrUpdateShortcutsHelper(
582 const std::vector<base::FilePath>& shortcuts,
583 const std::vector<base::win::ShortcutProperties>& properties,
584 base::win::ShortcutOperation operation,
585 win::CreateOrUpdateShortcutsResultCallback completion_callback)
586 : remote_util_win_(LaunchUtilWinServiceInstance()),
587 completion_callback_(std::move(completion_callback)) {
588 DCHECK(completion_callback_);
590 // |remote_util_win_| owns the callbacks and is guaranteed to be destroyed
591 // before |this|, therefore making base::Unretained() safe to use.
592 remote_util_win_.set_disconnect_handler(
593 base::BindOnce(&CreateOrUpdateShortcutsHelper::OnConnectionError,
594 base::Unretained(this)));
595 remote_util_win_->CreateOrUpdateShortcuts(
596 shortcuts, properties, operation,
598 &CreateOrUpdateShortcutsHelper::OnCreateOrUpdateShortcutResult,
599 base::Unretained(this)));
602 void CreateOrUpdateShortcutsHelper::OnConnectionError() {
603 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
604 std::move(completion_callback_).Run(false);
608 void CreateOrUpdateShortcutsHelper::OnCreateOrUpdateShortcutResult(
610 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
611 std::move(completion_callback_).Run(succeeded);
615 void MigrateChromeAndChromeProxyShortcuts(
616 const base::FilePath& chrome_exe,
617 const base::FilePath& chrome_proxy_path,
618 const base::FilePath& shortcut_path) {
619 win::MigrateShortcutsInPathInternal(chrome_exe, shortcut_path);
621 // Migrate any pinned PWA shortcuts in taskbar directory.
622 win::MigrateShortcutsInPathInternal(chrome_proxy_path, shortcut_path);
625 std::wstring GetHttpSchemeUserChoiceProgId() {
626 std::wstring prog_id;
627 base::win::RegKey key(HKEY_CURRENT_USER, ShellUtil::kRegVistaUrlPrefs,
630 key.ReadValue(L"ProgId", &prog_id);
636 bool SetAsDefaultBrowser() {
637 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
638 base::BlockingType::MAY_BLOCK);
640 base::FilePath chrome_exe;
641 if (!base::PathService::Get(base::FILE_EXE, &chrome_exe)) {
642 LOG(ERROR) << "Error getting app exe path";
646 // From UI currently we only allow setting default browser for current user.
647 if (!ShellUtil::MakeChromeDefault(ShellUtil::CURRENT_USER, chrome_exe,
648 true /* elevate_if_not_admin */)) {
649 LOG(ERROR) << "Chrome could not be set as default browser.";
653 VLOG(1) << "Chrome registered as default browser.";
657 bool SetAsDefaultClientForScheme(const std::string& scheme) {
658 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
659 base::BlockingType::MAY_BLOCK);
661 if (scheme.empty()) {
665 base::FilePath chrome_exe;
666 if (!base::PathService::Get(base::FILE_EXE, &chrome_exe)) {
667 LOG(ERROR) << "Error getting app exe path";
671 std::wstring wscheme(base::UTF8ToWide(scheme));
672 if (!ShellUtil::MakeChromeDefaultProtocolClient(chrome_exe, wscheme)) {
673 LOG(ERROR) << "Chrome could not be set as default handler for " << scheme
678 VLOG(1) << "Chrome registered as default handler for " << scheme << ".";
682 std::u16string GetApplicationNameForScheme(const GURL& url) {
683 std::u16string application_name = GetAppForSchemeUsingAssocQuery(url);
684 if (!application_name.empty()) {
685 return application_name;
688 return GetAppForSchemeUsingRegistry(url);
691 DefaultWebClientState GetDefaultBrowser() {
692 return GetDefaultWebClientStateFromShellUtilDefaultState(
693 ShellUtil::GetChromeDefaultState());
696 // This method checks if Firefox is default browser by checking for the default
697 // HTTP scheme handler. Returns false in case of error or if Firefox is not
698 // the user's default http scheme client.
699 bool IsFirefoxDefaultBrowser() {
700 return base::StartsWith(GetHttpSchemeUserChoiceProgId(), L"FirefoxURL",
701 base::CompareCase::SENSITIVE);
704 std::string GetFirefoxProgIdSuffix() {
705 const std::wstring app_cmd = GetHttpSchemeUserChoiceProgId();
706 static constexpr base::WStringPiece kFirefoxProgIdPrefix(L"FirefoxURL-");
707 if (base::StartsWith(app_cmd, kFirefoxProgIdPrefix,
708 base::CompareCase::SENSITIVE)) {
709 // Returns the id that appears after the prefix "FirefoxURL-".
710 return std::string(app_cmd.begin() + kFirefoxProgIdPrefix.size(),
713 return std::string();
716 bool IsIEDefaultBrowser() {
717 return GetHttpSchemeUserChoiceProgId() == L"IE.HTTP";
720 DefaultWebClientState IsDefaultClientForScheme(const std::string& scheme) {
721 return GetDefaultWebClientStateFromShellUtilDefaultState(
722 ShellUtil::GetChromeDefaultProtocolClientState(base::UTF8ToWide(scheme)));
727 DefaultWebClientSetPermission GetPlatformSpecificDefaultWebClientSetPermission(
728 WebClientSetMethod method) {
729 if (!install_static::SupportsSetAsDefaultBrowser()) {
730 return SET_DEFAULT_NOT_ALLOWED;
732 if (ShellUtil::CanMakeChromeDefaultUnattended()) {
733 return SET_DEFAULT_UNATTENDED;
735 // Setting the default web client generally requires user interaction in
736 // Windows 8+ with permitted exceptions above.
737 return SET_DEFAULT_INTERACTIVE;
740 } // namespace internal
744 void SetAsDefaultBrowserUsingSystemSettings(
745 base::OnceClosure on_finished_callback) {
746 base::FilePath chrome_exe;
747 if (!base::PathService::Get(base::FILE_EXE, &chrome_exe)) {
748 NOTREACHED() << "Error getting app exe path";
749 std::move(on_finished_callback).Run();
753 // Create an action recorder that will open the settings app once it has
755 std::unique_ptr<DefaultBrowserActionRecorder> recorder =
756 std::make_unique<DefaultBrowserActionRecorder>(base::BindOnce(
757 base::IgnoreResult(&ShellUtil::ShowMakeChromeDefaultSystemUI),
760 // The helper manages its own lifetime. Bind the action recorder
761 // into the finished callback to keep it alive throughout the
763 static const wchar_t* const kSchemes[] = {L"http", L"https", nullptr};
764 OpenSystemSettingsHelper::Begin(
765 kSchemes, base::BindOnce(&OnSettingsAppFinished, std::move(recorder),
766 std::move(on_finished_callback)));
769 void SetAsDefaultClientForSchemeUsingSystemSettings(
770 const std::string& scheme,
771 base::OnceClosure on_finished_callback) {
772 base::FilePath chrome_exe;
773 if (!base::PathService::Get(base::FILE_EXE, &chrome_exe)) {
774 NOTREACHED() << "Error getting app exe path";
775 std::move(on_finished_callback).Run();
779 // The helper manages its own lifetime.
780 std::wstring wscheme(base::UTF8ToWide(scheme));
781 const wchar_t* const kSchemes[] = {wscheme.c_str(), nullptr};
782 OpenSystemSettingsHelper::Begin(kSchemes, std::move(on_finished_callback));
784 ShellUtil::ShowMakeChromeDefaultProtocolClientSystemUI(chrome_exe, wscheme);
787 std::wstring GetAppUserModelIdForApp(const std::wstring& app_name,
788 const base::FilePath& profile_path) {
789 return GetAppUserModelIdImpl(install_static::GetBaseAppId(), app_name,
793 std::wstring GetAppUserModelIdForBrowser(const base::FilePath& profile_path) {
794 return GetAppUserModelIdImpl(
796 ShellUtil::GetBrowserModelId(InstallUtil::IsPerUserInstall()),
800 void UnpinShortcuts(const std::vector<base::FilePath>& shortcuts,
801 base::OnceClosure completion_callback) {
802 UnpinShortcutsHelper::DoUnpin(shortcuts, std::move(completion_callback));
805 void CreateOrUpdateShortcuts(
806 const std::vector<base::FilePath>& shortcuts,
807 const std::vector<base::win::ShortcutProperties>& properties,
808 base::win::ShortcutOperation operation,
809 win::CreateOrUpdateShortcutsResultCallback callback) {
810 CreateOrUpdateShortcutsHelper::DoCreateOrUpdateShortcuts(
811 shortcuts, properties, operation, std::move(callback));
814 void MigrateTaskbarPins(base::OnceClosure completion_callback) {
815 // This needs to happen (e.g. so that the appid is fixed and the
816 // run-time Chrome icon is merged with the taskbar shortcut), but it is not an
818 // MigrateTaskbarPinsCallback just calls MigrateShortcutsInPathInternal
819 // several times with different parameters. Each call may or may not load
820 // DLL's. Since the callback may take the loader lock several times, and this
821 // is the bulk of the callback's work, run the whole thing on a foreground
824 // BEST_EFFORT means it will be scheduled after higher-priority tasks, but
825 // MUST_USE_FOREGROUND means that when it is scheduled it will run in the
827 // SKIP_ON_SHUTDOWN means the task won't start after shutdown has started.
828 base::ThreadPool::CreateCOMSTATaskRunner(
829 {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
830 base::ThreadPolicy::MUST_USE_FOREGROUND,
831 base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})
833 FROM_HERE, base::BindOnce([]() {
834 base::FilePath taskbar_path;
835 base::FilePath implicit_apps_path;
836 base::PathService::Get(base::DIR_TASKBAR_PINS, &taskbar_path);
837 base::PathService::Get(base::DIR_IMPLICIT_APP_SHORTCUTS,
838 &implicit_apps_path);
839 MigrateTaskbarPinsCallback(taskbar_path, implicit_apps_path);
841 std::move(completion_callback));
844 void MigrateTaskbarPinsCallback(const base::FilePath& taskbar_path,
845 const base::FilePath& implicit_apps_path) {
846 // Get full path of chrome.
847 base::FilePath chrome_exe;
848 if (!base::PathService::Get(base::FILE_EXE, &chrome_exe))
850 base::FilePath chrome_proxy_path(web_app::GetChromeProxyPath());
852 if (!taskbar_path.empty()) {
853 MigrateChromeAndChromeProxyShortcuts(chrome_exe, chrome_proxy_path,
856 if (implicit_apps_path.empty())
858 base::FileEnumerator directory_enum(implicit_apps_path, /*recursive=*/false,
859 base::FileEnumerator::DIRECTORIES);
860 for (base::FilePath implicit_app_sub_directory = directory_enum.Next();
861 !implicit_app_sub_directory.empty();
862 implicit_app_sub_directory = directory_enum.Next()) {
863 MigrateChromeAndChromeProxyShortcuts(chrome_exe, chrome_proxy_path,
864 implicit_app_sub_directory);
868 void GetIsPinnedToTaskbarState(IsPinnedToTaskbarCallback result_callback) {
869 IsPinnedToTaskbarHelper::GetState(std::move(result_callback));
872 int MigrateShortcutsInPathInternal(const base::FilePath& chrome_exe,
873 const base::FilePath& path) {
874 // This function may load DLL's so ensure it is running in a foreground
876 DCHECK_GT(base::PlatformThread::GetCurrentThreadType(),
877 base::ThreadType::kBackground);
879 // Enumerate all pinned shortcuts in the given path directly.
880 base::FileEnumerator shortcuts_enum(
881 path, false, // not recursive
882 base::FileEnumerator::FILES, FILE_PATH_LITERAL("*.lnk"));
884 bool is_per_user_install = InstallUtil::IsPerUserInstall();
886 int shortcuts_migrated = 0;
887 base::FilePath target_path;
888 std::wstring arguments;
889 base::win::ScopedPropVariant propvariant;
890 for (base::FilePath shortcut = shortcuts_enum.Next(); !shortcut.empty();
891 shortcut = shortcuts_enum.Next()) {
892 // TODO(gab): Use ProgramCompare instead of comparing FilePaths below once
893 // it is fixed to work with FilePaths with spaces.
894 if (!base::win::ResolveShortcut(shortcut, &target_path, &arguments) ||
895 !base::FilePath::CompareEqualIgnoreCase(chrome_exe.value(),
896 target_path.value())) {
899 base::CommandLine command_line(base::CommandLine::FromString(
900 base::StrCat({L"\"", target_path.value(), L"\" ", arguments})));
902 // Get the expected AppId for this Chrome shortcut.
903 std::wstring expected_app_id(
904 GetExpectedAppId(command_line, is_per_user_install));
905 if (expected_app_id.empty())
908 // Load the shortcut.
909 Microsoft::WRL::ComPtr<IShellLink> shell_link;
910 Microsoft::WRL::ComPtr<IPersistFile> persist_file;
911 if (FAILED(::CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
912 IID_PPV_ARGS(&shell_link))) ||
913 FAILED(shell_link.As(&persist_file)) ||
914 FAILED(persist_file->Load(shortcut.value().c_str(), STGM_READ))) {
915 DLOG(WARNING) << "Failed loading shortcut at " << shortcut.value();
919 // Any properties that need to be updated on the shortcut will be stored in
920 // |updated_properties|.
921 base::win::ShortcutProperties updated_properties;
923 // Validate the existing app id for the shortcut.
924 Microsoft::WRL::ComPtr<IPropertyStore> property_store;
926 if (FAILED(shell_link.As(&property_store)) ||
927 property_store->GetValue(PKEY_AppUserModel_ID, propvariant.Receive()) !=
929 // When in doubt, prefer not updating the shortcut.
933 switch (propvariant.get().vt) {
935 // If there is no app_id set, set our app_id if one is expected.
936 if (!expected_app_id.empty())
937 updated_properties.set_app_id(expected_app_id);
940 if (expected_app_id != std::wstring(propvariant.get().pwszVal))
941 updated_properties.set_app_id(expected_app_id);
949 // Clear dual_mode property from any shortcuts that previously had it (it
950 // was only ever installed on shortcuts with the
951 // |default_chromium_model_id|).
952 std::wstring default_chromium_model_id(
953 ShellUtil::GetBrowserModelId(is_per_user_install));
954 if (expected_app_id == default_chromium_model_id) {
956 if (property_store->GetValue(PKEY_AppUserModel_IsDualMode,
957 propvariant.Receive()) != S_OK) {
958 // When in doubt, prefer to not update the shortcut.
962 if (propvariant.get().vt == VT_BOOL &&
963 !!propvariant.get().boolVal) {
964 updated_properties.set_dual_mode(false);
968 persist_file.Reset();
971 // Update the shortcut if some of its properties need to be updated.
972 if (updated_properties.options &&
973 base::win::CreateOrUpdateShortcutLink(
974 shortcut, updated_properties,
975 base::win::ShortcutOperation::kUpdateExisting)) {
976 ++shortcuts_migrated;
979 return shortcuts_migrated;
984 } // namespace shell_integration