blob: d59b982cc49ddca5ba04604b1349d1f5f7e7f143 [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>
robliaoa872e992017-05-18 06:36:198#include <objbase.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>
Robert Liaob2bc703d2017-10-17 20:52:3513#include <wrl/client.h>
[email protected]d24c4012009-07-28 01:57:3114
pmonettea98315242016-11-23 21:39:1815#include <memory>
16#include <utility>
pmonette32a5cfb42016-04-11 22:04:4417#include <vector>
18
[email protected]3a3e72c2011-11-29 02:59:3819#include "base/bind.h"
pmonette32a5cfb42016-04-11 22:04:4420#include "base/callback.h"
[email protected]d24c4012009-07-28 01:57:3121#include "base/command_line.h"
[email protected]25a4c1c2013-06-08 04:53:3622#include "base/files/file_enumerator.h"
thestig18dfb7a52014-08-26 10:44:0423#include "base/files/file_util.h"
pmonette32a5cfb42016-04-11 22:04:4424#include "base/memory/weak_ptr.h"
pmonette32a5cfb42016-04-11 22:04:4425#include "base/metrics/histogram_macros.h"
grtc291eea2016-05-26 15:38:4826#include "base/metrics/user_metrics.h"
27#include "base/metrics/user_metrics_action.h"
[email protected]d24c4012009-07-28 01:57:3128#include "base/path_service.h"
Avi Drissman22f82872018-12-25 23:09:0729#include "base/stl_util.h"
[email protected]24a555b62013-06-10 22:01:1730#include "base/strings/string_util.h"
31#include "base/strings/stringprintf.h"
[email protected]e309f312013-06-07 21:50:0832#include "base/strings/utf_string_conversions.h"
Gabriel Charette44db1422018-08-06 11:19:3333#include "base/task/post_task.h"
Etienne Pierre-doray6fa6aa52018-09-27 16:38:1234#include "base/threading/scoped_blocking_call.h"
Etienne Bergeron147591d2019-06-19 16:40:1235#include "base/threading/scoped_thread_priority.h"
pmonette32a5cfb42016-04-11 22:04:4436#include "base/time/time.h"
37#include "base/timer/timer.h"
[email protected]2d6503982010-10-17 04:41:5438#include "base/win/registry.h"
[email protected]07983302013-01-21 19:41:4439#include "base/win/scoped_propvariant.h"
Danil Chapovalov3518f362018-08-11 16:13:4340#include "base/win/shlwapi.h"
[email protected]f1024e22012-09-12 07:14:5541#include "base/win/shortcut.h"
[email protected]935aa542010-10-15 01:59:1542#include "base/win/windows_version.h"
[email protected]89d43832013-06-29 20:25:2043#include "chrome/browser/policy/policy_path_parser.h"
pmonette32a5cfb42016-04-11 22:04:4444#include "chrome/browser/shell_integration.h"
Alexey Baskakov2f8bf8162018-07-17 01:06:1145#include "chrome/browser/web_applications/components/web_app_helpers.h"
David Bienvenu7f9c2bf2019-07-15 13:37:3446#include "chrome/browser/web_applications/components/web_app_shortcut_win.h"
grtc291eea2016-05-26 15:38:4847#include "chrome/browser/win/settings_app_monitor.h"
Ken Rockot95fe3f62019-07-25 20:29:5148#include "chrome/browser/win/util_win_service.h"
[email protected]d24c4012009-07-28 01:57:3149#include "chrome/common/chrome_constants.h"
[email protected]12f520c2010-01-06 18:11:1550#include "chrome/common/chrome_paths_internal.h"
[email protected]c9bb06f42010-01-13 23:53:4851#include "chrome/common/chrome_switches.h"
grtc98803462017-03-08 10:05:0852#include "chrome/install_static/install_util.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/shell_util.h"
Patrick Monetteafe350c2019-01-21 17:59:3556#include "chrome/services/util_win/public/mojom/util_win.mojom.h"
pmonette2b1dbee2016-01-08 20:18:5857#include "components/variations/variations_associated_data.h"
Ken Rockot95fe3f62019-07-25 20:29:5158#include "mojo/public/cpp/bindings/remote.h"
[email protected]d24c4012009-07-28 01:57:3159
pmonette9fa59e882016-02-10 00:12:1960namespace shell_integration {
61
[email protected]12f520c2010-01-06 18:11:1562namespace {
63
pmonette9fa59e882016-02-10 00:12:1964// Helper function for GetAppId to generates profile id
[email protected]2f1c09d2011-01-14 14:58:1465// from profile path. "profile_id" is composed of sanitized basenames of
[email protected]12f520c2010-01-06 18:11:1566// user data dir and profile dir joined by a ".".
[email protected]6a72a632013-12-12 22:22:0067base::string16 GetProfileIdFromPath(const base::FilePath& profile_path) {
[email protected]12f520c2010-01-06 18:11:1568 // Return empty string if profile_path is empty
69 if (profile_path.empty())
[email protected]0085863a2013-12-06 21:19:0370 return base::string16();
[email protected]12f520c2010-01-06 18:11:1571
[email protected]12f520c2010-01-06 18:11:1572 // Get joined basenames of user data dir and profile.
[email protected]0085863a2013-12-06 21:19:0373 base::string16 basenames = profile_path.DirName().BaseName().value() +
[email protected]12f520c2010-01-06 18:11:1574 L"." + profile_path.BaseName().value();
75
[email protected]0085863a2013-12-06 21:19:0376 base::string16 profile_id;
[email protected]12f520c2010-01-06 18:11:1577 profile_id.reserve(basenames.size());
78
79 // Generate profile_id from sanitized basenames.
80 for (size_t i = 0; i < basenames.length(); ++i) {
brettwb3413062015-06-24 00:39:0281 if (base::IsAsciiAlpha(basenames[i]) ||
82 base::IsAsciiDigit(basenames[i]) ||
[email protected]12f520c2010-01-06 18:11:1583 basenames[i] == L'.')
84 profile_id += basenames[i];
85 }
86
87 return profile_id;
88}
89
taptedc2c690ab2016-05-25 02:49:0390base::string16 GetAppListAppName() {
91 static const base::char16 kAppListAppNameSuffix[] = L"AppList";
grtc98803462017-03-08 10:05:0892 base::string16 app_name(install_static::GetBaseAppId());
taptedc2c690ab2016-05-25 02:49:0393 app_name.append(kAppListAppNameSuffix);
94 return app_name;
95}
96
[email protected]8ea8f1ef2013-01-06 18:39:0397// Gets expected app id for given Chrome (based on |command_line| and
98// |is_per_user_install|).
avi556c05022014-12-22 23:31:4399base::string16 GetExpectedAppId(const base::CommandLine& command_line,
[email protected]6a72a632013-12-12 22:22:00100 bool is_per_user_install) {
[email protected]b39e32e2013-04-24 08:55:54101 base::FilePath user_data_dir;
102 if (command_line.HasSwitch(switches::kUserDataDir))
103 user_data_dir = command_line.GetSwitchValuePath(switches::kUserDataDir);
104 else
105 chrome::GetDefaultUserDataDirectory(&user_data_dir);
[email protected]89d43832013-06-29 20:25:20106 // Adjust with any policy that overrides any other way to set the path.
107 policy::path_parser::CheckUserDataDirPolicy(&user_data_dir);
[email protected]b39e32e2013-04-24 08:55:54108 DCHECK(!user_data_dir.empty());
[email protected]c9bb06f42010-01-13 23:53:48109
[email protected]b39e32e2013-04-24 08:55:54110 base::FilePath profile_subdir;
111 if (command_line.HasSwitch(switches::kProfileDirectory)) {
112 profile_subdir =
113 command_line.GetSwitchValuePath(switches::kProfileDirectory);
114 } else {
[email protected]f911df52013-12-24 23:24:23115 profile_subdir =
116 base::FilePath(base::ASCIIToUTF16(chrome::kInitialProfile));
[email protected]b39e32e2013-04-24 08:55:54117 }
118 DCHECK(!profile_subdir.empty());
119
120 base::FilePath profile_path = user_data_dir.Append(profile_subdir);
[email protected]0085863a2013-12-06 21:19:03121 base::string16 app_name;
[email protected]c9bb06f42010-01-13 23:53:48122 if (command_line.HasSwitch(switches::kApp)) {
[email protected]f911df52013-12-24 23:24:23123 app_name = base::UTF8ToUTF16(web_app::GenerateApplicationNameFromURL(
[email protected]57ecc4b2010-08-11 03:02:51124 GURL(command_line.GetSwitchValueASCII(switches::kApp))));
[email protected]2f1c09d2011-01-14 14:58:14125 } else if (command_line.HasSwitch(switches::kAppId)) {
Alexey Baskakov2ce2ffc842018-08-06 02:57:15126 app_name = base::UTF8ToUTF16(web_app::GenerateApplicationNameFromAppId(
127 command_line.GetSwitchValueASCII(switches::kAppId)));
taptedc2c690ab2016-05-25 02:49:03128 } else if (command_line.HasSwitch(switches::kShowAppList)) {
129 app_name = GetAppListAppName();
[email protected]c9bb06f42010-01-13 23:53:48130 } else {
grtc98803462017-03-08 10:05:08131 app_name = ShellUtil::GetBrowserModelId(is_per_user_install);
[email protected]c9bb06f42010-01-13 23:53:48132 }
[email protected]b39e32e2013-04-24 08:55:54133 DCHECK(!app_name.empty());
[email protected]c9bb06f42010-01-13 23:53:48134
pmonette9e4c1a82016-04-14 18:15:30135 return win::GetAppModelIdForProfile(app_name, profile_path);
[email protected]c9bb06f42010-01-13 23:53:48136}
137
Mustafa Emre Acerd19a75fc2018-10-08 18:15:14138// Windows treats a given scheme as an Internet scheme only if its registry
139// entry has a "URL Protocol" key. Check this, otherwise we allow ProgIDs to be
140// used as custom protocols which leads to security bugs.
141bool IsValidCustomProtocol(const base::string16& scheme) {
142 if (scheme.empty())
143 return false;
144 base::win::RegKey cmd_key(HKEY_CLASSES_ROOT, scheme.c_str(), KEY_QUERY_VALUE);
145 return cmd_key.Valid() && cmd_key.HasValue(L"URL Protocol");
146}
147
[email protected]caa05352014-03-01 00:43:05148// Windows 8 introduced a new protocol->executable binding system which cannot
149// be retrieved in the HKCR registry subkey method implemented below. We call
150// AssocQueryString with the new Win8-only flag ASSOCF_IS_PROTOCOL instead.
151base::string16 GetAppForProtocolUsingAssocQuery(const GURL& url) {
Mustafa Emre Acerd19a75fc2018-10-08 18:15:14152 const base::string16 url_scheme = base::ASCIIToUTF16(url.scheme());
153 if (!IsValidCustomProtocol(url_scheme))
[email protected]caa05352014-03-01 00:43:05154 return base::string16();
155
[email protected]caa05352014-03-01 00:43:05156 // Query AssocQueryString for a human-readable description of the program
157 // that will be invoked given the provided URL spec. This is used only to
158 // populate the external protocol dialog box the user sees when invoking
159 // an unknown external protocol.
160 wchar_t out_buffer[1024];
Avi Drissman22f82872018-12-25 23:09:07161 DWORD buffer_size = base::size(out_buffer);
Mustafa Emre Acerd19a75fc2018-10-08 18:15:14162 HRESULT hr =
163 AssocQueryString(ASSOCF_IS_PROTOCOL, ASSOCSTR_FRIENDLYAPPNAME,
164 url_scheme.c_str(), NULL, out_buffer, &buffer_size);
[email protected]caa05352014-03-01 00:43:05165 if (FAILED(hr)) {
166 DLOG(WARNING) << "AssocQueryString failed!";
167 return base::string16();
168 }
169 return base::string16(out_buffer);
170}
171
172base::string16 GetAppForProtocolUsingRegistry(const GURL& url) {
Mustafa Emre Acerd19a75fc2018-10-08 18:15:14173 const base::string16 url_scheme = base::ASCIIToUTF16(url.scheme());
174 if (!IsValidCustomProtocol(url_scheme))
175 return base::string16();
dominickn0c9a5062016-10-06 20:49:00176
177 // First, try and extract the application's display name.
Mustafa Emre Acerd19a75fc2018-10-08 18:15:14178 base::string16 command_to_launch;
179 base::win::RegKey cmd_key_name(HKEY_CLASSES_ROOT, url_scheme.c_str(),
dominickn0c9a5062016-10-06 20:49:00180 KEY_READ);
181 if (cmd_key_name.ReadValue(NULL, &command_to_launch) == ERROR_SUCCESS &&
182 !command_to_launch.empty()) {
183 return command_to_launch;
[email protected]caa05352014-03-01 00:43:05184 }
dominickn0c9a5062016-10-06 20:49:00185
186 // Otherwise, parse the command line in the registry, and return the basename
187 // of the program path if it exists.
Mustafa Emre Acerd19a75fc2018-10-08 18:15:14188 const base::string16 cmd_key_path = url_scheme + L"\\shell\\open\\command";
dominickn0c9a5062016-10-06 20:49:00189 base::win::RegKey cmd_key_exe(HKEY_CLASSES_ROOT, cmd_key_path.c_str(),
190 KEY_READ);
191 if (cmd_key_exe.ReadValue(NULL, &command_to_launch) == ERROR_SUCCESS) {
192 base::CommandLine command_line(
193 base::CommandLine::FromString(command_to_launch));
194 return command_line.GetProgram().BaseName().value();
195 }
196
[email protected]caa05352014-03-01 00:43:05197 return base::string16();
198}
199
pmonette9fa59e882016-02-10 00:12:19200DefaultWebClientState GetDefaultWebClientStateFromShellUtilDefaultState(
201 ShellUtil::DefaultState default_state) {
[email protected]eb63da72012-10-15 20:39:48202 switch (default_state) {
Greg Thompson75b1c8e2017-08-16 13:36:28203 case ShellUtil::UNKNOWN_DEFAULT:
204 return DefaultWebClientState::UNKNOWN_DEFAULT;
[email protected]eb63da72012-10-15 20:39:48205 case ShellUtil::NOT_DEFAULT:
pmonette9fa59e882016-02-10 00:12:19206 return DefaultWebClientState::NOT_DEFAULT;
[email protected]eb63da72012-10-15 20:39:48207 case ShellUtil::IS_DEFAULT:
pmonette9fa59e882016-02-10 00:12:19208 return DefaultWebClientState::IS_DEFAULT;
Greg Thompson75b1c8e2017-08-16 13:36:28209 case ShellUtil::OTHER_MODE_IS_DEFAULT:
210 return DefaultWebClientState::OTHER_MODE_IS_DEFAULT;
[email protected]eb63da72012-10-15 20:39:48211 }
Greg Thompson75b1c8e2017-08-16 13:36:28212 NOTREACHED();
213 return DefaultWebClientState::UNKNOWN_DEFAULT;
[email protected]eb63da72012-10-15 20:39:48214}
215
grtc291eea2016-05-26 15:38:48216// A recorder of user actions in the Windows Settings app.
Patrick Monette7ff99e02018-02-19 17:30:12217class DefaultBrowserActionRecorder : public SettingsAppMonitor::Delegate {
grtc291eea2016-05-26 15:38:48218 public:
219 // Creates the recorder and the monitor that drives it. |continuation| will be
220 // run once the monitor's initialization completes (regardless of success or
221 // failure).
222 explicit DefaultBrowserActionRecorder(base::Closure continuation)
223 : continuation_(std::move(continuation)), settings_app_monitor_(this) {}
224
225 private:
226 // win::SettingsAppMonitor::Delegate:
227 void OnInitialized(HRESULT result) override {
Greg Thompsonea27a2c2019-06-25 19:22:22228 // UMA indicates that this succeeds > 99.98% of the time.
grtc291eea2016-05-26 15:38:48229 if (SUCCEEDED(result)) {
230 base::RecordAction(
231 base::UserMetricsAction("SettingsAppMonitor.Initialized"));
232 }
233 continuation_.Run();
234 continuation_ = base::Closure();
235 }
236
237 void OnAppFocused() override {
238 base::RecordAction(
239 base::UserMetricsAction("SettingsAppMonitor.AppFocused"));
240 }
241
242 void OnChooserInvoked() override {
243 base::RecordAction(
244 base::UserMetricsAction("SettingsAppMonitor.ChooserInvoked"));
245 }
246
247 void OnBrowserChosen(const base::string16& browser_name) override {
Greg Thompson005a710c2018-09-06 06:54:38248 if (browser_name == InstallUtil::GetDisplayName()) {
grtc291eea2016-05-26 15:38:48249 base::RecordAction(
250 base::UserMetricsAction("SettingsAppMonitor.ChromeBrowserChosen"));
251 } else {
252 base::RecordAction(
253 base::UserMetricsAction("SettingsAppMonitor.OtherBrowserChosen"));
254 }
255 }
256
pmonetteb66514b42016-12-07 20:28:39257 void OnPromoFocused() override {
258 base::RecordAction(
259 base::UserMetricsAction("SettingsAppMonitor.PromoFocused"));
260 }
261
262 void OnPromoChoiceMade(bool accept_promo) override {
263 if (accept_promo) {
264 base::RecordAction(
265 base::UserMetricsAction("SettingsAppMonitor.CheckItOut"));
266 } else {
267 base::RecordAction(
268 base::UserMetricsAction("SettingsAppMonitor.SwitchAnyway"));
269 }
270 }
271
grtc291eea2016-05-26 15:38:48272 // A closure to be run once initialization completes.
273 base::Closure continuation_;
274
275 // Monitors user interaction with the Windows Settings app for the sake of
276 // reporting user actions.
Patrick Monette7ff99e02018-02-19 17:30:12277 SettingsAppMonitor settings_app_monitor_;
grtc291eea2016-05-26 15:38:48278
279 DISALLOW_COPY_AND_ASSIGN(DefaultBrowserActionRecorder);
280};
281
282// A function bound up in a callback with a DefaultBrowserActionRecorder and
283// a closure to keep the former alive until the time comes to run the latter.
284void OnSettingsAppFinished(
285 std::unique_ptr<DefaultBrowserActionRecorder> recorder,
286 const base::Closure& on_finished_callback) {
287 recorder.reset();
288 on_finished_callback.Run();
289}
290
pmonette32a5cfb42016-04-11 22:04:44291// There is no way to make sure the user is done with the system settings, but a
292// signal that the interaction is finished is needed for UMA. A timer of 2
293// minutes is used as a substitute. The registry keys for the protocol
294// association with an app are also monitored to signal the end of the
295// interaction early when it is clear that the user made a choice (e.g. http
296// and https for default browser).
297//
298// This helper class manages both the timer and the registry watchers and makes
299// sure the callback for the end of the settings interaction is only run once.
300// This class also manages its own lifetime.
301class OpenSystemSettingsHelper {
302 public:
303 // Begin the monitoring and will call |on_finished_callback| when done.
304 // Takes in a null-terminated array of |protocols| whose registry keys must be
pmonette0c40087a2016-04-21 00:05:16305 // watched. The array must contain at least one element.
pmonette32a5cfb42016-04-11 22:04:44306 static void Begin(const wchar_t* const protocols[],
307 const base::Closure& on_finished_callback) {
pmonettec541d942016-06-15 22:08:32308 delete instance_;
309 instance_ = new OpenSystemSettingsHelper(protocols, on_finished_callback);
pmonette32a5cfb42016-04-11 22:04:44310 }
311
312 private:
313 // The reason the settings interaction concluded. Do not modify the ordering
314 // because it is used for UMA.
315 enum ConcludeReason { REGISTRY_WATCHER, TIMEOUT, NUM_CONCLUDE_REASON_TYPES };
316
317 OpenSystemSettingsHelper(const wchar_t* const protocols[],
318 const base::Closure& on_finished_callback)
pmonette0c40087a2016-04-21 00:05:16319 : scoped_user_protocol_entry_(protocols[0]),
320 on_finished_callback_(on_finished_callback),
321 weak_ptr_factory_(this) {
pmonette32a5cfb42016-04-11 22:04:44322 static const wchar_t kUrlAssociationFormat[] =
323 L"SOFTWARE\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\"
324 L"%ls\\UserChoice";
325
326 // Remember the start time.
327 start_time_ = base::TimeTicks::Now();
328
329 for (const wchar_t* const* scan = &protocols[0]; *scan != nullptr; ++scan) {
330 AddRegistryKeyWatcher(
331 base::StringPrintf(kUrlAssociationFormat, *scan).c_str());
332 }
333 // Only the watchers that were succesfully initialized are counted.
334 registry_watcher_count_ = registry_key_watchers_.size();
335
336 timer_.Start(
337 FROM_HERE, base::TimeDelta::FromMinutes(2),
338 base::Bind(&OpenSystemSettingsHelper::ConcludeInteraction,
339 weak_ptr_factory_.GetWeakPtr(), ConcludeReason::TIMEOUT));
340 }
341
pmonetteb18379f2017-06-01 01:18:44342 ~OpenSystemSettingsHelper() {
343 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
344 }
345
pmonette32a5cfb42016-04-11 22:04:44346 // Called when a change is detected on one of the registry keys being watched.
347 // Note: All types of modification to the registry key will trigger this
348 // function even if the value change is the only one that matters. This
349 // is good enough for now.
350 void OnRegistryKeyChanged() {
pmonetteb18379f2017-06-01 01:18:44351 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
pmonette32a5cfb42016-04-11 22:04:44352 // Make sure all the registry watchers have fired.
353 if (--registry_watcher_count_ == 0) {
354 UMA_HISTOGRAM_MEDIUM_TIMES(
355 "DefaultBrowser.SettingsInteraction.RegistryWatcherDuration",
356 base::TimeTicks::Now() - start_time_);
357
358 ConcludeInteraction(ConcludeReason::REGISTRY_WATCHER);
359 }
360 }
361
362 // Ends the monitoring with the system settings. Will call
363 // |on_finished_callback_| and then dispose of this class instance to make
364 // sure the callback won't get called subsequently.
365 void ConcludeInteraction(ConcludeReason conclude_reason) {
pmonetteb18379f2017-06-01 01:18:44366 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
pmonette32a5cfb42016-04-11 22:04:44367
368 UMA_HISTOGRAM_ENUMERATION(
369 "DefaultBrowser.SettingsInteraction.ConcludeReason", conclude_reason,
370 NUM_CONCLUDE_REASON_TYPES);
371 on_finished_callback_.Run();
pmonettec541d942016-06-15 22:08:32372 delete instance_;
373 instance_ = nullptr;
pmonette32a5cfb42016-04-11 22:04:44374 }
375
376 // Helper function to create a registry watcher for a given |key_path|. Do
377 // nothing on initialization failure.
378 void AddRegistryKeyWatcher(const wchar_t* key_path) {
pmonetteb18379f2017-06-01 01:18:44379 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
380
Jeremy Romanec48d7a2018-03-01 17:35:09381 auto reg_key = std::make_unique<base::win::RegKey>(HKEY_CURRENT_USER,
ricea86fa1dd2016-09-13 05:59:45382 key_path, KEY_NOTIFY);
pmonette32a5cfb42016-04-11 22:04:44383
384 if (reg_key->Valid() &&
385 reg_key->StartWatching(
386 base::Bind(&OpenSystemSettingsHelper::OnRegistryKeyChanged,
387 weak_ptr_factory_.GetWeakPtr()))) {
388 registry_key_watchers_.push_back(std::move(reg_key));
389 }
390 }
391
pmonettec541d942016-06-15 22:08:32392 // Used to make sure only one instance is alive at the same time.
393 static OpenSystemSettingsHelper* instance_;
394
pmonette0c40087a2016-04-21 00:05:16395 // This is needed to make sure that Windows displays an entry for the protocol
396 // inside the "Choose default apps by protocol" settings page.
397 ScopedUserProtocolEntry scoped_user_protocol_entry_;
398
pmonette32a5cfb42016-04-11 22:04:44399 // The function to call when the interaction with the system settings is
400 // finished.
401 base::Closure on_finished_callback_;
402
403 // The number of time the registry key watchers must fire.
404 int registry_watcher_count_ = 0;
405
406 // There can be multiple registry key watchers as some settings modify
407 // multiple protocol associations. e.g. Changing the default browser modifies
408 // the http and https associations.
dcheng4af48582016-04-19 00:29:35409 std::vector<std::unique_ptr<base::win::RegKey>> registry_key_watchers_;
pmonette32a5cfb42016-04-11 22:04:44410
411 base::OneShotTimer timer_;
412
413 // Records the time it takes for the final registry watcher to get signaled.
414 base::TimeTicks start_time_;
415
pmonetteb18379f2017-06-01 01:18:44416 SEQUENCE_CHECKER(sequence_checker_);
417
pmonette32a5cfb42016-04-11 22:04:44418 // Weak ptrs are used to bind this class to the callbacks of the timer and the
419 // registry watcher. This makes it possible to self-delete after one of the
420 // callbacks is executed to cancel the remaining ones.
421 base::WeakPtrFactory<OpenSystemSettingsHelper> weak_ptr_factory_;
422
423 DISALLOW_COPY_AND_ASSIGN(OpenSystemSettingsHelper);
424};
425
pmonettec541d942016-06-15 22:08:32426OpenSystemSettingsHelper* OpenSystemSettingsHelper::instance_ = nullptr;
427
pmonettea98315242016-11-23 21:39:18428// Helper class to determine if Chrome is pinned to the taskbar. Hides the
Jay Civellid4b89052017-10-31 17:48:25429// complexity of managing the lifetime of the connection to the ChromeWinUtil
430// service.
pmonettea98315242016-11-23 21:39:18431class IsPinnedToTaskbarHelper {
432 public:
433 using ResultCallback = win::IsPinnedToTaskbarCallback;
434 using ErrorCallback = win::ConnectionErrorCallback;
Ken Rockot95fe3f62019-07-25 20:29:51435 static void GetState(const ErrorCallback& error_callback,
pmonettea98315242016-11-23 21:39:18436 const ResultCallback& result_callback);
437
438 private:
Ken Rockot95fe3f62019-07-25 20:29:51439 IsPinnedToTaskbarHelper(const ErrorCallback& error_callback,
pmonettea98315242016-11-23 21:39:18440 const ResultCallback& result_callback);
441
442 void OnConnectionError();
443 void OnIsPinnedToTaskbarResult(bool succeeded, bool is_pinned_to_taskbar);
444
Ken Rockot95fe3f62019-07-25 20:29:51445 mojo::Remote<chrome::mojom::UtilWin> remote_util_win_;
pmonettea98315242016-11-23 21:39:18446
447 ErrorCallback error_callback_;
448 ResultCallback result_callback_;
449
pmonetteb18379f2017-06-01 01:18:44450 SEQUENCE_CHECKER(sequence_checker_);
451
pmonettea98315242016-11-23 21:39:18452 DISALLOW_COPY_AND_ASSIGN(IsPinnedToTaskbarHelper);
453};
454
455// static
Jay Civellid4b89052017-10-31 17:48:25456void IsPinnedToTaskbarHelper::GetState(
Jay Civellid4b89052017-10-31 17:48:25457 const ErrorCallback& error_callback,
458 const ResultCallback& result_callback) {
pmonettea98315242016-11-23 21:39:18459 // Self-deleting when the ShellHandler completes.
Ken Rockot95fe3f62019-07-25 20:29:51460 new IsPinnedToTaskbarHelper(error_callback, result_callback);
pmonette5057ca3b2016-07-04 16:48:40461}
462
pmonettea98315242016-11-23 21:39:18463IsPinnedToTaskbarHelper::IsPinnedToTaskbarHelper(
pmonettea98315242016-11-23 21:39:18464 const ErrorCallback& error_callback,
465 const ResultCallback& result_callback)
Ken Rockot95fe3f62019-07-25 20:29:51466 : remote_util_win_(LaunchUtilWinServiceInstance()),
pmonettea98315242016-11-23 21:39:18467 error_callback_(error_callback),
468 result_callback_(result_callback) {
pmonette83edc1e2016-12-15 17:36:44469 DCHECK(error_callback_);
470 DCHECK(result_callback_);
471
Ken Rockot95fe3f62019-07-25 20:29:51472 // |remote_util_win_| owns the callbacks and is guaranteed to be destroyed
473 // before |this|, therefore making base::Unretained() safe to use.
474 remote_util_win_.set_disconnect_handler(base::BindOnce(
pmonettea98315242016-11-23 21:39:18475 &IsPinnedToTaskbarHelper::OnConnectionError, base::Unretained(this)));
Ken Rockot95fe3f62019-07-25 20:29:51476 remote_util_win_->IsPinnedToTaskbar(
pmonettea98315242016-11-23 21:39:18477 base::Bind(&IsPinnedToTaskbarHelper::OnIsPinnedToTaskbarResult,
478 base::Unretained(this)));
479}
480
481void IsPinnedToTaskbarHelper::OnConnectionError() {
pmonetteb18379f2017-06-01 01:18:44482 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
pmonettea98315242016-11-23 21:39:18483 error_callback_.Run();
484 delete this;
485}
486
487void IsPinnedToTaskbarHelper::OnIsPinnedToTaskbarResult(
pmonette5057ca3b2016-07-04 16:48:40488 bool succeeded,
489 bool is_pinned_to_taskbar) {
pmonetteb18379f2017-06-01 01:18:44490 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
491
pmonettea98315242016-11-23 21:39:18492 result_callback_.Run(succeeded, is_pinned_to_taskbar);
493 delete this;
pmonette5057ca3b2016-07-04 16:48:40494}
495
David Bienvenu41051862019-07-25 22:17:38496void MigrateChromeAndChromeProxyShortcuts(
497 const base::FilePath& chrome_exe,
498 const base::FilePath& chrome_proxy_path,
499 const base::FilePath& shortcut_path) {
500 win::MigrateShortcutsInPathInternal(chrome_exe, shortcut_path);
501
502 // Migrate any pinned PWA shortcuts in taskbar directory.
503 win::MigrateShortcutsInPathInternal(chrome_proxy_path, shortcut_path);
504}
505
[email protected]3a3e72c2011-11-29 02:59:38506} // namespace
[email protected]12f520c2010-01-06 18:11:15507
pmonette9fa59e882016-02-10 00:12:19508bool SetAsDefaultBrowser() {
Etienne Bergeron436d42212019-02-26 17:15:12509 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
510 base::BlockingType::MAY_BLOCK);
pmonetteb18379f2017-06-01 01:18:44511
[email protected]650b2d52013-02-10 03:41:45512 base::FilePath chrome_exe;
Avi Drissman9098f9002018-05-04 00:11:52513 if (!base::PathService::Get(base::FILE_EXE, &chrome_exe)) {
[email protected]d24c4012009-07-28 01:57:31514 LOG(ERROR) << "Error getting app exe path";
515 return false;
516 }
517
518 // From UI currently we only allow setting default browser for current user.
Greg Thompson8fc94ef2018-09-10 16:53:12519 if (!ShellUtil::MakeChromeDefault(ShellUtil::CURRENT_USER, chrome_exe,
grte76ca2852014-12-05 16:42:10520 true /* elevate_if_not_admin */)) {
[email protected]d24c4012009-07-28 01:57:31521 LOG(ERROR) << "Chrome could not be set as default browser.";
522 return false;
523 }
524
[email protected]8e96e502010-10-21 20:57:12525 VLOG(1) << "Chrome registered as default browser.";
[email protected]d24c4012009-07-28 01:57:31526 return true;
527}
528
pmonette9fa59e882016-02-10 00:12:19529bool SetAsDefaultProtocolClient(const std::string& protocol) {
Etienne Bergeron436d42212019-02-26 17:15:12530 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
531 base::BlockingType::MAY_BLOCK);
pmonetteb18379f2017-06-01 01:18:44532
[email protected]4468a5b2011-05-26 07:48:02533 if (protocol.empty())
534 return false;
535
[email protected]650b2d52013-02-10 03:41:45536 base::FilePath chrome_exe;
Avi Drissman9098f9002018-05-04 00:11:52537 if (!base::PathService::Get(base::FILE_EXE, &chrome_exe)) {
[email protected]4468a5b2011-05-26 07:48:02538 LOG(ERROR) << "Error getting app exe path";
539 return false;
540 }
541
[email protected]f911df52013-12-24 23:24:23542 base::string16 wprotocol(base::UTF8ToUTF16(protocol));
Greg Thompson8fc94ef2018-09-10 16:53:12543 if (!ShellUtil::MakeChromeDefaultProtocolClient(chrome_exe, wprotocol)) {
[email protected]4468a5b2011-05-26 07:48:02544 LOG(ERROR) << "Chrome could not be set as default handler for "
545 << protocol << ".";
546 return false;
547 }
548
549 VLOG(1) << "Chrome registered as default handler for " << protocol << ".";
550 return true;
551}
552
pmonette32a5cfb42016-04-11 22:04:44553DefaultWebClientSetPermission GetDefaultWebClientSetPermission() {
grt509bc942017-03-25 08:43:21554 if (!install_static::SupportsSetAsDefaultBrowser())
pmonette034a03d92015-10-02 21:04:27555 return SET_DEFAULT_NOT_ALLOWED;
pmonette034a03d92015-10-02 21:04:27556 if (ShellUtil::CanMakeChromeDefaultUnattended())
557 return SET_DEFAULT_UNATTENDED;
pmonette32a5cfb42016-04-11 22:04:44558 // Windows 8 and 10 both introduced a new way to set the default web client
559 // which require user interaction.
pmonettef89ac7c72015-10-06 03:22:01560 return SET_DEFAULT_INTERACTIVE;
pmonette034a03d92015-10-02 21:04:27561}
562
pmonette9fa59e882016-02-10 00:12:19563bool IsElevationNeededForSettingDefaultProtocolClient() {
Bruce Dawsonaed9bea2019-04-20 02:30:09564 return base::win::GetVersion() < base::win::Version::WIN8;
pmonette868ca642015-09-02 14:34:02565}
566
pmonette9fa59e882016-02-10 00:12:19567base::string16 GetApplicationNameForProtocol(const GURL& url) {
benwellsd0c67aa2016-07-28 02:57:20568 // Windows 8 or above has a new protocol association query.
Bruce Dawsonaed9bea2019-04-20 02:30:09569 if (base::win::GetVersion() >= base::win::Version::WIN8) {
Mustafa Emre Acerd19a75fc2018-10-08 18:15:14570 base::string16 application_name = GetAppForProtocolUsingAssocQuery(url);
benwellsd0c67aa2016-07-28 02:57:20571 if (!application_name.empty())
572 return application_name;
573 }
574
575 return GetAppForProtocolUsingRegistry(url);
[email protected]42dc9402013-01-30 07:54:20576}
577
pmonette9fa59e882016-02-10 00:12:19578DefaultWebClientState GetDefaultBrowser() {
pmonette034a03d92015-10-02 21:04:27579 return GetDefaultWebClientStateFromShellUtilDefaultState(
580 ShellUtil::GetChromeDefaultState());
581}
582
[email protected]d24c4012009-07-28 01:57:31583// There is no reliable way to say which browser is default on a machine (each
584// browser can have some of the protocols/shortcuts). So we look for only HTTP
585// protocol handler. Even this handler is located at different places in
586// registry on XP and Vista:
587// - HKCR\http\shell\open\command (XP)
588// - HKCU\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\
589// http\UserChoice (Vista)
thakise4a02ff32016-09-20 17:00:42590// This method checks if Firefox is default browser by checking these
[email protected]d24c4012009-07-28 01:57:31591// locations and returns true if Firefox traces are found there. In case of
592// error (or if Firefox is not found)it returns the default value which
593// is false.
pmonette9fa59e882016-02-10 00:12:19594bool IsFirefoxDefaultBrowser() {
Patrick Monetteab240ec2017-07-13 22:49:14595 base::string16 app_cmd;
596 base::win::RegKey key(HKEY_CURRENT_USER, ShellUtil::kRegVistaUrlPrefs,
597 KEY_READ);
598 return key.Valid() && key.ReadValue(L"Progid", &app_cmd) == ERROR_SUCCESS &&
599 app_cmd == L"FirefoxURL";
[email protected]d24c4012009-07-28 01:57:31600}
[email protected]12f520c2010-01-06 18:11:15601
pmonette9fa59e882016-02-10 00:12:19602DefaultWebClientState IsDefaultProtocolClient(const std::string& protocol) {
pmonette034a03d92015-10-02 21:04:27603 return GetDefaultWebClientStateFromShellUtilDefaultState(
604 ShellUtil::GetChromeDefaultProtocolClientState(
605 base::UTF8ToUTF16(protocol)));
606}
607
pmonette9e4c1a82016-04-14 18:15:30608namespace win {
609
610bool SetAsDefaultBrowserUsingIntentPicker() {
Etienne Bergeron436d42212019-02-26 17:15:12611 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
612 base::BlockingType::MAY_BLOCK);
pmonetteb18379f2017-06-01 01:18:44613
pmonette9e4c1a82016-04-14 18:15:30614 base::FilePath chrome_exe;
Avi Drissman9098f9002018-05-04 00:11:52615 if (!base::PathService::Get(base::FILE_EXE, &chrome_exe)) {
pmonette9e4c1a82016-04-14 18:15:30616 NOTREACHED() << "Error getting app exe path";
617 return false;
618 }
619
Greg Thompson8fc94ef2018-09-10 16:53:12620 if (!ShellUtil::ShowMakeChromeDefaultSystemUI(chrome_exe)) {
pmonette9e4c1a82016-04-14 18:15:30621 LOG(ERROR) << "Failed to launch the set-default-browser Windows UI.";
622 return false;
623 }
624
625 VLOG(1) << "Set-default-browser Windows UI completed.";
626 return true;
627}
628
629void SetAsDefaultBrowserUsingSystemSettings(
630 const base::Closure& on_finished_callback) {
pmonette9e4c1a82016-04-14 18:15:30631 base::FilePath chrome_exe;
Avi Drissman9098f9002018-05-04 00:11:52632 if (!base::PathService::Get(base::FILE_EXE, &chrome_exe)) {
pmonette9e4c1a82016-04-14 18:15:30633 NOTREACHED() << "Error getting app exe path";
634 on_finished_callback.Run();
635 return;
636 }
637
grtc291eea2016-05-26 15:38:48638 // Create an action recorder that will open the settings app once it has
639 // initialized.
640 std::unique_ptr<DefaultBrowserActionRecorder> recorder(
641 new DefaultBrowserActionRecorder(base::Bind(
642 base::IgnoreResult(&ShellUtil::ShowMakeChromeDefaultSystemUI),
grtc291eea2016-05-26 15:38:48643 chrome_exe)));
pmonette9e4c1a82016-04-14 18:15:30644
grtc291eea2016-05-26 15:38:48645 // The helper manages its own lifetime. Bind the action recorder
646 // into the finished callback to keep it alive throughout the
647 // interaction.
648 static const wchar_t* const kProtocols[] = {L"http", L"https", nullptr};
649 OpenSystemSettingsHelper::Begin(
650 kProtocols, base::Bind(&OnSettingsAppFinished, base::Passed(&recorder),
651 on_finished_callback));
pmonette9e4c1a82016-04-14 18:15:30652}
653
654bool SetAsDefaultProtocolClientUsingIntentPicker(const std::string& protocol) {
Etienne Bergeron436d42212019-02-26 17:15:12655 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
656 base::BlockingType::MAY_BLOCK);
pmonetteb18379f2017-06-01 01:18:44657
pmonette9e4c1a82016-04-14 18:15:30658 base::FilePath chrome_exe;
Avi Drissman9098f9002018-05-04 00:11:52659 if (!base::PathService::Get(base::FILE_EXE, &chrome_exe)) {
pmonette9e4c1a82016-04-14 18:15:30660 NOTREACHED() << "Error getting app exe path";
661 return false;
662 }
663
pmonette9e4c1a82016-04-14 18:15:30664 base::string16 wprotocol(base::UTF8ToUTF16(protocol));
Greg Thompson8fc94ef2018-09-10 16:53:12665 if (!ShellUtil::ShowMakeChromeDefaultProtocolClientSystemUI(chrome_exe,
pmonette9e4c1a82016-04-14 18:15:30666 wprotocol)) {
667 LOG(ERROR) << "Failed to launch the set-default-client Windows UI.";
668 return false;
669 }
670
671 VLOG(1) << "Set-default-client Windows UI completed.";
672 return true;
673}
674
pmonette0c40087a2016-04-21 00:05:16675void SetAsDefaultProtocolClientUsingSystemSettings(
676 const std::string& protocol,
677 const base::Closure& on_finished_callback) {
pmonette0c40087a2016-04-21 00:05:16678 base::FilePath chrome_exe;
Avi Drissman9098f9002018-05-04 00:11:52679 if (!base::PathService::Get(base::FILE_EXE, &chrome_exe)) {
pmonette0c40087a2016-04-21 00:05:16680 NOTREACHED() << "Error getting app exe path";
681 on_finished_callback.Run();
682 return;
683 }
684
685 // The helper manages its own lifetime.
686 base::string16 wprotocol(base::UTF8ToUTF16(protocol));
687 const wchar_t* const kProtocols[] = {wprotocol.c_str(), nullptr};
688 OpenSystemSettingsHelper::Begin(kProtocols, on_finished_callback);
689
Greg Thompson8fc94ef2018-09-10 16:53:12690 ShellUtil::ShowMakeChromeDefaultProtocolClientSystemUI(chrome_exe, wprotocol);
pmonette0c40087a2016-04-21 00:05:16691}
692
pmonette9fa59e882016-02-10 00:12:19693base::string16 GetAppModelIdForProfile(const base::string16& app_name,
694 const base::FilePath& profile_path) {
[email protected]d2065e062013-12-12 23:49:52695 std::vector<base::string16> components;
[email protected]a0448002012-06-19 04:32:10696 components.push_back(app_name);
[email protected]0085863a2013-12-06 21:19:03697 const base::string16 profile_id(GetProfileIdFromPath(profile_path));
[email protected]a0448002012-06-19 04:32:10698 if (!profile_id.empty())
699 components.push_back(profile_id);
700 return ShellUtil::BuildAppModelId(components);
[email protected]12f520c2010-01-06 18:11:15701}
702
pmonette9fa59e882016-02-10 00:12:19703base::string16 GetChromiumModelIdForProfile(
[email protected]650b2d52013-02-10 03:41:45704 const base::FilePath& profile_path) {
[email protected]a0448002012-06-19 04:32:10705 return GetAppModelIdForProfile(
grtc98803462017-03-08 10:05:08706 ShellUtil::GetBrowserModelId(InstallUtil::IsPerUserInstall()),
[email protected]786799692012-09-26 14:16:48707 profile_path);
[email protected]12f520c2010-01-06 18:11:15708}
[email protected]c9bb06f42010-01-13 23:53:48709
pmonette9fa59e882016-02-10 00:12:19710void MigrateTaskbarPins() {
pmonetteb18379f2017-06-01 01:18:44711 // This needs to happen (e.g. so that the appid is fixed and the
712 // run-time Chrome icon is merged with the taskbar shortcut), but it is not an
713 // urgent task.
David Bienvenu41051862019-07-25 22:17:38714 base::FilePath taskbar_path;
715 if (!base::PathService::Get(base::DIR_TASKBAR_PINS, &taskbar_path)) {
716 NOTREACHED();
717 return;
718 }
719
720 // Migrate any pinned shortcuts in ImplicitApps sub-directories.
721 base::FilePath implicit_apps_path;
722 if (!base::PathService::Get(base::DIR_IMPLICIT_APP_SHORTCUTS,
723 &implicit_apps_path)) {
David Bienvenu7f9c2bf2019-07-15 13:37:34724 NOTREACHED();
725 return;
726 }
727
Sami Kyostila95250402019-08-07 18:58:56728 base::CreateCOMSTATaskRunner(
729 {base::ThreadPool(), base::MayBlock(), base::TaskPriority::BEST_EFFORT})
David Bienvenu41051862019-07-25 22:17:38730 ->PostTask(FROM_HERE, base::BindOnce(&MigrateTaskbarPinsCallback,
731 taskbar_path, implicit_apps_path));
David Bienvenu7f9c2bf2019-07-15 13:37:34732}
733
David Bienvenu41051862019-07-25 22:17:38734void MigrateTaskbarPinsCallback(const base::FilePath& taskbar_path,
735 const base::FilePath& implicit_apps_path) {
David Bienvenu7f9c2bf2019-07-15 13:37:34736 // Get full path of chrome.
737 base::FilePath chrome_exe;
738 if (!base::PathService::Get(base::FILE_EXE, &chrome_exe))
739 return;
David Bienvenu41051862019-07-25 22:17:38740 base::FilePath chrome_proxy_path(web_app::GetChromeProxyPath());
David Bienvenu7f9c2bf2019-07-15 13:37:34741
David Bienvenu41051862019-07-25 22:17:38742 MigrateChromeAndChromeProxyShortcuts(chrome_exe, chrome_proxy_path,
743 taskbar_path);
744 base::FileEnumerator directory_enum(implicit_apps_path, /*recursive=*/false,
745 base::FileEnumerator::DIRECTORIES);
746 for (base::FilePath implicit_app_sub_directory = directory_enum.Next();
747 !implicit_app_sub_directory.empty();
748 implicit_app_sub_directory = directory_enum.Next()) {
749 MigrateChromeAndChromeProxyShortcuts(chrome_exe, chrome_proxy_path,
750 implicit_app_sub_directory);
751 }
[email protected]c9bb06f42010-01-13 23:53:48752}
[email protected]43903b82012-06-01 05:26:23753
pmonettea98315242016-11-23 21:39:18754void GetIsPinnedToTaskbarState(
pmonettea98315242016-11-23 21:39:18755 const ConnectionErrorCallback& on_error_callback,
756 const IsPinnedToTaskbarCallback& result_callback) {
Ken Rockot95fe3f62019-07-25 20:29:51757 IsPinnedToTaskbarHelper::GetState(on_error_callback, result_callback);
pmonette5057ca3b2016-07-04 16:48:40758}
759
pmonette9fa59e882016-02-10 00:12:19760int MigrateShortcutsInPathInternal(const base::FilePath& chrome_exe,
761 const base::FilePath& path) {
Etienne Bergeron147591d2019-06-19 16:40:12762 // Mitigate the issues caused by loading DLLs on a background thread
763 // (http://crbug/973868).
764 base::ScopedThreadMayLoadLibraryOnBackgroundThread priority_boost(FROM_HERE);
765
[email protected]8ea8f1ef2013-01-06 18:39:03766 // Enumerate all pinned shortcuts in the given path directly.
[email protected]25a4c1c2013-06-08 04:53:36767 base::FileEnumerator shortcuts_enum(
[email protected]8ea8f1ef2013-01-06 18:39:03768 path, false, // not recursive
[email protected]25a4c1c2013-06-08 04:53:36769 base::FileEnumerator::FILES, FILE_PATH_LITERAL("*.lnk"));
[email protected]8ea8f1ef2013-01-06 18:39:03770
grt973412f2017-02-25 08:04:21771 bool is_per_user_install = InstallUtil::IsPerUserInstall();
[email protected]8ea8f1ef2013-01-06 18:39:03772
773 int shortcuts_migrated = 0;
[email protected]650b2d52013-02-10 03:41:45774 base::FilePath target_path;
[email protected]0085863a2013-12-06 21:19:03775 base::string16 arguments;
[email protected]07983302013-01-21 19:41:44776 base::win::ScopedPropVariant propvariant;
[email protected]650b2d52013-02-10 03:41:45777 for (base::FilePath shortcut = shortcuts_enum.Next(); !shortcut.empty();
[email protected]8ea8f1ef2013-01-06 18:39:03778 shortcut = shortcuts_enum.Next()) {
779 // TODO(gab): Use ProgramCompare instead of comparing FilePaths below once
780 // it is fixed to work with FilePaths with spaces.
781 if (!base::win::ResolveShortcut(shortcut, &target_path, &arguments) ||
782 chrome_exe != target_path) {
783 continue;
784 }
avi556c05022014-12-22 23:31:43785 base::CommandLine command_line(
786 base::CommandLine::FromString(base::StringPrintf(
787 L"\"%ls\" %ls", target_path.value().c_str(), arguments.c_str())));
[email protected]8ea8f1ef2013-01-06 18:39:03788
789 // Get the expected AppId for this Chrome shortcut.
[email protected]0085863a2013-12-06 21:19:03790 base::string16 expected_app_id(
[email protected]8ea8f1ef2013-01-06 18:39:03791 GetExpectedAppId(command_line, is_per_user_install));
792 if (expected_app_id.empty())
793 continue;
794
795 // Load the shortcut.
Robert Liaob2bc703d2017-10-17 20:52:35796 Microsoft::WRL::ComPtr<IShellLink> shell_link;
797 Microsoft::WRL::ComPtr<IPersistFile> persist_file;
robliaoa872e992017-05-18 06:36:19798 if (FAILED(::CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
799 IID_PPV_ARGS(&shell_link))) ||
robliaoa7956aa2017-05-17 19:10:54800 FAILED(shell_link.CopyTo(persist_file.GetAddressOf())) ||
[email protected]8ea8f1ef2013-01-06 18:39:03801 FAILED(persist_file->Load(shortcut.value().c_str(), STGM_READ))) {
802 DLOG(WARNING) << "Failed loading shortcut at " << shortcut.value();
803 continue;
804 }
805
806 // Any properties that need to be updated on the shortcut will be stored in
807 // |updated_properties|.
808 base::win::ShortcutProperties updated_properties;
809
David Bienvenu7f9c2bf2019-07-15 13:37:34810 base::string16 current_app_id;
811
[email protected]8ea8f1ef2013-01-06 18:39:03812 // Validate the existing app id for the shortcut.
Robert Liaob2bc703d2017-10-17 20:52:35813 Microsoft::WRL::ComPtr<IPropertyStore> property_store;
[email protected]07983302013-01-21 19:41:44814 propvariant.Reset();
robliaoa7956aa2017-05-17 19:10:54815 if (FAILED(shell_link.CopyTo(property_store.GetAddressOf())) ||
dcheng81762e02014-11-21 21:22:28816 property_store->GetValue(PKEY_AppUserModel_ID, propvariant.Receive()) !=
817 S_OK) {
[email protected]8ea8f1ef2013-01-06 18:39:03818 // When in doubt, prefer not updating the shortcut.
819 NOTREACHED();
820 continue;
[email protected]8ea8f1ef2013-01-06 18:39:03821 } else {
[email protected]07983302013-01-21 19:41:44822 switch (propvariant.get().vt) {
823 case VT_EMPTY:
824 // If there is no app_id set, set our app_id if one is expected.
825 if (!expected_app_id.empty())
826 updated_properties.set_app_id(expected_app_id);
827 break;
828 case VT_LPWSTR:
David Bienvenu7f9c2bf2019-07-15 13:37:34829 current_app_id = base::string16(propvariant.get().pwszVal);
830 if (expected_app_id != current_app_id)
[email protected]07983302013-01-21 19:41:44831 updated_properties.set_app_id(expected_app_id);
832 break;
833 default:
834 NOTREACHED();
835 continue;
[email protected]8ea8f1ef2013-01-06 18:39:03836 }
837 }
838
gab88257b62016-01-15 03:19:19839 // Clear dual_mode property from any shortcuts that previously had it (it
840 // was only ever installed on shortcuts with the
841 // |default_chromium_model_id|).
[email protected]0085863a2013-12-06 21:19:03842 base::string16 default_chromium_model_id(
grtc98803462017-03-08 10:05:08843 ShellUtil::GetBrowserModelId(is_per_user_install));
David Bienvenu7f9c2bf2019-07-15 13:37:34844 if (current_app_id == default_chromium_model_id) {
[email protected]07983302013-01-21 19:41:44845 propvariant.Reset();
[email protected]8ea8f1ef2013-01-06 18:39:03846 if (property_store->GetValue(PKEY_AppUserModel_IsDualMode,
[email protected]07983302013-01-21 19:41:44847 propvariant.Receive()) != S_OK) {
[email protected]8ea8f1ef2013-01-06 18:39:03848 // When in doubt, prefer to not update the shortcut.
849 NOTREACHED();
850 continue;
gab38d242d4d2016-01-14 20:38:12851 }
852 if (propvariant.get().vt == VT_BOOL &&
853 !!propvariant.get().boolVal) {
854 updated_properties.set_dual_mode(false);
[email protected]8ea8f1ef2013-01-06 18:39:03855 }
856 }
857
robliao9212cb42017-04-06 17:43:39858 persist_file.Reset();
859 shell_link.Reset();
[email protected]8ea8f1ef2013-01-06 18:39:03860
861 // Update the shortcut if some of its properties need to be updated.
862 if (updated_properties.options &&
863 base::win::CreateOrUpdateShortcutLink(
864 shortcut, updated_properties,
865 base::win::SHORTCUT_UPDATE_EXISTING)) {
866 ++shortcuts_migrated;
867 }
868 }
869 return shortcuts_migrated;
870}
871
pmonette32a5cfb42016-04-11 22:04:44872} // namespace win
873
pmonette9fa59e882016-02-10 00:12:19874} // namespace shell_integration