Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(349)

Side by Side Diff: chrome/browser/shell_integration_win.cc

Issue 1832853003: DefaultBrowserWorker now fully supports opening the settings for Win10 (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Sky comments + refactoring Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/shell_integration.h" 5 #include "chrome/browser/shell_integration_win.h"
6 6
7 #include <windows.h> 7 #include <windows.h>
8 #include <shlwapi.h> 8 #include <shlwapi.h>
9 #include <shobjidl.h> 9 #include <shobjidl.h>
10 #include <propkey.h> // Needs to come after shobjidl.h. 10 #include <propkey.h> // Needs to come after shobjidl.h.
11 #include <stddef.h> 11 #include <stddef.h>
12 #include <stdint.h> 12 #include <stdint.h>
13 13
14 #include <vector>
15
14 #include "base/bind.h" 16 #include "base/bind.h"
15 #include "base/command_line.h" 17 #include "base/command_line.h"
16 #include "base/files/file_enumerator.h" 18 #include "base/files/file_enumerator.h"
17 #include "base/files/file_util.h" 19 #include "base/files/file_util.h"
18 #include "base/macros.h" 20 #include "base/macros.h"
21 #include "base/memory/weak_ptr.h"
19 #include "base/message_loop/message_loop.h" 22 #include "base/message_loop/message_loop.h"
23 #include "base/metrics/histogram_macros.h"
20 #include "base/path_service.h" 24 #include "base/path_service.h"
21 #include "base/strings/string_util.h" 25 #include "base/strings/string_util.h"
22 #include "base/strings/stringprintf.h" 26 #include "base/strings/stringprintf.h"
23 #include "base/strings/utf_string_conversions.h" 27 #include "base/strings/utf_string_conversions.h"
28 #include "base/time/time.h"
29 #include "base/timer/timer.h"
24 #include "base/win/registry.h" 30 #include "base/win/registry.h"
25 #include "base/win/scoped_comptr.h" 31 #include "base/win/scoped_comptr.h"
26 #include "base/win/scoped_propvariant.h" 32 #include "base/win/scoped_propvariant.h"
27 #include "base/win/shortcut.h" 33 #include "base/win/shortcut.h"
28 #include "base/win/windows_version.h" 34 #include "base/win/windows_version.h"
29 #include "chrome/browser/policy/policy_path_parser.h" 35 #include "chrome/browser/policy/policy_path_parser.h"
36 #include "chrome/browser/shell_integration.h"
30 #include "chrome/browser/web_applications/web_app.h" 37 #include "chrome/browser/web_applications/web_app.h"
31 #include "chrome/common/chrome_constants.h" 38 #include "chrome/common/chrome_constants.h"
32 #include "chrome/common/chrome_paths_internal.h" 39 #include "chrome/common/chrome_paths_internal.h"
33 #include "chrome/common/chrome_switches.h" 40 #include "chrome/common/chrome_switches.h"
34 #include "chrome/installer/setup/setup_util.h" 41 #include "chrome/installer/setup/setup_util.h"
35 #include "chrome/installer/util/browser_distribution.h" 42 #include "chrome/installer/util/browser_distribution.h"
36 #include "chrome/installer/util/create_reg_key_work_item.h" 43 #include "chrome/installer/util/create_reg_key_work_item.h"
37 #include "chrome/installer/util/install_util.h" 44 #include "chrome/installer/util/install_util.h"
38 #include "chrome/installer/util/set_reg_value_work_item.h" 45 #include "chrome/installer/util/set_reg_value_work_item.h"
39 #include "chrome/installer/util/shell_util.h" 46 #include "chrome/installer/util/shell_util.h"
(...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after
208 case ShellUtil::NOT_DEFAULT: 215 case ShellUtil::NOT_DEFAULT:
209 return DefaultWebClientState::NOT_DEFAULT; 216 return DefaultWebClientState::NOT_DEFAULT;
210 case ShellUtil::IS_DEFAULT: 217 case ShellUtil::IS_DEFAULT:
211 return DefaultWebClientState::IS_DEFAULT; 218 return DefaultWebClientState::IS_DEFAULT;
212 default: 219 default:
213 DCHECK_EQ(ShellUtil::UNKNOWN_DEFAULT, default_state); 220 DCHECK_EQ(ShellUtil::UNKNOWN_DEFAULT, default_state);
214 return DefaultWebClientState::UNKNOWN_DEFAULT; 221 return DefaultWebClientState::UNKNOWN_DEFAULT;
215 } 222 }
216 } 223 }
217 224
225 // There is no way to make sure the user is done with the system settings, but a
226 // signal that the interaction is finished is needed for UMA. A timer of 2
227 // minutes is used as a substitute. The registry keys for the protocol
228 // association with an app are also monitored to signal the end of the
229 // interaction early when it is clear that the user made a choice (e.g. http
230 // and https for default browser).
231 //
232 // This helper class manages both the timer and the registry watchers and makes
233 // sure the callback for the end of the settings interaction is only run once.
234 // This class also manages its own lifetime.
235 class OpenSystemSettingsHelper {
236 public:
237 // Begin the monitoring and will call |on_finished_callback| when done.
238 // Takes in a null-terminated array of |protocols| whose registry keys must be
239 // watched.
240 static void Begin(const wchar_t* const protocols[],
241 const base::Closure& on_finished_callback) {
242 new OpenSystemSettingsHelper(protocols, on_finished_callback);
243 }
244
245 private:
246 // The reason the settings interaction concluded. Do not modify the ordering
247 // because it is used for UMA.
248 enum ConcludeReason { REGISTRY_WATCHER, TIMEOUT, NUM_CONCLUDE_REASON_TYPES };
249
250 OpenSystemSettingsHelper(const wchar_t* const protocols[],
251 const base::Closure& on_finished_callback)
252 : on_finished_callback_(on_finished_callback), weak_ptr_factory_(this) {
253 static const wchar_t kUrlAssociationFormat[] =
254 L"SOFTWARE\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\"
255 L"%ls\\UserChoice";
256
257 // Remember the start time.
258 start_time_ = base::TimeTicks::Now();
259
260 for (const wchar_t* const* scan = &protocols[0]; *scan != nullptr; ++scan) {
261 AddRegistryKeyWatcher(
262 base::StringPrintf(kUrlAssociationFormat, *scan).c_str());
263 }
264 // Only the watchers that were succesfully initialized are counted.
265 registry_watcher_count_ = registry_key_watchers_.size();
266
267 timer_.Start(
268 FROM_HERE, base::TimeDelta::FromMinutes(2),
269 base::Bind(&OpenSystemSettingsHelper::ConcludeInteraction,
270 weak_ptr_factory_.GetWeakPtr(), ConcludeReason::TIMEOUT));
271 }
272
273 // Called when a change is detected on one of the registry keys being watched.
274 // Note: All types of modification to the registry key will trigger this
275 // function even if the value change is the only one that matters. This
276 // is good enough for now.
277 void OnRegistryKeyChanged() {
278 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
279
280 // Make sure all the registry watchers have fired.
281 if (--registry_watcher_count_ == 0) {
282 UMA_HISTOGRAM_MEDIUM_TIMES(
283 "DefaultBrowser.SettingsInteraction.RegistryWatcherDuration",
284 base::TimeTicks::Now() - start_time_);
285
286 ConcludeInteraction(ConcludeReason::REGISTRY_WATCHER);
287 }
288 }
289
290 // Ends the monitoring with the system settings. Will call
291 // |on_finished_callback_| and then dispose of this class instance to make
292 // sure the callback won't get called subsequently.
293 void ConcludeInteraction(ConcludeReason conclude_reason) {
294 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
295
296 UMA_HISTOGRAM_ENUMERATION(
297 "DefaultBrowser.SettingsInteraction.ConcludeReason", conclude_reason,
298 NUM_CONCLUDE_REASON_TYPES);
299 on_finished_callback_.Run();
300 delete this;
301 }
302
303 // Helper function to create a registry watcher for a given |key_path|. Do
304 // nothing on initialization failure.
305 void AddRegistryKeyWatcher(const wchar_t* key_path) {
306 auto reg_key = make_scoped_ptr(
307 new base::win::RegKey(HKEY_CURRENT_USER, key_path, KEY_NOTIFY));
308
309 if (reg_key->Valid() &&
310 reg_key->StartWatching(
311 base::Bind(&OpenSystemSettingsHelper::OnRegistryKeyChanged,
312 weak_ptr_factory_.GetWeakPtr()))) {
313 registry_key_watchers_.push_back(std::move(reg_key));
314 }
315 }
316
317 // The function to call when the interaction with the system settings is
318 // finished.
319 base::Closure on_finished_callback_;
320
321 // The number of time the registry key watchers must fire.
322 int registry_watcher_count_ = 0;
323
324 // There can be multiple registry key watchers as some settings modify
325 // multiple protocol associations. e.g. Changing the default browser modifies
326 // the http and https associations.
327 std::vector<scoped_ptr<base::win::RegKey>> registry_key_watchers_;
328
329 base::OneShotTimer timer_;
330
331 // Records the time it takes for the final registry watcher to get signaled.
332 base::TimeTicks start_time_;
333
334 // Weak ptrs are used to bind this class to the callbacks of the timer and the
335 // registry watcher. This makes it possible to self-delete after one of the
336 // callbacks is executed to cancel the remaining ones.
337 base::WeakPtrFactory<OpenSystemSettingsHelper> weak_ptr_factory_;
338
339 DISALLOW_COPY_AND_ASSIGN(OpenSystemSettingsHelper);
340 };
341
218 } // namespace 342 } // namespace
219 343
220 bool SetAsDefaultBrowser() { 344 bool SetAsDefaultBrowser() {
221 base::FilePath chrome_exe; 345 base::FilePath chrome_exe;
222 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) { 346 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
223 LOG(ERROR) << "Error getting app exe path"; 347 LOG(ERROR) << "Error getting app exe path";
224 return false; 348 return false;
225 } 349 }
226 350
227 // From UI currently we only allow setting default browser for current user. 351 // From UI currently we only allow setting default browser for current user.
228 BrowserDistribution* dist = BrowserDistribution::GetDistribution(); 352 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
229 if (!ShellUtil::MakeChromeDefault(dist, ShellUtil::CURRENT_USER, chrome_exe, 353 if (!ShellUtil::MakeChromeDefault(dist, ShellUtil::CURRENT_USER, chrome_exe,
230 true /* elevate_if_not_admin */)) { 354 true /* elevate_if_not_admin */)) {
231 LOG(ERROR) << "Chrome could not be set as default browser."; 355 LOG(ERROR) << "Chrome could not be set as default browser.";
232 return false; 356 return false;
233 } 357 }
234 358
235 VLOG(1) << "Chrome registered as default browser."; 359 VLOG(1) << "Chrome registered as default browser.";
236 return true; 360 return true;
237 } 361 }
238 362
239 bool SetAsDefaultBrowserInteractive() {
240 base::FilePath chrome_exe;
241 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
242 NOTREACHED() << "Error getting app exe path";
243 return false;
244 }
245
246 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
247 if (!ShellUtil::ShowMakeChromeDefaultSystemUI(dist, chrome_exe)) {
248 LOG(ERROR) << "Failed to launch the set-default-browser Windows UI.";
249 return false;
250 }
251
252 VLOG(1) << "Set-default-browser Windows UI completed.";
253 return true;
254 }
255
256 bool SetAsDefaultProtocolClient(const std::string& protocol) { 363 bool SetAsDefaultProtocolClient(const std::string& protocol) {
257 if (protocol.empty()) 364 if (protocol.empty())
258 return false; 365 return false;
259 366
260 base::FilePath chrome_exe; 367 base::FilePath chrome_exe;
261 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) { 368 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
262 LOG(ERROR) << "Error getting app exe path"; 369 LOG(ERROR) << "Error getting app exe path";
263 return false; 370 return false;
264 } 371 }
265 372
266 base::string16 wprotocol(base::UTF8ToUTF16(protocol)); 373 base::string16 wprotocol(base::UTF8ToUTF16(protocol));
267 BrowserDistribution* dist = BrowserDistribution::GetDistribution(); 374 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
268 if (!ShellUtil::MakeChromeDefaultProtocolClient(dist, chrome_exe, 375 if (!ShellUtil::MakeChromeDefaultProtocolClient(dist, chrome_exe,
269 wprotocol)) { 376 wprotocol)) {
270 LOG(ERROR) << "Chrome could not be set as default handler for " 377 LOG(ERROR) << "Chrome could not be set as default handler for "
271 << protocol << "."; 378 << protocol << ".";
272 return false; 379 return false;
273 } 380 }
274 381
275 VLOG(1) << "Chrome registered as default handler for " << protocol << "."; 382 VLOG(1) << "Chrome registered as default handler for " << protocol << ".";
276 return true; 383 return true;
277 } 384 }
278 385
279 bool SetAsDefaultProtocolClientInteractive(const std::string& protocol) { 386 DefaultWebClientSetPermission GetDefaultWebClientSetPermission() {
280 base::FilePath chrome_exe;
281 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
282 NOTREACHED() << "Error getting app exe path";
283 return false;
284 }
285
286 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
287 base::string16 wprotocol(base::UTF8ToUTF16(protocol));
288 if (!ShellUtil::ShowMakeChromeDefaultProtocolClientSystemUI(dist, chrome_exe,
289 wprotocol)) {
290 LOG(ERROR) << "Failed to launch the set-default-client Windows UI.";
291 return false;
292 }
293
294 VLOG(1) << "Set-default-client Windows UI completed.";
295 return true;
296 }
297
298 DefaultWebClientSetPermission CanSetAsDefaultBrowser() {
299 BrowserDistribution* distribution = BrowserDistribution::GetDistribution(); 387 BrowserDistribution* distribution = BrowserDistribution::GetDistribution();
300 if (distribution->GetDefaultBrowserControlPolicy() != 388 if (distribution->GetDefaultBrowserControlPolicy() !=
301 BrowserDistribution::DEFAULT_BROWSER_FULL_CONTROL) 389 BrowserDistribution::DEFAULT_BROWSER_FULL_CONTROL)
302 return SET_DEFAULT_NOT_ALLOWED; 390 return SET_DEFAULT_NOT_ALLOWED;
303 if (ShellUtil::CanMakeChromeDefaultUnattended()) 391 if (ShellUtil::CanMakeChromeDefaultUnattended())
304 return SET_DEFAULT_UNATTENDED; 392 return SET_DEFAULT_UNATTENDED;
393 // Windows 8 and 10 both introduced a new way to set the default web client
394 // which require user interaction.
305 return SET_DEFAULT_INTERACTIVE; 395 return SET_DEFAULT_INTERACTIVE;
306 } 396 }
307 397
308 bool IsElevationNeededForSettingDefaultProtocolClient() { 398 bool IsElevationNeededForSettingDefaultProtocolClient() {
309 return base::win::GetVersion() < base::win::VERSION_WIN8; 399 return base::win::GetVersion() < base::win::VERSION_WIN8;
310 } 400 }
311 401
312 base::string16 GetApplicationNameForProtocol(const GURL& url) { 402 base::string16 GetApplicationNameForProtocol(const GURL& url) {
313 // Windows 8 or above requires a new protocol association query. 403 // Windows 8 or above requires a new protocol association query.
314 if (base::win::GetVersion() >= base::win::VERSION_WIN8) 404 if (base::win::GetVersion() >= base::win::VERSION_WIN8)
(...skipping 219 matching lines...) Expand 10 before | Expand all | Expand 10 after
534 } 624 }
535 625
536 shortcut = programs_folder.Append(shortcut_name); 626 shortcut = programs_folder.Append(shortcut_name);
537 if (base::PathExists(shortcut)) 627 if (base::PathExists(shortcut))
538 return shortcut; 628 return shortcut;
539 } 629 }
540 630
541 return base::FilePath(); 631 return base::FilePath();
542 } 632 }
543 633
634 namespace win {
635
636 bool SetAsDefaultBrowserUsingIntentPicker() {
637 base::FilePath chrome_exe;
638 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
639 NOTREACHED() << "Error getting app exe path";
640 return false;
641 }
642
643 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
644 if (!ShellUtil::ShowMakeChromeDefaultSystemUI(dist, chrome_exe)) {
645 LOG(ERROR) << "Failed to launch the set-default-browser Windows UI.";
646 return false;
647 }
648
649 VLOG(1) << "Set-default-browser Windows UI completed.";
650 return true;
651 }
652
653 void SetAsDefaultBrowserUsingSystemSettings(
654 const base::Closure& on_finished_callback) {
655 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
656
657 base::FilePath chrome_exe;
658 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
659 NOTREACHED() << "Error getting app exe path";
660 on_finished_callback.Run();
661 return;
662 }
663
664 // The helper manages its own lifetime.
665 static const wchar_t* const kProtocols[] = {L"http", L"https", nullptr};
666 OpenSystemSettingsHelper::Begin(kProtocols, on_finished_callback);
667
668 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
669 ShellUtil::ShowMakeChromeDefaultSystemUI(dist, chrome_exe);
670 }
671
672 bool SetAsDefaultProtocolClientUsingIntentPicker(const std::string& protocol) {
673 base::FilePath chrome_exe;
674 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
675 NOTREACHED() << "Error getting app exe path";
676 return false;
677 }
678
679 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
680 base::string16 wprotocol(base::UTF8ToUTF16(protocol));
681 if (!ShellUtil::ShowMakeChromeDefaultProtocolClientSystemUI(dist, chrome_exe,
682 wprotocol)) {
683 LOG(ERROR) << "Failed to launch the set-default-client Windows UI.";
684 return false;
685 }
686
687 VLOG(1) << "Set-default-client Windows UI completed.";
688 return true;
689 }
690
691 } // namespace win
692
544 } // namespace shell_integration 693 } // namespace shell_integration
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698