blob: c8fc1ac86bd11b43b26bea15402ef5ec15fdea61 [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]caa05352014-03-01 00:43:059#include <shlwapi.h>
[email protected]d24c4012009-07-28 01:57:3110#include <shobjidl.h>
thestig18dfb7a52014-08-26 10:44:0411#include <propkey.h> // Needs to come after shobjidl.h.
avi664c07b2015-12-26 02:18:3112#include <stddef.h>
13#include <stdint.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"
dcheng4af48582016-04-19 00:29:3525#include "base/memory/ptr_util.h"
pmonette32a5cfb42016-04-11 22:04:4426#include "base/memory/weak_ptr.h"
[email protected]fa1e0e12013-07-18 00:10:1427#include "base/message_loop/message_loop.h"
pmonette32a5cfb42016-04-11 22:04:4428#include "base/metrics/histogram_macros.h"
grtc291eea2016-05-26 15:38:4829#include "base/metrics/user_metrics.h"
30#include "base/metrics/user_metrics_action.h"
[email protected]d24c4012009-07-28 01:57:3131#include "base/path_service.h"
[email protected]24a555b62013-06-10 22:01:1732#include "base/strings/string_util.h"
33#include "base/strings/stringprintf.h"
[email protected]e309f312013-06-07 21:50:0834#include "base/strings/utf_string_conversions.h"
pmonetteb18379f2017-06-01 01:18:4435#include "base/task_scheduler/post_task.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]8ee65ba2011-04-12 20:53:2339#include "base/win/scoped_comptr.h"
[email protected]07983302013-01-21 19:41:4440#include "base/win/scoped_propvariant.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"
[email protected]c9bb06f42010-01-13 23:53:4845#include "chrome/browser/web_applications/web_app.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"
pmonette5057ca3b2016-07-04 16:48:4050#include "chrome/common/shell_handler_win.mojom.h"
51#include "chrome/grit/generated_resources.h"
grtc98803462017-03-08 10:05:0852#include "chrome/install_static/install_util.h"
[email protected]d24c4012009-07-28 01:57:3153#include "chrome/installer/util/browser_distribution.h"
[email protected]3f69d6e612012-08-03 18:52:2754#include "chrome/installer/util/install_util.h"
pmonette0c40087a2016-04-21 00:05:1655#include "chrome/installer/util/scoped_user_protocol_entry.h"
[email protected]d24c4012009-07-28 01:57:3156#include "chrome/installer/util/shell_util.h"
pmonette2b1dbee2016-01-08 20:18:5857#include "components/variations/variations_associated_data.h"
pmonette5057ca3b2016-07-04 16:48:4058#include "content/public/browser/utility_process_mojo_client.h"
59#include "ui/base/l10n/l10n_util.h"
[email protected]d24c4012009-07-28 01:57:3160
pmonette9fa59e882016-02-10 00:12:1961namespace shell_integration {
62
[email protected]12f520c2010-01-06 18:11:1563namespace {
64
pmonette9fa59e882016-02-10 00:12:1965// Helper function for GetAppId to generates profile id
[email protected]2f1c09d2011-01-14 14:58:1466// from profile path. "profile_id" is composed of sanitized basenames of
[email protected]12f520c2010-01-06 18:11:1567// user data dir and profile dir joined by a ".".
[email protected]6a72a632013-12-12 22:22:0068base::string16 GetProfileIdFromPath(const base::FilePath& profile_path) {
[email protected]12f520c2010-01-06 18:11:1569 // Return empty string if profile_path is empty
70 if (profile_path.empty())
[email protected]0085863a2013-12-06 21:19:0371 return base::string16();
[email protected]12f520c2010-01-06 18:11:1572
[email protected]650b2d52013-02-10 03:41:4573 base::FilePath default_user_data_dir;
[email protected]12f520c2010-01-06 18:11:1574 // Return empty string if profile_path is in default user data
75 // dir and is the default profile.
76 if (chrome::GetDefaultUserDataDirectory(&default_user_data_dir) &&
77 profile_path.DirName() == default_user_data_dir &&
[email protected]162b5992011-03-15 19:40:4878 profile_path.BaseName().value() ==
[email protected]f911df52013-12-24 23:24:2379 base::ASCIIToUTF16(chrome::kInitialProfile)) {
[email protected]0085863a2013-12-06 21:19:0380 return base::string16();
[email protected]162b5992011-03-15 19:40:4881 }
[email protected]12f520c2010-01-06 18:11:1582
83 // Get joined basenames of user data dir and profile.
[email protected]0085863a2013-12-06 21:19:0384 base::string16 basenames = profile_path.DirName().BaseName().value() +
[email protected]12f520c2010-01-06 18:11:1585 L"." + profile_path.BaseName().value();
86
[email protected]0085863a2013-12-06 21:19:0387 base::string16 profile_id;
[email protected]12f520c2010-01-06 18:11:1588 profile_id.reserve(basenames.size());
89
90 // Generate profile_id from sanitized basenames.
91 for (size_t i = 0; i < basenames.length(); ++i) {
brettwb3413062015-06-24 00:39:0292 if (base::IsAsciiAlpha(basenames[i]) ||
93 base::IsAsciiDigit(basenames[i]) ||
[email protected]12f520c2010-01-06 18:11:1594 basenames[i] == L'.')
95 profile_id += basenames[i];
96 }
97
98 return profile_id;
99}
100
taptedc2c690ab2016-05-25 02:49:03101base::string16 GetAppListAppName() {
102 static const base::char16 kAppListAppNameSuffix[] = L"AppList";
grtc98803462017-03-08 10:05:08103 base::string16 app_name(install_static::GetBaseAppId());
taptedc2c690ab2016-05-25 02:49:03104 app_name.append(kAppListAppNameSuffix);
105 return app_name;
106}
107
[email protected]8ea8f1ef2013-01-06 18:39:03108// Gets expected app id for given Chrome (based on |command_line| and
109// |is_per_user_install|).
avi556c05022014-12-22 23:31:43110base::string16 GetExpectedAppId(const base::CommandLine& command_line,
[email protected]6a72a632013-12-12 22:22:00111 bool is_per_user_install) {
[email protected]b39e32e2013-04-24 08:55:54112 base::FilePath user_data_dir;
113 if (command_line.HasSwitch(switches::kUserDataDir))
114 user_data_dir = command_line.GetSwitchValuePath(switches::kUserDataDir);
115 else
116 chrome::GetDefaultUserDataDirectory(&user_data_dir);
[email protected]89d43832013-06-29 20:25:20117 // Adjust with any policy that overrides any other way to set the path.
118 policy::path_parser::CheckUserDataDirPolicy(&user_data_dir);
[email protected]b39e32e2013-04-24 08:55:54119 DCHECK(!user_data_dir.empty());
[email protected]c9bb06f42010-01-13 23:53:48120
[email protected]b39e32e2013-04-24 08:55:54121 base::FilePath profile_subdir;
122 if (command_line.HasSwitch(switches::kProfileDirectory)) {
123 profile_subdir =
124 command_line.GetSwitchValuePath(switches::kProfileDirectory);
125 } else {
[email protected]f911df52013-12-24 23:24:23126 profile_subdir =
127 base::FilePath(base::ASCIIToUTF16(chrome::kInitialProfile));
[email protected]b39e32e2013-04-24 08:55:54128 }
129 DCHECK(!profile_subdir.empty());
130
131 base::FilePath profile_path = user_data_dir.Append(profile_subdir);
[email protected]0085863a2013-12-06 21:19:03132 base::string16 app_name;
[email protected]c9bb06f42010-01-13 23:53:48133 if (command_line.HasSwitch(switches::kApp)) {
[email protected]f911df52013-12-24 23:24:23134 app_name = base::UTF8ToUTF16(web_app::GenerateApplicationNameFromURL(
[email protected]57ecc4b2010-08-11 03:02:51135 GURL(command_line.GetSwitchValueASCII(switches::kApp))));
[email protected]2f1c09d2011-01-14 14:58:14136 } else if (command_line.HasSwitch(switches::kAppId)) {
[email protected]f911df52013-12-24 23:24:23137 app_name = base::UTF8ToUTF16(
138 web_app::GenerateApplicationNameFromExtensionId(
139 command_line.GetSwitchValueASCII(switches::kAppId)));
taptedc2c690ab2016-05-25 02:49:03140 } else if (command_line.HasSwitch(switches::kShowAppList)) {
141 app_name = GetAppListAppName();
[email protected]c9bb06f42010-01-13 23:53:48142 } else {
grtc98803462017-03-08 10:05:08143 app_name = ShellUtil::GetBrowserModelId(is_per_user_install);
[email protected]c9bb06f42010-01-13 23:53:48144 }
[email protected]b39e32e2013-04-24 08:55:54145 DCHECK(!app_name.empty());
[email protected]c9bb06f42010-01-13 23:53:48146
pmonette9e4c1a82016-04-14 18:15:30147 return win::GetAppModelIdForProfile(app_name, profile_path);
[email protected]c9bb06f42010-01-13 23:53:48148}
149
gab88257b62016-01-15 03:19:19150void MigrateTaskbarPinsCallback() {
pmonetteb18379f2017-06-01 01:18:44151 base::ThreadRestrictions::AssertIOAllowed();
[email protected]3a3e72c2011-11-29 02:59:38152
153 // Get full path of chrome.
[email protected]650b2d52013-02-10 03:41:45154 base::FilePath chrome_exe;
[email protected]3a3e72c2011-11-29 02:59:38155 if (!PathService::Get(base::FILE_EXE, &chrome_exe))
156 return;
157
gab88257b62016-01-15 03:19:19158 base::FilePath pins_path;
159 if (!PathService::Get(base::DIR_TASKBAR_PINS, &pins_path)) {
160 NOTREACHED();
161 return;
[email protected]3a3e72c2011-11-29 02:59:38162 }
gab88257b62016-01-15 03:19:19163
pmonette9e4c1a82016-04-14 18:15:30164 win::MigrateShortcutsInPathInternal(chrome_exe, pins_path);
[email protected]3a3e72c2011-11-29 02:59:38165}
166
[email protected]caa05352014-03-01 00:43:05167// Windows 8 introduced a new protocol->executable binding system which cannot
168// be retrieved in the HKCR registry subkey method implemented below. We call
169// AssocQueryString with the new Win8-only flag ASSOCF_IS_PROTOCOL instead.
170base::string16 GetAppForProtocolUsingAssocQuery(const GURL& url) {
thestig11bf74d2014-11-24 20:14:42171 base::string16 url_scheme = base::ASCIIToUTF16(url.scheme());
[email protected]caa05352014-03-01 00:43:05172 // Don't attempt to query protocol association on an empty string.
[email protected]6e84d372014-05-29 23:36:39173 if (url_scheme.empty())
[email protected]caa05352014-03-01 00:43:05174 return base::string16();
175
[email protected]caa05352014-03-01 00:43:05176 // Query AssocQueryString for a human-readable description of the program
177 // that will be invoked given the provided URL spec. This is used only to
178 // populate the external protocol dialog box the user sees when invoking
179 // an unknown external protocol.
180 wchar_t out_buffer[1024];
181 DWORD buffer_size = arraysize(out_buffer);
182 HRESULT hr = AssocQueryString(ASSOCF_IS_PROTOCOL,
183 ASSOCSTR_FRIENDLYAPPNAME,
[email protected]6e84d372014-05-29 23:36:39184 url_scheme.c_str(),
[email protected]caa05352014-03-01 00:43:05185 NULL,
186 out_buffer,
187 &buffer_size);
188 if (FAILED(hr)) {
189 DLOG(WARNING) << "AssocQueryString failed!";
190 return base::string16();
191 }
192 return base::string16(out_buffer);
193}
194
195base::string16 GetAppForProtocolUsingRegistry(const GURL& url) {
dominickn0c9a5062016-10-06 20:49:00196 base::string16 command_to_launch;
197
198 // First, try and extract the application's display name.
199 base::string16 cmd_key_path = base::ASCIIToUTF16(url.scheme());
200 base::win::RegKey cmd_key_name(HKEY_CLASSES_ROOT, cmd_key_path.c_str(),
201 KEY_READ);
202 if (cmd_key_name.ReadValue(NULL, &command_to_launch) == ERROR_SUCCESS &&
203 !command_to_launch.empty()) {
204 return command_to_launch;
[email protected]caa05352014-03-01 00:43:05205 }
dominickn0c9a5062016-10-06 20:49:00206
207 // Otherwise, parse the command line in the registry, and return the basename
208 // of the program path if it exists.
209 cmd_key_path = base::ASCIIToUTF16(url.scheme() + "\\shell\\open\\command");
210 base::win::RegKey cmd_key_exe(HKEY_CLASSES_ROOT, cmd_key_path.c_str(),
211 KEY_READ);
212 if (cmd_key_exe.ReadValue(NULL, &command_to_launch) == ERROR_SUCCESS) {
213 base::CommandLine command_line(
214 base::CommandLine::FromString(command_to_launch));
215 return command_line.GetProgram().BaseName().value();
216 }
217
[email protected]caa05352014-03-01 00:43:05218 return base::string16();
219}
220
pmonette9fa59e882016-02-10 00:12:19221DefaultWebClientState GetDefaultWebClientStateFromShellUtilDefaultState(
222 ShellUtil::DefaultState default_state) {
[email protected]eb63da72012-10-15 20:39:48223 switch (default_state) {
224 case ShellUtil::NOT_DEFAULT:
pmonette9fa59e882016-02-10 00:12:19225 return DefaultWebClientState::NOT_DEFAULT;
[email protected]eb63da72012-10-15 20:39:48226 case ShellUtil::IS_DEFAULT:
pmonette9fa59e882016-02-10 00:12:19227 return DefaultWebClientState::IS_DEFAULT;
[email protected]eb63da72012-10-15 20:39:48228 default:
229 DCHECK_EQ(ShellUtil::UNKNOWN_DEFAULT, default_state);
pmonette9fa59e882016-02-10 00:12:19230 return DefaultWebClientState::UNKNOWN_DEFAULT;
[email protected]eb63da72012-10-15 20:39:48231 }
232}
233
grtc291eea2016-05-26 15:38:48234// A recorder of user actions in the Windows Settings app.
235class DefaultBrowserActionRecorder : public win::SettingsAppMonitor::Delegate {
236 public:
237 // Creates the recorder and the monitor that drives it. |continuation| will be
238 // run once the monitor's initialization completes (regardless of success or
239 // failure).
240 explicit DefaultBrowserActionRecorder(base::Closure continuation)
241 : continuation_(std::move(continuation)), settings_app_monitor_(this) {}
242
243 private:
244 // win::SettingsAppMonitor::Delegate:
245 void OnInitialized(HRESULT result) override {
246 UMA_HISTOGRAM_BOOLEAN("SettingsAppMonitor.InitializationResult",
247 SUCCEEDED(result));
248 if (SUCCEEDED(result)) {
249 base::RecordAction(
250 base::UserMetricsAction("SettingsAppMonitor.Initialized"));
251 }
252 continuation_.Run();
253 continuation_ = base::Closure();
254 }
255
256 void OnAppFocused() override {
257 base::RecordAction(
258 base::UserMetricsAction("SettingsAppMonitor.AppFocused"));
259 }
260
261 void OnChooserInvoked() override {
262 base::RecordAction(
263 base::UserMetricsAction("SettingsAppMonitor.ChooserInvoked"));
264 }
265
266 void OnBrowserChosen(const base::string16& browser_name) override {
267 if (browser_name ==
268 BrowserDistribution::GetDistribution()->GetDisplayName()) {
269 base::RecordAction(
270 base::UserMetricsAction("SettingsAppMonitor.ChromeBrowserChosen"));
271 } else {
272 base::RecordAction(
273 base::UserMetricsAction("SettingsAppMonitor.OtherBrowserChosen"));
274 }
275 }
276
pmonetteb66514b42016-12-07 20:28:39277 void OnPromoFocused() override {
278 base::RecordAction(
279 base::UserMetricsAction("SettingsAppMonitor.PromoFocused"));
280 }
281
282 void OnPromoChoiceMade(bool accept_promo) override {
283 if (accept_promo) {
284 base::RecordAction(
285 base::UserMetricsAction("SettingsAppMonitor.CheckItOut"));
286 } else {
287 base::RecordAction(
288 base::UserMetricsAction("SettingsAppMonitor.SwitchAnyway"));
289 }
290 }
291
grtc291eea2016-05-26 15:38:48292 // A closure to be run once initialization completes.
293 base::Closure continuation_;
294
295 // Monitors user interaction with the Windows Settings app for the sake of
296 // reporting user actions.
297 win::SettingsAppMonitor settings_app_monitor_;
298
299 DISALLOW_COPY_AND_ASSIGN(DefaultBrowserActionRecorder);
300};
301
302// A function bound up in a callback with a DefaultBrowserActionRecorder and
303// a closure to keep the former alive until the time comes to run the latter.
304void OnSettingsAppFinished(
305 std::unique_ptr<DefaultBrowserActionRecorder> recorder,
306 const base::Closure& on_finished_callback) {
307 recorder.reset();
308 on_finished_callback.Run();
309}
310
pmonette32a5cfb42016-04-11 22:04:44311// There is no way to make sure the user is done with the system settings, but a
312// signal that the interaction is finished is needed for UMA. A timer of 2
313// minutes is used as a substitute. The registry keys for the protocol
314// association with an app are also monitored to signal the end of the
315// interaction early when it is clear that the user made a choice (e.g. http
316// and https for default browser).
317//
318// This helper class manages both the timer and the registry watchers and makes
319// sure the callback for the end of the settings interaction is only run once.
320// This class also manages its own lifetime.
321class OpenSystemSettingsHelper {
322 public:
323 // Begin the monitoring and will call |on_finished_callback| when done.
324 // Takes in a null-terminated array of |protocols| whose registry keys must be
pmonette0c40087a2016-04-21 00:05:16325 // watched. The array must contain at least one element.
pmonette32a5cfb42016-04-11 22:04:44326 static void Begin(const wchar_t* const protocols[],
327 const base::Closure& on_finished_callback) {
pmonetteb18379f2017-06-01 01:18:44328 base::ThreadRestrictions::AssertIOAllowed();
pmonettec541d942016-06-15 22:08:32329
330 delete instance_;
331 instance_ = new OpenSystemSettingsHelper(protocols, on_finished_callback);
pmonette32a5cfb42016-04-11 22:04:44332 }
333
334 private:
335 // The reason the settings interaction concluded. Do not modify the ordering
336 // because it is used for UMA.
337 enum ConcludeReason { REGISTRY_WATCHER, TIMEOUT, NUM_CONCLUDE_REASON_TYPES };
338
339 OpenSystemSettingsHelper(const wchar_t* const protocols[],
340 const base::Closure& on_finished_callback)
pmonette0c40087a2016-04-21 00:05:16341 : scoped_user_protocol_entry_(protocols[0]),
342 on_finished_callback_(on_finished_callback),
343 weak_ptr_factory_(this) {
pmonette32a5cfb42016-04-11 22:04:44344 static const wchar_t kUrlAssociationFormat[] =
345 L"SOFTWARE\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\"
346 L"%ls\\UserChoice";
347
348 // Remember the start time.
349 start_time_ = base::TimeTicks::Now();
350
351 for (const wchar_t* const* scan = &protocols[0]; *scan != nullptr; ++scan) {
352 AddRegistryKeyWatcher(
353 base::StringPrintf(kUrlAssociationFormat, *scan).c_str());
354 }
355 // Only the watchers that were succesfully initialized are counted.
356 registry_watcher_count_ = registry_key_watchers_.size();
357
358 timer_.Start(
359 FROM_HERE, base::TimeDelta::FromMinutes(2),
360 base::Bind(&OpenSystemSettingsHelper::ConcludeInteraction,
361 weak_ptr_factory_.GetWeakPtr(), ConcludeReason::TIMEOUT));
362 }
363
pmonetteb18379f2017-06-01 01:18:44364 ~OpenSystemSettingsHelper() {
365 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
366 }
367
pmonette32a5cfb42016-04-11 22:04:44368 // Called when a change is detected on one of the registry keys being watched.
369 // Note: All types of modification to the registry key will trigger this
370 // function even if the value change is the only one that matters. This
371 // is good enough for now.
372 void OnRegistryKeyChanged() {
pmonetteb18379f2017-06-01 01:18:44373 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
pmonette32a5cfb42016-04-11 22:04:44374 // Make sure all the registry watchers have fired.
375 if (--registry_watcher_count_ == 0) {
376 UMA_HISTOGRAM_MEDIUM_TIMES(
377 "DefaultBrowser.SettingsInteraction.RegistryWatcherDuration",
378 base::TimeTicks::Now() - start_time_);
379
380 ConcludeInteraction(ConcludeReason::REGISTRY_WATCHER);
381 }
382 }
383
384 // Ends the monitoring with the system settings. Will call
385 // |on_finished_callback_| and then dispose of this class instance to make
386 // sure the callback won't get called subsequently.
387 void ConcludeInteraction(ConcludeReason conclude_reason) {
pmonetteb18379f2017-06-01 01:18:44388 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
pmonette32a5cfb42016-04-11 22:04:44389
390 UMA_HISTOGRAM_ENUMERATION(
391 "DefaultBrowser.SettingsInteraction.ConcludeReason", conclude_reason,
392 NUM_CONCLUDE_REASON_TYPES);
393 on_finished_callback_.Run();
pmonettec541d942016-06-15 22:08:32394 delete instance_;
395 instance_ = nullptr;
pmonette32a5cfb42016-04-11 22:04:44396 }
397
398 // Helper function to create a registry watcher for a given |key_path|. Do
399 // nothing on initialization failure.
400 void AddRegistryKeyWatcher(const wchar_t* key_path) {
pmonetteb18379f2017-06-01 01:18:44401 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
402
ricea86fa1dd2016-09-13 05:59:45403 auto reg_key = base::MakeUnique<base::win::RegKey>(HKEY_CURRENT_USER,
404 key_path, KEY_NOTIFY);
pmonette32a5cfb42016-04-11 22:04:44405
406 if (reg_key->Valid() &&
407 reg_key->StartWatching(
408 base::Bind(&OpenSystemSettingsHelper::OnRegistryKeyChanged,
409 weak_ptr_factory_.GetWeakPtr()))) {
410 registry_key_watchers_.push_back(std::move(reg_key));
411 }
412 }
413
pmonettec541d942016-06-15 22:08:32414 // Used to make sure only one instance is alive at the same time.
415 static OpenSystemSettingsHelper* instance_;
416
pmonette0c40087a2016-04-21 00:05:16417 // This is needed to make sure that Windows displays an entry for the protocol
418 // inside the "Choose default apps by protocol" settings page.
419 ScopedUserProtocolEntry scoped_user_protocol_entry_;
420
pmonette32a5cfb42016-04-11 22:04:44421 // The function to call when the interaction with the system settings is
422 // finished.
423 base::Closure on_finished_callback_;
424
425 // The number of time the registry key watchers must fire.
426 int registry_watcher_count_ = 0;
427
428 // There can be multiple registry key watchers as some settings modify
429 // multiple protocol associations. e.g. Changing the default browser modifies
430 // the http and https associations.
dcheng4af48582016-04-19 00:29:35431 std::vector<std::unique_ptr<base::win::RegKey>> registry_key_watchers_;
pmonette32a5cfb42016-04-11 22:04:44432
433 base::OneShotTimer timer_;
434
435 // Records the time it takes for the final registry watcher to get signaled.
436 base::TimeTicks start_time_;
437
pmonetteb18379f2017-06-01 01:18:44438 SEQUENCE_CHECKER(sequence_checker_);
439
pmonette32a5cfb42016-04-11 22:04:44440 // Weak ptrs are used to bind this class to the callbacks of the timer and the
441 // registry watcher. This makes it possible to self-delete after one of the
442 // callbacks is executed to cancel the remaining ones.
443 base::WeakPtrFactory<OpenSystemSettingsHelper> weak_ptr_factory_;
444
445 DISALLOW_COPY_AND_ASSIGN(OpenSystemSettingsHelper);
446};
447
pmonettec541d942016-06-15 22:08:32448OpenSystemSettingsHelper* OpenSystemSettingsHelper::instance_ = nullptr;
449
pmonettea98315242016-11-23 21:39:18450// Helper class to determine if Chrome is pinned to the taskbar. Hides the
451// complexity of managing the lifetime of a UtilityProcessMojoClient.
452class IsPinnedToTaskbarHelper {
453 public:
454 using ResultCallback = win::IsPinnedToTaskbarCallback;
455 using ErrorCallback = win::ConnectionErrorCallback;
456 static void GetState(const ErrorCallback& error_callback,
457 const ResultCallback& result_callback);
458
459 private:
460 IsPinnedToTaskbarHelper(const ErrorCallback& error_callback,
461 const ResultCallback& result_callback);
462
463 void OnConnectionError();
464 void OnIsPinnedToTaskbarResult(bool succeeded, bool is_pinned_to_taskbar);
465
466 content::UtilityProcessMojoClient<chrome::mojom::ShellHandler> shell_handler_;
467
468 ErrorCallback error_callback_;
469 ResultCallback result_callback_;
470
pmonetteb18379f2017-06-01 01:18:44471 SEQUENCE_CHECKER(sequence_checker_);
472
pmonettea98315242016-11-23 21:39:18473 DISALLOW_COPY_AND_ASSIGN(IsPinnedToTaskbarHelper);
474};
475
476// static
477void IsPinnedToTaskbarHelper::GetState(const ErrorCallback& error_callback,
478 const ResultCallback& result_callback) {
479 // Self-deleting when the ShellHandler completes.
480 new IsPinnedToTaskbarHelper(error_callback, result_callback);
pmonette5057ca3b2016-07-04 16:48:40481}
482
pmonettea98315242016-11-23 21:39:18483IsPinnedToTaskbarHelper::IsPinnedToTaskbarHelper(
484 const ErrorCallback& error_callback,
485 const ResultCallback& result_callback)
486 : shell_handler_(
487 l10n_util::GetStringUTF16(IDS_UTILITY_PROCESS_SHELL_HANDLER_NAME)),
488 error_callback_(error_callback),
489 result_callback_(result_callback) {
pmonette83edc1e2016-12-15 17:36:44490 DCHECK(error_callback_);
491 DCHECK(result_callback_);
492
pmonettea98315242016-11-23 21:39:18493 // |shell_handler_| owns the callbacks and is guaranteed to be destroyed
494 // before |this|, therefore making base::Unretained() safe to use.
495 shell_handler_.set_error_callback(base::Bind(
496 &IsPinnedToTaskbarHelper::OnConnectionError, base::Unretained(this)));
497 shell_handler_.set_disable_sandbox();
498 shell_handler_.Start();
499
500 shell_handler_.service()->IsPinnedToTaskbar(
501 base::Bind(&IsPinnedToTaskbarHelper::OnIsPinnedToTaskbarResult,
502 base::Unretained(this)));
503}
504
505void IsPinnedToTaskbarHelper::OnConnectionError() {
pmonetteb18379f2017-06-01 01:18:44506 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
507
pmonettea98315242016-11-23 21:39:18508 error_callback_.Run();
509 delete this;
510}
511
512void IsPinnedToTaskbarHelper::OnIsPinnedToTaskbarResult(
pmonette5057ca3b2016-07-04 16:48:40513 bool succeeded,
514 bool is_pinned_to_taskbar) {
pmonetteb18379f2017-06-01 01:18:44515 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
516
pmonettea98315242016-11-23 21:39:18517 result_callback_.Run(succeeded, is_pinned_to_taskbar);
518 delete this;
pmonette5057ca3b2016-07-04 16:48:40519}
520
[email protected]3a3e72c2011-11-29 02:59:38521} // namespace
[email protected]12f520c2010-01-06 18:11:15522
pmonette9fa59e882016-02-10 00:12:19523bool SetAsDefaultBrowser() {
pmonetteb18379f2017-06-01 01:18:44524 base::ThreadRestrictions::AssertIOAllowed();
525
[email protected]650b2d52013-02-10 03:41:45526 base::FilePath chrome_exe;
[email protected]d24c4012009-07-28 01:57:31527 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
528 LOG(ERROR) << "Error getting app exe path";
529 return false;
530 }
531
532 // From UI currently we only allow setting default browser for current user.
[email protected]bf6117c7e2010-12-01 06:00:25533 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
grte76ca2852014-12-05 16:42:10534 if (!ShellUtil::MakeChromeDefault(dist, ShellUtil::CURRENT_USER, chrome_exe,
535 true /* elevate_if_not_admin */)) {
[email protected]d24c4012009-07-28 01:57:31536 LOG(ERROR) << "Chrome could not be set as default browser.";
537 return false;
538 }
539
[email protected]8e96e502010-10-21 20:57:12540 VLOG(1) << "Chrome registered as default browser.";
[email protected]d24c4012009-07-28 01:57:31541 return true;
542}
543
pmonette9fa59e882016-02-10 00:12:19544bool SetAsDefaultProtocolClient(const std::string& protocol) {
pmonetteb18379f2017-06-01 01:18:44545 base::ThreadRestrictions::AssertIOAllowed();
546
[email protected]4468a5b2011-05-26 07:48:02547 if (protocol.empty())
548 return false;
549
[email protected]650b2d52013-02-10 03:41:45550 base::FilePath chrome_exe;
[email protected]4468a5b2011-05-26 07:48:02551 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
552 LOG(ERROR) << "Error getting app exe path";
553 return false;
554 }
555
[email protected]f911df52013-12-24 23:24:23556 base::string16 wprotocol(base::UTF8ToUTF16(protocol));
[email protected]4468a5b2011-05-26 07:48:02557 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
grte76ca2852014-12-05 16:42:10558 if (!ShellUtil::MakeChromeDefaultProtocolClient(dist, chrome_exe,
559 wprotocol)) {
[email protected]4468a5b2011-05-26 07:48:02560 LOG(ERROR) << "Chrome could not be set as default handler for "
561 << protocol << ".";
562 return false;
563 }
564
565 VLOG(1) << "Chrome registered as default handler for " << protocol << ".";
566 return true;
567}
568
pmonette32a5cfb42016-04-11 22:04:44569DefaultWebClientSetPermission GetDefaultWebClientSetPermission() {
grt509bc942017-03-25 08:43:21570 if (!install_static::SupportsSetAsDefaultBrowser())
pmonette034a03d92015-10-02 21:04:27571 return SET_DEFAULT_NOT_ALLOWED;
pmonette034a03d92015-10-02 21:04:27572 if (ShellUtil::CanMakeChromeDefaultUnattended())
573 return SET_DEFAULT_UNATTENDED;
pmonette32a5cfb42016-04-11 22:04:44574 // Windows 8 and 10 both introduced a new way to set the default web client
575 // which require user interaction.
pmonettef89ac7c72015-10-06 03:22:01576 return SET_DEFAULT_INTERACTIVE;
pmonette034a03d92015-10-02 21:04:27577}
578
pmonette9fa59e882016-02-10 00:12:19579bool IsElevationNeededForSettingDefaultProtocolClient() {
pmonette868ca642015-09-02 14:34:02580 return base::win::GetVersion() < base::win::VERSION_WIN8;
581}
582
pmonette9fa59e882016-02-10 00:12:19583base::string16 GetApplicationNameForProtocol(const GURL& url) {
benwellsd0c67aa2016-07-28 02:57:20584 base::string16 application_name;
585 // Windows 8 or above has a new protocol association query.
586 if (base::win::GetVersion() >= base::win::VERSION_WIN8) {
587 application_name = GetAppForProtocolUsingAssocQuery(url);
588 if (!application_name.empty())
589 return application_name;
590 }
591
592 return GetAppForProtocolUsingRegistry(url);
[email protected]42dc9402013-01-30 07:54:20593}
594
pmonette9fa59e882016-02-10 00:12:19595DefaultWebClientState GetDefaultBrowser() {
pmonette034a03d92015-10-02 21:04:27596 return GetDefaultWebClientStateFromShellUtilDefaultState(
597 ShellUtil::GetChromeDefaultState());
598}
599
[email protected]d24c4012009-07-28 01:57:31600// There is no reliable way to say which browser is default on a machine (each
601// browser can have some of the protocols/shortcuts). So we look for only HTTP
602// protocol handler. Even this handler is located at different places in
603// registry on XP and Vista:
604// - HKCR\http\shell\open\command (XP)
605// - HKCU\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\
606// http\UserChoice (Vista)
thakise4a02ff32016-09-20 17:00:42607// This method checks if Firefox is default browser by checking these
[email protected]d24c4012009-07-28 01:57:31608// locations and returns true if Firefox traces are found there. In case of
609// error (or if Firefox is not found)it returns the default value which
610// is false.
pmonette9fa59e882016-02-10 00:12:19611bool IsFirefoxDefaultBrowser() {
[email protected]d24c4012009-07-28 01:57:31612 bool ff_default = false;
[email protected]935aa542010-10-15 01:59:15613 if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
[email protected]0085863a2013-12-06 21:19:03614 base::string16 app_cmd;
[email protected]2d6503982010-10-17 04:41:54615 base::win::RegKey key(HKEY_CURRENT_USER,
616 ShellUtil::kRegVistaUrlPrefs, KEY_READ);
[email protected]e06f4d52011-01-19 07:28:46617 if (key.Valid() && (key.ReadValue(L"Progid", &app_cmd) == ERROR_SUCCESS) &&
[email protected]d24c4012009-07-28 01:57:31618 app_cmd == L"FirefoxURL")
619 ff_default = true;
620 } else {
[email protected]0085863a2013-12-06 21:19:03621 base::string16 key_path(L"http");
[email protected]d24c4012009-07-28 01:57:31622 key_path.append(ShellUtil::kRegShellOpen);
[email protected]2d6503982010-10-17 04:41:54623 base::win::RegKey key(HKEY_CLASSES_ROOT, key_path.c_str(), KEY_READ);
[email protected]0085863a2013-12-06 21:19:03624 base::string16 app_cmd;
[email protected]e06f4d52011-01-19 07:28:46625 if (key.Valid() && (key.ReadValue(L"", &app_cmd) == ERROR_SUCCESS) &&
[email protected]cb1f4ac2014-08-07 16:55:42626 base::string16::npos !=
brettwfce8d192015-08-10 19:07:51627 base::ToLowerASCII(app_cmd).find(L"firefox"))
[email protected]d24c4012009-07-28 01:57:31628 ff_default = true;
629 }
630 return ff_default;
631}
[email protected]12f520c2010-01-06 18:11:15632
pmonette9fa59e882016-02-10 00:12:19633DefaultWebClientState IsDefaultProtocolClient(const std::string& protocol) {
pmonette034a03d92015-10-02 21:04:27634 return GetDefaultWebClientStateFromShellUtilDefaultState(
635 ShellUtil::GetChromeDefaultProtocolClientState(
636 base::UTF8ToUTF16(protocol)));
637}
638
pmonette9e4c1a82016-04-14 18:15:30639namespace win {
640
641bool SetAsDefaultBrowserUsingIntentPicker() {
pmonetteb18379f2017-06-01 01:18:44642 base::ThreadRestrictions::AssertIOAllowed();
643
pmonette9e4c1a82016-04-14 18:15:30644 base::FilePath chrome_exe;
645 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
646 NOTREACHED() << "Error getting app exe path";
647 return false;
648 }
649
650 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
651 if (!ShellUtil::ShowMakeChromeDefaultSystemUI(dist, chrome_exe)) {
652 LOG(ERROR) << "Failed to launch the set-default-browser Windows UI.";
653 return false;
654 }
655
656 VLOG(1) << "Set-default-browser Windows UI completed.";
657 return true;
658}
659
660void SetAsDefaultBrowserUsingSystemSettings(
661 const base::Closure& on_finished_callback) {
pmonetteb18379f2017-06-01 01:18:44662 base::ThreadRestrictions::AssertIOAllowed();
pmonette9e4c1a82016-04-14 18:15:30663
664 base::FilePath chrome_exe;
665 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
666 NOTREACHED() << "Error getting app exe path";
667 on_finished_callback.Run();
668 return;
669 }
670
grtc291eea2016-05-26 15:38:48671 // Create an action recorder that will open the settings app once it has
672 // initialized.
673 std::unique_ptr<DefaultBrowserActionRecorder> recorder(
674 new DefaultBrowserActionRecorder(base::Bind(
675 base::IgnoreResult(&ShellUtil::ShowMakeChromeDefaultSystemUI),
676 base::Unretained(BrowserDistribution::GetDistribution()),
677 chrome_exe)));
pmonette9e4c1a82016-04-14 18:15:30678
grtc291eea2016-05-26 15:38:48679 // The helper manages its own lifetime. Bind the action recorder
680 // into the finished callback to keep it alive throughout the
681 // interaction.
682 static const wchar_t* const kProtocols[] = {L"http", L"https", nullptr};
683 OpenSystemSettingsHelper::Begin(
684 kProtocols, base::Bind(&OnSettingsAppFinished, base::Passed(&recorder),
685 on_finished_callback));
pmonette9e4c1a82016-04-14 18:15:30686}
687
688bool SetAsDefaultProtocolClientUsingIntentPicker(const std::string& protocol) {
pmonetteb18379f2017-06-01 01:18:44689 base::ThreadRestrictions::AssertIOAllowed();
690
pmonette9e4c1a82016-04-14 18:15:30691 base::FilePath chrome_exe;
692 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
693 NOTREACHED() << "Error getting app exe path";
694 return false;
695 }
696
697 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
698 base::string16 wprotocol(base::UTF8ToUTF16(protocol));
699 if (!ShellUtil::ShowMakeChromeDefaultProtocolClientSystemUI(dist, chrome_exe,
700 wprotocol)) {
701 LOG(ERROR) << "Failed to launch the set-default-client Windows UI.";
702 return false;
703 }
704
705 VLOG(1) << "Set-default-client Windows UI completed.";
706 return true;
707}
708
pmonette0c40087a2016-04-21 00:05:16709void SetAsDefaultProtocolClientUsingSystemSettings(
710 const std::string& protocol,
711 const base::Closure& on_finished_callback) {
pmonetteb18379f2017-06-01 01:18:44712 base::ThreadRestrictions::AssertIOAllowed();
pmonette0c40087a2016-04-21 00:05:16713
714 base::FilePath chrome_exe;
715 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
716 NOTREACHED() << "Error getting app exe path";
717 on_finished_callback.Run();
718 return;
719 }
720
721 // The helper manages its own lifetime.
722 base::string16 wprotocol(base::UTF8ToUTF16(protocol));
723 const wchar_t* const kProtocols[] = {wprotocol.c_str(), nullptr};
724 OpenSystemSettingsHelper::Begin(kProtocols, on_finished_callback);
725
726 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
727 ShellUtil::ShowMakeChromeDefaultProtocolClientSystemUI(dist, chrome_exe,
728 wprotocol);
729}
730
pmonette9fa59e882016-02-10 00:12:19731base::string16 GetAppModelIdForProfile(const base::string16& app_name,
732 const base::FilePath& profile_path) {
[email protected]d2065e062013-12-12 23:49:52733 std::vector<base::string16> components;
[email protected]a0448002012-06-19 04:32:10734 components.push_back(app_name);
[email protected]0085863a2013-12-06 21:19:03735 const base::string16 profile_id(GetProfileIdFromPath(profile_path));
[email protected]a0448002012-06-19 04:32:10736 if (!profile_id.empty())
737 components.push_back(profile_id);
738 return ShellUtil::BuildAppModelId(components);
[email protected]12f520c2010-01-06 18:11:15739}
740
pmonette9fa59e882016-02-10 00:12:19741base::string16 GetChromiumModelIdForProfile(
[email protected]650b2d52013-02-10 03:41:45742 const base::FilePath& profile_path) {
[email protected]a0448002012-06-19 04:32:10743 return GetAppModelIdForProfile(
grtc98803462017-03-08 10:05:08744 ShellUtil::GetBrowserModelId(InstallUtil::IsPerUserInstall()),
[email protected]786799692012-09-26 14:16:48745 profile_path);
[email protected]12f520c2010-01-06 18:11:15746}
[email protected]c9bb06f42010-01-13 23:53:48747
pmonette9fa59e882016-02-10 00:12:19748void MigrateTaskbarPins() {
[email protected]935aa542010-10-15 01:59:15749 if (base::win::GetVersion() < base::win::VERSION_WIN7)
[email protected]c9bb06f42010-01-13 23:53:48750 return;
751
pmonetteb18379f2017-06-01 01:18:44752 // This needs to happen (e.g. so that the appid is fixed and the
753 // run-time Chrome icon is merged with the taskbar shortcut), but it is not an
754 // urgent task.
755 base::PostTaskWithTraits(FROM_HERE,
756 {base::MayBlock(), base::TaskPriority::BACKGROUND},
757 base::Bind(&MigrateTaskbarPinsCallback));
[email protected]c9bb06f42010-01-13 23:53:48758}
[email protected]43903b82012-06-01 05:26:23759
pmonettea98315242016-11-23 21:39:18760void GetIsPinnedToTaskbarState(
761 const ConnectionErrorCallback& on_error_callback,
762 const IsPinnedToTaskbarCallback& result_callback) {
763 IsPinnedToTaskbarHelper::GetState(on_error_callback, result_callback);
pmonette5057ca3b2016-07-04 16:48:40764}
765
pmonette9fa59e882016-02-10 00:12:19766int MigrateShortcutsInPathInternal(const base::FilePath& chrome_exe,
767 const base::FilePath& path) {
[email protected]8ea8f1ef2013-01-06 18:39:03768 DCHECK(base::win::GetVersion() >= base::win::VERSION_WIN7);
769
770 // Enumerate all pinned shortcuts in the given path directly.
[email protected]25a4c1c2013-06-08 04:53:36771 base::FileEnumerator shortcuts_enum(
[email protected]8ea8f1ef2013-01-06 18:39:03772 path, false, // not recursive
[email protected]25a4c1c2013-06-08 04:53:36773 base::FileEnumerator::FILES, FILE_PATH_LITERAL("*.lnk"));
[email protected]8ea8f1ef2013-01-06 18:39:03774
grt973412f2017-02-25 08:04:21775 bool is_per_user_install = InstallUtil::IsPerUserInstall();
[email protected]8ea8f1ef2013-01-06 18:39:03776
777 int shortcuts_migrated = 0;
[email protected]650b2d52013-02-10 03:41:45778 base::FilePath target_path;
[email protected]0085863a2013-12-06 21:19:03779 base::string16 arguments;
[email protected]07983302013-01-21 19:41:44780 base::win::ScopedPropVariant propvariant;
[email protected]650b2d52013-02-10 03:41:45781 for (base::FilePath shortcut = shortcuts_enum.Next(); !shortcut.empty();
[email protected]8ea8f1ef2013-01-06 18:39:03782 shortcut = shortcuts_enum.Next()) {
783 // TODO(gab): Use ProgramCompare instead of comparing FilePaths below once
784 // it is fixed to work with FilePaths with spaces.
785 if (!base::win::ResolveShortcut(shortcut, &target_path, &arguments) ||
786 chrome_exe != target_path) {
787 continue;
788 }
avi556c05022014-12-22 23:31:43789 base::CommandLine command_line(
790 base::CommandLine::FromString(base::StringPrintf(
791 L"\"%ls\" %ls", target_path.value().c_str(), arguments.c_str())));
[email protected]8ea8f1ef2013-01-06 18:39:03792
793 // Get the expected AppId for this Chrome shortcut.
[email protected]0085863a2013-12-06 21:19:03794 base::string16 expected_app_id(
[email protected]8ea8f1ef2013-01-06 18:39:03795 GetExpectedAppId(command_line, is_per_user_install));
796 if (expected_app_id.empty())
797 continue;
798
799 // Load the shortcut.
800 base::win::ScopedComPtr<IShellLink> shell_link;
801 base::win::ScopedComPtr<IPersistFile> persist_file;
robliaoa872e992017-05-18 06:36:19802 if (FAILED(::CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
803 IID_PPV_ARGS(&shell_link))) ||
robliaoa7956aa2017-05-17 19:10:54804 FAILED(shell_link.CopyTo(persist_file.GetAddressOf())) ||
[email protected]8ea8f1ef2013-01-06 18:39:03805 FAILED(persist_file->Load(shortcut.value().c_str(), STGM_READ))) {
806 DLOG(WARNING) << "Failed loading shortcut at " << shortcut.value();
807 continue;
808 }
809
810 // Any properties that need to be updated on the shortcut will be stored in
811 // |updated_properties|.
812 base::win::ShortcutProperties updated_properties;
813
814 // Validate the existing app id for the shortcut.
815 base::win::ScopedComPtr<IPropertyStore> property_store;
[email protected]07983302013-01-21 19:41:44816 propvariant.Reset();
robliaoa7956aa2017-05-17 19:10:54817 if (FAILED(shell_link.CopyTo(property_store.GetAddressOf())) ||
dcheng81762e02014-11-21 21:22:28818 property_store->GetValue(PKEY_AppUserModel_ID, propvariant.Receive()) !=
819 S_OK) {
[email protected]8ea8f1ef2013-01-06 18:39:03820 // When in doubt, prefer not updating the shortcut.
821 NOTREACHED();
822 continue;
[email protected]8ea8f1ef2013-01-06 18:39:03823 } else {
[email protected]07983302013-01-21 19:41:44824 switch (propvariant.get().vt) {
825 case VT_EMPTY:
826 // If there is no app_id set, set our app_id if one is expected.
827 if (!expected_app_id.empty())
828 updated_properties.set_app_id(expected_app_id);
829 break;
830 case VT_LPWSTR:
[email protected]0085863a2013-12-06 21:19:03831 if (expected_app_id != base::string16(propvariant.get().pwszVal))
[email protected]07983302013-01-21 19:41:44832 updated_properties.set_app_id(expected_app_id);
833 break;
834 default:
835 NOTREACHED();
836 continue;
[email protected]8ea8f1ef2013-01-06 18:39:03837 }
838 }
839
gab88257b62016-01-15 03:19:19840 // Clear dual_mode property from any shortcuts that previously had it (it
841 // was only ever installed on shortcuts with the
842 // |default_chromium_model_id|).
[email protected]0085863a2013-12-06 21:19:03843 base::string16 default_chromium_model_id(
grtc98803462017-03-08 10:05:08844 ShellUtil::GetBrowserModelId(is_per_user_install));
gab88257b62016-01-15 03:19:19845 if (expected_app_id == default_chromium_model_id) {
[email protected]07983302013-01-21 19:41:44846 propvariant.Reset();
[email protected]8ea8f1ef2013-01-06 18:39:03847 if (property_store->GetValue(PKEY_AppUserModel_IsDualMode,
[email protected]07983302013-01-21 19:41:44848 propvariant.Receive()) != S_OK) {
[email protected]8ea8f1ef2013-01-06 18:39:03849 // When in doubt, prefer to not update the shortcut.
850 NOTREACHED();
851 continue;
gab38d242d4d2016-01-14 20:38:12852 }
853 if (propvariant.get().vt == VT_BOOL &&
854 !!propvariant.get().boolVal) {
855 updated_properties.set_dual_mode(false);
[email protected]8ea8f1ef2013-01-06 18:39:03856 }
857 }
858
robliao9212cb42017-04-06 17:43:39859 persist_file.Reset();
860 shell_link.Reset();
[email protected]8ea8f1ef2013-01-06 18:39:03861
862 // Update the shortcut if some of its properties need to be updated.
863 if (updated_properties.options &&
864 base::win::CreateOrUpdateShortcutLink(
865 shortcut, updated_properties,
866 base::win::SHORTCUT_UPDATE_EXISTING)) {
867 ++shortcuts_migrated;
868 }
869 }
870 return shortcuts_migrated;
871}
872
pmonette32a5cfb42016-04-11 22:04:44873} // namespace win
874
pmonette9fa59e882016-02-10 00:12:19875} // namespace shell_integration