blob: 7f4e8012caf189b8edb48eaec685193769a7b24f [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"
avi664c07b2015-12-26 02:18:3124#include "base/macros.h"
pmonette32a5cfb42016-04-11 22:04:4425#include "base/memory/weak_ptr.h"
pmonette32a5cfb42016-04-11 22:04:4426#include "base/metrics/histogram_macros.h"
grtc291eea2016-05-26 15:38:4827#include "base/metrics/user_metrics.h"
28#include "base/metrics/user_metrics_action.h"
[email protected]d24c4012009-07-28 01:57:3129#include "base/path_service.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"
pmonette32a5cfb42016-04-11 22:04:4434#include "base/time/time.h"
35#include "base/timer/timer.h"
[email protected]2d6503982010-10-17 04:41:5436#include "base/win/registry.h"
[email protected]07983302013-01-21 19:41:4437#include "base/win/scoped_propvariant.h"
Danil Chapovalov3518f362018-08-11 16:13:4338#include "base/win/shlwapi.h"
[email protected]f1024e22012-09-12 07:14:5539#include "base/win/shortcut.h"
[email protected]935aa542010-10-15 01:59:1540#include "base/win/windows_version.h"
[email protected]89d43832013-06-29 20:25:2041#include "chrome/browser/policy/policy_path_parser.h"
pmonette32a5cfb42016-04-11 22:04:4442#include "chrome/browser/shell_integration.h"
Alexey Baskakov2f8bf8162018-07-17 01:06:1143#include "chrome/browser/web_applications/components/web_app_helpers.h"
grtc291eea2016-05-26 15:38:4844#include "chrome/browser/win/settings_app_monitor.h"
[email protected]d24c4012009-07-28 01:57:3145#include "chrome/common/chrome_constants.h"
[email protected]12f520c2010-01-06 18:11:1546#include "chrome/common/chrome_paths_internal.h"
[email protected]c9bb06f42010-01-13 23:53:4847#include "chrome/common/chrome_switches.h"
grtc98803462017-03-08 10:05:0848#include "chrome/install_static/install_util.h"
[email protected]3f69d6e612012-08-03 18:52:2749#include "chrome/installer/util/install_util.h"
pmonette0c40087a2016-04-21 00:05:1650#include "chrome/installer/util/scoped_user_protocol_entry.h"
[email protected]d24c4012009-07-28 01:57:3151#include "chrome/installer/util/shell_util.h"
Ken Rockotc74e3792018-02-10 16:31:3052#include "chrome/services/util_win/public/mojom/constants.mojom.h"
53#include "chrome/services/util_win/public/mojom/shell_util_win.mojom.h"
pmonette2b1dbee2016-01-08 20:18:5854#include "components/variations/variations_associated_data.h"
Jay Civellid4b89052017-10-31 17:48:2555#include "content/public/common/service_manager_connection.h"
56#include "services/service_manager/public/cpp/connector.h"
[email protected]d24c4012009-07-28 01:57:3157
pmonette9fa59e882016-02-10 00:12:1958namespace shell_integration {
59
[email protected]12f520c2010-01-06 18:11:1560namespace {
61
pmonette9fa59e882016-02-10 00:12:1962// Helper function for GetAppId to generates profile id
[email protected]2f1c09d2011-01-14 14:58:1463// from profile path. "profile_id" is composed of sanitized basenames of
[email protected]12f520c2010-01-06 18:11:1564// user data dir and profile dir joined by a ".".
[email protected]6a72a632013-12-12 22:22:0065base::string16 GetProfileIdFromPath(const base::FilePath& profile_path) {
[email protected]12f520c2010-01-06 18:11:1566 // Return empty string if profile_path is empty
67 if (profile_path.empty())
[email protected]0085863a2013-12-06 21:19:0368 return base::string16();
[email protected]12f520c2010-01-06 18:11:1569
[email protected]650b2d52013-02-10 03:41:4570 base::FilePath default_user_data_dir;
[email protected]12f520c2010-01-06 18:11:1571 // Return empty string if profile_path is in default user data
72 // dir and is the default profile.
73 if (chrome::GetDefaultUserDataDirectory(&default_user_data_dir) &&
74 profile_path.DirName() == default_user_data_dir &&
[email protected]162b5992011-03-15 19:40:4875 profile_path.BaseName().value() ==
[email protected]f911df52013-12-24 23:24:2376 base::ASCIIToUTF16(chrome::kInitialProfile)) {
[email protected]0085863a2013-12-06 21:19:0377 return base::string16();
[email protected]162b5992011-03-15 19:40:4878 }
[email protected]12f520c2010-01-06 18:11:1579
80 // Get joined basenames of user data dir and profile.
[email protected]0085863a2013-12-06 21:19:0381 base::string16 basenames = profile_path.DirName().BaseName().value() +
[email protected]12f520c2010-01-06 18:11:1582 L"." + profile_path.BaseName().value();
83
[email protected]0085863a2013-12-06 21:19:0384 base::string16 profile_id;
[email protected]12f520c2010-01-06 18:11:1585 profile_id.reserve(basenames.size());
86
87 // Generate profile_id from sanitized basenames.
88 for (size_t i = 0; i < basenames.length(); ++i) {
brettwb3413062015-06-24 00:39:0289 if (base::IsAsciiAlpha(basenames[i]) ||
90 base::IsAsciiDigit(basenames[i]) ||
[email protected]12f520c2010-01-06 18:11:1591 basenames[i] == L'.')
92 profile_id += basenames[i];
93 }
94
95 return profile_id;
96}
97
taptedc2c690ab2016-05-25 02:49:0398base::string16 GetAppListAppName() {
99 static const base::char16 kAppListAppNameSuffix[] = L"AppList";
grtc98803462017-03-08 10:05:08100 base::string16 app_name(install_static::GetBaseAppId());
taptedc2c690ab2016-05-25 02:49:03101 app_name.append(kAppListAppNameSuffix);
102 return app_name;
103}
104
[email protected]8ea8f1ef2013-01-06 18:39:03105// Gets expected app id for given Chrome (based on |command_line| and
106// |is_per_user_install|).
avi556c05022014-12-22 23:31:43107base::string16 GetExpectedAppId(const base::CommandLine& command_line,
[email protected]6a72a632013-12-12 22:22:00108 bool is_per_user_install) {
[email protected]b39e32e2013-04-24 08:55:54109 base::FilePath user_data_dir;
110 if (command_line.HasSwitch(switches::kUserDataDir))
111 user_data_dir = command_line.GetSwitchValuePath(switches::kUserDataDir);
112 else
113 chrome::GetDefaultUserDataDirectory(&user_data_dir);
[email protected]89d43832013-06-29 20:25:20114 // Adjust with any policy that overrides any other way to set the path.
115 policy::path_parser::CheckUserDataDirPolicy(&user_data_dir);
[email protected]b39e32e2013-04-24 08:55:54116 DCHECK(!user_data_dir.empty());
[email protected]c9bb06f42010-01-13 23:53:48117
[email protected]b39e32e2013-04-24 08:55:54118 base::FilePath profile_subdir;
119 if (command_line.HasSwitch(switches::kProfileDirectory)) {
120 profile_subdir =
121 command_line.GetSwitchValuePath(switches::kProfileDirectory);
122 } else {
[email protected]f911df52013-12-24 23:24:23123 profile_subdir =
124 base::FilePath(base::ASCIIToUTF16(chrome::kInitialProfile));
[email protected]b39e32e2013-04-24 08:55:54125 }
126 DCHECK(!profile_subdir.empty());
127
128 base::FilePath profile_path = user_data_dir.Append(profile_subdir);
[email protected]0085863a2013-12-06 21:19:03129 base::string16 app_name;
[email protected]c9bb06f42010-01-13 23:53:48130 if (command_line.HasSwitch(switches::kApp)) {
[email protected]f911df52013-12-24 23:24:23131 app_name = base::UTF8ToUTF16(web_app::GenerateApplicationNameFromURL(
[email protected]57ecc4b2010-08-11 03:02:51132 GURL(command_line.GetSwitchValueASCII(switches::kApp))));
[email protected]2f1c09d2011-01-14 14:58:14133 } else if (command_line.HasSwitch(switches::kAppId)) {
Alexey Baskakov2ce2ffc842018-08-06 02:57:15134 app_name = base::UTF8ToUTF16(web_app::GenerateApplicationNameFromAppId(
135 command_line.GetSwitchValueASCII(switches::kAppId)));
taptedc2c690ab2016-05-25 02:49:03136 } else if (command_line.HasSwitch(switches::kShowAppList)) {
137 app_name = GetAppListAppName();
[email protected]c9bb06f42010-01-13 23:53:48138 } else {
grtc98803462017-03-08 10:05:08139 app_name = ShellUtil::GetBrowserModelId(is_per_user_install);
[email protected]c9bb06f42010-01-13 23:53:48140 }
[email protected]b39e32e2013-04-24 08:55:54141 DCHECK(!app_name.empty());
[email protected]c9bb06f42010-01-13 23:53:48142
pmonette9e4c1a82016-04-14 18:15:30143 return win::GetAppModelIdForProfile(app_name, profile_path);
[email protected]c9bb06f42010-01-13 23:53:48144}
145
gab88257b62016-01-15 03:19:19146void MigrateTaskbarPinsCallback() {
Francois Doray66bdfd82017-10-20 13:50:37147 base::AssertBlockingAllowed();
[email protected]3a3e72c2011-11-29 02:59:38148
149 // Get full path of chrome.
[email protected]650b2d52013-02-10 03:41:45150 base::FilePath chrome_exe;
Avi Drissman9098f9002018-05-04 00:11:52151 if (!base::PathService::Get(base::FILE_EXE, &chrome_exe))
[email protected]3a3e72c2011-11-29 02:59:38152 return;
153
gab88257b62016-01-15 03:19:19154 base::FilePath pins_path;
Avi Drissman9098f9002018-05-04 00:11:52155 if (!base::PathService::Get(base::DIR_TASKBAR_PINS, &pins_path)) {
gab88257b62016-01-15 03:19:19156 NOTREACHED();
157 return;
[email protected]3a3e72c2011-11-29 02:59:38158 }
gab88257b62016-01-15 03:19:19159
pmonette9e4c1a82016-04-14 18:15:30160 win::MigrateShortcutsInPathInternal(chrome_exe, pins_path);
[email protected]3a3e72c2011-11-29 02:59:38161}
162
[email protected]caa05352014-03-01 00:43:05163// Windows 8 introduced a new protocol->executable binding system which cannot
164// be retrieved in the HKCR registry subkey method implemented below. We call
165// AssocQueryString with the new Win8-only flag ASSOCF_IS_PROTOCOL instead.
166base::string16 GetAppForProtocolUsingAssocQuery(const GURL& url) {
thestig11bf74d2014-11-24 20:14:42167 base::string16 url_scheme = base::ASCIIToUTF16(url.scheme());
[email protected]caa05352014-03-01 00:43:05168 // Don't attempt to query protocol association on an empty string.
[email protected]6e84d372014-05-29 23:36:39169 if (url_scheme.empty())
[email protected]caa05352014-03-01 00:43:05170 return base::string16();
171
[email protected]caa05352014-03-01 00:43:05172 // Query AssocQueryString for a human-readable description of the program
173 // that will be invoked given the provided URL spec. This is used only to
174 // populate the external protocol dialog box the user sees when invoking
175 // an unknown external protocol.
176 wchar_t out_buffer[1024];
177 DWORD buffer_size = arraysize(out_buffer);
178 HRESULT hr = AssocQueryString(ASSOCF_IS_PROTOCOL,
179 ASSOCSTR_FRIENDLYAPPNAME,
[email protected]6e84d372014-05-29 23:36:39180 url_scheme.c_str(),
[email protected]caa05352014-03-01 00:43:05181 NULL,
182 out_buffer,
183 &buffer_size);
184 if (FAILED(hr)) {
185 DLOG(WARNING) << "AssocQueryString failed!";
186 return base::string16();
187 }
188 return base::string16(out_buffer);
189}
190
191base::string16 GetAppForProtocolUsingRegistry(const GURL& url) {
dominickn0c9a5062016-10-06 20:49:00192 base::string16 command_to_launch;
193
194 // First, try and extract the application's display name.
195 base::string16 cmd_key_path = base::ASCIIToUTF16(url.scheme());
196 base::win::RegKey cmd_key_name(HKEY_CLASSES_ROOT, cmd_key_path.c_str(),
197 KEY_READ);
198 if (cmd_key_name.ReadValue(NULL, &command_to_launch) == ERROR_SUCCESS &&
199 !command_to_launch.empty()) {
200 return command_to_launch;
[email protected]caa05352014-03-01 00:43:05201 }
dominickn0c9a5062016-10-06 20:49:00202
203 // Otherwise, parse the command line in the registry, and return the basename
204 // of the program path if it exists.
205 cmd_key_path = base::ASCIIToUTF16(url.scheme() + "\\shell\\open\\command");
206 base::win::RegKey cmd_key_exe(HKEY_CLASSES_ROOT, cmd_key_path.c_str(),
207 KEY_READ);
208 if (cmd_key_exe.ReadValue(NULL, &command_to_launch) == ERROR_SUCCESS) {
209 base::CommandLine command_line(
210 base::CommandLine::FromString(command_to_launch));
211 return command_line.GetProgram().BaseName().value();
212 }
213
[email protected]caa05352014-03-01 00:43:05214 return base::string16();
215}
216
pmonette9fa59e882016-02-10 00:12:19217DefaultWebClientState GetDefaultWebClientStateFromShellUtilDefaultState(
218 ShellUtil::DefaultState default_state) {
[email protected]eb63da72012-10-15 20:39:48219 switch (default_state) {
Greg Thompson75b1c8e2017-08-16 13:36:28220 case ShellUtil::UNKNOWN_DEFAULT:
221 return DefaultWebClientState::UNKNOWN_DEFAULT;
[email protected]eb63da72012-10-15 20:39:48222 case ShellUtil::NOT_DEFAULT:
pmonette9fa59e882016-02-10 00:12:19223 return DefaultWebClientState::NOT_DEFAULT;
[email protected]eb63da72012-10-15 20:39:48224 case ShellUtil::IS_DEFAULT:
pmonette9fa59e882016-02-10 00:12:19225 return DefaultWebClientState::IS_DEFAULT;
Greg Thompson75b1c8e2017-08-16 13:36:28226 case ShellUtil::OTHER_MODE_IS_DEFAULT:
227 return DefaultWebClientState::OTHER_MODE_IS_DEFAULT;
[email protected]eb63da72012-10-15 20:39:48228 }
Greg Thompson75b1c8e2017-08-16 13:36:28229 NOTREACHED();
230 return DefaultWebClientState::UNKNOWN_DEFAULT;
[email protected]eb63da72012-10-15 20:39:48231}
232
grtc291eea2016-05-26 15:38:48233// A recorder of user actions in the Windows Settings app.
Patrick Monette7ff99e02018-02-19 17:30:12234class DefaultBrowserActionRecorder : public SettingsAppMonitor::Delegate {
grtc291eea2016-05-26 15:38:48235 public:
236 // Creates the recorder and the monitor that drives it. |continuation| will be
237 // run once the monitor's initialization completes (regardless of success or
238 // failure).
239 explicit DefaultBrowserActionRecorder(base::Closure continuation)
240 : continuation_(std::move(continuation)), settings_app_monitor_(this) {}
241
242 private:
243 // win::SettingsAppMonitor::Delegate:
244 void OnInitialized(HRESULT result) override {
245 UMA_HISTOGRAM_BOOLEAN("SettingsAppMonitor.InitializationResult",
246 SUCCEEDED(result));
247 if (SUCCEEDED(result)) {
248 base::RecordAction(
249 base::UserMetricsAction("SettingsAppMonitor.Initialized"));
250 }
251 continuation_.Run();
252 continuation_ = base::Closure();
253 }
254
255 void OnAppFocused() override {
256 base::RecordAction(
257 base::UserMetricsAction("SettingsAppMonitor.AppFocused"));
258 }
259
260 void OnChooserInvoked() override {
261 base::RecordAction(
262 base::UserMetricsAction("SettingsAppMonitor.ChooserInvoked"));
263 }
264
265 void OnBrowserChosen(const base::string16& browser_name) override {
Greg Thompson005a710c2018-09-06 06:54:38266 if (browser_name == InstallUtil::GetDisplayName()) {
grtc291eea2016-05-26 15:38:48267 base::RecordAction(
268 base::UserMetricsAction("SettingsAppMonitor.ChromeBrowserChosen"));
269 } else {
270 base::RecordAction(
271 base::UserMetricsAction("SettingsAppMonitor.OtherBrowserChosen"));
272 }
273 }
274
pmonetteb66514b42016-12-07 20:28:39275 void OnPromoFocused() override {
276 base::RecordAction(
277 base::UserMetricsAction("SettingsAppMonitor.PromoFocused"));
278 }
279
280 void OnPromoChoiceMade(bool accept_promo) override {
281 if (accept_promo) {
282 base::RecordAction(
283 base::UserMetricsAction("SettingsAppMonitor.CheckItOut"));
284 } else {
285 base::RecordAction(
286 base::UserMetricsAction("SettingsAppMonitor.SwitchAnyway"));
287 }
288 }
289
grtc291eea2016-05-26 15:38:48290 // A closure to be run once initialization completes.
291 base::Closure continuation_;
292
293 // Monitors user interaction with the Windows Settings app for the sake of
294 // reporting user actions.
Patrick Monette7ff99e02018-02-19 17:30:12295 SettingsAppMonitor settings_app_monitor_;
grtc291eea2016-05-26 15:38:48296
297 DISALLOW_COPY_AND_ASSIGN(DefaultBrowserActionRecorder);
298};
299
300// A function bound up in a callback with a DefaultBrowserActionRecorder and
301// a closure to keep the former alive until the time comes to run the latter.
302void OnSettingsAppFinished(
303 std::unique_ptr<DefaultBrowserActionRecorder> recorder,
304 const base::Closure& on_finished_callback) {
305 recorder.reset();
306 on_finished_callback.Run();
307}
308
pmonette32a5cfb42016-04-11 22:04:44309// There is no way to make sure the user is done with the system settings, but a
310// signal that the interaction is finished is needed for UMA. A timer of 2
311// minutes is used as a substitute. The registry keys for the protocol
312// association with an app are also monitored to signal the end of the
313// interaction early when it is clear that the user made a choice (e.g. http
314// and https for default browser).
315//
316// This helper class manages both the timer and the registry watchers and makes
317// sure the callback for the end of the settings interaction is only run once.
318// This class also manages its own lifetime.
319class OpenSystemSettingsHelper {
320 public:
321 // Begin the monitoring and will call |on_finished_callback| when done.
322 // Takes in a null-terminated array of |protocols| whose registry keys must be
pmonette0c40087a2016-04-21 00:05:16323 // watched. The array must contain at least one element.
pmonette32a5cfb42016-04-11 22:04:44324 static void Begin(const wchar_t* const protocols[],
325 const base::Closure& on_finished_callback) {
Francois Doray66bdfd82017-10-20 13:50:37326 base::AssertBlockingAllowed();
pmonettec541d942016-06-15 22:08:32327
328 delete instance_;
329 instance_ = new OpenSystemSettingsHelper(protocols, on_finished_callback);
pmonette32a5cfb42016-04-11 22:04:44330 }
331
332 private:
333 // The reason the settings interaction concluded. Do not modify the ordering
334 // because it is used for UMA.
335 enum ConcludeReason { REGISTRY_WATCHER, TIMEOUT, NUM_CONCLUDE_REASON_TYPES };
336
337 OpenSystemSettingsHelper(const wchar_t* const protocols[],
338 const base::Closure& on_finished_callback)
pmonette0c40087a2016-04-21 00:05:16339 : scoped_user_protocol_entry_(protocols[0]),
340 on_finished_callback_(on_finished_callback),
341 weak_ptr_factory_(this) {
pmonette32a5cfb42016-04-11 22:04:44342 static const wchar_t kUrlAssociationFormat[] =
343 L"SOFTWARE\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\"
344 L"%ls\\UserChoice";
345
346 // Remember the start time.
347 start_time_ = base::TimeTicks::Now();
348
349 for (const wchar_t* const* scan = &protocols[0]; *scan != nullptr; ++scan) {
350 AddRegistryKeyWatcher(
351 base::StringPrintf(kUrlAssociationFormat, *scan).c_str());
352 }
353 // Only the watchers that were succesfully initialized are counted.
354 registry_watcher_count_ = registry_key_watchers_.size();
355
356 timer_.Start(
357 FROM_HERE, base::TimeDelta::FromMinutes(2),
358 base::Bind(&OpenSystemSettingsHelper::ConcludeInteraction,
359 weak_ptr_factory_.GetWeakPtr(), ConcludeReason::TIMEOUT));
360 }
361
pmonetteb18379f2017-06-01 01:18:44362 ~OpenSystemSettingsHelper() {
363 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
364 }
365
pmonette32a5cfb42016-04-11 22:04:44366 // Called when a change is detected on one of the registry keys being watched.
367 // Note: All types of modification to the registry key will trigger this
368 // function even if the value change is the only one that matters. This
369 // is good enough for now.
370 void OnRegistryKeyChanged() {
pmonetteb18379f2017-06-01 01:18:44371 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
pmonette32a5cfb42016-04-11 22:04:44372 // Make sure all the registry watchers have fired.
373 if (--registry_watcher_count_ == 0) {
374 UMA_HISTOGRAM_MEDIUM_TIMES(
375 "DefaultBrowser.SettingsInteraction.RegistryWatcherDuration",
376 base::TimeTicks::Now() - start_time_);
377
378 ConcludeInteraction(ConcludeReason::REGISTRY_WATCHER);
379 }
380 }
381
382 // Ends the monitoring with the system settings. Will call
383 // |on_finished_callback_| and then dispose of this class instance to make
384 // sure the callback won't get called subsequently.
385 void ConcludeInteraction(ConcludeReason conclude_reason) {
pmonetteb18379f2017-06-01 01:18:44386 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
pmonette32a5cfb42016-04-11 22:04:44387
388 UMA_HISTOGRAM_ENUMERATION(
389 "DefaultBrowser.SettingsInteraction.ConcludeReason", conclude_reason,
390 NUM_CONCLUDE_REASON_TYPES);
391 on_finished_callback_.Run();
pmonettec541d942016-06-15 22:08:32392 delete instance_;
393 instance_ = nullptr;
pmonette32a5cfb42016-04-11 22:04:44394 }
395
396 // Helper function to create a registry watcher for a given |key_path|. Do
397 // nothing on initialization failure.
398 void AddRegistryKeyWatcher(const wchar_t* key_path) {
pmonetteb18379f2017-06-01 01:18:44399 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
400
Jeremy Romanec48d7a2018-03-01 17:35:09401 auto reg_key = std::make_unique<base::win::RegKey>(HKEY_CURRENT_USER,
ricea86fa1dd2016-09-13 05:59:45402 key_path, KEY_NOTIFY);
pmonette32a5cfb42016-04-11 22:04:44403
404 if (reg_key->Valid() &&
405 reg_key->StartWatching(
406 base::Bind(&OpenSystemSettingsHelper::OnRegistryKeyChanged,
407 weak_ptr_factory_.GetWeakPtr()))) {
408 registry_key_watchers_.push_back(std::move(reg_key));
409 }
410 }
411
pmonettec541d942016-06-15 22:08:32412 // Used to make sure only one instance is alive at the same time.
413 static OpenSystemSettingsHelper* instance_;
414
pmonette0c40087a2016-04-21 00:05:16415 // This is needed to make sure that Windows displays an entry for the protocol
416 // inside the "Choose default apps by protocol" settings page.
417 ScopedUserProtocolEntry scoped_user_protocol_entry_;
418
pmonette32a5cfb42016-04-11 22:04:44419 // The function to call when the interaction with the system settings is
420 // finished.
421 base::Closure on_finished_callback_;
422
423 // The number of time the registry key watchers must fire.
424 int registry_watcher_count_ = 0;
425
426 // There can be multiple registry key watchers as some settings modify
427 // multiple protocol associations. e.g. Changing the default browser modifies
428 // the http and https associations.
dcheng4af48582016-04-19 00:29:35429 std::vector<std::unique_ptr<base::win::RegKey>> registry_key_watchers_;
pmonette32a5cfb42016-04-11 22:04:44430
431 base::OneShotTimer timer_;
432
433 // Records the time it takes for the final registry watcher to get signaled.
434 base::TimeTicks start_time_;
435
pmonetteb18379f2017-06-01 01:18:44436 SEQUENCE_CHECKER(sequence_checker_);
437
pmonette32a5cfb42016-04-11 22:04:44438 // Weak ptrs are used to bind this class to the callbacks of the timer and the
439 // registry watcher. This makes it possible to self-delete after one of the
440 // callbacks is executed to cancel the remaining ones.
441 base::WeakPtrFactory<OpenSystemSettingsHelper> weak_ptr_factory_;
442
443 DISALLOW_COPY_AND_ASSIGN(OpenSystemSettingsHelper);
444};
445
pmonettec541d942016-06-15 22:08:32446OpenSystemSettingsHelper* OpenSystemSettingsHelper::instance_ = nullptr;
447
pmonettea98315242016-11-23 21:39:18448// Helper class to determine if Chrome is pinned to the taskbar. Hides the
Jay Civellid4b89052017-10-31 17:48:25449// complexity of managing the lifetime of the connection to the ChromeWinUtil
450// service.
pmonettea98315242016-11-23 21:39:18451class IsPinnedToTaskbarHelper {
452 public:
453 using ResultCallback = win::IsPinnedToTaskbarCallback;
454 using ErrorCallback = win::ConnectionErrorCallback;
Jay Civellid4b89052017-10-31 17:48:25455 static void GetState(std::unique_ptr<service_manager::Connector> connector,
456 const ErrorCallback& error_callback,
pmonettea98315242016-11-23 21:39:18457 const ResultCallback& result_callback);
458
459 private:
Jay Civellid4b89052017-10-31 17:48:25460 IsPinnedToTaskbarHelper(std::unique_ptr<service_manager::Connector> connector,
461 const ErrorCallback& error_callback,
pmonettea98315242016-11-23 21:39:18462 const ResultCallback& result_callback);
463
464 void OnConnectionError();
465 void OnIsPinnedToTaskbarResult(bool succeeded, bool is_pinned_to_taskbar);
466
Jay Civellid4b89052017-10-31 17:48:25467 chrome::mojom::ShellUtilWinPtr shell_util_win_ptr_;
468 // The connector used to retrieve the Patch service. We can't simply use
469 // content::ServiceManagerConnection::GetForProcess()->GetConnector() as this
470 // is called on a background thread.
471 std::unique_ptr<service_manager::Connector> connector_;
pmonettea98315242016-11-23 21:39:18472
473 ErrorCallback error_callback_;
474 ResultCallback result_callback_;
475
pmonetteb18379f2017-06-01 01:18:44476 SEQUENCE_CHECKER(sequence_checker_);
477
pmonettea98315242016-11-23 21:39:18478 DISALLOW_COPY_AND_ASSIGN(IsPinnedToTaskbarHelper);
479};
480
481// static
Jay Civellid4b89052017-10-31 17:48:25482void IsPinnedToTaskbarHelper::GetState(
483 std::unique_ptr<service_manager::Connector> connector,
484 const ErrorCallback& error_callback,
485 const ResultCallback& result_callback) {
pmonettea98315242016-11-23 21:39:18486 // Self-deleting when the ShellHandler completes.
Jay Civellid4b89052017-10-31 17:48:25487 new IsPinnedToTaskbarHelper(std::move(connector), error_callback,
488 result_callback);
pmonette5057ca3b2016-07-04 16:48:40489}
490
pmonettea98315242016-11-23 21:39:18491IsPinnedToTaskbarHelper::IsPinnedToTaskbarHelper(
Jay Civellid4b89052017-10-31 17:48:25492 std::unique_ptr<service_manager::Connector> connector,
pmonettea98315242016-11-23 21:39:18493 const ErrorCallback& error_callback,
494 const ResultCallback& result_callback)
Jay Civellid4b89052017-10-31 17:48:25495 : connector_(std::move(connector)),
pmonettea98315242016-11-23 21:39:18496 error_callback_(error_callback),
497 result_callback_(result_callback) {
pmonette83edc1e2016-12-15 17:36:44498 DCHECK(error_callback_);
499 DCHECK(result_callback_);
500
Jay Civellid4b89052017-10-31 17:48:25501 connector_->BindInterface(chrome::mojom::kUtilWinServiceName,
502 &shell_util_win_ptr_);
503 // |shell_util_win_ptr_| owns the callbacks and is guaranteed to be destroyed
pmonettea98315242016-11-23 21:39:18504 // before |this|, therefore making base::Unretained() safe to use.
Jay Civellid4b89052017-10-31 17:48:25505 shell_util_win_ptr_.set_connection_error_handler(base::Bind(
pmonettea98315242016-11-23 21:39:18506 &IsPinnedToTaskbarHelper::OnConnectionError, base::Unretained(this)));
Jay Civellid4b89052017-10-31 17:48:25507 shell_util_win_ptr_->IsPinnedToTaskbar(
pmonettea98315242016-11-23 21:39:18508 base::Bind(&IsPinnedToTaskbarHelper::OnIsPinnedToTaskbarResult,
509 base::Unretained(this)));
510}
511
512void IsPinnedToTaskbarHelper::OnConnectionError() {
pmonetteb18379f2017-06-01 01:18:44513 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
pmonettea98315242016-11-23 21:39:18514 error_callback_.Run();
515 delete this;
516}
517
518void IsPinnedToTaskbarHelper::OnIsPinnedToTaskbarResult(
pmonette5057ca3b2016-07-04 16:48:40519 bool succeeded,
520 bool is_pinned_to_taskbar) {
pmonetteb18379f2017-06-01 01:18:44521 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
522
pmonettea98315242016-11-23 21:39:18523 result_callback_.Run(succeeded, is_pinned_to_taskbar);
524 delete this;
pmonette5057ca3b2016-07-04 16:48:40525}
526
[email protected]3a3e72c2011-11-29 02:59:38527} // namespace
[email protected]12f520c2010-01-06 18:11:15528
pmonette9fa59e882016-02-10 00:12:19529bool SetAsDefaultBrowser() {
Francois Doray66bdfd82017-10-20 13:50:37530 base::AssertBlockingAllowed();
pmonetteb18379f2017-06-01 01:18:44531
[email protected]650b2d52013-02-10 03:41:45532 base::FilePath chrome_exe;
Avi Drissman9098f9002018-05-04 00:11:52533 if (!base::PathService::Get(base::FILE_EXE, &chrome_exe)) {
[email protected]d24c4012009-07-28 01:57:31534 LOG(ERROR) << "Error getting app exe path";
535 return false;
536 }
537
538 // From UI currently we only allow setting default browser for current user.
Greg Thompson8fc94ef2018-09-10 16:53:12539 if (!ShellUtil::MakeChromeDefault(ShellUtil::CURRENT_USER, chrome_exe,
grte76ca2852014-12-05 16:42:10540 true /* elevate_if_not_admin */)) {
[email protected]d24c4012009-07-28 01:57:31541 LOG(ERROR) << "Chrome could not be set as default browser.";
542 return false;
543 }
544
[email protected]8e96e502010-10-21 20:57:12545 VLOG(1) << "Chrome registered as default browser.";
[email protected]d24c4012009-07-28 01:57:31546 return true;
547}
548
pmonette9fa59e882016-02-10 00:12:19549bool SetAsDefaultProtocolClient(const std::string& protocol) {
Francois Doray66bdfd82017-10-20 13:50:37550 base::AssertBlockingAllowed();
pmonetteb18379f2017-06-01 01:18:44551
[email protected]4468a5b2011-05-26 07:48:02552 if (protocol.empty())
553 return false;
554
[email protected]650b2d52013-02-10 03:41:45555 base::FilePath chrome_exe;
Avi Drissman9098f9002018-05-04 00:11:52556 if (!base::PathService::Get(base::FILE_EXE, &chrome_exe)) {
[email protected]4468a5b2011-05-26 07:48:02557 LOG(ERROR) << "Error getting app exe path";
558 return false;
559 }
560
[email protected]f911df52013-12-24 23:24:23561 base::string16 wprotocol(base::UTF8ToUTF16(protocol));
Greg Thompson8fc94ef2018-09-10 16:53:12562 if (!ShellUtil::MakeChromeDefaultProtocolClient(chrome_exe, wprotocol)) {
[email protected]4468a5b2011-05-26 07:48:02563 LOG(ERROR) << "Chrome could not be set as default handler for "
564 << protocol << ".";
565 return false;
566 }
567
568 VLOG(1) << "Chrome registered as default handler for " << protocol << ".";
569 return true;
570}
571
pmonette32a5cfb42016-04-11 22:04:44572DefaultWebClientSetPermission GetDefaultWebClientSetPermission() {
grt509bc942017-03-25 08:43:21573 if (!install_static::SupportsSetAsDefaultBrowser())
pmonette034a03d92015-10-02 21:04:27574 return SET_DEFAULT_NOT_ALLOWED;
pmonette034a03d92015-10-02 21:04:27575 if (ShellUtil::CanMakeChromeDefaultUnattended())
576 return SET_DEFAULT_UNATTENDED;
pmonette32a5cfb42016-04-11 22:04:44577 // Windows 8 and 10 both introduced a new way to set the default web client
578 // which require user interaction.
pmonettef89ac7c72015-10-06 03:22:01579 return SET_DEFAULT_INTERACTIVE;
pmonette034a03d92015-10-02 21:04:27580}
581
pmonette9fa59e882016-02-10 00:12:19582bool IsElevationNeededForSettingDefaultProtocolClient() {
pmonette868ca642015-09-02 14:34:02583 return base::win::GetVersion() < base::win::VERSION_WIN8;
584}
585
pmonette9fa59e882016-02-10 00:12:19586base::string16 GetApplicationNameForProtocol(const GURL& url) {
benwellsd0c67aa2016-07-28 02:57:20587 base::string16 application_name;
588 // Windows 8 or above has a new protocol association query.
589 if (base::win::GetVersion() >= base::win::VERSION_WIN8) {
590 application_name = GetAppForProtocolUsingAssocQuery(url);
591 if (!application_name.empty())
592 return application_name;
593 }
594
595 return GetAppForProtocolUsingRegistry(url);
[email protected]42dc9402013-01-30 07:54:20596}
597
pmonette9fa59e882016-02-10 00:12:19598DefaultWebClientState GetDefaultBrowser() {
pmonette034a03d92015-10-02 21:04:27599 return GetDefaultWebClientStateFromShellUtilDefaultState(
600 ShellUtil::GetChromeDefaultState());
601}
602
[email protected]d24c4012009-07-28 01:57:31603// There is no reliable way to say which browser is default on a machine (each
604// browser can have some of the protocols/shortcuts). So we look for only HTTP
605// protocol handler. Even this handler is located at different places in
606// registry on XP and Vista:
607// - HKCR\http\shell\open\command (XP)
608// - HKCU\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\
609// http\UserChoice (Vista)
thakise4a02ff32016-09-20 17:00:42610// This method checks if Firefox is default browser by checking these
[email protected]d24c4012009-07-28 01:57:31611// locations and returns true if Firefox traces are found there. In case of
612// error (or if Firefox is not found)it returns the default value which
613// is false.
pmonette9fa59e882016-02-10 00:12:19614bool IsFirefoxDefaultBrowser() {
Patrick Monetteab240ec2017-07-13 22:49:14615 base::string16 app_cmd;
616 base::win::RegKey key(HKEY_CURRENT_USER, ShellUtil::kRegVistaUrlPrefs,
617 KEY_READ);
618 return key.Valid() && key.ReadValue(L"Progid", &app_cmd) == ERROR_SUCCESS &&
619 app_cmd == L"FirefoxURL";
[email protected]d24c4012009-07-28 01:57:31620}
[email protected]12f520c2010-01-06 18:11:15621
pmonette9fa59e882016-02-10 00:12:19622DefaultWebClientState IsDefaultProtocolClient(const std::string& protocol) {
pmonette034a03d92015-10-02 21:04:27623 return GetDefaultWebClientStateFromShellUtilDefaultState(
624 ShellUtil::GetChromeDefaultProtocolClientState(
625 base::UTF8ToUTF16(protocol)));
626}
627
pmonette9e4c1a82016-04-14 18:15:30628namespace win {
629
630bool SetAsDefaultBrowserUsingIntentPicker() {
Francois Doray66bdfd82017-10-20 13:50:37631 base::AssertBlockingAllowed();
pmonetteb18379f2017-06-01 01:18:44632
pmonette9e4c1a82016-04-14 18:15:30633 base::FilePath chrome_exe;
Avi Drissman9098f9002018-05-04 00:11:52634 if (!base::PathService::Get(base::FILE_EXE, &chrome_exe)) {
pmonette9e4c1a82016-04-14 18:15:30635 NOTREACHED() << "Error getting app exe path";
636 return false;
637 }
638
Greg Thompson8fc94ef2018-09-10 16:53:12639 if (!ShellUtil::ShowMakeChromeDefaultSystemUI(chrome_exe)) {
pmonette9e4c1a82016-04-14 18:15:30640 LOG(ERROR) << "Failed to launch the set-default-browser Windows UI.";
641 return false;
642 }
643
644 VLOG(1) << "Set-default-browser Windows UI completed.";
645 return true;
646}
647
648void SetAsDefaultBrowserUsingSystemSettings(
649 const base::Closure& on_finished_callback) {
Francois Doray66bdfd82017-10-20 13:50:37650 base::AssertBlockingAllowed();
pmonette9e4c1a82016-04-14 18:15:30651
652 base::FilePath chrome_exe;
Avi Drissman9098f9002018-05-04 00:11:52653 if (!base::PathService::Get(base::FILE_EXE, &chrome_exe)) {
pmonette9e4c1a82016-04-14 18:15:30654 NOTREACHED() << "Error getting app exe path";
655 on_finished_callback.Run();
656 return;
657 }
658
grtc291eea2016-05-26 15:38:48659 // Create an action recorder that will open the settings app once it has
660 // initialized.
661 std::unique_ptr<DefaultBrowserActionRecorder> recorder(
662 new DefaultBrowserActionRecorder(base::Bind(
663 base::IgnoreResult(&ShellUtil::ShowMakeChromeDefaultSystemUI),
grtc291eea2016-05-26 15:38:48664 chrome_exe)));
pmonette9e4c1a82016-04-14 18:15:30665
grtc291eea2016-05-26 15:38:48666 // The helper manages its own lifetime. Bind the action recorder
667 // into the finished callback to keep it alive throughout the
668 // interaction.
669 static const wchar_t* const kProtocols[] = {L"http", L"https", nullptr};
670 OpenSystemSettingsHelper::Begin(
671 kProtocols, base::Bind(&OnSettingsAppFinished, base::Passed(&recorder),
672 on_finished_callback));
pmonette9e4c1a82016-04-14 18:15:30673}
674
675bool SetAsDefaultProtocolClientUsingIntentPicker(const std::string& protocol) {
Francois Doray66bdfd82017-10-20 13:50:37676 base::AssertBlockingAllowed();
pmonetteb18379f2017-06-01 01:18:44677
pmonette9e4c1a82016-04-14 18:15:30678 base::FilePath chrome_exe;
Avi Drissman9098f9002018-05-04 00:11:52679 if (!base::PathService::Get(base::FILE_EXE, &chrome_exe)) {
pmonette9e4c1a82016-04-14 18:15:30680 NOTREACHED() << "Error getting app exe path";
681 return false;
682 }
683
pmonette9e4c1a82016-04-14 18:15:30684 base::string16 wprotocol(base::UTF8ToUTF16(protocol));
Greg Thompson8fc94ef2018-09-10 16:53:12685 if (!ShellUtil::ShowMakeChromeDefaultProtocolClientSystemUI(chrome_exe,
pmonette9e4c1a82016-04-14 18:15:30686 wprotocol)) {
687 LOG(ERROR) << "Failed to launch the set-default-client Windows UI.";
688 return false;
689 }
690
691 VLOG(1) << "Set-default-client Windows UI completed.";
692 return true;
693}
694
pmonette0c40087a2016-04-21 00:05:16695void SetAsDefaultProtocolClientUsingSystemSettings(
696 const std::string& protocol,
697 const base::Closure& on_finished_callback) {
Francois Doray66bdfd82017-10-20 13:50:37698 base::AssertBlockingAllowed();
pmonette0c40087a2016-04-21 00:05:16699
700 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})
Robert Liaoe9450f8c2017-06-15 17:23:57738 ->PostTask(FROM_HERE, base::Bind(&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) {
[email protected]8ea8f1ef2013-01-06 18:39:03751 // Enumerate all pinned shortcuts in the given path directly.
[email protected]25a4c1c2013-06-08 04:53:36752 base::FileEnumerator shortcuts_enum(
[email protected]8ea8f1ef2013-01-06 18:39:03753 path, false, // not recursive
[email protected]25a4c1c2013-06-08 04:53:36754 base::FileEnumerator::FILES, FILE_PATH_LITERAL("*.lnk"));
[email protected]8ea8f1ef2013-01-06 18:39:03755
grt973412f2017-02-25 08:04:21756 bool is_per_user_install = InstallUtil::IsPerUserInstall();
[email protected]8ea8f1ef2013-01-06 18:39:03757
758 int shortcuts_migrated = 0;
[email protected]650b2d52013-02-10 03:41:45759 base::FilePath target_path;
[email protected]0085863a2013-12-06 21:19:03760 base::string16 arguments;
[email protected]07983302013-01-21 19:41:44761 base::win::ScopedPropVariant propvariant;
[email protected]650b2d52013-02-10 03:41:45762 for (base::FilePath shortcut = shortcuts_enum.Next(); !shortcut.empty();
[email protected]8ea8f1ef2013-01-06 18:39:03763 shortcut = shortcuts_enum.Next()) {
764 // TODO(gab): Use ProgramCompare instead of comparing FilePaths below once
765 // it is fixed to work with FilePaths with spaces.
766 if (!base::win::ResolveShortcut(shortcut, &target_path, &arguments) ||
767 chrome_exe != target_path) {
768 continue;
769 }
avi556c05022014-12-22 23:31:43770 base::CommandLine command_line(
771 base::CommandLine::FromString(base::StringPrintf(
772 L"\"%ls\" %ls", target_path.value().c_str(), arguments.c_str())));
[email protected]8ea8f1ef2013-01-06 18:39:03773
774 // Get the expected AppId for this Chrome shortcut.
[email protected]0085863a2013-12-06 21:19:03775 base::string16 expected_app_id(
[email protected]8ea8f1ef2013-01-06 18:39:03776 GetExpectedAppId(command_line, is_per_user_install));
777 if (expected_app_id.empty())
778 continue;
779
780 // Load the shortcut.
Robert Liaob2bc703d2017-10-17 20:52:35781 Microsoft::WRL::ComPtr<IShellLink> shell_link;
782 Microsoft::WRL::ComPtr<IPersistFile> persist_file;
robliaoa872e992017-05-18 06:36:19783 if (FAILED(::CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
784 IID_PPV_ARGS(&shell_link))) ||
robliaoa7956aa2017-05-17 19:10:54785 FAILED(shell_link.CopyTo(persist_file.GetAddressOf())) ||
[email protected]8ea8f1ef2013-01-06 18:39:03786 FAILED(persist_file->Load(shortcut.value().c_str(), STGM_READ))) {
787 DLOG(WARNING) << "Failed loading shortcut at " << shortcut.value();
788 continue;
789 }
790
791 // Any properties that need to be updated on the shortcut will be stored in
792 // |updated_properties|.
793 base::win::ShortcutProperties updated_properties;
794
795 // Validate the existing app id for the shortcut.
Robert Liaob2bc703d2017-10-17 20:52:35796 Microsoft::WRL::ComPtr<IPropertyStore> property_store;
[email protected]07983302013-01-21 19:41:44797 propvariant.Reset();
robliaoa7956aa2017-05-17 19:10:54798 if (FAILED(shell_link.CopyTo(property_store.GetAddressOf())) ||
dcheng81762e02014-11-21 21:22:28799 property_store->GetValue(PKEY_AppUserModel_ID, propvariant.Receive()) !=
800 S_OK) {
[email protected]8ea8f1ef2013-01-06 18:39:03801 // When in doubt, prefer not updating the shortcut.
802 NOTREACHED();
803 continue;
[email protected]8ea8f1ef2013-01-06 18:39:03804 } else {
[email protected]07983302013-01-21 19:41:44805 switch (propvariant.get().vt) {
806 case VT_EMPTY:
807 // If there is no app_id set, set our app_id if one is expected.
808 if (!expected_app_id.empty())
809 updated_properties.set_app_id(expected_app_id);
810 break;
811 case VT_LPWSTR:
[email protected]0085863a2013-12-06 21:19:03812 if (expected_app_id != base::string16(propvariant.get().pwszVal))
[email protected]07983302013-01-21 19:41:44813 updated_properties.set_app_id(expected_app_id);
814 break;
815 default:
816 NOTREACHED();
817 continue;
[email protected]8ea8f1ef2013-01-06 18:39:03818 }
819 }
820
gab88257b62016-01-15 03:19:19821 // Clear dual_mode property from any shortcuts that previously had it (it
822 // was only ever installed on shortcuts with the
823 // |default_chromium_model_id|).
[email protected]0085863a2013-12-06 21:19:03824 base::string16 default_chromium_model_id(
grtc98803462017-03-08 10:05:08825 ShellUtil::GetBrowserModelId(is_per_user_install));
gab88257b62016-01-15 03:19:19826 if (expected_app_id == default_chromium_model_id) {
[email protected]07983302013-01-21 19:41:44827 propvariant.Reset();
[email protected]8ea8f1ef2013-01-06 18:39:03828 if (property_store->GetValue(PKEY_AppUserModel_IsDualMode,
[email protected]07983302013-01-21 19:41:44829 propvariant.Receive()) != S_OK) {
[email protected]8ea8f1ef2013-01-06 18:39:03830 // When in doubt, prefer to not update the shortcut.
831 NOTREACHED();
832 continue;
gab38d242d4d2016-01-14 20:38:12833 }
834 if (propvariant.get().vt == VT_BOOL &&
835 !!propvariant.get().boolVal) {
836 updated_properties.set_dual_mode(false);
[email protected]8ea8f1ef2013-01-06 18:39:03837 }
838 }
839
robliao9212cb42017-04-06 17:43:39840 persist_file.Reset();
841 shell_link.Reset();
[email protected]8ea8f1ef2013-01-06 18:39:03842
843 // Update the shortcut if some of its properties need to be updated.
844 if (updated_properties.options &&
845 base::win::CreateOrUpdateShortcutLink(
846 shortcut, updated_properties,
847 base::win::SHORTCUT_UPDATE_EXISTING)) {
848 ++shortcuts_migrated;
849 }
850 }
851 return shortcuts_migrated;
852}
853
pmonette32a5cfb42016-04-11 22:04:44854} // namespace win
855
pmonette9fa59e882016-02-10 00:12:19856} // namespace shell_integration