blob: 4ee45c55a33e9e2c5a81d908e6838bcd9931df0d [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"
grtc291eea2016-05-26 15:38:4846#include "chrome/browser/win/settings_app_monitor.h"
[email protected]d24c4012009-07-28 01:57:3147#include "chrome/common/chrome_constants.h"
[email protected]12f520c2010-01-06 18:11:1548#include "chrome/common/chrome_paths_internal.h"
[email protected]c9bb06f42010-01-13 23:53:4849#include "chrome/common/chrome_switches.h"
grtc98803462017-03-08 10:05:0850#include "chrome/install_static/install_util.h"
[email protected]3f69d6e612012-08-03 18:52:2751#include "chrome/installer/util/install_util.h"
pmonette0c40087a2016-04-21 00:05:1652#include "chrome/installer/util/scoped_user_protocol_entry.h"
[email protected]d24c4012009-07-28 01:57:3153#include "chrome/installer/util/shell_util.h"
Ken Rockotc74e3792018-02-10 16:31:3054#include "chrome/services/util_win/public/mojom/constants.mojom.h"
Patrick Monetteafe350c2019-01-21 17:59:3555#include "chrome/services/util_win/public/mojom/util_win.mojom.h"
pmonette2b1dbee2016-01-08 20:18:5856#include "components/variations/variations_associated_data.h"
Jay Civellid4b89052017-10-31 17:48:2557#include "services/service_manager/public/cpp/connector.h"
[email protected]d24c4012009-07-28 01:57:3158
pmonette9fa59e882016-02-10 00:12:1959namespace shell_integration {
60
[email protected]12f520c2010-01-06 18:11:1561namespace {
62
pmonette9fa59e882016-02-10 00:12:1963// Helper function for GetAppId to generates profile id
[email protected]2f1c09d2011-01-14 14:58:1464// from profile path. "profile_id" is composed of sanitized basenames of
[email protected]12f520c2010-01-06 18:11:1565// user data dir and profile dir joined by a ".".
[email protected]6a72a632013-12-12 22:22:0066base::string16 GetProfileIdFromPath(const base::FilePath& profile_path) {
[email protected]12f520c2010-01-06 18:11:1567 // Return empty string if profile_path is empty
68 if (profile_path.empty())
[email protected]0085863a2013-12-06 21:19:0369 return base::string16();
[email protected]12f520c2010-01-06 18:11:1570
[email protected]650b2d52013-02-10 03:41:4571 base::FilePath default_user_data_dir;
[email protected]12f520c2010-01-06 18:11:1572 // Return empty string if profile_path is in default user data
73 // dir and is the default profile.
74 if (chrome::GetDefaultUserDataDirectory(&default_user_data_dir) &&
75 profile_path.DirName() == default_user_data_dir &&
[email protected]162b5992011-03-15 19:40:4876 profile_path.BaseName().value() ==
[email protected]f911df52013-12-24 23:24:2377 base::ASCIIToUTF16(chrome::kInitialProfile)) {
[email protected]0085863a2013-12-06 21:19:0378 return base::string16();
[email protected]162b5992011-03-15 19:40:4879 }
[email protected]12f520c2010-01-06 18:11:1580
81 // Get joined basenames of user data dir and profile.
[email protected]0085863a2013-12-06 21:19:0382 base::string16 basenames = profile_path.DirName().BaseName().value() +
[email protected]12f520c2010-01-06 18:11:1583 L"." + profile_path.BaseName().value();
84
[email protected]0085863a2013-12-06 21:19:0385 base::string16 profile_id;
[email protected]12f520c2010-01-06 18:11:1586 profile_id.reserve(basenames.size());
87
88 // Generate profile_id from sanitized basenames.
89 for (size_t i = 0; i < basenames.length(); ++i) {
brettwb3413062015-06-24 00:39:0290 if (base::IsAsciiAlpha(basenames[i]) ||
91 base::IsAsciiDigit(basenames[i]) ||
[email protected]12f520c2010-01-06 18:11:1592 basenames[i] == L'.')
93 profile_id += basenames[i];
94 }
95
96 return profile_id;
97}
98
taptedc2c690ab2016-05-25 02:49:0399base::string16 GetAppListAppName() {
100 static const base::char16 kAppListAppNameSuffix[] = L"AppList";
grtc98803462017-03-08 10:05:08101 base::string16 app_name(install_static::GetBaseAppId());
taptedc2c690ab2016-05-25 02:49:03102 app_name.append(kAppListAppNameSuffix);
103 return app_name;
104}
105
[email protected]8ea8f1ef2013-01-06 18:39:03106// Gets expected app id for given Chrome (based on |command_line| and
107// |is_per_user_install|).
avi556c05022014-12-22 23:31:43108base::string16 GetExpectedAppId(const base::CommandLine& command_line,
[email protected]6a72a632013-12-12 22:22:00109 bool is_per_user_install) {
[email protected]b39e32e2013-04-24 08:55:54110 base::FilePath user_data_dir;
111 if (command_line.HasSwitch(switches::kUserDataDir))
112 user_data_dir = command_line.GetSwitchValuePath(switches::kUserDataDir);
113 else
114 chrome::GetDefaultUserDataDirectory(&user_data_dir);
[email protected]89d43832013-06-29 20:25:20115 // Adjust with any policy that overrides any other way to set the path.
116 policy::path_parser::CheckUserDataDirPolicy(&user_data_dir);
[email protected]b39e32e2013-04-24 08:55:54117 DCHECK(!user_data_dir.empty());
[email protected]c9bb06f42010-01-13 23:53:48118
[email protected]b39e32e2013-04-24 08:55:54119 base::FilePath profile_subdir;
120 if (command_line.HasSwitch(switches::kProfileDirectory)) {
121 profile_subdir =
122 command_line.GetSwitchValuePath(switches::kProfileDirectory);
123 } else {
[email protected]f911df52013-12-24 23:24:23124 profile_subdir =
125 base::FilePath(base::ASCIIToUTF16(chrome::kInitialProfile));
[email protected]b39e32e2013-04-24 08:55:54126 }
127 DCHECK(!profile_subdir.empty());
128
129 base::FilePath profile_path = user_data_dir.Append(profile_subdir);
[email protected]0085863a2013-12-06 21:19:03130 base::string16 app_name;
[email protected]c9bb06f42010-01-13 23:53:48131 if (command_line.HasSwitch(switches::kApp)) {
[email protected]f911df52013-12-24 23:24:23132 app_name = base::UTF8ToUTF16(web_app::GenerateApplicationNameFromURL(
[email protected]57ecc4b2010-08-11 03:02:51133 GURL(command_line.GetSwitchValueASCII(switches::kApp))));
[email protected]2f1c09d2011-01-14 14:58:14134 } else if (command_line.HasSwitch(switches::kAppId)) {
Alexey Baskakov2ce2ffc842018-08-06 02:57:15135 app_name = base::UTF8ToUTF16(web_app::GenerateApplicationNameFromAppId(
136 command_line.GetSwitchValueASCII(switches::kAppId)));
taptedc2c690ab2016-05-25 02:49:03137 } else if (command_line.HasSwitch(switches::kShowAppList)) {
138 app_name = GetAppListAppName();
[email protected]c9bb06f42010-01-13 23:53:48139 } else {
grtc98803462017-03-08 10:05:08140 app_name = ShellUtil::GetBrowserModelId(is_per_user_install);
[email protected]c9bb06f42010-01-13 23:53:48141 }
[email protected]b39e32e2013-04-24 08:55:54142 DCHECK(!app_name.empty());
[email protected]c9bb06f42010-01-13 23:53:48143
pmonette9e4c1a82016-04-14 18:15:30144 return win::GetAppModelIdForProfile(app_name, profile_path);
[email protected]c9bb06f42010-01-13 23:53:48145}
146
gab88257b62016-01-15 03:19:19147void MigrateTaskbarPinsCallback() {
[email protected]3a3e72c2011-11-29 02:59:38148 // Get full path of chrome.
[email protected]650b2d52013-02-10 03:41:45149 base::FilePath chrome_exe;
Avi Drissman9098f9002018-05-04 00:11:52150 if (!base::PathService::Get(base::FILE_EXE, &chrome_exe))
[email protected]3a3e72c2011-11-29 02:59:38151 return;
152
gab88257b62016-01-15 03:19:19153 base::FilePath pins_path;
Avi Drissman9098f9002018-05-04 00:11:52154 if (!base::PathService::Get(base::DIR_TASKBAR_PINS, &pins_path)) {
gab88257b62016-01-15 03:19:19155 NOTREACHED();
156 return;
[email protected]3a3e72c2011-11-29 02:59:38157 }
gab88257b62016-01-15 03:19:19158
pmonette9e4c1a82016-04-14 18:15:30159 win::MigrateShortcutsInPathInternal(chrome_exe, pins_path);
[email protected]3a3e72c2011-11-29 02:59:38160}
161
Mustafa Emre Acerd19a75fc2018-10-08 18:15:14162// Windows treats a given scheme as an Internet scheme only if its registry
163// entry has a "URL Protocol" key. Check this, otherwise we allow ProgIDs to be
164// used as custom protocols which leads to security bugs.
165bool IsValidCustomProtocol(const base::string16& scheme) {
166 if (scheme.empty())
167 return false;
168 base::win::RegKey cmd_key(HKEY_CLASSES_ROOT, scheme.c_str(), KEY_QUERY_VALUE);
169 return cmd_key.Valid() && cmd_key.HasValue(L"URL Protocol");
170}
171
[email protected]caa05352014-03-01 00:43:05172// Windows 8 introduced a new protocol->executable binding system which cannot
173// be retrieved in the HKCR registry subkey method implemented below. We call
174// AssocQueryString with the new Win8-only flag ASSOCF_IS_PROTOCOL instead.
175base::string16 GetAppForProtocolUsingAssocQuery(const GURL& url) {
Mustafa Emre Acerd19a75fc2018-10-08 18:15:14176 const base::string16 url_scheme = base::ASCIIToUTF16(url.scheme());
177 if (!IsValidCustomProtocol(url_scheme))
[email protected]caa05352014-03-01 00:43:05178 return base::string16();
179
[email protected]caa05352014-03-01 00:43:05180 // Query AssocQueryString for a human-readable description of the program
181 // that will be invoked given the provided URL spec. This is used only to
182 // populate the external protocol dialog box the user sees when invoking
183 // an unknown external protocol.
184 wchar_t out_buffer[1024];
Avi Drissman22f82872018-12-25 23:09:07185 DWORD buffer_size = base::size(out_buffer);
Mustafa Emre Acerd19a75fc2018-10-08 18:15:14186 HRESULT hr =
187 AssocQueryString(ASSOCF_IS_PROTOCOL, ASSOCSTR_FRIENDLYAPPNAME,
188 url_scheme.c_str(), NULL, out_buffer, &buffer_size);
[email protected]caa05352014-03-01 00:43:05189 if (FAILED(hr)) {
190 DLOG(WARNING) << "AssocQueryString failed!";
191 return base::string16();
192 }
193 return base::string16(out_buffer);
194}
195
196base::string16 GetAppForProtocolUsingRegistry(const GURL& url) {
Mustafa Emre Acerd19a75fc2018-10-08 18:15:14197 const base::string16 url_scheme = base::ASCIIToUTF16(url.scheme());
198 if (!IsValidCustomProtocol(url_scheme))
199 return base::string16();
dominickn0c9a5062016-10-06 20:49:00200
201 // First, try and extract the application's display name.
Mustafa Emre Acerd19a75fc2018-10-08 18:15:14202 base::string16 command_to_launch;
203 base::win::RegKey cmd_key_name(HKEY_CLASSES_ROOT, url_scheme.c_str(),
dominickn0c9a5062016-10-06 20:49:00204 KEY_READ);
205 if (cmd_key_name.ReadValue(NULL, &command_to_launch) == ERROR_SUCCESS &&
206 !command_to_launch.empty()) {
207 return command_to_launch;
[email protected]caa05352014-03-01 00:43:05208 }
dominickn0c9a5062016-10-06 20:49:00209
210 // Otherwise, parse the command line in the registry, and return the basename
211 // of the program path if it exists.
Mustafa Emre Acerd19a75fc2018-10-08 18:15:14212 const base::string16 cmd_key_path = url_scheme + L"\\shell\\open\\command";
dominickn0c9a5062016-10-06 20:49:00213 base::win::RegKey cmd_key_exe(HKEY_CLASSES_ROOT, cmd_key_path.c_str(),
214 KEY_READ);
215 if (cmd_key_exe.ReadValue(NULL, &command_to_launch) == ERROR_SUCCESS) {
216 base::CommandLine command_line(
217 base::CommandLine::FromString(command_to_launch));
218 return command_line.GetProgram().BaseName().value();
219 }
220
[email protected]caa05352014-03-01 00:43:05221 return base::string16();
222}
223
pmonette9fa59e882016-02-10 00:12:19224DefaultWebClientState GetDefaultWebClientStateFromShellUtilDefaultState(
225 ShellUtil::DefaultState default_state) {
[email protected]eb63da72012-10-15 20:39:48226 switch (default_state) {
Greg Thompson75b1c8e2017-08-16 13:36:28227 case ShellUtil::UNKNOWN_DEFAULT:
228 return DefaultWebClientState::UNKNOWN_DEFAULT;
[email protected]eb63da72012-10-15 20:39:48229 case ShellUtil::NOT_DEFAULT:
pmonette9fa59e882016-02-10 00:12:19230 return DefaultWebClientState::NOT_DEFAULT;
[email protected]eb63da72012-10-15 20:39:48231 case ShellUtil::IS_DEFAULT:
pmonette9fa59e882016-02-10 00:12:19232 return DefaultWebClientState::IS_DEFAULT;
Greg Thompson75b1c8e2017-08-16 13:36:28233 case ShellUtil::OTHER_MODE_IS_DEFAULT:
234 return DefaultWebClientState::OTHER_MODE_IS_DEFAULT;
[email protected]eb63da72012-10-15 20:39:48235 }
Greg Thompson75b1c8e2017-08-16 13:36:28236 NOTREACHED();
237 return DefaultWebClientState::UNKNOWN_DEFAULT;
[email protected]eb63da72012-10-15 20:39:48238}
239
grtc291eea2016-05-26 15:38:48240// A recorder of user actions in the Windows Settings app.
Patrick Monette7ff99e02018-02-19 17:30:12241class DefaultBrowserActionRecorder : public SettingsAppMonitor::Delegate {
grtc291eea2016-05-26 15:38:48242 public:
243 // Creates the recorder and the monitor that drives it. |continuation| will be
244 // run once the monitor's initialization completes (regardless of success or
245 // failure).
246 explicit DefaultBrowserActionRecorder(base::Closure continuation)
247 : continuation_(std::move(continuation)), settings_app_monitor_(this) {}
248
249 private:
250 // win::SettingsAppMonitor::Delegate:
251 void OnInitialized(HRESULT result) override {
Greg Thompsonea27a2c2019-06-25 19:22:22252 // UMA indicates that this succeeds > 99.98% of the time.
grtc291eea2016-05-26 15:38:48253 if (SUCCEEDED(result)) {
254 base::RecordAction(
255 base::UserMetricsAction("SettingsAppMonitor.Initialized"));
256 }
257 continuation_.Run();
258 continuation_ = base::Closure();
259 }
260
261 void OnAppFocused() override {
262 base::RecordAction(
263 base::UserMetricsAction("SettingsAppMonitor.AppFocused"));
264 }
265
266 void OnChooserInvoked() override {
267 base::RecordAction(
268 base::UserMetricsAction("SettingsAppMonitor.ChooserInvoked"));
269 }
270
271 void OnBrowserChosen(const base::string16& browser_name) override {
Greg Thompson005a710c2018-09-06 06:54:38272 if (browser_name == InstallUtil::GetDisplayName()) {
grtc291eea2016-05-26 15:38:48273 base::RecordAction(
274 base::UserMetricsAction("SettingsAppMonitor.ChromeBrowserChosen"));
275 } else {
276 base::RecordAction(
277 base::UserMetricsAction("SettingsAppMonitor.OtherBrowserChosen"));
278 }
279 }
280
pmonetteb66514b42016-12-07 20:28:39281 void OnPromoFocused() override {
282 base::RecordAction(
283 base::UserMetricsAction("SettingsAppMonitor.PromoFocused"));
284 }
285
286 void OnPromoChoiceMade(bool accept_promo) override {
287 if (accept_promo) {
288 base::RecordAction(
289 base::UserMetricsAction("SettingsAppMonitor.CheckItOut"));
290 } else {
291 base::RecordAction(
292 base::UserMetricsAction("SettingsAppMonitor.SwitchAnyway"));
293 }
294 }
295
grtc291eea2016-05-26 15:38:48296 // A closure to be run once initialization completes.
297 base::Closure continuation_;
298
299 // Monitors user interaction with the Windows Settings app for the sake of
300 // reporting user actions.
Patrick Monette7ff99e02018-02-19 17:30:12301 SettingsAppMonitor settings_app_monitor_;
grtc291eea2016-05-26 15:38:48302
303 DISALLOW_COPY_AND_ASSIGN(DefaultBrowserActionRecorder);
304};
305
306// A function bound up in a callback with a DefaultBrowserActionRecorder and
307// a closure to keep the former alive until the time comes to run the latter.
308void OnSettingsAppFinished(
309 std::unique_ptr<DefaultBrowserActionRecorder> recorder,
310 const base::Closure& on_finished_callback) {
311 recorder.reset();
312 on_finished_callback.Run();
313}
314
pmonette32a5cfb42016-04-11 22:04:44315// There is no way to make sure the user is done with the system settings, but a
316// signal that the interaction is finished is needed for UMA. A timer of 2
317// minutes is used as a substitute. The registry keys for the protocol
318// association with an app are also monitored to signal the end of the
319// interaction early when it is clear that the user made a choice (e.g. http
320// and https for default browser).
321//
322// This helper class manages both the timer and the registry watchers and makes
323// sure the callback for the end of the settings interaction is only run once.
324// This class also manages its own lifetime.
325class OpenSystemSettingsHelper {
326 public:
327 // Begin the monitoring and will call |on_finished_callback| when done.
328 // Takes in a null-terminated array of |protocols| whose registry keys must be
pmonette0c40087a2016-04-21 00:05:16329 // watched. The array must contain at least one element.
pmonette32a5cfb42016-04-11 22:04:44330 static void Begin(const wchar_t* const protocols[],
331 const base::Closure& on_finished_callback) {
pmonettec541d942016-06-15 22:08:32332 delete instance_;
333 instance_ = new OpenSystemSettingsHelper(protocols, on_finished_callback);
pmonette32a5cfb42016-04-11 22:04:44334 }
335
336 private:
337 // The reason the settings interaction concluded. Do not modify the ordering
338 // because it is used for UMA.
339 enum ConcludeReason { REGISTRY_WATCHER, TIMEOUT, NUM_CONCLUDE_REASON_TYPES };
340
341 OpenSystemSettingsHelper(const wchar_t* const protocols[],
342 const base::Closure& on_finished_callback)
pmonette0c40087a2016-04-21 00:05:16343 : scoped_user_protocol_entry_(protocols[0]),
344 on_finished_callback_(on_finished_callback),
345 weak_ptr_factory_(this) {
pmonette32a5cfb42016-04-11 22:04:44346 static const wchar_t kUrlAssociationFormat[] =
347 L"SOFTWARE\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\"
348 L"%ls\\UserChoice";
349
350 // Remember the start time.
351 start_time_ = base::TimeTicks::Now();
352
353 for (const wchar_t* const* scan = &protocols[0]; *scan != nullptr; ++scan) {
354 AddRegistryKeyWatcher(
355 base::StringPrintf(kUrlAssociationFormat, *scan).c_str());
356 }
357 // Only the watchers that were succesfully initialized are counted.
358 registry_watcher_count_ = registry_key_watchers_.size();
359
360 timer_.Start(
361 FROM_HERE, base::TimeDelta::FromMinutes(2),
362 base::Bind(&OpenSystemSettingsHelper::ConcludeInteraction,
363 weak_ptr_factory_.GetWeakPtr(), ConcludeReason::TIMEOUT));
364 }
365
pmonetteb18379f2017-06-01 01:18:44366 ~OpenSystemSettingsHelper() {
367 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
368 }
369
pmonette32a5cfb42016-04-11 22:04:44370 // Called when a change is detected on one of the registry keys being watched.
371 // Note: All types of modification to the registry key will trigger this
372 // function even if the value change is the only one that matters. This
373 // is good enough for now.
374 void OnRegistryKeyChanged() {
pmonetteb18379f2017-06-01 01:18:44375 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
pmonette32a5cfb42016-04-11 22:04:44376 // Make sure all the registry watchers have fired.
377 if (--registry_watcher_count_ == 0) {
378 UMA_HISTOGRAM_MEDIUM_TIMES(
379 "DefaultBrowser.SettingsInteraction.RegistryWatcherDuration",
380 base::TimeTicks::Now() - start_time_);
381
382 ConcludeInteraction(ConcludeReason::REGISTRY_WATCHER);
383 }
384 }
385
386 // Ends the monitoring with the system settings. Will call
387 // |on_finished_callback_| and then dispose of this class instance to make
388 // sure the callback won't get called subsequently.
389 void ConcludeInteraction(ConcludeReason conclude_reason) {
pmonetteb18379f2017-06-01 01:18:44390 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
pmonette32a5cfb42016-04-11 22:04:44391
392 UMA_HISTOGRAM_ENUMERATION(
393 "DefaultBrowser.SettingsInteraction.ConcludeReason", conclude_reason,
394 NUM_CONCLUDE_REASON_TYPES);
395 on_finished_callback_.Run();
pmonettec541d942016-06-15 22:08:32396 delete instance_;
397 instance_ = nullptr;
pmonette32a5cfb42016-04-11 22:04:44398 }
399
400 // Helper function to create a registry watcher for a given |key_path|. Do
401 // nothing on initialization failure.
402 void AddRegistryKeyWatcher(const wchar_t* key_path) {
pmonetteb18379f2017-06-01 01:18:44403 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
404
Jeremy Romanec48d7a2018-03-01 17:35:09405 auto reg_key = std::make_unique<base::win::RegKey>(HKEY_CURRENT_USER,
ricea86fa1dd2016-09-13 05:59:45406 key_path, KEY_NOTIFY);
pmonette32a5cfb42016-04-11 22:04:44407
408 if (reg_key->Valid() &&
409 reg_key->StartWatching(
410 base::Bind(&OpenSystemSettingsHelper::OnRegistryKeyChanged,
411 weak_ptr_factory_.GetWeakPtr()))) {
412 registry_key_watchers_.push_back(std::move(reg_key));
413 }
414 }
415
pmonettec541d942016-06-15 22:08:32416 // Used to make sure only one instance is alive at the same time.
417 static OpenSystemSettingsHelper* instance_;
418
pmonette0c40087a2016-04-21 00:05:16419 // This is needed to make sure that Windows displays an entry for the protocol
420 // inside the "Choose default apps by protocol" settings page.
421 ScopedUserProtocolEntry scoped_user_protocol_entry_;
422
pmonette32a5cfb42016-04-11 22:04:44423 // The function to call when the interaction with the system settings is
424 // finished.
425 base::Closure on_finished_callback_;
426
427 // The number of time the registry key watchers must fire.
428 int registry_watcher_count_ = 0;
429
430 // There can be multiple registry key watchers as some settings modify
431 // multiple protocol associations. e.g. Changing the default browser modifies
432 // the http and https associations.
dcheng4af48582016-04-19 00:29:35433 std::vector<std::unique_ptr<base::win::RegKey>> registry_key_watchers_;
pmonette32a5cfb42016-04-11 22:04:44434
435 base::OneShotTimer timer_;
436
437 // Records the time it takes for the final registry watcher to get signaled.
438 base::TimeTicks start_time_;
439
pmonetteb18379f2017-06-01 01:18:44440 SEQUENCE_CHECKER(sequence_checker_);
441
pmonette32a5cfb42016-04-11 22:04:44442 // Weak ptrs are used to bind this class to the callbacks of the timer and the
443 // registry watcher. This makes it possible to self-delete after one of the
444 // callbacks is executed to cancel the remaining ones.
445 base::WeakPtrFactory<OpenSystemSettingsHelper> weak_ptr_factory_;
446
447 DISALLOW_COPY_AND_ASSIGN(OpenSystemSettingsHelper);
448};
449
pmonettec541d942016-06-15 22:08:32450OpenSystemSettingsHelper* OpenSystemSettingsHelper::instance_ = nullptr;
451
pmonettea98315242016-11-23 21:39:18452// Helper class to determine if Chrome is pinned to the taskbar. Hides the
Jay Civellid4b89052017-10-31 17:48:25453// complexity of managing the lifetime of the connection to the ChromeWinUtil
454// service.
pmonettea98315242016-11-23 21:39:18455class IsPinnedToTaskbarHelper {
456 public:
457 using ResultCallback = win::IsPinnedToTaskbarCallback;
458 using ErrorCallback = win::ConnectionErrorCallback;
Jay Civellid4b89052017-10-31 17:48:25459 static void GetState(std::unique_ptr<service_manager::Connector> connector,
460 const ErrorCallback& error_callback,
pmonettea98315242016-11-23 21:39:18461 const ResultCallback& result_callback);
462
463 private:
Jay Civellid4b89052017-10-31 17:48:25464 IsPinnedToTaskbarHelper(std::unique_ptr<service_manager::Connector> connector,
465 const ErrorCallback& error_callback,
pmonettea98315242016-11-23 21:39:18466 const ResultCallback& result_callback);
467
468 void OnConnectionError();
469 void OnIsPinnedToTaskbarResult(bool succeeded, bool is_pinned_to_taskbar);
470
Patrick Monetteafe350c2019-01-21 17:59:35471 chrome::mojom::UtilWinPtr util_win_ptr_;
Ken Rockoteff2b822019-06-20 22:56:53472 // The connector used to retrieve the Patch service.
Jay Civellid4b89052017-10-31 17:48:25473 std::unique_ptr<service_manager::Connector> connector_;
pmonettea98315242016-11-23 21:39:18474
475 ErrorCallback error_callback_;
476 ResultCallback result_callback_;
477
pmonetteb18379f2017-06-01 01:18:44478 SEQUENCE_CHECKER(sequence_checker_);
479
pmonettea98315242016-11-23 21:39:18480 DISALLOW_COPY_AND_ASSIGN(IsPinnedToTaskbarHelper);
481};
482
483// static
Jay Civellid4b89052017-10-31 17:48:25484void IsPinnedToTaskbarHelper::GetState(
485 std::unique_ptr<service_manager::Connector> connector,
486 const ErrorCallback& error_callback,
487 const ResultCallback& result_callback) {
pmonettea98315242016-11-23 21:39:18488 // Self-deleting when the ShellHandler completes.
Jay Civellid4b89052017-10-31 17:48:25489 new IsPinnedToTaskbarHelper(std::move(connector), error_callback,
490 result_callback);
pmonette5057ca3b2016-07-04 16:48:40491}
492
pmonettea98315242016-11-23 21:39:18493IsPinnedToTaskbarHelper::IsPinnedToTaskbarHelper(
Jay Civellid4b89052017-10-31 17:48:25494 std::unique_ptr<service_manager::Connector> connector,
pmonettea98315242016-11-23 21:39:18495 const ErrorCallback& error_callback,
496 const ResultCallback& result_callback)
Jay Civellid4b89052017-10-31 17:48:25497 : connector_(std::move(connector)),
pmonettea98315242016-11-23 21:39:18498 error_callback_(error_callback),
499 result_callback_(result_callback) {
pmonette83edc1e2016-12-15 17:36:44500 DCHECK(error_callback_);
501 DCHECK(result_callback_);
502
Patrick Monetteafe350c2019-01-21 17:59:35503 connector_->BindInterface(chrome::mojom::kUtilWinServiceName, &util_win_ptr_);
504 // |util_win_ptr_| owns the callbacks and is guaranteed to be destroyed before
505 // |this|, therefore making base::Unretained() safe to use.
506 util_win_ptr_.set_connection_error_handler(base::Bind(
pmonettea98315242016-11-23 21:39:18507 &IsPinnedToTaskbarHelper::OnConnectionError, base::Unretained(this)));
Patrick Monetteafe350c2019-01-21 17:59:35508 util_win_ptr_->IsPinnedToTaskbar(
pmonettea98315242016-11-23 21:39:18509 base::Bind(&IsPinnedToTaskbarHelper::OnIsPinnedToTaskbarResult,
510 base::Unretained(this)));
511}
512
513void IsPinnedToTaskbarHelper::OnConnectionError() {
pmonetteb18379f2017-06-01 01:18:44514 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
pmonettea98315242016-11-23 21:39:18515 error_callback_.Run();
516 delete this;
517}
518
519void IsPinnedToTaskbarHelper::OnIsPinnedToTaskbarResult(
pmonette5057ca3b2016-07-04 16:48:40520 bool succeeded,
521 bool is_pinned_to_taskbar) {
pmonetteb18379f2017-06-01 01:18:44522 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
523
pmonettea98315242016-11-23 21:39:18524 result_callback_.Run(succeeded, is_pinned_to_taskbar);
525 delete this;
pmonette5057ca3b2016-07-04 16:48:40526}
527
[email protected]3a3e72c2011-11-29 02:59:38528} // namespace
[email protected]12f520c2010-01-06 18:11:15529
pmonette9fa59e882016-02-10 00:12:19530bool SetAsDefaultBrowser() {
Etienne Bergeron436d42212019-02-26 17:15:12531 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
532 base::BlockingType::MAY_BLOCK);
pmonetteb18379f2017-06-01 01:18:44533
[email protected]650b2d52013-02-10 03:41:45534 base::FilePath chrome_exe;
Avi Drissman9098f9002018-05-04 00:11:52535 if (!base::PathService::Get(base::FILE_EXE, &chrome_exe)) {
[email protected]d24c4012009-07-28 01:57:31536 LOG(ERROR) << "Error getting app exe path";
537 return false;
538 }
539
540 // From UI currently we only allow setting default browser for current user.
Greg Thompson8fc94ef2018-09-10 16:53:12541 if (!ShellUtil::MakeChromeDefault(ShellUtil::CURRENT_USER, chrome_exe,
grte76ca2852014-12-05 16:42:10542 true /* elevate_if_not_admin */)) {
[email protected]d24c4012009-07-28 01:57:31543 LOG(ERROR) << "Chrome could not be set as default browser.";
544 return false;
545 }
546
[email protected]8e96e502010-10-21 20:57:12547 VLOG(1) << "Chrome registered as default browser.";
[email protected]d24c4012009-07-28 01:57:31548 return true;
549}
550
pmonette9fa59e882016-02-10 00:12:19551bool SetAsDefaultProtocolClient(const std::string& protocol) {
Etienne Bergeron436d42212019-02-26 17:15:12552 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
553 base::BlockingType::MAY_BLOCK);
pmonetteb18379f2017-06-01 01:18:44554
[email protected]4468a5b2011-05-26 07:48:02555 if (protocol.empty())
556 return false;
557
[email protected]650b2d52013-02-10 03:41:45558 base::FilePath chrome_exe;
Avi Drissman9098f9002018-05-04 00:11:52559 if (!base::PathService::Get(base::FILE_EXE, &chrome_exe)) {
[email protected]4468a5b2011-05-26 07:48:02560 LOG(ERROR) << "Error getting app exe path";
561 return false;
562 }
563
[email protected]f911df52013-12-24 23:24:23564 base::string16 wprotocol(base::UTF8ToUTF16(protocol));
Greg Thompson8fc94ef2018-09-10 16:53:12565 if (!ShellUtil::MakeChromeDefaultProtocolClient(chrome_exe, wprotocol)) {
[email protected]4468a5b2011-05-26 07:48:02566 LOG(ERROR) << "Chrome could not be set as default handler for "
567 << protocol << ".";
568 return false;
569 }
570
571 VLOG(1) << "Chrome registered as default handler for " << protocol << ".";
572 return true;
573}
574
pmonette32a5cfb42016-04-11 22:04:44575DefaultWebClientSetPermission GetDefaultWebClientSetPermission() {
grt509bc942017-03-25 08:43:21576 if (!install_static::SupportsSetAsDefaultBrowser())
pmonette034a03d92015-10-02 21:04:27577 return SET_DEFAULT_NOT_ALLOWED;
pmonette034a03d92015-10-02 21:04:27578 if (ShellUtil::CanMakeChromeDefaultUnattended())
579 return SET_DEFAULT_UNATTENDED;
pmonette32a5cfb42016-04-11 22:04:44580 // Windows 8 and 10 both introduced a new way to set the default web client
581 // which require user interaction.
pmonettef89ac7c72015-10-06 03:22:01582 return SET_DEFAULT_INTERACTIVE;
pmonette034a03d92015-10-02 21:04:27583}
584
pmonette9fa59e882016-02-10 00:12:19585bool IsElevationNeededForSettingDefaultProtocolClient() {
Bruce Dawsonaed9bea2019-04-20 02:30:09586 return base::win::GetVersion() < base::win::Version::WIN8;
pmonette868ca642015-09-02 14:34:02587}
588
pmonette9fa59e882016-02-10 00:12:19589base::string16 GetApplicationNameForProtocol(const GURL& url) {
benwellsd0c67aa2016-07-28 02:57:20590 // Windows 8 or above has a new protocol association query.
Bruce Dawsonaed9bea2019-04-20 02:30:09591 if (base::win::GetVersion() >= base::win::Version::WIN8) {
Mustafa Emre Acerd19a75fc2018-10-08 18:15:14592 base::string16 application_name = GetAppForProtocolUsingAssocQuery(url);
benwellsd0c67aa2016-07-28 02:57:20593 if (!application_name.empty())
594 return application_name;
595 }
596
597 return GetAppForProtocolUsingRegistry(url);
[email protected]42dc9402013-01-30 07:54:20598}
599
pmonette9fa59e882016-02-10 00:12:19600DefaultWebClientState GetDefaultBrowser() {
pmonette034a03d92015-10-02 21:04:27601 return GetDefaultWebClientStateFromShellUtilDefaultState(
602 ShellUtil::GetChromeDefaultState());
603}
604
[email protected]d24c4012009-07-28 01:57:31605// There is no reliable way to say which browser is default on a machine (each
606// browser can have some of the protocols/shortcuts). So we look for only HTTP
607// protocol handler. Even this handler is located at different places in
608// registry on XP and Vista:
609// - HKCR\http\shell\open\command (XP)
610// - HKCU\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\
611// http\UserChoice (Vista)
thakise4a02ff32016-09-20 17:00:42612// This method checks if Firefox is default browser by checking these
[email protected]d24c4012009-07-28 01:57:31613// locations and returns true if Firefox traces are found there. In case of
614// error (or if Firefox is not found)it returns the default value which
615// is false.
pmonette9fa59e882016-02-10 00:12:19616bool IsFirefoxDefaultBrowser() {
Patrick Monetteab240ec2017-07-13 22:49:14617 base::string16 app_cmd;
618 base::win::RegKey key(HKEY_CURRENT_USER, ShellUtil::kRegVistaUrlPrefs,
619 KEY_READ);
620 return key.Valid() && key.ReadValue(L"Progid", &app_cmd) == ERROR_SUCCESS &&
621 app_cmd == L"FirefoxURL";
[email protected]d24c4012009-07-28 01:57:31622}
[email protected]12f520c2010-01-06 18:11:15623
pmonette9fa59e882016-02-10 00:12:19624DefaultWebClientState IsDefaultProtocolClient(const std::string& protocol) {
pmonette034a03d92015-10-02 21:04:27625 return GetDefaultWebClientStateFromShellUtilDefaultState(
626 ShellUtil::GetChromeDefaultProtocolClientState(
627 base::UTF8ToUTF16(protocol)));
628}
629
pmonette9e4c1a82016-04-14 18:15:30630namespace win {
631
632bool SetAsDefaultBrowserUsingIntentPicker() {
Etienne Bergeron436d42212019-02-26 17:15:12633 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
634 base::BlockingType::MAY_BLOCK);
pmonetteb18379f2017-06-01 01:18:44635
pmonette9e4c1a82016-04-14 18:15:30636 base::FilePath chrome_exe;
Avi Drissman9098f9002018-05-04 00:11:52637 if (!base::PathService::Get(base::FILE_EXE, &chrome_exe)) {
pmonette9e4c1a82016-04-14 18:15:30638 NOTREACHED() << "Error getting app exe path";
639 return false;
640 }
641
Greg Thompson8fc94ef2018-09-10 16:53:12642 if (!ShellUtil::ShowMakeChromeDefaultSystemUI(chrome_exe)) {
pmonette9e4c1a82016-04-14 18:15:30643 LOG(ERROR) << "Failed to launch the set-default-browser Windows UI.";
644 return false;
645 }
646
647 VLOG(1) << "Set-default-browser Windows UI completed.";
648 return true;
649}
650
651void SetAsDefaultBrowserUsingSystemSettings(
652 const base::Closure& on_finished_callback) {
pmonette9e4c1a82016-04-14 18:15:30653 base::FilePath chrome_exe;
Avi Drissman9098f9002018-05-04 00:11:52654 if (!base::PathService::Get(base::FILE_EXE, &chrome_exe)) {
pmonette9e4c1a82016-04-14 18:15:30655 NOTREACHED() << "Error getting app exe path";
656 on_finished_callback.Run();
657 return;
658 }
659
grtc291eea2016-05-26 15:38:48660 // Create an action recorder that will open the settings app once it has
661 // initialized.
662 std::unique_ptr<DefaultBrowserActionRecorder> recorder(
663 new DefaultBrowserActionRecorder(base::Bind(
664 base::IgnoreResult(&ShellUtil::ShowMakeChromeDefaultSystemUI),
grtc291eea2016-05-26 15:38:48665 chrome_exe)));
pmonette9e4c1a82016-04-14 18:15:30666
grtc291eea2016-05-26 15:38:48667 // The helper manages its own lifetime. Bind the action recorder
668 // into the finished callback to keep it alive throughout the
669 // interaction.
670 static const wchar_t* const kProtocols[] = {L"http", L"https", nullptr};
671 OpenSystemSettingsHelper::Begin(
672 kProtocols, base::Bind(&OnSettingsAppFinished, base::Passed(&recorder),
673 on_finished_callback));
pmonette9e4c1a82016-04-14 18:15:30674}
675
676bool SetAsDefaultProtocolClientUsingIntentPicker(const std::string& protocol) {
Etienne Bergeron436d42212019-02-26 17:15:12677 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
678 base::BlockingType::MAY_BLOCK);
pmonetteb18379f2017-06-01 01:18:44679
pmonette9e4c1a82016-04-14 18:15:30680 base::FilePath chrome_exe;
Avi Drissman9098f9002018-05-04 00:11:52681 if (!base::PathService::Get(base::FILE_EXE, &chrome_exe)) {
pmonette9e4c1a82016-04-14 18:15:30682 NOTREACHED() << "Error getting app exe path";
683 return false;
684 }
685
pmonette9e4c1a82016-04-14 18:15:30686 base::string16 wprotocol(base::UTF8ToUTF16(protocol));
Greg Thompson8fc94ef2018-09-10 16:53:12687 if (!ShellUtil::ShowMakeChromeDefaultProtocolClientSystemUI(chrome_exe,
pmonette9e4c1a82016-04-14 18:15:30688 wprotocol)) {
689 LOG(ERROR) << "Failed to launch the set-default-client Windows UI.";
690 return false;
691 }
692
693 VLOG(1) << "Set-default-client Windows UI completed.";
694 return true;
695}
696
pmonette0c40087a2016-04-21 00:05:16697void SetAsDefaultProtocolClientUsingSystemSettings(
698 const std::string& protocol,
699 const base::Closure& on_finished_callback) {
pmonette0c40087a2016-04-21 00:05:16700 base::FilePath chrome_exe;
Avi Drissman9098f9002018-05-04 00:11:52701 if (!base::PathService::Get(base::FILE_EXE, &chrome_exe)) {
pmonette0c40087a2016-04-21 00:05:16702 NOTREACHED() << "Error getting app exe path";
703 on_finished_callback.Run();
704 return;
705 }
706
707 // The helper manages its own lifetime.
708 base::string16 wprotocol(base::UTF8ToUTF16(protocol));
709 const wchar_t* const kProtocols[] = {wprotocol.c_str(), nullptr};
710 OpenSystemSettingsHelper::Begin(kProtocols, on_finished_callback);
711
Greg Thompson8fc94ef2018-09-10 16:53:12712 ShellUtil::ShowMakeChromeDefaultProtocolClientSystemUI(chrome_exe, wprotocol);
pmonette0c40087a2016-04-21 00:05:16713}
714
pmonette9fa59e882016-02-10 00:12:19715base::string16 GetAppModelIdForProfile(const base::string16& app_name,
716 const base::FilePath& profile_path) {
[email protected]d2065e062013-12-12 23:49:52717 std::vector<base::string16> components;
[email protected]a0448002012-06-19 04:32:10718 components.push_back(app_name);
[email protected]0085863a2013-12-06 21:19:03719 const base::string16 profile_id(GetProfileIdFromPath(profile_path));
[email protected]a0448002012-06-19 04:32:10720 if (!profile_id.empty())
721 components.push_back(profile_id);
722 return ShellUtil::BuildAppModelId(components);
[email protected]12f520c2010-01-06 18:11:15723}
724
pmonette9fa59e882016-02-10 00:12:19725base::string16 GetChromiumModelIdForProfile(
[email protected]650b2d52013-02-10 03:41:45726 const base::FilePath& profile_path) {
[email protected]a0448002012-06-19 04:32:10727 return GetAppModelIdForProfile(
grtc98803462017-03-08 10:05:08728 ShellUtil::GetBrowserModelId(InstallUtil::IsPerUserInstall()),
[email protected]786799692012-09-26 14:16:48729 profile_path);
[email protected]12f520c2010-01-06 18:11:15730}
[email protected]c9bb06f42010-01-13 23:53:48731
pmonette9fa59e882016-02-10 00:12:19732void MigrateTaskbarPins() {
pmonetteb18379f2017-06-01 01:18:44733 // This needs to happen (e.g. so that the appid is fixed and the
734 // run-time Chrome icon is merged with the taskbar shortcut), but it is not an
735 // urgent task.
Robert Liaoe9450f8c2017-06-15 17:23:57736 base::CreateCOMSTATaskRunnerWithTraits(
Gabriel Charetteb10aeebc2018-07-26 20:15:00737 {base::MayBlock(), base::TaskPriority::BEST_EFFORT})
kylechar893ad5772019-02-15 18:31:59738 ->PostTask(FROM_HERE, base::BindOnce(&MigrateTaskbarPinsCallback));
[email protected]c9bb06f42010-01-13 23:53:48739}
[email protected]43903b82012-06-01 05:26:23740
pmonettea98315242016-11-23 21:39:18741void GetIsPinnedToTaskbarState(
Jay Civellid4b89052017-10-31 17:48:25742 std::unique_ptr<service_manager::Connector> connector,
pmonettea98315242016-11-23 21:39:18743 const ConnectionErrorCallback& on_error_callback,
744 const IsPinnedToTaskbarCallback& result_callback) {
Jay Civellid4b89052017-10-31 17:48:25745 IsPinnedToTaskbarHelper::GetState(std::move(connector), on_error_callback,
746 result_callback);
pmonette5057ca3b2016-07-04 16:48:40747}
748
pmonette9fa59e882016-02-10 00:12:19749int MigrateShortcutsInPathInternal(const base::FilePath& chrome_exe,
750 const base::FilePath& path) {
Etienne Bergeron147591d2019-06-19 16:40:12751 // Mitigate the issues caused by loading DLLs on a background thread
752 // (http://crbug/973868).
753 base::ScopedThreadMayLoadLibraryOnBackgroundThread priority_boost(FROM_HERE);
754
[email protected]8ea8f1ef2013-01-06 18:39:03755 // Enumerate all pinned shortcuts in the given path directly.
[email protected]25a4c1c2013-06-08 04:53:36756 base::FileEnumerator shortcuts_enum(
[email protected]8ea8f1ef2013-01-06 18:39:03757 path, false, // not recursive
[email protected]25a4c1c2013-06-08 04:53:36758 base::FileEnumerator::FILES, FILE_PATH_LITERAL("*.lnk"));
[email protected]8ea8f1ef2013-01-06 18:39:03759
grt973412f2017-02-25 08:04:21760 bool is_per_user_install = InstallUtil::IsPerUserInstall();
[email protected]8ea8f1ef2013-01-06 18:39:03761
762 int shortcuts_migrated = 0;
[email protected]650b2d52013-02-10 03:41:45763 base::FilePath target_path;
[email protected]0085863a2013-12-06 21:19:03764 base::string16 arguments;
[email protected]07983302013-01-21 19:41:44765 base::win::ScopedPropVariant propvariant;
[email protected]650b2d52013-02-10 03:41:45766 for (base::FilePath shortcut = shortcuts_enum.Next(); !shortcut.empty();
[email protected]8ea8f1ef2013-01-06 18:39:03767 shortcut = shortcuts_enum.Next()) {
768 // TODO(gab): Use ProgramCompare instead of comparing FilePaths below once
769 // it is fixed to work with FilePaths with spaces.
770 if (!base::win::ResolveShortcut(shortcut, &target_path, &arguments) ||
771 chrome_exe != target_path) {
772 continue;
773 }
avi556c05022014-12-22 23:31:43774 base::CommandLine command_line(
775 base::CommandLine::FromString(base::StringPrintf(
776 L"\"%ls\" %ls", target_path.value().c_str(), arguments.c_str())));
[email protected]8ea8f1ef2013-01-06 18:39:03777
778 // Get the expected AppId for this Chrome shortcut.
[email protected]0085863a2013-12-06 21:19:03779 base::string16 expected_app_id(
[email protected]8ea8f1ef2013-01-06 18:39:03780 GetExpectedAppId(command_line, is_per_user_install));
781 if (expected_app_id.empty())
782 continue;
783
784 // Load the shortcut.
Robert Liaob2bc703d2017-10-17 20:52:35785 Microsoft::WRL::ComPtr<IShellLink> shell_link;
786 Microsoft::WRL::ComPtr<IPersistFile> persist_file;
robliaoa872e992017-05-18 06:36:19787 if (FAILED(::CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
788 IID_PPV_ARGS(&shell_link))) ||
robliaoa7956aa2017-05-17 19:10:54789 FAILED(shell_link.CopyTo(persist_file.GetAddressOf())) ||
[email protected]8ea8f1ef2013-01-06 18:39:03790 FAILED(persist_file->Load(shortcut.value().c_str(), STGM_READ))) {
791 DLOG(WARNING) << "Failed loading shortcut at " << shortcut.value();
792 continue;
793 }
794
795 // Any properties that need to be updated on the shortcut will be stored in
796 // |updated_properties|.
797 base::win::ShortcutProperties updated_properties;
798
799 // Validate the existing app id for the shortcut.
Robert Liaob2bc703d2017-10-17 20:52:35800 Microsoft::WRL::ComPtr<IPropertyStore> property_store;
[email protected]07983302013-01-21 19:41:44801 propvariant.Reset();
robliaoa7956aa2017-05-17 19:10:54802 if (FAILED(shell_link.CopyTo(property_store.GetAddressOf())) ||
dcheng81762e02014-11-21 21:22:28803 property_store->GetValue(PKEY_AppUserModel_ID, propvariant.Receive()) !=
804 S_OK) {
[email protected]8ea8f1ef2013-01-06 18:39:03805 // When in doubt, prefer not updating the shortcut.
806 NOTREACHED();
807 continue;
[email protected]8ea8f1ef2013-01-06 18:39:03808 } else {
[email protected]07983302013-01-21 19:41:44809 switch (propvariant.get().vt) {
810 case VT_EMPTY:
811 // If there is no app_id set, set our app_id if one is expected.
812 if (!expected_app_id.empty())
813 updated_properties.set_app_id(expected_app_id);
814 break;
815 case VT_LPWSTR:
[email protected]0085863a2013-12-06 21:19:03816 if (expected_app_id != base::string16(propvariant.get().pwszVal))
[email protected]07983302013-01-21 19:41:44817 updated_properties.set_app_id(expected_app_id);
818 break;
819 default:
820 NOTREACHED();
821 continue;
[email protected]8ea8f1ef2013-01-06 18:39:03822 }
823 }
824
gab88257b62016-01-15 03:19:19825 // Clear dual_mode property from any shortcuts that previously had it (it
826 // was only ever installed on shortcuts with the
827 // |default_chromium_model_id|).
[email protected]0085863a2013-12-06 21:19:03828 base::string16 default_chromium_model_id(
grtc98803462017-03-08 10:05:08829 ShellUtil::GetBrowserModelId(is_per_user_install));
gab88257b62016-01-15 03:19:19830 if (expected_app_id == default_chromium_model_id) {
[email protected]07983302013-01-21 19:41:44831 propvariant.Reset();
[email protected]8ea8f1ef2013-01-06 18:39:03832 if (property_store->GetValue(PKEY_AppUserModel_IsDualMode,
[email protected]07983302013-01-21 19:41:44833 propvariant.Receive()) != S_OK) {
[email protected]8ea8f1ef2013-01-06 18:39:03834 // When in doubt, prefer to not update the shortcut.
835 NOTREACHED();
836 continue;
gab38d242d4d2016-01-14 20:38:12837 }
838 if (propvariant.get().vt == VT_BOOL &&
839 !!propvariant.get().boolVal) {
840 updated_properties.set_dual_mode(false);
[email protected]8ea8f1ef2013-01-06 18:39:03841 }
842 }
843
robliao9212cb42017-04-06 17:43:39844 persist_file.Reset();
845 shell_link.Reset();
[email protected]8ea8f1ef2013-01-06 18:39:03846
847 // Update the shortcut if some of its properties need to be updated.
848 if (updated_properties.options &&
849 base::win::CreateOrUpdateShortcutLink(
850 shortcut, updated_properties,
851 base::win::SHORTCUT_UPDATE_EXISTING)) {
852 ++shortcuts_migrated;
853 }
854 }
855 return shortcuts_migrated;
856}
857
pmonette32a5cfb42016-04-11 22:04:44858} // namespace win
859
pmonette9fa59e882016-02-10 00:12:19860} // namespace shell_integration