blob: 21cbc719a9be9e7354c75e978af75da17b3518c9 [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>
[email protected]caa05352014-03-01 00:43:058#include <shlwapi.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>
[email protected]d24c4012009-07-28 01:57:3113
pmonette32a5cfb42016-04-11 22:04:4414#include <vector>
15
[email protected]3a3e72c2011-11-29 02:59:3816#include "base/bind.h"
pmonette32a5cfb42016-04-11 22:04:4417#include "base/callback.h"
[email protected]d24c4012009-07-28 01:57:3118#include "base/command_line.h"
[email protected]25a4c1c2013-06-08 04:53:3619#include "base/files/file_enumerator.h"
thestig18dfb7a52014-08-26 10:44:0420#include "base/files/file_util.h"
avi664c07b2015-12-26 02:18:3121#include "base/macros.h"
dcheng4af48582016-04-19 00:29:3522#include "base/memory/ptr_util.h"
pmonette32a5cfb42016-04-11 22:04:4423#include "base/memory/weak_ptr.h"
[email protected]fa1e0e12013-07-18 00:10:1424#include "base/message_loop/message_loop.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"
[email protected]24a555b62013-06-10 22:01:1729#include "base/strings/string_util.h"
30#include "base/strings/stringprintf.h"
[email protected]e309f312013-06-07 21:50:0831#include "base/strings/utf_string_conversions.h"
pmonette32a5cfb42016-04-11 22:04:4432#include "base/time/time.h"
33#include "base/timer/timer.h"
[email protected]2d6503982010-10-17 04:41:5434#include "base/win/registry.h"
[email protected]8ee65ba2011-04-12 20:53:2335#include "base/win/scoped_comptr.h"
[email protected]07983302013-01-21 19:41:4436#include "base/win/scoped_propvariant.h"
[email protected]f1024e22012-09-12 07:14:5537#include "base/win/shortcut.h"
[email protected]935aa542010-10-15 01:59:1538#include "base/win/windows_version.h"
[email protected]89d43832013-06-29 20:25:2039#include "chrome/browser/policy/policy_path_parser.h"
pmonette32a5cfb42016-04-11 22:04:4440#include "chrome/browser/shell_integration.h"
[email protected]c9bb06f42010-01-13 23:53:4841#include "chrome/browser/web_applications/web_app.h"
grtc291eea2016-05-26 15:38:4842#include "chrome/browser/win/settings_app_monitor.h"
[email protected]d24c4012009-07-28 01:57:3143#include "chrome/common/chrome_constants.h"
[email protected]12f520c2010-01-06 18:11:1544#include "chrome/common/chrome_paths_internal.h"
[email protected]c9bb06f42010-01-13 23:53:4845#include "chrome/common/chrome_switches.h"
[email protected]4468a5b2011-05-26 07:48:0246#include "chrome/installer/setup/setup_util.h"
[email protected]d24c4012009-07-28 01:57:3147#include "chrome/installer/util/browser_distribution.h"
48#include "chrome/installer/util/create_reg_key_work_item.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/set_reg_value_work_item.h"
52#include "chrome/installer/util/shell_util.h"
53#include "chrome/installer/util/util_constants.h"
54#include "chrome/installer/util/work_item.h"
55#include "chrome/installer/util/work_item_list.h"
pmonette2b1dbee2016-01-08 20:18:5856#include "components/variations/variations_associated_data.h"
[email protected]c38831a12011-10-28 12:44:4957#include "content/public/browser/browser_thread.h"
[email protected]d24c4012009-07-28 01:57:3158
[email protected]631bb742011-11-02 11:29:3959using content::BrowserThread;
60
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";
103 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
104 base::string16 app_name(dist->GetBaseAppId());
105 app_name.append(kAppListAppNameSuffix);
106 return app_name;
107}
108
[email protected]8ea8f1ef2013-01-06 18:39:03109// Gets expected app id for given Chrome (based on |command_line| and
110// |is_per_user_install|).
avi556c05022014-12-22 23:31:43111base::string16 GetExpectedAppId(const base::CommandLine& command_line,
[email protected]6a72a632013-12-12 22:22:00112 bool is_per_user_install) {
[email protected]b39e32e2013-04-24 08:55:54113 base::FilePath user_data_dir;
114 if (command_line.HasSwitch(switches::kUserDataDir))
115 user_data_dir = command_line.GetSwitchValuePath(switches::kUserDataDir);
116 else
117 chrome::GetDefaultUserDataDirectory(&user_data_dir);
[email protected]89d43832013-06-29 20:25:20118 // Adjust with any policy that overrides any other way to set the path.
119 policy::path_parser::CheckUserDataDirPolicy(&user_data_dir);
[email protected]b39e32e2013-04-24 08:55:54120 DCHECK(!user_data_dir.empty());
[email protected]c9bb06f42010-01-13 23:53:48121
[email protected]b39e32e2013-04-24 08:55:54122 base::FilePath profile_subdir;
123 if (command_line.HasSwitch(switches::kProfileDirectory)) {
124 profile_subdir =
125 command_line.GetSwitchValuePath(switches::kProfileDirectory);
126 } else {
[email protected]f911df52013-12-24 23:24:23127 profile_subdir =
128 base::FilePath(base::ASCIIToUTF16(chrome::kInitialProfile));
[email protected]b39e32e2013-04-24 08:55:54129 }
130 DCHECK(!profile_subdir.empty());
131
132 base::FilePath profile_path = user_data_dir.Append(profile_subdir);
[email protected]0085863a2013-12-06 21:19:03133 base::string16 app_name;
[email protected]c9bb06f42010-01-13 23:53:48134 if (command_line.HasSwitch(switches::kApp)) {
[email protected]f911df52013-12-24 23:24:23135 app_name = base::UTF8ToUTF16(web_app::GenerateApplicationNameFromURL(
[email protected]57ecc4b2010-08-11 03:02:51136 GURL(command_line.GetSwitchValueASCII(switches::kApp))));
[email protected]2f1c09d2011-01-14 14:58:14137 } else if (command_line.HasSwitch(switches::kAppId)) {
[email protected]f911df52013-12-24 23:24:23138 app_name = base::UTF8ToUTF16(
139 web_app::GenerateApplicationNameFromExtensionId(
140 command_line.GetSwitchValueASCII(switches::kAppId)));
taptedc2c690ab2016-05-25 02:49:03141 } else if (command_line.HasSwitch(switches::kShowAppList)) {
142 app_name = GetAppListAppName();
[email protected]c9bb06f42010-01-13 23:53:48143 } else {
[email protected]a0448002012-06-19 04:32:10144 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
[email protected]8ea8f1ef2013-01-06 18:39:03145 app_name = ShellUtil::GetBrowserModelId(dist, is_per_user_install);
[email protected]c9bb06f42010-01-13 23:53:48146 }
[email protected]b39e32e2013-04-24 08:55:54147 DCHECK(!app_name.empty());
[email protected]c9bb06f42010-01-13 23:53:48148
pmonette9e4c1a82016-04-14 18:15:30149 return win::GetAppModelIdForProfile(app_name, profile_path);
[email protected]c9bb06f42010-01-13 23:53:48150}
151
gab88257b62016-01-15 03:19:19152void MigrateTaskbarPinsCallback() {
[email protected]3a3e72c2011-11-29 02:59:38153 // This should run on the file thread.
thestig00844cea2015-09-08 21:44:52154 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
[email protected]3a3e72c2011-11-29 02:59:38155
156 // Get full path of chrome.
[email protected]650b2d52013-02-10 03:41:45157 base::FilePath chrome_exe;
[email protected]3a3e72c2011-11-29 02:59:38158 if (!PathService::Get(base::FILE_EXE, &chrome_exe))
159 return;
160
gab88257b62016-01-15 03:19:19161 base::FilePath pins_path;
162 if (!PathService::Get(base::DIR_TASKBAR_PINS, &pins_path)) {
163 NOTREACHED();
164 return;
[email protected]3a3e72c2011-11-29 02:59:38165 }
gab88257b62016-01-15 03:19:19166
pmonette9e4c1a82016-04-14 18:15:30167 win::MigrateShortcutsInPathInternal(chrome_exe, pins_path);
[email protected]3a3e72c2011-11-29 02:59:38168}
169
[email protected]caa05352014-03-01 00:43:05170// Windows 8 introduced a new protocol->executable binding system which cannot
171// be retrieved in the HKCR registry subkey method implemented below. We call
172// AssocQueryString with the new Win8-only flag ASSOCF_IS_PROTOCOL instead.
173base::string16 GetAppForProtocolUsingAssocQuery(const GURL& url) {
thestig11bf74d2014-11-24 20:14:42174 base::string16 url_scheme = base::ASCIIToUTF16(url.scheme());
[email protected]caa05352014-03-01 00:43:05175 // Don't attempt to query protocol association on an empty string.
[email protected]6e84d372014-05-29 23:36:39176 if (url_scheme.empty())
[email protected]caa05352014-03-01 00:43:05177 return base::string16();
178
[email protected]caa05352014-03-01 00:43:05179 // Query AssocQueryString for a human-readable description of the program
180 // that will be invoked given the provided URL spec. This is used only to
181 // populate the external protocol dialog box the user sees when invoking
182 // an unknown external protocol.
183 wchar_t out_buffer[1024];
184 DWORD buffer_size = arraysize(out_buffer);
185 HRESULT hr = AssocQueryString(ASSOCF_IS_PROTOCOL,
186 ASSOCSTR_FRIENDLYAPPNAME,
[email protected]6e84d372014-05-29 23:36:39187 url_scheme.c_str(),
[email protected]caa05352014-03-01 00:43:05188 NULL,
189 out_buffer,
190 &buffer_size);
191 if (FAILED(hr)) {
192 DLOG(WARNING) << "AssocQueryString failed!";
193 return base::string16();
194 }
195 return base::string16(out_buffer);
196}
197
198base::string16 GetAppForProtocolUsingRegistry(const GURL& url) {
[email protected]caa05352014-03-01 00:43:05199 const base::string16 cmd_key_path =
thestig11bf74d2014-11-24 20:14:42200 base::ASCIIToUTF16(url.scheme() + "\\shell\\open\\command");
[email protected]caa05352014-03-01 00:43:05201 base::win::RegKey cmd_key(HKEY_CLASSES_ROOT,
202 cmd_key_path.c_str(),
203 KEY_READ);
[email protected]caa05352014-03-01 00:43:05204 base::string16 application_to_launch;
205 if (cmd_key.ReadValue(NULL, &application_to_launch) == ERROR_SUCCESS) {
benwellse84d8392015-08-18 05:23:58206 const base::string16 url_spec =
207 base::ASCIIToUTF16(url.possibly_invalid_spec());
brettwe6dae462015-06-24 20:54:45208 base::ReplaceSubstringsAfterOffset(&application_to_launch,
209 0,
210 L"%1",
benwellse84d8392015-08-18 05:23:58211 url_spec);
[email protected]caa05352014-03-01 00:43:05212 return application_to_launch;
213 }
214 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) {
220 case ShellUtil::NOT_DEFAULT:
pmonette9fa59e882016-02-10 00:12:19221 return DefaultWebClientState::NOT_DEFAULT;
[email protected]eb63da72012-10-15 20:39:48222 case ShellUtil::IS_DEFAULT:
pmonette9fa59e882016-02-10 00:12:19223 return DefaultWebClientState::IS_DEFAULT;
[email protected]eb63da72012-10-15 20:39:48224 default:
225 DCHECK_EQ(ShellUtil::UNKNOWN_DEFAULT, default_state);
pmonette9fa59e882016-02-10 00:12:19226 return DefaultWebClientState::UNKNOWN_DEFAULT;
[email protected]eb63da72012-10-15 20:39:48227 }
228}
229
grtc291eea2016-05-26 15:38:48230// A recorder of user actions in the Windows Settings app.
231class DefaultBrowserActionRecorder : public win::SettingsAppMonitor::Delegate {
232 public:
233 // Creates the recorder and the monitor that drives it. |continuation| will be
234 // run once the monitor's initialization completes (regardless of success or
235 // failure).
236 explicit DefaultBrowserActionRecorder(base::Closure continuation)
237 : continuation_(std::move(continuation)), settings_app_monitor_(this) {}
238
239 private:
240 // win::SettingsAppMonitor::Delegate:
241 void OnInitialized(HRESULT result) override {
242 UMA_HISTOGRAM_BOOLEAN("SettingsAppMonitor.InitializationResult",
243 SUCCEEDED(result));
244 if (SUCCEEDED(result)) {
245 base::RecordAction(
246 base::UserMetricsAction("SettingsAppMonitor.Initialized"));
247 }
248 continuation_.Run();
249 continuation_ = base::Closure();
250 }
251
252 void OnAppFocused() override {
253 base::RecordAction(
254 base::UserMetricsAction("SettingsAppMonitor.AppFocused"));
255 }
256
257 void OnChooserInvoked() override {
258 base::RecordAction(
259 base::UserMetricsAction("SettingsAppMonitor.ChooserInvoked"));
260 }
261
262 void OnBrowserChosen(const base::string16& browser_name) override {
263 if (browser_name ==
264 BrowserDistribution::GetDistribution()->GetDisplayName()) {
265 base::RecordAction(
266 base::UserMetricsAction("SettingsAppMonitor.ChromeBrowserChosen"));
267 } else {
268 base::RecordAction(
269 base::UserMetricsAction("SettingsAppMonitor.OtherBrowserChosen"));
270 }
271 }
272
273 // A closure to be run once initialization completes.
274 base::Closure continuation_;
275
276 // Monitors user interaction with the Windows Settings app for the sake of
277 // reporting user actions.
278 win::SettingsAppMonitor settings_app_monitor_;
279
280 DISALLOW_COPY_AND_ASSIGN(DefaultBrowserActionRecorder);
281};
282
283// A function bound up in a callback with a DefaultBrowserActionRecorder and
284// a closure to keep the former alive until the time comes to run the latter.
285void OnSettingsAppFinished(
286 std::unique_ptr<DefaultBrowserActionRecorder> recorder,
287 const base::Closure& on_finished_callback) {
288 recorder.reset();
289 on_finished_callback.Run();
290}
291
pmonette32a5cfb42016-04-11 22:04:44292// There is no way to make sure the user is done with the system settings, but a
293// signal that the interaction is finished is needed for UMA. A timer of 2
294// minutes is used as a substitute. The registry keys for the protocol
295// association with an app are also monitored to signal the end of the
296// interaction early when it is clear that the user made a choice (e.g. http
297// and https for default browser).
298//
299// This helper class manages both the timer and the registry watchers and makes
300// sure the callback for the end of the settings interaction is only run once.
301// This class also manages its own lifetime.
302class OpenSystemSettingsHelper {
303 public:
304 // Begin the monitoring and will call |on_finished_callback| when done.
305 // Takes in a null-terminated array of |protocols| whose registry keys must be
pmonette0c40087a2016-04-21 00:05:16306 // watched. The array must contain at least one element.
pmonette32a5cfb42016-04-11 22:04:44307 static void Begin(const wchar_t* const protocols[],
308 const base::Closure& on_finished_callback) {
309 new OpenSystemSettingsHelper(protocols, on_finished_callback);
310 }
311
312 private:
313 // The reason the settings interaction concluded. Do not modify the ordering
314 // because it is used for UMA.
315 enum ConcludeReason { REGISTRY_WATCHER, TIMEOUT, NUM_CONCLUDE_REASON_TYPES };
316
317 OpenSystemSettingsHelper(const wchar_t* const protocols[],
318 const base::Closure& on_finished_callback)
pmonette0c40087a2016-04-21 00:05:16319 : scoped_user_protocol_entry_(protocols[0]),
320 on_finished_callback_(on_finished_callback),
321 weak_ptr_factory_(this) {
pmonette32a5cfb42016-04-11 22:04:44322 static const wchar_t kUrlAssociationFormat[] =
323 L"SOFTWARE\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\"
324 L"%ls\\UserChoice";
325
326 // Remember the start time.
327 start_time_ = base::TimeTicks::Now();
328
329 for (const wchar_t* const* scan = &protocols[0]; *scan != nullptr; ++scan) {
330 AddRegistryKeyWatcher(
331 base::StringPrintf(kUrlAssociationFormat, *scan).c_str());
332 }
333 // Only the watchers that were succesfully initialized are counted.
334 registry_watcher_count_ = registry_key_watchers_.size();
335
336 timer_.Start(
337 FROM_HERE, base::TimeDelta::FromMinutes(2),
338 base::Bind(&OpenSystemSettingsHelper::ConcludeInteraction,
339 weak_ptr_factory_.GetWeakPtr(), ConcludeReason::TIMEOUT));
340 }
341
342 // Called when a change is detected on one of the registry keys being watched.
343 // Note: All types of modification to the registry key will trigger this
344 // function even if the value change is the only one that matters. This
345 // is good enough for now.
346 void OnRegistryKeyChanged() {
347 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
348
349 // Make sure all the registry watchers have fired.
350 if (--registry_watcher_count_ == 0) {
351 UMA_HISTOGRAM_MEDIUM_TIMES(
352 "DefaultBrowser.SettingsInteraction.RegistryWatcherDuration",
353 base::TimeTicks::Now() - start_time_);
354
355 ConcludeInteraction(ConcludeReason::REGISTRY_WATCHER);
356 }
357 }
358
359 // Ends the monitoring with the system settings. Will call
360 // |on_finished_callback_| and then dispose of this class instance to make
361 // sure the callback won't get called subsequently.
362 void ConcludeInteraction(ConcludeReason conclude_reason) {
363 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
364
365 UMA_HISTOGRAM_ENUMERATION(
366 "DefaultBrowser.SettingsInteraction.ConcludeReason", conclude_reason,
367 NUM_CONCLUDE_REASON_TYPES);
368 on_finished_callback_.Run();
369 delete this;
370 }
371
372 // Helper function to create a registry watcher for a given |key_path|. Do
373 // nothing on initialization failure.
374 void AddRegistryKeyWatcher(const wchar_t* key_path) {
dcheng4af48582016-04-19 00:29:35375 auto reg_key = base::WrapUnique(
pmonette32a5cfb42016-04-11 22:04:44376 new base::win::RegKey(HKEY_CURRENT_USER, key_path, KEY_NOTIFY));
377
378 if (reg_key->Valid() &&
379 reg_key->StartWatching(
380 base::Bind(&OpenSystemSettingsHelper::OnRegistryKeyChanged,
381 weak_ptr_factory_.GetWeakPtr()))) {
382 registry_key_watchers_.push_back(std::move(reg_key));
383 }
384 }
385
pmonette0c40087a2016-04-21 00:05:16386 // This is needed to make sure that Windows displays an entry for the protocol
387 // inside the "Choose default apps by protocol" settings page.
388 ScopedUserProtocolEntry scoped_user_protocol_entry_;
389
pmonette32a5cfb42016-04-11 22:04:44390 // The function to call when the interaction with the system settings is
391 // finished.
392 base::Closure on_finished_callback_;
393
394 // The number of time the registry key watchers must fire.
395 int registry_watcher_count_ = 0;
396
397 // There can be multiple registry key watchers as some settings modify
398 // multiple protocol associations. e.g. Changing the default browser modifies
399 // the http and https associations.
dcheng4af48582016-04-19 00:29:35400 std::vector<std::unique_ptr<base::win::RegKey>> registry_key_watchers_;
pmonette32a5cfb42016-04-11 22:04:44401
402 base::OneShotTimer timer_;
403
404 // Records the time it takes for the final registry watcher to get signaled.
405 base::TimeTicks start_time_;
406
407 // Weak ptrs are used to bind this class to the callbacks of the timer and the
408 // registry watcher. This makes it possible to self-delete after one of the
409 // callbacks is executed to cancel the remaining ones.
410 base::WeakPtrFactory<OpenSystemSettingsHelper> weak_ptr_factory_;
411
412 DISALLOW_COPY_AND_ASSIGN(OpenSystemSettingsHelper);
413};
414
[email protected]3a3e72c2011-11-29 02:59:38415} // namespace
[email protected]12f520c2010-01-06 18:11:15416
pmonette9fa59e882016-02-10 00:12:19417bool SetAsDefaultBrowser() {
[email protected]650b2d52013-02-10 03:41:45418 base::FilePath chrome_exe;
[email protected]d24c4012009-07-28 01:57:31419 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
420 LOG(ERROR) << "Error getting app exe path";
421 return false;
422 }
423
424 // From UI currently we only allow setting default browser for current user.
[email protected]bf6117c7e2010-12-01 06:00:25425 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
grte76ca2852014-12-05 16:42:10426 if (!ShellUtil::MakeChromeDefault(dist, ShellUtil::CURRENT_USER, chrome_exe,
427 true /* elevate_if_not_admin */)) {
[email protected]d24c4012009-07-28 01:57:31428 LOG(ERROR) << "Chrome could not be set as default browser.";
429 return false;
430 }
431
[email protected]8e96e502010-10-21 20:57:12432 VLOG(1) << "Chrome registered as default browser.";
[email protected]d24c4012009-07-28 01:57:31433 return true;
434}
435
pmonette9fa59e882016-02-10 00:12:19436bool SetAsDefaultProtocolClient(const std::string& protocol) {
[email protected]4468a5b2011-05-26 07:48:02437 if (protocol.empty())
438 return false;
439
[email protected]650b2d52013-02-10 03:41:45440 base::FilePath chrome_exe;
[email protected]4468a5b2011-05-26 07:48:02441 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
442 LOG(ERROR) << "Error getting app exe path";
443 return false;
444 }
445
[email protected]f911df52013-12-24 23:24:23446 base::string16 wprotocol(base::UTF8ToUTF16(protocol));
[email protected]4468a5b2011-05-26 07:48:02447 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
grte76ca2852014-12-05 16:42:10448 if (!ShellUtil::MakeChromeDefaultProtocolClient(dist, chrome_exe,
449 wprotocol)) {
[email protected]4468a5b2011-05-26 07:48:02450 LOG(ERROR) << "Chrome could not be set as default handler for "
451 << protocol << ".";
452 return false;
453 }
454
455 VLOG(1) << "Chrome registered as default handler for " << protocol << ".";
456 return true;
457}
458
pmonette32a5cfb42016-04-11 22:04:44459DefaultWebClientSetPermission GetDefaultWebClientSetPermission() {
pmonette034a03d92015-10-02 21:04:27460 BrowserDistribution* distribution = BrowserDistribution::GetDistribution();
461 if (distribution->GetDefaultBrowserControlPolicy() !=
462 BrowserDistribution::DEFAULT_BROWSER_FULL_CONTROL)
463 return SET_DEFAULT_NOT_ALLOWED;
pmonette034a03d92015-10-02 21:04:27464 if (ShellUtil::CanMakeChromeDefaultUnattended())
465 return SET_DEFAULT_UNATTENDED;
pmonette32a5cfb42016-04-11 22:04:44466 // Windows 8 and 10 both introduced a new way to set the default web client
467 // which require user interaction.
pmonettef89ac7c72015-10-06 03:22:01468 return SET_DEFAULT_INTERACTIVE;
pmonette034a03d92015-10-02 21:04:27469}
470
pmonette9fa59e882016-02-10 00:12:19471bool IsElevationNeededForSettingDefaultProtocolClient() {
pmonette868ca642015-09-02 14:34:02472 return base::win::GetVersion() < base::win::VERSION_WIN8;
473}
474
pmonette9fa59e882016-02-10 00:12:19475base::string16 GetApplicationNameForProtocol(const GURL& url) {
[email protected]caa05352014-03-01 00:43:05476 // Windows 8 or above requires a new protocol association query.
477 if (base::win::GetVersion() >= base::win::VERSION_WIN8)
478 return GetAppForProtocolUsingAssocQuery(url);
479 else
480 return GetAppForProtocolUsingRegistry(url);
[email protected]42dc9402013-01-30 07:54:20481}
482
pmonette9fa59e882016-02-10 00:12:19483DefaultWebClientState GetDefaultBrowser() {
pmonette034a03d92015-10-02 21:04:27484 return GetDefaultWebClientStateFromShellUtilDefaultState(
485 ShellUtil::GetChromeDefaultState());
486}
487
[email protected]d24c4012009-07-28 01:57:31488// There is no reliable way to say which browser is default on a machine (each
489// browser can have some of the protocols/shortcuts). So we look for only HTTP
490// protocol handler. Even this handler is located at different places in
491// registry on XP and Vista:
492// - HKCR\http\shell\open\command (XP)
493// - HKCU\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\
494// http\UserChoice (Vista)
495// This method checks if Firefox is defualt browser by checking these
496// locations and returns true if Firefox traces are found there. In case of
497// error (or if Firefox is not found)it returns the default value which
498// is false.
pmonette9fa59e882016-02-10 00:12:19499bool IsFirefoxDefaultBrowser() {
[email protected]d24c4012009-07-28 01:57:31500 bool ff_default = false;
[email protected]935aa542010-10-15 01:59:15501 if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
[email protected]0085863a2013-12-06 21:19:03502 base::string16 app_cmd;
[email protected]2d6503982010-10-17 04:41:54503 base::win::RegKey key(HKEY_CURRENT_USER,
504 ShellUtil::kRegVistaUrlPrefs, KEY_READ);
[email protected]e06f4d52011-01-19 07:28:46505 if (key.Valid() && (key.ReadValue(L"Progid", &app_cmd) == ERROR_SUCCESS) &&
[email protected]d24c4012009-07-28 01:57:31506 app_cmd == L"FirefoxURL")
507 ff_default = true;
508 } else {
[email protected]0085863a2013-12-06 21:19:03509 base::string16 key_path(L"http");
[email protected]d24c4012009-07-28 01:57:31510 key_path.append(ShellUtil::kRegShellOpen);
[email protected]2d6503982010-10-17 04:41:54511 base::win::RegKey key(HKEY_CLASSES_ROOT, key_path.c_str(), KEY_READ);
[email protected]0085863a2013-12-06 21:19:03512 base::string16 app_cmd;
[email protected]e06f4d52011-01-19 07:28:46513 if (key.Valid() && (key.ReadValue(L"", &app_cmd) == ERROR_SUCCESS) &&
[email protected]cb1f4ac2014-08-07 16:55:42514 base::string16::npos !=
brettwfce8d192015-08-10 19:07:51515 base::ToLowerASCII(app_cmd).find(L"firefox"))
[email protected]d24c4012009-07-28 01:57:31516 ff_default = true;
517 }
518 return ff_default;
519}
[email protected]12f520c2010-01-06 18:11:15520
pmonette9fa59e882016-02-10 00:12:19521DefaultWebClientState IsDefaultProtocolClient(const std::string& protocol) {
pmonette034a03d92015-10-02 21:04:27522 return GetDefaultWebClientStateFromShellUtilDefaultState(
523 ShellUtil::GetChromeDefaultProtocolClientState(
524 base::UTF8ToUTF16(protocol)));
525}
526
pmonette9e4c1a82016-04-14 18:15:30527namespace win {
528
529bool SetAsDefaultBrowserUsingIntentPicker() {
530 base::FilePath chrome_exe;
531 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
532 NOTREACHED() << "Error getting app exe path";
533 return false;
534 }
535
536 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
537 if (!ShellUtil::ShowMakeChromeDefaultSystemUI(dist, chrome_exe)) {
538 LOG(ERROR) << "Failed to launch the set-default-browser Windows UI.";
539 return false;
540 }
541
542 VLOG(1) << "Set-default-browser Windows UI completed.";
543 return true;
544}
545
546void SetAsDefaultBrowserUsingSystemSettings(
547 const base::Closure& on_finished_callback) {
548 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
549
550 base::FilePath chrome_exe;
551 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
552 NOTREACHED() << "Error getting app exe path";
553 on_finished_callback.Run();
554 return;
555 }
556
grtc291eea2016-05-26 15:38:48557 // Create an action recorder that will open the settings app once it has
558 // initialized.
559 std::unique_ptr<DefaultBrowserActionRecorder> recorder(
560 new DefaultBrowserActionRecorder(base::Bind(
561 base::IgnoreResult(&ShellUtil::ShowMakeChromeDefaultSystemUI),
562 base::Unretained(BrowserDistribution::GetDistribution()),
563 chrome_exe)));
pmonette9e4c1a82016-04-14 18:15:30564
grtc291eea2016-05-26 15:38:48565 // The helper manages its own lifetime. Bind the action recorder
566 // into the finished callback to keep it alive throughout the
567 // interaction.
568 static const wchar_t* const kProtocols[] = {L"http", L"https", nullptr};
569 OpenSystemSettingsHelper::Begin(
570 kProtocols, base::Bind(&OnSettingsAppFinished, base::Passed(&recorder),
571 on_finished_callback));
pmonette9e4c1a82016-04-14 18:15:30572}
573
574bool SetAsDefaultProtocolClientUsingIntentPicker(const std::string& protocol) {
575 base::FilePath chrome_exe;
576 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
577 NOTREACHED() << "Error getting app exe path";
578 return false;
579 }
580
581 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
582 base::string16 wprotocol(base::UTF8ToUTF16(protocol));
583 if (!ShellUtil::ShowMakeChromeDefaultProtocolClientSystemUI(dist, chrome_exe,
584 wprotocol)) {
585 LOG(ERROR) << "Failed to launch the set-default-client Windows UI.";
586 return false;
587 }
588
589 VLOG(1) << "Set-default-client Windows UI completed.";
590 return true;
591}
592
pmonette0c40087a2016-04-21 00:05:16593void SetAsDefaultProtocolClientUsingSystemSettings(
594 const std::string& protocol,
595 const base::Closure& on_finished_callback) {
596 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
597
598 base::FilePath chrome_exe;
599 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
600 NOTREACHED() << "Error getting app exe path";
601 on_finished_callback.Run();
602 return;
603 }
604
605 // The helper manages its own lifetime.
606 base::string16 wprotocol(base::UTF8ToUTF16(protocol));
607 const wchar_t* const kProtocols[] = {wprotocol.c_str(), nullptr};
608 OpenSystemSettingsHelper::Begin(kProtocols, on_finished_callback);
609
610 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
611 ShellUtil::ShowMakeChromeDefaultProtocolClientSystemUI(dist, chrome_exe,
612 wprotocol);
613}
614
pmonette9fa59e882016-02-10 00:12:19615base::string16 GetAppModelIdForProfile(const base::string16& app_name,
616 const base::FilePath& profile_path) {
[email protected]d2065e062013-12-12 23:49:52617 std::vector<base::string16> components;
[email protected]a0448002012-06-19 04:32:10618 components.push_back(app_name);
[email protected]0085863a2013-12-06 21:19:03619 const base::string16 profile_id(GetProfileIdFromPath(profile_path));
[email protected]a0448002012-06-19 04:32:10620 if (!profile_id.empty())
621 components.push_back(profile_id);
622 return ShellUtil::BuildAppModelId(components);
[email protected]12f520c2010-01-06 18:11:15623}
624
pmonette9fa59e882016-02-10 00:12:19625base::string16 GetChromiumModelIdForProfile(
[email protected]650b2d52013-02-10 03:41:45626 const base::FilePath& profile_path) {
[email protected]a0448002012-06-19 04:32:10627 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
[email protected]650b2d52013-02-10 03:41:45628 base::FilePath chrome_exe;
[email protected]a0448002012-06-19 04:32:10629 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
630 NOTREACHED();
631 return dist->GetBaseAppId();
632 }
633 return GetAppModelIdForProfile(
grte76ca2852014-12-05 16:42:10634 ShellUtil::GetBrowserModelId(dist,
635 InstallUtil::IsPerUserInstall(chrome_exe)),
[email protected]786799692012-09-26 14:16:48636 profile_path);
[email protected]12f520c2010-01-06 18:11:15637}
[email protected]c9bb06f42010-01-13 23:53:48638
pmonette9fa59e882016-02-10 00:12:19639void MigrateTaskbarPins() {
[email protected]935aa542010-10-15 01:59:15640 if (base::win::GetVersion() < base::win::VERSION_WIN7)
[email protected]c9bb06f42010-01-13 23:53:48641 return;
642
[email protected]81fcf952012-11-26 22:25:14643 // This needs to happen eventually (e.g. so that the appid is fixed and the
644 // run-time Chrome icon is merged with the taskbar shortcut), but this is not
645 // urgent and shouldn't delay Chrome startup.
gab88257b62016-01-15 03:19:19646 static const int64_t kMigrateTaskbarPinsDelaySeconds = 15;
[email protected]81fcf952012-11-26 22:25:14647 BrowserThread::PostDelayedTask(
[email protected]3a3e72c2011-11-29 02:59:38648 BrowserThread::FILE, FROM_HERE,
gab88257b62016-01-15 03:19:19649 base::Bind(&MigrateTaskbarPinsCallback),
650 base::TimeDelta::FromSeconds(kMigrateTaskbarPinsDelaySeconds));
[email protected]c9bb06f42010-01-13 23:53:48651}
[email protected]43903b82012-06-01 05:26:23652
pmonette9fa59e882016-02-10 00:12:19653int MigrateShortcutsInPathInternal(const base::FilePath& chrome_exe,
654 const base::FilePath& path) {
[email protected]8ea8f1ef2013-01-06 18:39:03655 DCHECK(base::win::GetVersion() >= base::win::VERSION_WIN7);
656
657 // Enumerate all pinned shortcuts in the given path directly.
[email protected]25a4c1c2013-06-08 04:53:36658 base::FileEnumerator shortcuts_enum(
[email protected]8ea8f1ef2013-01-06 18:39:03659 path, false, // not recursive
[email protected]25a4c1c2013-06-08 04:53:36660 base::FileEnumerator::FILES, FILE_PATH_LITERAL("*.lnk"));
[email protected]8ea8f1ef2013-01-06 18:39:03661
grte76ca2852014-12-05 16:42:10662 bool is_per_user_install = InstallUtil::IsPerUserInstall(chrome_exe);
[email protected]8ea8f1ef2013-01-06 18:39:03663
664 int shortcuts_migrated = 0;
[email protected]650b2d52013-02-10 03:41:45665 base::FilePath target_path;
[email protected]0085863a2013-12-06 21:19:03666 base::string16 arguments;
[email protected]07983302013-01-21 19:41:44667 base::win::ScopedPropVariant propvariant;
[email protected]650b2d52013-02-10 03:41:45668 for (base::FilePath shortcut = shortcuts_enum.Next(); !shortcut.empty();
[email protected]8ea8f1ef2013-01-06 18:39:03669 shortcut = shortcuts_enum.Next()) {
670 // TODO(gab): Use ProgramCompare instead of comparing FilePaths below once
671 // it is fixed to work with FilePaths with spaces.
672 if (!base::win::ResolveShortcut(shortcut, &target_path, &arguments) ||
673 chrome_exe != target_path) {
674 continue;
675 }
avi556c05022014-12-22 23:31:43676 base::CommandLine command_line(
677 base::CommandLine::FromString(base::StringPrintf(
678 L"\"%ls\" %ls", target_path.value().c_str(), arguments.c_str())));
[email protected]8ea8f1ef2013-01-06 18:39:03679
680 // Get the expected AppId for this Chrome shortcut.
[email protected]0085863a2013-12-06 21:19:03681 base::string16 expected_app_id(
[email protected]8ea8f1ef2013-01-06 18:39:03682 GetExpectedAppId(command_line, is_per_user_install));
683 if (expected_app_id.empty())
684 continue;
685
686 // Load the shortcut.
687 base::win::ScopedComPtr<IShellLink> shell_link;
688 base::win::ScopedComPtr<IPersistFile> persist_file;
689 if (FAILED(shell_link.CreateInstance(CLSID_ShellLink, NULL,
690 CLSCTX_INPROC_SERVER)) ||
dcheng81762e02014-11-21 21:22:28691 FAILED(persist_file.QueryFrom(shell_link.get())) ||
[email protected]8ea8f1ef2013-01-06 18:39:03692 FAILED(persist_file->Load(shortcut.value().c_str(), STGM_READ))) {
693 DLOG(WARNING) << "Failed loading shortcut at " << shortcut.value();
694 continue;
695 }
696
697 // Any properties that need to be updated on the shortcut will be stored in
698 // |updated_properties|.
699 base::win::ShortcutProperties updated_properties;
700
701 // Validate the existing app id for the shortcut.
702 base::win::ScopedComPtr<IPropertyStore> property_store;
[email protected]07983302013-01-21 19:41:44703 propvariant.Reset();
dcheng81762e02014-11-21 21:22:28704 if (FAILED(property_store.QueryFrom(shell_link.get())) ||
705 property_store->GetValue(PKEY_AppUserModel_ID, propvariant.Receive()) !=
706 S_OK) {
[email protected]8ea8f1ef2013-01-06 18:39:03707 // When in doubt, prefer not updating the shortcut.
708 NOTREACHED();
709 continue;
[email protected]8ea8f1ef2013-01-06 18:39:03710 } else {
[email protected]07983302013-01-21 19:41:44711 switch (propvariant.get().vt) {
712 case VT_EMPTY:
713 // If there is no app_id set, set our app_id if one is expected.
714 if (!expected_app_id.empty())
715 updated_properties.set_app_id(expected_app_id);
716 break;
717 case VT_LPWSTR:
[email protected]0085863a2013-12-06 21:19:03718 if (expected_app_id != base::string16(propvariant.get().pwszVal))
[email protected]07983302013-01-21 19:41:44719 updated_properties.set_app_id(expected_app_id);
720 break;
721 default:
722 NOTREACHED();
723 continue;
[email protected]8ea8f1ef2013-01-06 18:39:03724 }
725 }
726
gab88257b62016-01-15 03:19:19727 // Clear dual_mode property from any shortcuts that previously had it (it
728 // was only ever installed on shortcuts with the
729 // |default_chromium_model_id|).
[email protected]b39e32e2013-04-24 08:55:54730 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
[email protected]0085863a2013-12-06 21:19:03731 base::string16 default_chromium_model_id(
[email protected]b39e32e2013-04-24 08:55:54732 ShellUtil::GetBrowserModelId(dist, is_per_user_install));
gab88257b62016-01-15 03:19:19733 if (expected_app_id == default_chromium_model_id) {
[email protected]07983302013-01-21 19:41:44734 propvariant.Reset();
[email protected]8ea8f1ef2013-01-06 18:39:03735 if (property_store->GetValue(PKEY_AppUserModel_IsDualMode,
[email protected]07983302013-01-21 19:41:44736 propvariant.Receive()) != S_OK) {
[email protected]8ea8f1ef2013-01-06 18:39:03737 // When in doubt, prefer to not update the shortcut.
738 NOTREACHED();
739 continue;
gab38d242d4d2016-01-14 20:38:12740 }
741 if (propvariant.get().vt == VT_BOOL &&
742 !!propvariant.get().boolVal) {
743 updated_properties.set_dual_mode(false);
[email protected]8ea8f1ef2013-01-06 18:39:03744 }
745 }
746
747 persist_file.Release();
748 shell_link.Release();
749
750 // Update the shortcut if some of its properties need to be updated.
751 if (updated_properties.options &&
752 base::win::CreateOrUpdateShortcutLink(
753 shortcut, updated_properties,
754 base::win::SHORTCUT_UPDATE_EXISTING)) {
755 ++shortcuts_migrated;
756 }
757 }
758 return shortcuts_migrated;
759}
760
pmonette9fa59e882016-02-10 00:12:19761base::FilePath GetStartMenuShortcut(const base::FilePath& chrome_exe) {
[email protected]3f69d6e612012-08-03 18:52:27762 static const int kFolderIds[] = {
763 base::DIR_COMMON_START_MENU,
764 base::DIR_START_MENU,
765 };
766 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
taptedd7ef82ae2016-05-13 01:10:19767 const base::string16 shortcut_name(dist->GetShortcutName() +
768 installer::kLnkExt);
grt2be0cd082015-11-12 15:32:21769 base::FilePath programs_folder;
[email protected]650b2d52013-02-10 03:41:45770 base::FilePath shortcut;
[email protected]3f69d6e612012-08-03 18:52:27771
772 // Check both the common and the per-user Start Menu folders for system-level
773 // installs.
grte76ca2852014-12-05 16:42:10774 size_t folder = InstallUtil::IsPerUserInstall(chrome_exe) ? 1 : 0;
[email protected]3f69d6e612012-08-03 18:52:27775 for (; folder < arraysize(kFolderIds); ++folder) {
grt2be0cd082015-11-12 15:32:21776 if (!PathService::Get(kFolderIds[folder], &programs_folder)) {
[email protected]3f69d6e612012-08-03 18:52:27777 NOTREACHED();
778 continue;
779 }
780
grt2be0cd082015-11-12 15:32:21781 shortcut = programs_folder.Append(shortcut_name);
782 if (base::PathExists(shortcut))
783 return shortcut;
[email protected]3f69d6e612012-08-03 18:52:27784 }
785
[email protected]650b2d52013-02-10 03:41:45786 return base::FilePath();
[email protected]3f69d6e612012-08-03 18:52:27787}
pmonettef89ac7c72015-10-06 03:22:01788
pmonette32a5cfb42016-04-11 22:04:44789} // namespace win
790
pmonette9fa59e882016-02-10 00:12:19791} // namespace shell_integration