blob: a23aaa426a79e4bef2d0745063e5441000bdbab2 [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"
[email protected]d24c4012009-07-28 01:57:3126#include "base/path_service.h"
[email protected]24a555b62013-06-10 22:01:1727#include "base/strings/string_util.h"
28#include "base/strings/stringprintf.h"
[email protected]e309f312013-06-07 21:50:0829#include "base/strings/utf_string_conversions.h"
pmonette32a5cfb42016-04-11 22:04:4430#include "base/time/time.h"
31#include "base/timer/timer.h"
[email protected]2d6503982010-10-17 04:41:5432#include "base/win/registry.h"
[email protected]8ee65ba2011-04-12 20:53:2333#include "base/win/scoped_comptr.h"
[email protected]07983302013-01-21 19:41:4434#include "base/win/scoped_propvariant.h"
[email protected]f1024e22012-09-12 07:14:5535#include "base/win/shortcut.h"
[email protected]935aa542010-10-15 01:59:1536#include "base/win/windows_version.h"
[email protected]89d43832013-06-29 20:25:2037#include "chrome/browser/policy/policy_path_parser.h"
pmonette32a5cfb42016-04-11 22:04:4438#include "chrome/browser/shell_integration.h"
[email protected]c9bb06f42010-01-13 23:53:4839#include "chrome/browser/web_applications/web_app.h"
[email protected]d24c4012009-07-28 01:57:3140#include "chrome/common/chrome_constants.h"
[email protected]12f520c2010-01-06 18:11:1541#include "chrome/common/chrome_paths_internal.h"
[email protected]c9bb06f42010-01-13 23:53:4842#include "chrome/common/chrome_switches.h"
[email protected]4468a5b2011-05-26 07:48:0243#include "chrome/installer/setup/setup_util.h"
[email protected]d24c4012009-07-28 01:57:3144#include "chrome/installer/util/browser_distribution.h"
45#include "chrome/installer/util/create_reg_key_work_item.h"
[email protected]3f69d6e612012-08-03 18:52:2746#include "chrome/installer/util/install_util.h"
[email protected]d24c4012009-07-28 01:57:3147#include "chrome/installer/util/set_reg_value_work_item.h"
48#include "chrome/installer/util/shell_util.h"
49#include "chrome/installer/util/util_constants.h"
50#include "chrome/installer/util/work_item.h"
51#include "chrome/installer/util/work_item_list.h"
pmonette2b1dbee2016-01-08 20:18:5852#include "components/variations/variations_associated_data.h"
[email protected]c38831a12011-10-28 12:44:4953#include "content/public/browser/browser_thread.h"
[email protected]d24c4012009-07-28 01:57:3154
[email protected]631bb742011-11-02 11:29:3955using content::BrowserThread;
56
pmonette9fa59e882016-02-10 00:12:1957namespace shell_integration {
58
[email protected]12f520c2010-01-06 18:11:1559namespace {
60
[email protected]da1ffc32013-02-15 21:22:5661const wchar_t kAppListAppNameSuffix[] = L"AppList";
[email protected]99002fd2012-11-06 04:35:5262
pmonette9fa59e882016-02-10 00:12:1963// Helper function for GetAppId to generates profile id
[email protected]2f1c09d2011-01-14 14:58:1464// from profile path. "profile_id" is composed of sanitized basenames of
[email protected]12f520c2010-01-06 18:11:1565// user data dir and profile dir joined by a ".".
[email protected]6a72a632013-12-12 22:22:0066base::string16 GetProfileIdFromPath(const base::FilePath& profile_path) {
[email protected]12f520c2010-01-06 18:11:1567 // Return empty string if profile_path is empty
68 if (profile_path.empty())
[email protected]0085863a2013-12-06 21:19:0369 return base::string16();
[email protected]12f520c2010-01-06 18:11:1570
[email protected]650b2d52013-02-10 03:41:4571 base::FilePath default_user_data_dir;
[email protected]12f520c2010-01-06 18:11:1572 // Return empty string if profile_path is in default user data
73 // dir and is the default profile.
74 if (chrome::GetDefaultUserDataDirectory(&default_user_data_dir) &&
75 profile_path.DirName() == default_user_data_dir &&
[email protected]162b5992011-03-15 19:40:4876 profile_path.BaseName().value() ==
[email protected]f911df52013-12-24 23:24:2377 base::ASCIIToUTF16(chrome::kInitialProfile)) {
[email protected]0085863a2013-12-06 21:19:0378 return base::string16();
[email protected]162b5992011-03-15 19:40:4879 }
[email protected]12f520c2010-01-06 18:11:1580
81 // Get joined basenames of user data dir and profile.
[email protected]0085863a2013-12-06 21:19:0382 base::string16 basenames = profile_path.DirName().BaseName().value() +
[email protected]12f520c2010-01-06 18:11:1583 L"." + profile_path.BaseName().value();
84
[email protected]0085863a2013-12-06 21:19:0385 base::string16 profile_id;
[email protected]12f520c2010-01-06 18:11:1586 profile_id.reserve(basenames.size());
87
88 // Generate profile_id from sanitized basenames.
89 for (size_t i = 0; i < basenames.length(); ++i) {
brettwb3413062015-06-24 00:39:0290 if (base::IsAsciiAlpha(basenames[i]) ||
91 base::IsAsciiDigit(basenames[i]) ||
[email protected]12f520c2010-01-06 18:11:1592 basenames[i] == L'.')
93 profile_id += basenames[i];
94 }
95
96 return profile_id;
97}
98
[email protected]6a72a632013-12-12 22:22:0099base::string16 GetAppListAppName() {
[email protected]da1ffc32013-02-15 21:22:56100 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
[email protected]0085863a2013-12-06 21:19:03101 base::string16 app_name(dist->GetBaseAppId());
[email protected]da1ffc32013-02-15 21:22:56102 app_name.append(kAppListAppNameSuffix);
103 return app_name;
104}
105
[email protected]8ea8f1ef2013-01-06 18:39:03106// Gets expected app id for given Chrome (based on |command_line| and
107// |is_per_user_install|).
avi556c05022014-12-22 23:31:43108base::string16 GetExpectedAppId(const base::CommandLine& command_line,
[email protected]6a72a632013-12-12 22:22:00109 bool is_per_user_install) {
[email protected]b39e32e2013-04-24 08:55:54110 base::FilePath user_data_dir;
111 if (command_line.HasSwitch(switches::kUserDataDir))
112 user_data_dir = command_line.GetSwitchValuePath(switches::kUserDataDir);
113 else
114 chrome::GetDefaultUserDataDirectory(&user_data_dir);
[email protected]89d43832013-06-29 20:25:20115 // Adjust with any policy that overrides any other way to set the path.
116 policy::path_parser::CheckUserDataDirPolicy(&user_data_dir);
[email protected]b39e32e2013-04-24 08:55:54117 DCHECK(!user_data_dir.empty());
[email protected]c9bb06f42010-01-13 23:53:48118
[email protected]b39e32e2013-04-24 08:55:54119 base::FilePath profile_subdir;
120 if (command_line.HasSwitch(switches::kProfileDirectory)) {
121 profile_subdir =
122 command_line.GetSwitchValuePath(switches::kProfileDirectory);
123 } else {
[email protected]f911df52013-12-24 23:24:23124 profile_subdir =
125 base::FilePath(base::ASCIIToUTF16(chrome::kInitialProfile));
[email protected]b39e32e2013-04-24 08:55:54126 }
127 DCHECK(!profile_subdir.empty());
128
129 base::FilePath profile_path = user_data_dir.Append(profile_subdir);
[email protected]0085863a2013-12-06 21:19:03130 base::string16 app_name;
[email protected]c9bb06f42010-01-13 23:53:48131 if (command_line.HasSwitch(switches::kApp)) {
[email protected]f911df52013-12-24 23:24:23132 app_name = base::UTF8ToUTF16(web_app::GenerateApplicationNameFromURL(
[email protected]57ecc4b2010-08-11 03:02:51133 GURL(command_line.GetSwitchValueASCII(switches::kApp))));
[email protected]2f1c09d2011-01-14 14:58:14134 } else if (command_line.HasSwitch(switches::kAppId)) {
[email protected]f911df52013-12-24 23:24:23135 app_name = base::UTF8ToUTF16(
136 web_app::GenerateApplicationNameFromExtensionId(
137 command_line.GetSwitchValueASCII(switches::kAppId)));
[email protected]99002fd2012-11-06 04:35:52138 } else if (command_line.HasSwitch(switches::kShowAppList)) {
[email protected]da1ffc32013-02-15 21:22:56139 app_name = GetAppListAppName();
[email protected]c9bb06f42010-01-13 23:53:48140 } else {
[email protected]a0448002012-06-19 04:32:10141 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
[email protected]8ea8f1ef2013-01-06 18:39:03142 app_name = ShellUtil::GetBrowserModelId(dist, is_per_user_install);
[email protected]c9bb06f42010-01-13 23:53:48143 }
[email protected]b39e32e2013-04-24 08:55:54144 DCHECK(!app_name.empty());
[email protected]c9bb06f42010-01-13 23:53:48145
pmonette9e4c1a82016-04-14 18:15:30146 return win::GetAppModelIdForProfile(app_name, profile_path);
[email protected]c9bb06f42010-01-13 23:53:48147}
148
gab88257b62016-01-15 03:19:19149void MigrateTaskbarPinsCallback() {
[email protected]3a3e72c2011-11-29 02:59:38150 // This should run on the file thread.
thestig00844cea2015-09-08 21:44:52151 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
[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) {
[email protected]caa05352014-03-01 00:43:05196 const base::string16 cmd_key_path =
thestig11bf74d2014-11-24 20:14:42197 base::ASCIIToUTF16(url.scheme() + "\\shell\\open\\command");
[email protected]caa05352014-03-01 00:43:05198 base::win::RegKey cmd_key(HKEY_CLASSES_ROOT,
199 cmd_key_path.c_str(),
200 KEY_READ);
[email protected]caa05352014-03-01 00:43:05201 base::string16 application_to_launch;
202 if (cmd_key.ReadValue(NULL, &application_to_launch) == ERROR_SUCCESS) {
benwellse84d8392015-08-18 05:23:58203 const base::string16 url_spec =
204 base::ASCIIToUTF16(url.possibly_invalid_spec());
brettwe6dae462015-06-24 20:54:45205 base::ReplaceSubstringsAfterOffset(&application_to_launch,
206 0,
207 L"%1",
benwellse84d8392015-08-18 05:23:58208 url_spec);
[email protected]caa05352014-03-01 00:43:05209 return application_to_launch;
210 }
211 return base::string16();
212}
213
pmonette9fa59e882016-02-10 00:12:19214DefaultWebClientState GetDefaultWebClientStateFromShellUtilDefaultState(
215 ShellUtil::DefaultState default_state) {
[email protected]eb63da72012-10-15 20:39:48216 switch (default_state) {
217 case ShellUtil::NOT_DEFAULT:
pmonette9fa59e882016-02-10 00:12:19218 return DefaultWebClientState::NOT_DEFAULT;
[email protected]eb63da72012-10-15 20:39:48219 case ShellUtil::IS_DEFAULT:
pmonette9fa59e882016-02-10 00:12:19220 return DefaultWebClientState::IS_DEFAULT;
[email protected]eb63da72012-10-15 20:39:48221 default:
222 DCHECK_EQ(ShellUtil::UNKNOWN_DEFAULT, default_state);
pmonette9fa59e882016-02-10 00:12:19223 return DefaultWebClientState::UNKNOWN_DEFAULT;
[email protected]eb63da72012-10-15 20:39:48224 }
225}
226
pmonette32a5cfb42016-04-11 22:04:44227// There is no way to make sure the user is done with the system settings, but a
228// signal that the interaction is finished is needed for UMA. A timer of 2
229// minutes is used as a substitute. The registry keys for the protocol
230// association with an app are also monitored to signal the end of the
231// interaction early when it is clear that the user made a choice (e.g. http
232// and https for default browser).
233//
234// This helper class manages both the timer and the registry watchers and makes
235// sure the callback for the end of the settings interaction is only run once.
236// This class also manages its own lifetime.
237class OpenSystemSettingsHelper {
238 public:
239 // Begin the monitoring and will call |on_finished_callback| when done.
240 // Takes in a null-terminated array of |protocols| whose registry keys must be
241 // watched.
242 static void Begin(const wchar_t* const protocols[],
243 const base::Closure& on_finished_callback) {
244 new OpenSystemSettingsHelper(protocols, on_finished_callback);
245 }
246
247 private:
248 // The reason the settings interaction concluded. Do not modify the ordering
249 // because it is used for UMA.
250 enum ConcludeReason { REGISTRY_WATCHER, TIMEOUT, NUM_CONCLUDE_REASON_TYPES };
251
252 OpenSystemSettingsHelper(const wchar_t* const protocols[],
253 const base::Closure& on_finished_callback)
254 : on_finished_callback_(on_finished_callback), weak_ptr_factory_(this) {
255 static const wchar_t kUrlAssociationFormat[] =
256 L"SOFTWARE\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\"
257 L"%ls\\UserChoice";
258
259 // Remember the start time.
260 start_time_ = base::TimeTicks::Now();
261
262 for (const wchar_t* const* scan = &protocols[0]; *scan != nullptr; ++scan) {
263 AddRegistryKeyWatcher(
264 base::StringPrintf(kUrlAssociationFormat, *scan).c_str());
265 }
266 // Only the watchers that were succesfully initialized are counted.
267 registry_watcher_count_ = registry_key_watchers_.size();
268
269 timer_.Start(
270 FROM_HERE, base::TimeDelta::FromMinutes(2),
271 base::Bind(&OpenSystemSettingsHelper::ConcludeInteraction,
272 weak_ptr_factory_.GetWeakPtr(), ConcludeReason::TIMEOUT));
273 }
274
275 // Called when a change is detected on one of the registry keys being watched.
276 // Note: All types of modification to the registry key will trigger this
277 // function even if the value change is the only one that matters. This
278 // is good enough for now.
279 void OnRegistryKeyChanged() {
280 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
281
282 // Make sure all the registry watchers have fired.
283 if (--registry_watcher_count_ == 0) {
284 UMA_HISTOGRAM_MEDIUM_TIMES(
285 "DefaultBrowser.SettingsInteraction.RegistryWatcherDuration",
286 base::TimeTicks::Now() - start_time_);
287
288 ConcludeInteraction(ConcludeReason::REGISTRY_WATCHER);
289 }
290 }
291
292 // Ends the monitoring with the system settings. Will call
293 // |on_finished_callback_| and then dispose of this class instance to make
294 // sure the callback won't get called subsequently.
295 void ConcludeInteraction(ConcludeReason conclude_reason) {
296 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
297
298 UMA_HISTOGRAM_ENUMERATION(
299 "DefaultBrowser.SettingsInteraction.ConcludeReason", conclude_reason,
300 NUM_CONCLUDE_REASON_TYPES);
301 on_finished_callback_.Run();
302 delete this;
303 }
304
305 // Helper function to create a registry watcher for a given |key_path|. Do
306 // nothing on initialization failure.
307 void AddRegistryKeyWatcher(const wchar_t* key_path) {
dcheng4af48582016-04-19 00:29:35308 auto reg_key = base::WrapUnique(
pmonette32a5cfb42016-04-11 22:04:44309 new base::win::RegKey(HKEY_CURRENT_USER, key_path, KEY_NOTIFY));
310
311 if (reg_key->Valid() &&
312 reg_key->StartWatching(
313 base::Bind(&OpenSystemSettingsHelper::OnRegistryKeyChanged,
314 weak_ptr_factory_.GetWeakPtr()))) {
315 registry_key_watchers_.push_back(std::move(reg_key));
316 }
317 }
318
319 // The function to call when the interaction with the system settings is
320 // finished.
321 base::Closure on_finished_callback_;
322
323 // The number of time the registry key watchers must fire.
324 int registry_watcher_count_ = 0;
325
326 // There can be multiple registry key watchers as some settings modify
327 // multiple protocol associations. e.g. Changing the default browser modifies
328 // the http and https associations.
dcheng4af48582016-04-19 00:29:35329 std::vector<std::unique_ptr<base::win::RegKey>> registry_key_watchers_;
pmonette32a5cfb42016-04-11 22:04:44330
331 base::OneShotTimer timer_;
332
333 // Records the time it takes for the final registry watcher to get signaled.
334 base::TimeTicks start_time_;
335
336 // Weak ptrs are used to bind this class to the callbacks of the timer and the
337 // registry watcher. This makes it possible to self-delete after one of the
338 // callbacks is executed to cancel the remaining ones.
339 base::WeakPtrFactory<OpenSystemSettingsHelper> weak_ptr_factory_;
340
341 DISALLOW_COPY_AND_ASSIGN(OpenSystemSettingsHelper);
342};
343
[email protected]3a3e72c2011-11-29 02:59:38344} // namespace
[email protected]12f520c2010-01-06 18:11:15345
pmonette9fa59e882016-02-10 00:12:19346bool SetAsDefaultBrowser() {
[email protected]650b2d52013-02-10 03:41:45347 base::FilePath chrome_exe;
[email protected]d24c4012009-07-28 01:57:31348 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
349 LOG(ERROR) << "Error getting app exe path";
350 return false;
351 }
352
353 // From UI currently we only allow setting default browser for current user.
[email protected]bf6117c7e2010-12-01 06:00:25354 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
grte76ca2852014-12-05 16:42:10355 if (!ShellUtil::MakeChromeDefault(dist, ShellUtil::CURRENT_USER, chrome_exe,
356 true /* elevate_if_not_admin */)) {
[email protected]d24c4012009-07-28 01:57:31357 LOG(ERROR) << "Chrome could not be set as default browser.";
358 return false;
359 }
360
[email protected]8e96e502010-10-21 20:57:12361 VLOG(1) << "Chrome registered as default browser.";
[email protected]d24c4012009-07-28 01:57:31362 return true;
363}
364
pmonette9fa59e882016-02-10 00:12:19365bool SetAsDefaultProtocolClient(const std::string& protocol) {
[email protected]4468a5b2011-05-26 07:48:02366 if (protocol.empty())
367 return false;
368
[email protected]650b2d52013-02-10 03:41:45369 base::FilePath chrome_exe;
[email protected]4468a5b2011-05-26 07:48:02370 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
371 LOG(ERROR) << "Error getting app exe path";
372 return false;
373 }
374
[email protected]f911df52013-12-24 23:24:23375 base::string16 wprotocol(base::UTF8ToUTF16(protocol));
[email protected]4468a5b2011-05-26 07:48:02376 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
grte76ca2852014-12-05 16:42:10377 if (!ShellUtil::MakeChromeDefaultProtocolClient(dist, chrome_exe,
378 wprotocol)) {
[email protected]4468a5b2011-05-26 07:48:02379 LOG(ERROR) << "Chrome could not be set as default handler for "
380 << protocol << ".";
381 return false;
382 }
383
384 VLOG(1) << "Chrome registered as default handler for " << protocol << ".";
385 return true;
386}
387
pmonette32a5cfb42016-04-11 22:04:44388DefaultWebClientSetPermission GetDefaultWebClientSetPermission() {
pmonette034a03d92015-10-02 21:04:27389 BrowserDistribution* distribution = BrowserDistribution::GetDistribution();
390 if (distribution->GetDefaultBrowserControlPolicy() !=
391 BrowserDistribution::DEFAULT_BROWSER_FULL_CONTROL)
392 return SET_DEFAULT_NOT_ALLOWED;
pmonette034a03d92015-10-02 21:04:27393 if (ShellUtil::CanMakeChromeDefaultUnattended())
394 return SET_DEFAULT_UNATTENDED;
pmonette32a5cfb42016-04-11 22:04:44395 // Windows 8 and 10 both introduced a new way to set the default web client
396 // which require user interaction.
pmonettef89ac7c72015-10-06 03:22:01397 return SET_DEFAULT_INTERACTIVE;
pmonette034a03d92015-10-02 21:04:27398}
399
pmonette9fa59e882016-02-10 00:12:19400bool IsElevationNeededForSettingDefaultProtocolClient() {
pmonette868ca642015-09-02 14:34:02401 return base::win::GetVersion() < base::win::VERSION_WIN8;
402}
403
pmonette9fa59e882016-02-10 00:12:19404base::string16 GetApplicationNameForProtocol(const GURL& url) {
[email protected]caa05352014-03-01 00:43:05405 // Windows 8 or above requires a new protocol association query.
406 if (base::win::GetVersion() >= base::win::VERSION_WIN8)
407 return GetAppForProtocolUsingAssocQuery(url);
408 else
409 return GetAppForProtocolUsingRegistry(url);
[email protected]42dc9402013-01-30 07:54:20410}
411
pmonette9fa59e882016-02-10 00:12:19412DefaultWebClientState GetDefaultBrowser() {
pmonette034a03d92015-10-02 21:04:27413 return GetDefaultWebClientStateFromShellUtilDefaultState(
414 ShellUtil::GetChromeDefaultState());
415}
416
[email protected]d24c4012009-07-28 01:57:31417// There is no reliable way to say which browser is default on a machine (each
418// browser can have some of the protocols/shortcuts). So we look for only HTTP
419// protocol handler. Even this handler is located at different places in
420// registry on XP and Vista:
421// - HKCR\http\shell\open\command (XP)
422// - HKCU\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\
423// http\UserChoice (Vista)
424// This method checks if Firefox is defualt browser by checking these
425// locations and returns true if Firefox traces are found there. In case of
426// error (or if Firefox is not found)it returns the default value which
427// is false.
pmonette9fa59e882016-02-10 00:12:19428bool IsFirefoxDefaultBrowser() {
[email protected]d24c4012009-07-28 01:57:31429 bool ff_default = false;
[email protected]935aa542010-10-15 01:59:15430 if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
[email protected]0085863a2013-12-06 21:19:03431 base::string16 app_cmd;
[email protected]2d6503982010-10-17 04:41:54432 base::win::RegKey key(HKEY_CURRENT_USER,
433 ShellUtil::kRegVistaUrlPrefs, KEY_READ);
[email protected]e06f4d52011-01-19 07:28:46434 if (key.Valid() && (key.ReadValue(L"Progid", &app_cmd) == ERROR_SUCCESS) &&
[email protected]d24c4012009-07-28 01:57:31435 app_cmd == L"FirefoxURL")
436 ff_default = true;
437 } else {
[email protected]0085863a2013-12-06 21:19:03438 base::string16 key_path(L"http");
[email protected]d24c4012009-07-28 01:57:31439 key_path.append(ShellUtil::kRegShellOpen);
[email protected]2d6503982010-10-17 04:41:54440 base::win::RegKey key(HKEY_CLASSES_ROOT, key_path.c_str(), KEY_READ);
[email protected]0085863a2013-12-06 21:19:03441 base::string16 app_cmd;
[email protected]e06f4d52011-01-19 07:28:46442 if (key.Valid() && (key.ReadValue(L"", &app_cmd) == ERROR_SUCCESS) &&
[email protected]cb1f4ac2014-08-07 16:55:42443 base::string16::npos !=
brettwfce8d192015-08-10 19:07:51444 base::ToLowerASCII(app_cmd).find(L"firefox"))
[email protected]d24c4012009-07-28 01:57:31445 ff_default = true;
446 }
447 return ff_default;
448}
[email protected]12f520c2010-01-06 18:11:15449
pmonette9fa59e882016-02-10 00:12:19450DefaultWebClientState IsDefaultProtocolClient(const std::string& protocol) {
pmonette034a03d92015-10-02 21:04:27451 return GetDefaultWebClientStateFromShellUtilDefaultState(
452 ShellUtil::GetChromeDefaultProtocolClientState(
453 base::UTF8ToUTF16(protocol)));
454}
455
pmonette9e4c1a82016-04-14 18:15:30456namespace win {
457
458bool SetAsDefaultBrowserUsingIntentPicker() {
459 base::FilePath chrome_exe;
460 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
461 NOTREACHED() << "Error getting app exe path";
462 return false;
463 }
464
465 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
466 if (!ShellUtil::ShowMakeChromeDefaultSystemUI(dist, chrome_exe)) {
467 LOG(ERROR) << "Failed to launch the set-default-browser Windows UI.";
468 return false;
469 }
470
471 VLOG(1) << "Set-default-browser Windows UI completed.";
472 return true;
473}
474
475void SetAsDefaultBrowserUsingSystemSettings(
476 const base::Closure& on_finished_callback) {
477 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
478
479 base::FilePath chrome_exe;
480 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
481 NOTREACHED() << "Error getting app exe path";
482 on_finished_callback.Run();
483 return;
484 }
485
486 // The helper manages its own lifetime.
487 static const wchar_t* const kProtocols[] = {L"http", L"https", nullptr};
488 OpenSystemSettingsHelper::Begin(kProtocols, on_finished_callback);
489
490 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
491 ShellUtil::ShowMakeChromeDefaultSystemUI(dist, chrome_exe);
492}
493
494bool SetAsDefaultProtocolClientUsingIntentPicker(const std::string& protocol) {
495 base::FilePath chrome_exe;
496 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
497 NOTREACHED() << "Error getting app exe path";
498 return false;
499 }
500
501 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
502 base::string16 wprotocol(base::UTF8ToUTF16(protocol));
503 if (!ShellUtil::ShowMakeChromeDefaultProtocolClientSystemUI(dist, chrome_exe,
504 wprotocol)) {
505 LOG(ERROR) << "Failed to launch the set-default-client Windows UI.";
506 return false;
507 }
508
509 VLOG(1) << "Set-default-client Windows UI completed.";
510 return true;
511}
512
pmonette9fa59e882016-02-10 00:12:19513base::string16 GetAppModelIdForProfile(const base::string16& app_name,
514 const base::FilePath& profile_path) {
[email protected]d2065e062013-12-12 23:49:52515 std::vector<base::string16> components;
[email protected]a0448002012-06-19 04:32:10516 components.push_back(app_name);
[email protected]0085863a2013-12-06 21:19:03517 const base::string16 profile_id(GetProfileIdFromPath(profile_path));
[email protected]a0448002012-06-19 04:32:10518 if (!profile_id.empty())
519 components.push_back(profile_id);
520 return ShellUtil::BuildAppModelId(components);
[email protected]12f520c2010-01-06 18:11:15521}
522
pmonette9fa59e882016-02-10 00:12:19523base::string16 GetChromiumModelIdForProfile(
[email protected]650b2d52013-02-10 03:41:45524 const base::FilePath& profile_path) {
[email protected]a0448002012-06-19 04:32:10525 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
[email protected]650b2d52013-02-10 03:41:45526 base::FilePath chrome_exe;
[email protected]a0448002012-06-19 04:32:10527 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
528 NOTREACHED();
529 return dist->GetBaseAppId();
530 }
531 return GetAppModelIdForProfile(
grte76ca2852014-12-05 16:42:10532 ShellUtil::GetBrowserModelId(dist,
533 InstallUtil::IsPerUserInstall(chrome_exe)),
[email protected]786799692012-09-26 14:16:48534 profile_path);
[email protected]12f520c2010-01-06 18:11:15535}
[email protected]c9bb06f42010-01-13 23:53:48536
pmonette9fa59e882016-02-10 00:12:19537base::string16 GetAppListAppModelIdForProfile(
[email protected]650b2d52013-02-10 03:41:45538 const base::FilePath& profile_path) {
pmonettef89ac7c72015-10-06 03:22:01539 return GetAppModelIdForProfile(GetAppListAppName(), profile_path);
[email protected]99002fd2012-11-06 04:35:52540}
541
pmonette9fa59e882016-02-10 00:12:19542void MigrateTaskbarPins() {
[email protected]935aa542010-10-15 01:59:15543 if (base::win::GetVersion() < base::win::VERSION_WIN7)
[email protected]c9bb06f42010-01-13 23:53:48544 return;
545
[email protected]81fcf952012-11-26 22:25:14546 // This needs to happen eventually (e.g. so that the appid is fixed and the
547 // run-time Chrome icon is merged with the taskbar shortcut), but this is not
548 // urgent and shouldn't delay Chrome startup.
gab88257b62016-01-15 03:19:19549 static const int64_t kMigrateTaskbarPinsDelaySeconds = 15;
[email protected]81fcf952012-11-26 22:25:14550 BrowserThread::PostDelayedTask(
[email protected]3a3e72c2011-11-29 02:59:38551 BrowserThread::FILE, FROM_HERE,
gab88257b62016-01-15 03:19:19552 base::Bind(&MigrateTaskbarPinsCallback),
553 base::TimeDelta::FromSeconds(kMigrateTaskbarPinsDelaySeconds));
[email protected]c9bb06f42010-01-13 23:53:48554}
[email protected]43903b82012-06-01 05:26:23555
pmonette9fa59e882016-02-10 00:12:19556int MigrateShortcutsInPathInternal(const base::FilePath& chrome_exe,
557 const base::FilePath& path) {
[email protected]8ea8f1ef2013-01-06 18:39:03558 DCHECK(base::win::GetVersion() >= base::win::VERSION_WIN7);
559
560 // Enumerate all pinned shortcuts in the given path directly.
[email protected]25a4c1c2013-06-08 04:53:36561 base::FileEnumerator shortcuts_enum(
[email protected]8ea8f1ef2013-01-06 18:39:03562 path, false, // not recursive
[email protected]25a4c1c2013-06-08 04:53:36563 base::FileEnumerator::FILES, FILE_PATH_LITERAL("*.lnk"));
[email protected]8ea8f1ef2013-01-06 18:39:03564
grte76ca2852014-12-05 16:42:10565 bool is_per_user_install = InstallUtil::IsPerUserInstall(chrome_exe);
[email protected]8ea8f1ef2013-01-06 18:39:03566
567 int shortcuts_migrated = 0;
[email protected]650b2d52013-02-10 03:41:45568 base::FilePath target_path;
[email protected]0085863a2013-12-06 21:19:03569 base::string16 arguments;
[email protected]07983302013-01-21 19:41:44570 base::win::ScopedPropVariant propvariant;
[email protected]650b2d52013-02-10 03:41:45571 for (base::FilePath shortcut = shortcuts_enum.Next(); !shortcut.empty();
[email protected]8ea8f1ef2013-01-06 18:39:03572 shortcut = shortcuts_enum.Next()) {
573 // TODO(gab): Use ProgramCompare instead of comparing FilePaths below once
574 // it is fixed to work with FilePaths with spaces.
575 if (!base::win::ResolveShortcut(shortcut, &target_path, &arguments) ||
576 chrome_exe != target_path) {
577 continue;
578 }
avi556c05022014-12-22 23:31:43579 base::CommandLine command_line(
580 base::CommandLine::FromString(base::StringPrintf(
581 L"\"%ls\" %ls", target_path.value().c_str(), arguments.c_str())));
[email protected]8ea8f1ef2013-01-06 18:39:03582
583 // Get the expected AppId for this Chrome shortcut.
[email protected]0085863a2013-12-06 21:19:03584 base::string16 expected_app_id(
[email protected]8ea8f1ef2013-01-06 18:39:03585 GetExpectedAppId(command_line, is_per_user_install));
586 if (expected_app_id.empty())
587 continue;
588
589 // Load the shortcut.
590 base::win::ScopedComPtr<IShellLink> shell_link;
591 base::win::ScopedComPtr<IPersistFile> persist_file;
592 if (FAILED(shell_link.CreateInstance(CLSID_ShellLink, NULL,
593 CLSCTX_INPROC_SERVER)) ||
dcheng81762e02014-11-21 21:22:28594 FAILED(persist_file.QueryFrom(shell_link.get())) ||
[email protected]8ea8f1ef2013-01-06 18:39:03595 FAILED(persist_file->Load(shortcut.value().c_str(), STGM_READ))) {
596 DLOG(WARNING) << "Failed loading shortcut at " << shortcut.value();
597 continue;
598 }
599
600 // Any properties that need to be updated on the shortcut will be stored in
601 // |updated_properties|.
602 base::win::ShortcutProperties updated_properties;
603
604 // Validate the existing app id for the shortcut.
605 base::win::ScopedComPtr<IPropertyStore> property_store;
[email protected]07983302013-01-21 19:41:44606 propvariant.Reset();
dcheng81762e02014-11-21 21:22:28607 if (FAILED(property_store.QueryFrom(shell_link.get())) ||
608 property_store->GetValue(PKEY_AppUserModel_ID, propvariant.Receive()) !=
609 S_OK) {
[email protected]8ea8f1ef2013-01-06 18:39:03610 // When in doubt, prefer not updating the shortcut.
611 NOTREACHED();
612 continue;
[email protected]8ea8f1ef2013-01-06 18:39:03613 } else {
[email protected]07983302013-01-21 19:41:44614 switch (propvariant.get().vt) {
615 case VT_EMPTY:
616 // If there is no app_id set, set our app_id if one is expected.
617 if (!expected_app_id.empty())
618 updated_properties.set_app_id(expected_app_id);
619 break;
620 case VT_LPWSTR:
[email protected]0085863a2013-12-06 21:19:03621 if (expected_app_id != base::string16(propvariant.get().pwszVal))
[email protected]07983302013-01-21 19:41:44622 updated_properties.set_app_id(expected_app_id);
623 break;
624 default:
625 NOTREACHED();
626 continue;
[email protected]8ea8f1ef2013-01-06 18:39:03627 }
628 }
629
gab88257b62016-01-15 03:19:19630 // Clear dual_mode property from any shortcuts that previously had it (it
631 // was only ever installed on shortcuts with the
632 // |default_chromium_model_id|).
[email protected]b39e32e2013-04-24 08:55:54633 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
[email protected]0085863a2013-12-06 21:19:03634 base::string16 default_chromium_model_id(
[email protected]b39e32e2013-04-24 08:55:54635 ShellUtil::GetBrowserModelId(dist, is_per_user_install));
gab88257b62016-01-15 03:19:19636 if (expected_app_id == default_chromium_model_id) {
[email protected]07983302013-01-21 19:41:44637 propvariant.Reset();
[email protected]8ea8f1ef2013-01-06 18:39:03638 if (property_store->GetValue(PKEY_AppUserModel_IsDualMode,
[email protected]07983302013-01-21 19:41:44639 propvariant.Receive()) != S_OK) {
[email protected]8ea8f1ef2013-01-06 18:39:03640 // When in doubt, prefer to not update the shortcut.
641 NOTREACHED();
642 continue;
gab38d242d4d2016-01-14 20:38:12643 }
644 if (propvariant.get().vt == VT_BOOL &&
645 !!propvariant.get().boolVal) {
646 updated_properties.set_dual_mode(false);
[email protected]8ea8f1ef2013-01-06 18:39:03647 }
648 }
649
650 persist_file.Release();
651 shell_link.Release();
652
653 // Update the shortcut if some of its properties need to be updated.
654 if (updated_properties.options &&
655 base::win::CreateOrUpdateShortcutLink(
656 shortcut, updated_properties,
657 base::win::SHORTCUT_UPDATE_EXISTING)) {
658 ++shortcuts_migrated;
659 }
660 }
661 return shortcuts_migrated;
662}
663
pmonette9fa59e882016-02-10 00:12:19664base::FilePath GetStartMenuShortcut(const base::FilePath& chrome_exe) {
[email protected]3f69d6e612012-08-03 18:52:27665 static const int kFolderIds[] = {
666 base::DIR_COMMON_START_MENU,
667 base::DIR_START_MENU,
668 };
669 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
grt2be0cd082015-11-12 15:32:21670 const base::string16 shortcut_name(
671 dist->GetShortcutName(BrowserDistribution::SHORTCUT_CHROME) +
672 installer::kLnkExt);
673 base::FilePath programs_folder;
[email protected]650b2d52013-02-10 03:41:45674 base::FilePath shortcut;
[email protected]3f69d6e612012-08-03 18:52:27675
676 // Check both the common and the per-user Start Menu folders for system-level
677 // installs.
grte76ca2852014-12-05 16:42:10678 size_t folder = InstallUtil::IsPerUserInstall(chrome_exe) ? 1 : 0;
[email protected]3f69d6e612012-08-03 18:52:27679 for (; folder < arraysize(kFolderIds); ++folder) {
grt2be0cd082015-11-12 15:32:21680 if (!PathService::Get(kFolderIds[folder], &programs_folder)) {
[email protected]3f69d6e612012-08-03 18:52:27681 NOTREACHED();
682 continue;
683 }
684
grt2be0cd082015-11-12 15:32:21685 shortcut = programs_folder.Append(shortcut_name);
686 if (base::PathExists(shortcut))
687 return shortcut;
[email protected]3f69d6e612012-08-03 18:52:27688 }
689
[email protected]650b2d52013-02-10 03:41:45690 return base::FilePath();
[email protected]3f69d6e612012-08-03 18:52:27691}
pmonettef89ac7c72015-10-06 03:22:01692
pmonette32a5cfb42016-04-11 22:04:44693} // namespace win
694
pmonette9fa59e882016-02-10 00:12:19695} // namespace shell_integration