[M120][Tizen][Onscreen] Fix build errors for TV profile
[platform/framework/web/chromium-efl.git] / chrome / browser / shell_integration_win.cc
1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/shell_integration_win.h"
6
7 #include <shobjidl.h>
8 #include <windows.h>
9
10 #include <objbase.h>
11 #include <propkey.h>  // Needs to come after shobjidl.h.
12 #include <stddef.h>
13 #include <stdint.h>
14 #include <wrl/client.h>
15
16 #include <memory>
17 #include <utility>
18 #include <vector>
19
20 #include "base/command_line.h"
21 #include "base/files/file_enumerator.h"
22 #include "base/files/file_util.h"
23 #include "base/functional/bind.h"
24 #include "base/functional/callback.h"
25 #include "base/memory/weak_ptr.h"
26 #include "base/metrics/user_metrics.h"
27 #include "base/metrics/user_metrics_action.h"
28 #include "base/path_service.h"
29 #include "base/strings/strcat_win.h"
30 #include "base/strings/string_util.h"
31 #include "base/strings/utf_string_conversions.h"
32 #include "base/task/thread_pool.h"
33 #include "base/threading/platform_thread.h"
34 #include "base/threading/scoped_blocking_call.h"
35 #include "base/time/time.h"
36 #include "base/timer/timer.h"
37 #include "base/win/registry.h"
38 #include "base/win/scoped_propvariant.h"
39 #include "base/win/shlwapi.h"
40 #include "base/win/shortcut.h"
41 #include "chrome/browser/policy/policy_path_parser.h"
42 #include "chrome/browser/shell_integration.h"
43 #include "chrome/browser/web_applications/os_integration/web_app_shortcut_win.h"
44 #include "chrome/browser/web_applications/web_app_helpers.h"
45 #include "chrome/browser/win/settings_app_monitor.h"
46 #include "chrome/browser/win/util_win_service.h"
47 #include "chrome/common/chrome_constants.h"
48 #include "chrome/common/chrome_paths_internal.h"
49 #include "chrome/common/chrome_switches.h"
50 #include "chrome/install_static/install_util.h"
51 #include "chrome/installer/util/install_util.h"
52 #include "chrome/installer/util/shell_util.h"
53 #include "chrome/services/util_win/public/mojom/util_win.mojom.h"
54 #include "components/variations/variations_associated_data.h"
55 #include "mojo/public/cpp/bindings/remote.h"
56
57 namespace shell_integration {
58
59 namespace {
60
61 // Helper function for GetAppId to generates profile id
62 // from profile path. "profile_id" is composed of sanitized basenames of
63 // user data dir and profile dir joined by a ".".
64 std::wstring GetProfileIdFromPath(const base::FilePath& profile_path) {
65   // Return empty string if profile_path is empty
66   if (profile_path.empty())
67     return std::wstring();
68
69   base::FilePath default_user_data_dir;
70   // Return empty string if profile_path is in default user data
71   // dir and is the default profile.
72   if (chrome::GetDefaultUserDataDirectory(&default_user_data_dir) &&
73       profile_path.DirName() == default_user_data_dir &&
74       profile_path.BaseName().value() ==
75           base::ASCIIToWide(chrome::kInitialProfile)) {
76     return std::wstring();
77   }
78
79   // Get joined basenames of user data dir and profile.
80   std::wstring basenames = profile_path.DirName().BaseName().value() + L"." +
81                            profile_path.BaseName().value();
82
83   std::wstring profile_id;
84   profile_id.reserve(basenames.size());
85
86   // Generate profile_id from sanitized basenames.
87   for (size_t i = 0; i < basenames.length(); ++i) {
88     if (base::IsAsciiAlpha(basenames[i]) ||
89         base::IsAsciiDigit(basenames[i]) ||
90         basenames[i] == L'.')
91       profile_id += basenames[i];
92   }
93
94   return profile_id;
95 }
96
97 std::wstring GetAppUserModelIdImpl(const std::wstring& prefix,
98                                    const std::wstring& app_name,
99                                    const base::FilePath& profile_path) {
100   std::vector<std::wstring> components;
101   if (!prefix.empty())
102     components.push_back(prefix);
103   components.push_back(app_name);
104   const std::wstring profile_id(GetProfileIdFromPath(profile_path));
105   if (!profile_id.empty())
106     components.push_back(profile_id);
107   return ShellUtil::BuildAppUserModelId(components);
108 }
109
110 // Gets expected app id for given Chrome (based on |command_line| and
111 // |is_per_user_install|).
112 std::wstring GetExpectedAppId(const base::CommandLine& command_line,
113                               bool is_per_user_install) {
114   base::FilePath user_data_dir;
115   if (command_line.HasSwitch(switches::kUserDataDir))
116     user_data_dir = command_line.GetSwitchValuePath(switches::kUserDataDir);
117   // Adjust with any policy that overrides any other way to set the path.
118   policy::path_parser::CheckUserDataDirPolicy(&user_data_dir);
119   if (user_data_dir.empty())
120     chrome::GetDefaultUserDataDirectory(&user_data_dir);
121   DCHECK(!user_data_dir.empty());
122
123   base::FilePath profile_subdir;
124   if (command_line.HasSwitch(switches::kProfileDirectory)) {
125     profile_subdir =
126         command_line.GetSwitchValuePath(switches::kProfileDirectory);
127   } else {
128     profile_subdir = base::FilePath(base::ASCIIToWide(chrome::kInitialProfile));
129   }
130   DCHECK(!profile_subdir.empty());
131
132   base::FilePath profile_path = user_data_dir.Append(profile_subdir);
133   std::wstring prefix;
134   std::wstring app_name;
135   if (command_line.HasSwitch(switches::kApp)) {
136     app_name = base::UTF8ToWide(web_app::GenerateApplicationNameFromURL(
137         GURL(command_line.GetSwitchValueASCII(switches::kApp))));
138     prefix = install_static::GetBaseAppId();
139   } else if (command_line.HasSwitch(switches::kAppId)) {
140     app_name = base::UTF8ToWide(web_app::GenerateApplicationNameFromAppId(
141         command_line.GetSwitchValueASCII(switches::kAppId)));
142     prefix = install_static::GetBaseAppId();
143   } else {
144     app_name = ShellUtil::GetBrowserModelId(is_per_user_install);
145   }
146   DCHECK(!app_name.empty());
147
148   return GetAppUserModelIdImpl(prefix, app_name, profile_path);
149 }
150
151 // Windows treats a given scheme as an Internet scheme only if its registry
152 // entry has a "URL Protocol" key. Check this, otherwise we allow ProgIDs to be
153 // used as custom scheme which leads to security bugs.
154 bool IsValidCustomScheme(const std::wstring& scheme) {
155   if (scheme.empty())
156     return false;
157   base::win::RegKey cmd_key(HKEY_CLASSES_ROOT, scheme.c_str(), KEY_QUERY_VALUE);
158   return cmd_key.Valid() && cmd_key.HasValue(L"URL Protocol");
159 }
160
161 // Windows 8 introduced a new scheme->executable binding system which cannot
162 // be retrieved in the HKCR registry subkey method implemented below. We call
163 // AssocQueryString with the new Win8-only flag ASSOCF_IS_PROTOCOL instead.
164 std::u16string GetAppForSchemeUsingAssocQuery(const GURL& url) {
165   const std::wstring url_scheme = base::ASCIIToWide(url.scheme());
166   if (!IsValidCustomScheme(url_scheme)) {
167     return std::u16string();
168   }
169
170   // Query AssocQueryString for a human-readable description of the program
171   // that will be invoked given the provided URL spec. This is used only to
172   // populate the external scheme dialog box the user sees when invoking
173   // an unknown external scheme.
174   wchar_t out_buffer[1024];
175   DWORD buffer_size = std::size(out_buffer);
176   HRESULT hr =
177       AssocQueryString(ASSOCF_IS_PROTOCOL, ASSOCSTR_FRIENDLYAPPNAME,
178                        url_scheme.c_str(), NULL, out_buffer, &buffer_size);
179   if (FAILED(hr)) {
180     DLOG(WARNING) << "AssocQueryString failed!";
181     return std::u16string();
182   }
183   return base::AsString16(std::wstring(out_buffer));
184 }
185
186 std::u16string GetAppForSchemeUsingRegistry(const GURL& url) {
187   const std::wstring url_scheme = base::ASCIIToWide(url.scheme());
188   if (!IsValidCustomScheme(url_scheme)) {
189     return std::u16string();
190   }
191
192   // First, try and extract the application's display name.
193   std::wstring command_to_launch;
194   base::win::RegKey cmd_key_name(HKEY_CLASSES_ROOT, url_scheme.c_str(),
195                                  KEY_READ);
196   if (cmd_key_name.ReadValue(NULL, &command_to_launch) == ERROR_SUCCESS &&
197       !command_to_launch.empty()) {
198     return base::AsString16(command_to_launch);
199   }
200
201   // Otherwise, parse the command line in the registry, and return the basename
202   // of the program path if it exists.
203   const std::wstring cmd_key_path = url_scheme + L"\\shell\\open\\command";
204   base::win::RegKey cmd_key_exe(HKEY_CLASSES_ROOT, cmd_key_path.c_str(),
205                                 KEY_READ);
206   if (cmd_key_exe.ReadValue(NULL, &command_to_launch) == ERROR_SUCCESS) {
207     base::CommandLine command_line(
208         base::CommandLine::FromString(command_to_launch));
209     return command_line.GetProgram().BaseName().AsUTF16Unsafe();
210   }
211
212   return std::u16string();
213 }
214
215 DefaultWebClientState GetDefaultWebClientStateFromShellUtilDefaultState(
216     ShellUtil::DefaultState default_state) {
217   switch (default_state) {
218     case ShellUtil::UNKNOWN_DEFAULT:
219       return DefaultWebClientState::UNKNOWN_DEFAULT;
220     case ShellUtil::NOT_DEFAULT:
221       return DefaultWebClientState::NOT_DEFAULT;
222     case ShellUtil::IS_DEFAULT:
223       return DefaultWebClientState::IS_DEFAULT;
224     case ShellUtil::OTHER_MODE_IS_DEFAULT:
225       return DefaultWebClientState::OTHER_MODE_IS_DEFAULT;
226   }
227   NOTREACHED();
228   return DefaultWebClientState::UNKNOWN_DEFAULT;
229 }
230
231 // A recorder of user actions in the Windows Settings app.
232 class DefaultBrowserActionRecorder : public SettingsAppMonitor::Delegate {
233  public:
234   // Creates the recorder and the monitor that drives it. |continuation| will be
235   // run once the monitor's initialization completes (regardless of success or
236   // failure).
237   explicit DefaultBrowserActionRecorder(base::OnceClosure continuation)
238       : continuation_(std::move(continuation)), settings_app_monitor_(this) {}
239
240   DefaultBrowserActionRecorder(const DefaultBrowserActionRecorder&) = delete;
241   DefaultBrowserActionRecorder& operator=(const DefaultBrowserActionRecorder&) =
242       delete;
243
244  private:
245   // win::SettingsAppMonitor::Delegate:
246   void OnInitialized(HRESULT result) override {
247     // UMA indicates that this succeeds > 99.98% of the time.
248     if (SUCCEEDED(result)) {
249       base::RecordAction(
250           base::UserMetricsAction("SettingsAppMonitor.Initialized"));
251     }
252     std::move(continuation_).Run();
253   }
254
255   void OnAppFocused() override {
256     base::RecordAction(
257         base::UserMetricsAction("SettingsAppMonitor.AppFocused"));
258   }
259
260   void OnChooserInvoked() override {
261     base::RecordAction(
262         base::UserMetricsAction("SettingsAppMonitor.ChooserInvoked"));
263   }
264
265   void OnBrowserChosen(const std::wstring& browser_name) override {
266     if (browser_name == InstallUtil::GetDisplayName()) {
267       base::RecordAction(
268           base::UserMetricsAction("SettingsAppMonitor.ChromeBrowserChosen"));
269     } else {
270       base::RecordAction(
271           base::UserMetricsAction("SettingsAppMonitor.OtherBrowserChosen"));
272     }
273   }
274
275   void OnPromoFocused() override {
276     base::RecordAction(
277         base::UserMetricsAction("SettingsAppMonitor.PromoFocused"));
278   }
279
280   void OnPromoChoiceMade(bool accept_promo) override {
281     if (accept_promo) {
282       base::RecordAction(
283           base::UserMetricsAction("SettingsAppMonitor.CheckItOut"));
284     } else {
285       base::RecordAction(
286           base::UserMetricsAction("SettingsAppMonitor.SwitchAnyway"));
287     }
288   }
289
290   // A closure to be run once initialization completes.
291   base::OnceClosure continuation_;
292
293   // Monitors user interaction with the Windows Settings app for the sake of
294   // reporting user actions.
295   SettingsAppMonitor settings_app_monitor_;
296 };
297
298 // A function bound up in a callback with a DefaultBrowserActionRecorder and
299 // a closure to keep the former alive until the time comes to run the latter.
300 void OnSettingsAppFinished(
301     std::unique_ptr<DefaultBrowserActionRecorder> recorder,
302     base::OnceClosure on_finished_callback) {
303   recorder.reset();
304   std::move(on_finished_callback).Run();
305 }
306
307 // There is no way to make sure the user is done with the system settings, but a
308 // signal that the interaction is finished is needed for UMA. A timer of 2
309 // minutes is used as a substitute. The registry keys for the scheme
310 // association with an app are also monitored to signal the end of the
311 // interaction early when it is clear that the user made a choice (e.g. http
312 // and https for default browser).
313 //
314 // This helper class manages both the timer and the registry watchers and makes
315 // sure the callback for the end of the settings interaction is only run once.
316 // This class also manages its own lifetime.
317 class OpenSystemSettingsHelper {
318  public:
319   OpenSystemSettingsHelper(const OpenSystemSettingsHelper&) = delete;
320   OpenSystemSettingsHelper& operator=(const OpenSystemSettingsHelper&) = delete;
321
322   // Begin the monitoring and will call |on_finished_callback| when done.
323   // Takes in a null-terminated array of |schemes| whose registry keys must be
324   // watched. The array must contain at least one element.
325   static void Begin(const wchar_t* const schemes[],
326                     base::OnceClosure on_finished_callback) {
327     delete instance_;
328     instance_ =
329         new OpenSystemSettingsHelper(schemes, std::move(on_finished_callback));
330   }
331
332  private:
333   OpenSystemSettingsHelper(const wchar_t* const schemes[],
334                            base::OnceClosure on_finished_callback)
335       : on_finished_callback_(std::move(on_finished_callback)) {
336     for (const wchar_t* const* scan = &schemes[0]; *scan != nullptr; ++scan) {
337       AddRegistryKeyWatcher(base::StrCat({L"SOFTWARE\\Microsoft\\Windows\\Shell"
338                                           L"\\Associations\\UrlAssociations\\",
339                                           *scan, L"\\UserChoice"})
340                                 .c_str());
341     }
342     // Only the watchers that were succesfully initialized are counted.
343     registry_watcher_count_ = registry_key_watchers_.size();
344
345     timer_.Start(FROM_HERE, base::Minutes(2),
346                  base::BindOnce(&OpenSystemSettingsHelper::ConcludeInteraction,
347                                 weak_ptr_factory_.GetWeakPtr()));
348   }
349
350   ~OpenSystemSettingsHelper() {
351     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
352   }
353
354   // Called when a change is detected on one of the registry keys being watched.
355   // Note: All types of modification to the registry key will trigger this
356   //       function even if the value change is the only one that matters. This
357   //       is good enough for now.
358   void OnRegistryKeyChanged() {
359     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
360     // Make sure all the registry watchers have fired.
361     if (--registry_watcher_count_ == 0) {
362       ConcludeInteraction();
363     }
364   }
365
366   // Ends the monitoring with the system settings. Will call
367   // |on_finished_callback_| and then dispose of this class instance to make
368   // sure the callback won't get called subsequently.
369   void ConcludeInteraction() {
370     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
371
372     std::move(on_finished_callback_).Run();
373     delete instance_;
374     instance_ = nullptr;
375   }
376
377   // Helper function to create a registry watcher for a given |key_path|. Do
378   // nothing on initialization failure.
379   void AddRegistryKeyWatcher(const wchar_t* key_path) {
380     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
381
382     auto reg_key = std::make_unique<base::win::RegKey>(HKEY_CURRENT_USER,
383                                                        key_path, KEY_NOTIFY);
384
385     if (reg_key->Valid() && reg_key->StartWatching(base::BindOnce(
386                                 &OpenSystemSettingsHelper::OnRegistryKeyChanged,
387                                 weak_ptr_factory_.GetWeakPtr()))) {
388       registry_key_watchers_.push_back(std::move(reg_key));
389     }
390   }
391
392   // Used to make sure only one instance is alive at the same time.
393   static OpenSystemSettingsHelper* instance_;
394
395   // The function to call when the interaction with the system settings is
396   // finished.
397   base::OnceClosure on_finished_callback_;
398
399   // The number of time the registry key watchers must fire.
400   int registry_watcher_count_ = 0;
401
402   // There can be multiple registry key watchers as some settings modify
403   // multiple scheme associations. e.g. Changing the default browser modifies
404   // the http and https associations.
405   std::vector<std::unique_ptr<base::win::RegKey>> registry_key_watchers_;
406
407   base::OneShotTimer timer_;
408
409   SEQUENCE_CHECKER(sequence_checker_);
410
411   // Weak ptrs are used to bind this class to the callbacks of the timer and the
412   // registry watcher. This makes it possible to self-delete after one of the
413   // callbacks is executed to cancel the remaining ones.
414   base::WeakPtrFactory<OpenSystemSettingsHelper> weak_ptr_factory_{this};
415 };
416
417 OpenSystemSettingsHelper* OpenSystemSettingsHelper::instance_ = nullptr;
418
419 // Helper class to determine if Chrome is pinned to the taskbar. Hides the
420 // complexity of managing the lifetime of the connection to the ChromeWinUtil
421 // service.
422 class IsPinnedToTaskbarHelper {
423  public:
424   using ResultCallback = win::IsPinnedToTaskbarCallback;
425   using ErrorCallback = win::ConnectionErrorCallback;
426
427   IsPinnedToTaskbarHelper(const IsPinnedToTaskbarHelper&) = delete;
428   IsPinnedToTaskbarHelper& operator=(const IsPinnedToTaskbarHelper&) = delete;
429
430   static void GetState(ResultCallback result_callback);
431
432  private:
433   explicit IsPinnedToTaskbarHelper(ResultCallback result_callback);
434
435   void OnConnectionError();
436   void OnIsPinnedToTaskbarResult(bool succeeded, bool is_pinned_to_taskbar);
437
438   mojo::Remote<chrome::mojom::UtilWin> remote_util_win_;
439
440   ResultCallback result_callback_;
441
442   SEQUENCE_CHECKER(sequence_checker_);
443 };
444
445 // static
446 void IsPinnedToTaskbarHelper::GetState(ResultCallback result_callback) {
447   // Self-deleting when the ShellHandler completes.
448   new IsPinnedToTaskbarHelper(std::move(result_callback));
449 }
450
451 IsPinnedToTaskbarHelper::IsPinnedToTaskbarHelper(ResultCallback result_callback)
452     : remote_util_win_(LaunchUtilWinServiceInstance()),
453       result_callback_(std::move(result_callback)) {
454   DCHECK(result_callback_);
455
456   // |remote_util_win_| owns the callbacks and is guaranteed to be destroyed
457   // before |this|, therefore making base::Unretained() safe to use.
458   remote_util_win_.set_disconnect_handler(base::BindOnce(
459       &IsPinnedToTaskbarHelper::OnConnectionError, base::Unretained(this)));
460   remote_util_win_->IsPinnedToTaskbar(
461       base::BindOnce(&IsPinnedToTaskbarHelper::OnIsPinnedToTaskbarResult,
462                      base::Unretained(this)));
463 }
464
465 void IsPinnedToTaskbarHelper::OnConnectionError() {
466   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
467   delete this;
468 }
469
470 void IsPinnedToTaskbarHelper::OnIsPinnedToTaskbarResult(
471     bool succeeded,
472     bool is_pinned_to_taskbar) {
473   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
474
475   std::move(result_callback_).Run(succeeded, is_pinned_to_taskbar);
476   delete this;
477 }
478
479 // Helper class to unpin shortcuts from the taskbar. Hides the complexity of
480 //  managing the lifetime of the connection to the Windows utility service.
481 class UnpinShortcutsHelper {
482  public:
483   UnpinShortcutsHelper(const UnpinShortcutsHelper&) = delete;
484   UnpinShortcutsHelper& operator=(const UnpinShortcutsHelper&) = delete;
485
486   static void DoUnpin(const std::vector<base::FilePath>& shortcuts,
487                       base::OnceClosure completion_callback);
488
489  private:
490   UnpinShortcutsHelper(const std::vector<base::FilePath>& shortcuts,
491                        base::OnceClosure completion_callback);
492
493   void OnConnectionError();
494   void OnUnpinShortcutResult();
495
496   mojo::Remote<chrome::mojom::UtilWin> remote_util_win_;
497
498   base::OnceClosure completion_callback_;
499
500   SEQUENCE_CHECKER(sequence_checker_);
501 };
502
503 // static
504 void UnpinShortcutsHelper::DoUnpin(const std::vector<base::FilePath>& shortcuts,
505                                    base::OnceClosure completion_callback) {
506   // Self-deleting when the ShellHandler completes.
507   new UnpinShortcutsHelper(shortcuts, std::move(completion_callback));
508 }
509
510 UnpinShortcutsHelper::UnpinShortcutsHelper(
511     const std::vector<base::FilePath>& shortcuts,
512     base::OnceClosure completion_callback)
513     : remote_util_win_(LaunchUtilWinServiceInstance()),
514       completion_callback_(std::move(completion_callback)) {
515   DCHECK(completion_callback_);
516
517   // |remote_util_win_| owns the callbacks and is guaranteed to be destroyed
518   // before |this|, therefore making base::Unretained() safe to use.
519   remote_util_win_.set_disconnect_handler(base::BindOnce(
520       &UnpinShortcutsHelper::OnConnectionError, base::Unretained(this)));
521   remote_util_win_->UnpinShortcuts(
522       shortcuts, base::BindOnce(&UnpinShortcutsHelper::OnUnpinShortcutResult,
523                                 base::Unretained(this)));
524 }
525
526 void UnpinShortcutsHelper::OnConnectionError() {
527   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
528   std::move(completion_callback_).Run();
529   delete this;
530 }
531
532 void UnpinShortcutsHelper::OnUnpinShortcutResult() {
533   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
534
535   std::move(completion_callback_).Run();
536   delete this;
537 }
538
539 // Helper class to create or update desktop shortcuts Hides the complexity of
540 // managing the lifetime of the connection to the Windows utility service.
541 class CreateOrUpdateShortcutsHelper {
542  public:
543   CreateOrUpdateShortcutsHelper(const CreateOrUpdateShortcutsHelper&) = delete;
544   CreateOrUpdateShortcutsHelper& operator=(
545       const CreateOrUpdateShortcutsHelper&) = delete;
546
547   static void DoCreateOrUpdateShortcuts(
548       const std::vector<base::FilePath>& shortcuts,
549       const std::vector<base::win::ShortcutProperties>& properties,
550       base::win::ShortcutOperation operation,
551       win::CreateOrUpdateShortcutsResultCallback);
552
553  private:
554   CreateOrUpdateShortcutsHelper(
555       const std::vector<base::FilePath>& shortcuts,
556       const std::vector<base::win::ShortcutProperties>& properties,
557       base::win::ShortcutOperation operation,
558       win::CreateOrUpdateShortcutsResultCallback completion_callback);
559
560   void OnConnectionError();
561   void OnCreateOrUpdateShortcutResult(bool succeeded);
562
563   mojo::Remote<chrome::mojom::UtilWin> remote_util_win_;
564
565   win::CreateOrUpdateShortcutsResultCallback completion_callback_;
566
567   SEQUENCE_CHECKER(sequence_checker_);
568 };
569
570 // static
571 void CreateOrUpdateShortcutsHelper::DoCreateOrUpdateShortcuts(
572     const std::vector<base::FilePath>& shortcuts,
573     const std::vector<base::win::ShortcutProperties>& properties,
574     base::win::ShortcutOperation operation,
575     win::CreateOrUpdateShortcutsResultCallback completion_callback) {
576   // Self-deleting when the ShellHandler completes.
577   new CreateOrUpdateShortcutsHelper(shortcuts, properties, operation,
578                                     std::move(completion_callback));
579 }
580
581 CreateOrUpdateShortcutsHelper::CreateOrUpdateShortcutsHelper(
582     const std::vector<base::FilePath>& shortcuts,
583     const std::vector<base::win::ShortcutProperties>& properties,
584     base::win::ShortcutOperation operation,
585     win::CreateOrUpdateShortcutsResultCallback completion_callback)
586     : remote_util_win_(LaunchUtilWinServiceInstance()),
587       completion_callback_(std::move(completion_callback)) {
588   DCHECK(completion_callback_);
589
590   // |remote_util_win_| owns the callbacks and is guaranteed to be destroyed
591   // before |this|, therefore making base::Unretained() safe to use.
592   remote_util_win_.set_disconnect_handler(
593       base::BindOnce(&CreateOrUpdateShortcutsHelper::OnConnectionError,
594                      base::Unretained(this)));
595   remote_util_win_->CreateOrUpdateShortcuts(
596       shortcuts, properties, operation,
597       base::BindOnce(
598           &CreateOrUpdateShortcutsHelper::OnCreateOrUpdateShortcutResult,
599           base::Unretained(this)));
600 }
601
602 void CreateOrUpdateShortcutsHelper::OnConnectionError() {
603   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
604   std::move(completion_callback_).Run(false);
605   delete this;
606 }
607
608 void CreateOrUpdateShortcutsHelper::OnCreateOrUpdateShortcutResult(
609     bool succeeded) {
610   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
611   std::move(completion_callback_).Run(succeeded);
612   delete this;
613 }
614
615 void MigrateChromeAndChromeProxyShortcuts(
616     const base::FilePath& chrome_exe,
617     const base::FilePath& chrome_proxy_path,
618     const base::FilePath& shortcut_path) {
619   win::MigrateShortcutsInPathInternal(chrome_exe, shortcut_path);
620
621   // Migrate any pinned PWA shortcuts in taskbar directory.
622   win::MigrateShortcutsInPathInternal(chrome_proxy_path, shortcut_path);
623 }
624
625 std::wstring GetHttpSchemeUserChoiceProgId() {
626   std::wstring prog_id;
627   base::win::RegKey key(HKEY_CURRENT_USER, ShellUtil::kRegVistaUrlPrefs,
628                         KEY_QUERY_VALUE);
629   if (key.Valid())
630     key.ReadValue(L"ProgId", &prog_id);
631   return prog_id;
632 }
633
634 }  // namespace
635
636 bool SetAsDefaultBrowser() {
637   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
638                                                 base::BlockingType::MAY_BLOCK);
639
640   base::FilePath chrome_exe;
641   if (!base::PathService::Get(base::FILE_EXE, &chrome_exe)) {
642     LOG(ERROR) << "Error getting app exe path";
643     return false;
644   }
645
646   // From UI currently we only allow setting default browser for current user.
647   if (!ShellUtil::MakeChromeDefault(ShellUtil::CURRENT_USER, chrome_exe,
648                                     true /* elevate_if_not_admin */)) {
649     LOG(ERROR) << "Chrome could not be set as default browser.";
650     return false;
651   }
652
653   VLOG(1) << "Chrome registered as default browser.";
654   return true;
655 }
656
657 bool SetAsDefaultClientForScheme(const std::string& scheme) {
658   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
659                                                 base::BlockingType::MAY_BLOCK);
660
661   if (scheme.empty()) {
662     return false;
663   }
664
665   base::FilePath chrome_exe;
666   if (!base::PathService::Get(base::FILE_EXE, &chrome_exe)) {
667     LOG(ERROR) << "Error getting app exe path";
668     return false;
669   }
670
671   std::wstring wscheme(base::UTF8ToWide(scheme));
672   if (!ShellUtil::MakeChromeDefaultProtocolClient(chrome_exe, wscheme)) {
673     LOG(ERROR) << "Chrome could not be set as default handler for " << scheme
674                << ".";
675     return false;
676   }
677
678   VLOG(1) << "Chrome registered as default handler for " << scheme << ".";
679   return true;
680 }
681
682 std::u16string GetApplicationNameForScheme(const GURL& url) {
683   std::u16string application_name = GetAppForSchemeUsingAssocQuery(url);
684   if (!application_name.empty()) {
685     return application_name;
686   }
687
688   return GetAppForSchemeUsingRegistry(url);
689 }
690
691 DefaultWebClientState GetDefaultBrowser() {
692   return GetDefaultWebClientStateFromShellUtilDefaultState(
693       ShellUtil::GetChromeDefaultState());
694 }
695
696 // This method checks if Firefox is default browser by checking for the default
697 // HTTP scheme handler. Returns false in case of error or if Firefox is not
698 // the user's default http scheme client.
699 bool IsFirefoxDefaultBrowser() {
700   return base::StartsWith(GetHttpSchemeUserChoiceProgId(), L"FirefoxURL",
701                           base::CompareCase::SENSITIVE);
702 }
703
704 std::string GetFirefoxProgIdSuffix() {
705   const std::wstring app_cmd = GetHttpSchemeUserChoiceProgId();
706   static constexpr base::WStringPiece kFirefoxProgIdPrefix(L"FirefoxURL-");
707   if (base::StartsWith(app_cmd, kFirefoxProgIdPrefix,
708                        base::CompareCase::SENSITIVE)) {
709     // Returns the id that appears after the prefix "FirefoxURL-".
710     return std::string(app_cmd.begin() + kFirefoxProgIdPrefix.size(),
711                        app_cmd.end());
712   }
713   return std::string();
714 }
715
716 bool IsIEDefaultBrowser() {
717   return GetHttpSchemeUserChoiceProgId() == L"IE.HTTP";
718 }
719
720 DefaultWebClientState IsDefaultClientForScheme(const std::string& scheme) {
721   return GetDefaultWebClientStateFromShellUtilDefaultState(
722       ShellUtil::GetChromeDefaultProtocolClientState(base::UTF8ToWide(scheme)));
723 }
724
725 namespace internal {
726
727 DefaultWebClientSetPermission GetPlatformSpecificDefaultWebClientSetPermission(
728     WebClientSetMethod method) {
729   if (!install_static::SupportsSetAsDefaultBrowser()) {
730     return SET_DEFAULT_NOT_ALLOWED;
731   }
732   if (ShellUtil::CanMakeChromeDefaultUnattended()) {
733     return SET_DEFAULT_UNATTENDED;
734   }
735   // Setting the default web client generally requires user interaction in
736   // Windows 8+ with permitted exceptions above.
737   return SET_DEFAULT_INTERACTIVE;
738 }
739
740 }  // namespace internal
741
742 namespace win {
743
744 void SetAsDefaultBrowserUsingSystemSettings(
745     base::OnceClosure on_finished_callback) {
746   base::FilePath chrome_exe;
747   if (!base::PathService::Get(base::FILE_EXE, &chrome_exe)) {
748     NOTREACHED() << "Error getting app exe path";
749     std::move(on_finished_callback).Run();
750     return;
751   }
752
753   // Create an action recorder that will open the settings app once it has
754   // initialized.
755   std::unique_ptr<DefaultBrowserActionRecorder> recorder =
756           std::make_unique<DefaultBrowserActionRecorder>(base::BindOnce(
757           base::IgnoreResult(&ShellUtil::ShowMakeChromeDefaultSystemUI),
758           chrome_exe));
759
760   // The helper manages its own lifetime. Bind the action recorder
761   // into the finished callback to keep it alive throughout the
762   // interaction.
763   static const wchar_t* const kSchemes[] = {L"http", L"https", nullptr};
764   OpenSystemSettingsHelper::Begin(
765       kSchemes, base::BindOnce(&OnSettingsAppFinished, std::move(recorder),
766                                std::move(on_finished_callback)));
767 }
768
769 void SetAsDefaultClientForSchemeUsingSystemSettings(
770     const std::string& scheme,
771     base::OnceClosure on_finished_callback) {
772   base::FilePath chrome_exe;
773   if (!base::PathService::Get(base::FILE_EXE, &chrome_exe)) {
774     NOTREACHED() << "Error getting app exe path";
775     std::move(on_finished_callback).Run();
776     return;
777   }
778
779   // The helper manages its own lifetime.
780   std::wstring wscheme(base::UTF8ToWide(scheme));
781   const wchar_t* const kSchemes[] = {wscheme.c_str(), nullptr};
782   OpenSystemSettingsHelper::Begin(kSchemes, std::move(on_finished_callback));
783
784   ShellUtil::ShowMakeChromeDefaultProtocolClientSystemUI(chrome_exe, wscheme);
785 }
786
787 std::wstring GetAppUserModelIdForApp(const std::wstring& app_name,
788                                      const base::FilePath& profile_path) {
789   return GetAppUserModelIdImpl(install_static::GetBaseAppId(), app_name,
790                                profile_path);
791 }
792
793 std::wstring GetAppUserModelIdForBrowser(const base::FilePath& profile_path) {
794   return GetAppUserModelIdImpl(
795       std::wstring(),
796       ShellUtil::GetBrowserModelId(InstallUtil::IsPerUserInstall()),
797       profile_path);
798 }
799
800 void UnpinShortcuts(const std::vector<base::FilePath>& shortcuts,
801                     base::OnceClosure completion_callback) {
802   UnpinShortcutsHelper::DoUnpin(shortcuts, std::move(completion_callback));
803 }
804
805 void CreateOrUpdateShortcuts(
806     const std::vector<base::FilePath>& shortcuts,
807     const std::vector<base::win::ShortcutProperties>& properties,
808     base::win::ShortcutOperation operation,
809     win::CreateOrUpdateShortcutsResultCallback callback) {
810   CreateOrUpdateShortcutsHelper::DoCreateOrUpdateShortcuts(
811       shortcuts, properties, operation, std::move(callback));
812 }
813
814 void MigrateTaskbarPins(base::OnceClosure completion_callback) {
815   // This needs to happen (e.g. so that the appid is fixed and the
816   // run-time Chrome icon is merged with the taskbar shortcut), but it is not an
817   // urgent task.
818   // MigrateTaskbarPinsCallback just calls MigrateShortcutsInPathInternal
819   // several times with different parameters.  Each call may or may not load
820   // DLL's. Since the callback may take the loader lock several times, and this
821   // is the bulk of the callback's work, run the whole thing on a foreground
822   // thread.
823   //
824   // BEST_EFFORT means it will be scheduled after higher-priority tasks, but
825   // MUST_USE_FOREGROUND means that when it is scheduled it will run in the
826   // foregound.
827   // SKIP_ON_SHUTDOWN means the task won't start after shutdown has started.
828   base::ThreadPool::CreateCOMSTATaskRunner(
829       {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
830        base::ThreadPolicy::MUST_USE_FOREGROUND,
831        base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})
832       ->PostTaskAndReply(
833           FROM_HERE, base::BindOnce([]() {
834             base::FilePath taskbar_path;
835             base::FilePath implicit_apps_path;
836             base::PathService::Get(base::DIR_TASKBAR_PINS, &taskbar_path);
837             base::PathService::Get(base::DIR_IMPLICIT_APP_SHORTCUTS,
838                                    &implicit_apps_path);
839             MigrateTaskbarPinsCallback(taskbar_path, implicit_apps_path);
840           }),
841           std::move(completion_callback));
842 }
843
844 void MigrateTaskbarPinsCallback(const base::FilePath& taskbar_path,
845                                 const base::FilePath& implicit_apps_path) {
846   // Get full path of chrome.
847   base::FilePath chrome_exe;
848   if (!base::PathService::Get(base::FILE_EXE, &chrome_exe))
849     return;
850   base::FilePath chrome_proxy_path(web_app::GetChromeProxyPath());
851
852   if (!taskbar_path.empty()) {
853     MigrateChromeAndChromeProxyShortcuts(chrome_exe, chrome_proxy_path,
854                                          taskbar_path);
855   }
856   if (implicit_apps_path.empty())
857     return;
858   base::FileEnumerator directory_enum(implicit_apps_path, /*recursive=*/false,
859                                       base::FileEnumerator::DIRECTORIES);
860   for (base::FilePath implicit_app_sub_directory = directory_enum.Next();
861        !implicit_app_sub_directory.empty();
862        implicit_app_sub_directory = directory_enum.Next()) {
863     MigrateChromeAndChromeProxyShortcuts(chrome_exe, chrome_proxy_path,
864                                          implicit_app_sub_directory);
865   }
866 }
867
868 void GetIsPinnedToTaskbarState(IsPinnedToTaskbarCallback result_callback) {
869   IsPinnedToTaskbarHelper::GetState(std::move(result_callback));
870 }
871
872 int MigrateShortcutsInPathInternal(const base::FilePath& chrome_exe,
873                                    const base::FilePath& path) {
874   // This function may load DLL's so ensure it is running in a foreground
875   // thread.
876   DCHECK_GT(base::PlatformThread::GetCurrentThreadType(),
877             base::ThreadType::kBackground);
878
879   // Enumerate all pinned shortcuts in the given path directly.
880   base::FileEnumerator shortcuts_enum(
881       path, false,  // not recursive
882       base::FileEnumerator::FILES, FILE_PATH_LITERAL("*.lnk"));
883
884   bool is_per_user_install = InstallUtil::IsPerUserInstall();
885
886   int shortcuts_migrated = 0;
887   base::FilePath target_path;
888   std::wstring arguments;
889   base::win::ScopedPropVariant propvariant;
890   for (base::FilePath shortcut = shortcuts_enum.Next(); !shortcut.empty();
891        shortcut = shortcuts_enum.Next()) {
892     // TODO(gab): Use ProgramCompare instead of comparing FilePaths below once
893     // it is fixed to work with FilePaths with spaces.
894     if (!base::win::ResolveShortcut(shortcut, &target_path, &arguments) ||
895         !base::FilePath::CompareEqualIgnoreCase(chrome_exe.value(),
896                                                 target_path.value())) {
897       continue;
898     }
899     base::CommandLine command_line(base::CommandLine::FromString(
900         base::StrCat({L"\"", target_path.value(), L"\" ", arguments})));
901
902     // Get the expected AppId for this Chrome shortcut.
903     std::wstring expected_app_id(
904         GetExpectedAppId(command_line, is_per_user_install));
905     if (expected_app_id.empty())
906       continue;
907
908     // Load the shortcut.
909     Microsoft::WRL::ComPtr<IShellLink> shell_link;
910     Microsoft::WRL::ComPtr<IPersistFile> persist_file;
911     if (FAILED(::CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
912                                   IID_PPV_ARGS(&shell_link))) ||
913         FAILED(shell_link.As(&persist_file)) ||
914         FAILED(persist_file->Load(shortcut.value().c_str(), STGM_READ))) {
915       DLOG(WARNING) << "Failed loading shortcut at " << shortcut.value();
916       continue;
917     }
918
919     // Any properties that need to be updated on the shortcut will be stored in
920     // |updated_properties|.
921     base::win::ShortcutProperties updated_properties;
922
923     // Validate the existing app id for the shortcut.
924     Microsoft::WRL::ComPtr<IPropertyStore> property_store;
925     propvariant.Reset();
926     if (FAILED(shell_link.As(&property_store)) ||
927         property_store->GetValue(PKEY_AppUserModel_ID, propvariant.Receive()) !=
928             S_OK) {
929       // When in doubt, prefer not updating the shortcut.
930       NOTREACHED();
931       continue;
932     } else {
933       switch (propvariant.get().vt) {
934         case VT_EMPTY:
935           // If there is no app_id set, set our app_id if one is expected.
936           if (!expected_app_id.empty())
937             updated_properties.set_app_id(expected_app_id);
938           break;
939         case VT_LPWSTR:
940           if (expected_app_id != std::wstring(propvariant.get().pwszVal))
941             updated_properties.set_app_id(expected_app_id);
942           break;
943         default:
944           NOTREACHED();
945           continue;
946       }
947     }
948
949     // Clear dual_mode property from any shortcuts that previously had it (it
950     // was only ever installed on shortcuts with the
951     // |default_chromium_model_id|).
952     std::wstring default_chromium_model_id(
953         ShellUtil::GetBrowserModelId(is_per_user_install));
954     if (expected_app_id == default_chromium_model_id) {
955       propvariant.Reset();
956       if (property_store->GetValue(PKEY_AppUserModel_IsDualMode,
957                                    propvariant.Receive()) != S_OK) {
958         // When in doubt, prefer to not update the shortcut.
959         NOTREACHED();
960         continue;
961       }
962       if (propvariant.get().vt == VT_BOOL &&
963                  !!propvariant.get().boolVal) {
964         updated_properties.set_dual_mode(false);
965       }
966     }
967
968     persist_file.Reset();
969     shell_link.Reset();
970
971     // Update the shortcut if some of its properties need to be updated.
972     if (updated_properties.options &&
973         base::win::CreateOrUpdateShortcutLink(
974             shortcut, updated_properties,
975             base::win::ShortcutOperation::kUpdateExisting)) {
976       ++shortcuts_migrated;
977     }
978   }
979   return shortcuts_migrated;
980 }
981
982 }  // namespace win
983
984 }  // namespace shell_integration