123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245 |
- // Copyright 2021 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include <objbase.h>
- #include <shlobj.h>
- #include <shobjidl.h>
- #include <wrl/client.h>
- #include <cwchar>
- #include <memory>
- #include <string>
- #include "base/bind.h"
- #include "base/callback_forward.h"
- #include "base/command_line.h"
- #include "base/logging.h"
- #include "base/message_loop/message_pump_type.h"
- #include "base/notreached.h"
- #include "base/run_loop.h"
- #include "base/strings/utf_string_conversions.h"
- #include "base/task/single_thread_task_executor.h"
- #include "base/task/thread_pool/thread_pool_instance.h"
- #include "base/threading/sequenced_task_runner_handle.h"
- #include "base/time/time.h"
- #include "base/win/default_apps_util.h"
- #include "base/win/scoped_co_mem.h"
- #include "base/win/scoped_com_initializer.h"
- #include "base/win/windows_types.h"
- #include "remoting/base/logging.h"
- #include "remoting/base/user_settings.h"
- #include "remoting/host/base/switches.h"
- #include "remoting/host/remote_open_url/remote_open_url_constants.h"
- #include "remoting/host/user_setting_keys.h"
- #include "remoting/host/win/core_resource.h"
- #include "remoting/host/win/simple_task_dialog.h"
- namespace remoting {
- namespace {
- constexpr wchar_t kProtocolToTestSetup[] = L"http";
- constexpr base::TimeDelta kPollingInterval = base::Milliseconds(500);
- constexpr base::TimeDelta kPollingTimeout = base::Minutes(1);
- // Returns the current default browser's ProgID, or an empty string if failed.
- std::wstring GetDefaultBrowserProgId() {
- // This method is modified from chrome/installer/util/shell_util.cc
- Microsoft::WRL::ComPtr<IApplicationAssociationRegistration> registration;
- HRESULT hr =
- ::CoCreateInstance(CLSID_ApplicationAssociationRegistration, nullptr,
- CLSCTX_INPROC, IID_PPV_ARGS(®istration));
- if (FAILED(hr)) {
- PLOG(ERROR) << "Failed to create IApplicationAssociationRegistration";
- return std::wstring();
- }
- base::win::ScopedCoMem<wchar_t> current_app;
- hr = registration->QueryCurrentDefault(kProtocolToTestSetup, AT_URLPROTOCOL,
- AL_EFFECTIVE, ¤t_app);
- if (FAILED(hr)) {
- PLOG(ERROR) << "Failed to query default app for protocol "
- << kProtocolToTestSetup;
- return std::wstring();
- }
- return current_app.get();
- }
- // |log_current_default_app| logs the current default app if it is not the CRD
- // URL forwarder.
- bool IsUrlForwarderSetUp(bool log_current_default_app = false) {
- std::wstring current_app = GetDefaultBrowserProgId();
- if (current_app.empty()) {
- return false;
- }
- if (current_app != kUrlForwarderProgId) {
- if (log_current_default_app) {
- HOST_LOG << "Current default app for " << kProtocolToTestSetup << " is "
- << current_app << " instead of " << kUrlForwarderProgId;
- }
- return false;
- }
- return true;
- }
- bool ShowSetUpUrlForwarderDialog() {
- // |resource_module| does not need to be freed as GetModuleHandle() does not
- // increment the refcount for the module. This DLL is not unloaded until the
- // process exits so using a stored handle is safe.
- HMODULE resource_module = GetModuleHandle(L"remoting_core.dll");
- if (resource_module == nullptr) {
- PLOG(ERROR) << "GetModuleHandle() failed";
- return false;
- }
- SimpleTaskDialog task_dialog(resource_module);
- if (!task_dialog.SetTitleTextWithStringId(IDS_URL_FORWARDER_NAME) ||
- !task_dialog.SetMessageTextWithStringId(
- IDS_SET_UP_URL_FORWARDER_MESSAGE) ||
- !task_dialog.AppendButtonWithStringId(
- IDOK, IDS_OPEN_DEFAULT_APPS_SETTINGS_BUTTON) ||
- !task_dialog.AppendButtonWithStringId(IDCANCEL, IDS_CANCEL)) {
- LOG(ERROR) << "Failed to load text for the setup dialog.";
- return false;
- }
- task_dialog.set_default_button(IDOK);
- absl::optional<int> button_result = task_dialog.Show();
- if (!button_result.has_value()) {
- LOG(ERROR) << "Failed to show the setup dialog.";
- return false;
- }
- switch (*button_result) {
- case IDOK:
- return true;
- case IDCANCEL:
- return false;
- default:
- NOTREACHED() << "Unknown button: " << *button_result;
- return false;
- }
- }
- // Class for running the setup process.
- class SetUpProcess {
- public:
- SetUpProcess();
- ~SetUpProcess();
- // Starts the setup process and calls |done_callback| once done.
- void Start(base::OnceCallback<void(bool)> done_callback);
- private:
- void OnSetUpDialogContinue();
- void OnSetUpDialogCancel();
- void PollUrlForwarderSetupState();
- base::OnceCallback<void(bool)> done_callback_;
- base::TimeDelta total_poll_time_;
- };
- SetUpProcess::SetUpProcess() = default;
- SetUpProcess::~SetUpProcess() {
- DCHECK(!done_callback_);
- }
- void SetUpProcess::Start(base::OnceCallback<void(bool)> done_callback) {
- DCHECK(!done_callback_);
- done_callback_ = std::move(done_callback);
- if (IsUrlForwarderSetUp()) {
- HOST_LOG << "URL forwarder has already been set up.";
- std::move(done_callback_).Run(true);
- return;
- }
- std::wstring prog_id = GetDefaultBrowserProgId();
- LOG(INFO) << "Setting previous default browser to " << prog_id;
- UserSettings::GetInstance()->SetString(kWinPreviousDefaultWebBrowserProgId,
- base::WideToUTF8(prog_id));
- if (ShowSetUpUrlForwarderDialog()) {
- OnSetUpDialogContinue();
- } else {
- OnSetUpDialogCancel();
- }
- }
- void SetUpProcess::OnSetUpDialogContinue() {
- // Windows does not pick up changes in RegisteredApplications until
- // SHChangeNotify() is called, so we call it here in case the user has just
- // added the URL forwarder entry to the registry.
- SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nullptr, nullptr);
- HOST_LOG << "Launching default apps settings dialog";
- if (!base::win::LaunchDefaultAppsSettingsModernDialog(
- /*protocol=*/std::wstring())) {
- std::move(done_callback_).Run(false);
- return;
- }
- DCHECK(total_poll_time_.is_zero());
- HOST_LOG << "Polling default app for protocol " << kProtocolToTestSetup;
- PollUrlForwarderSetupState();
- }
- void SetUpProcess::OnSetUpDialogCancel() {
- HOST_LOG << "User canceled the setup process";
- std::move(done_callback_).Run(false);
- }
- void SetUpProcess::PollUrlForwarderSetupState() {
- if (IsUrlForwarderSetUp()) {
- std::move(done_callback_).Run(true);
- return;
- }
- if (total_poll_time_ >= kPollingTimeout) {
- LOG(ERROR)
- << "Timed out waiting for the URL forwarder to become the default app";
- std::move(done_callback_).Run(false);
- return;
- }
- total_poll_time_ += kPollingInterval;
- base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
- FROM_HERE,
- base::BindOnce(&SetUpProcess::PollUrlForwarderSetupState,
- base::Unretained(this)),
- kPollingInterval);
- }
- } // namespace
- int UrlForwarderConfiguratorMain() {
- base::ThreadPoolInstance::CreateAndStartWithDefaultParams(
- "UrlForwarderConfigurator");
- base::SingleThreadTaskExecutor task_executor(base::MessagePumpType::UI);
- base::win::ScopedCOMInitializer com;
- if (base::CommandLine::ForCurrentProcess()->HasSwitch(
- kSetUpUrlForwarderSwitchName)) {
- base::RunLoop run_loop;
- SetUpProcess set_up_process;
- bool success = false;
- set_up_process.Start(base::BindOnce(
- [](base::OnceClosure quit_closure, bool& out_success, bool success) {
- out_success = success;
- std::move(quit_closure).Run();
- },
- run_loop.QuitClosure(), std::ref(success)));
- run_loop.Run();
- return success ? EXIT_SUCCESS : EXIT_FAILURE;
- }
- // The default action is to check if the URL forwarder has been properly set
- // up.
- return IsUrlForwarderSetUp(/* log_current_default_app= */ true)
- ? EXIT_SUCCESS
- : EXIT_FAILURE;
- }
- } // namespace remoting
|