blob: 147d87ac33949782d310e344cf2f52523905229c [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/default_browser/default_browser_manager.h"
#include <algorithm>
#include <array>
#include <memory>
#include <string>
#include <utility>
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/memory/scoped_refptr.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "chrome/browser/default_browser/default_browser_features.h"
#include "chrome/browser/default_browser/default_browser_monitor.h"
#include "chrome/browser/default_browser/setters/shell_integration_default_browser_setter.h"
#include "chrome/browser/shell_integration.h"
#include "url/gurl.h"
#if BUILDFLAG(IS_WIN)
#include "base/win/registry.h"
#endif // BUILDFLAG(IS_WIN)
namespace {
class ShellDelegateImpl
: public default_browser::DefaultBrowserManager::ShellDelegate {
public:
ShellDelegateImpl() = default;
~ShellDelegateImpl() override = default;
ShellDelegateImpl(const ShellDelegateImpl&) = delete;
ShellDelegateImpl& operator=(const ShellDelegateImpl&) = delete;
void StartCheckIsDefault(
shell_integration::DefaultWebClientWorkerCallback callback) override {
auto worker =
base::MakeRefCounted<shell_integration::DefaultBrowserWorker>();
worker->StartCheckIsDefault(std::move(callback));
}
#if BUILDFLAG(IS_WIN)
void StartCheckDefaultClientProgId(
const GURL& scheme,
base::OnceCallback<void(const std::u16string&)> callback) override {
auto worker =
base::MakeRefCounted<shell_integration::DefaultSchemeClientWorker>(
scheme);
worker->StartCheckIsDefaultAndGetDefaultClientProgId(base::BindOnce(
[](base::OnceCallback<void(const std::u16string&)>
prog_id_handle_callback,
shell_integration::DefaultWebClientState,
const std::u16string& prog_id) {
std::move(prog_id_handle_callback).Run(prog_id);
},
std::move(callback)));
}
#endif // BUILDFLAG(IS_WIN)
};
// UMA enum for logging browser state validation result.
//
// LINT.IfChange(DefaultBrowserStateValidationResult)
enum class DefaultBrowserStateValidationResult {
kTruePositive = 0,
kTrueNegative = 1,
kFalsePositive = 2,
kFalseNegative = 3,
kMaxValue = kFalseNegative
};
// LINT.ThenChange(//tools/metrics/histograms/metadata/ui/enums.xml:DefaultBrowserStateValidationResult)
#if BUILDFLAG(IS_WIN)
constexpr wchar_t kHttpUserChoiceKeyPath[] =
L"Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\htt"
L"p\\UserChoice";
std::wstring GetProgIdFromRegistry(const wchar_t user_choice_path[]) {
const wchar_t kProgIdEntryName[] = L"ProgId";
base::win::RegKey key;
std::wstring prog_id;
// First, check the current user's choice.
if (key.Open(HKEY_CURRENT_USER, user_choice_path, KEY_READ) ==
ERROR_SUCCESS) {
if (key.ReadValue(kProgIdEntryName, &prog_id) == ERROR_SUCCESS) {
return prog_id;
}
}
return L"";
}
// Returns whether a give program ID belongs to Chrome.
constexpr bool IsProgIdChrome(const std::u16string& prog_id) {
constexpr std::array<std::u16string_view, 5> kChromeProgIds = {
u"ChromeHTML", u"ChromeBHTML", u"ChromeDHTML", u"ChromeSSHTM",
u"ChromiumHTM"};
return std::find(kChromeProgIds.begin(), kChromeProgIds.end(), prog_id) !=
kChromeProgIds.end();
}
// Reports whether the default browser state matches the current default program
// ID for HTTP.
void CompareHttpProgIdWithDefaultState(
default_browser::DefaultBrowserState default_state,
const std::string_view histogram_name,
const std::u16string& http_prog_id) {
CHECK(default_state == shell_integration::IS_DEFAULT ||
default_state == shell_integration::NOT_DEFAULT);
const bool is_http_prog_id_chrome = IsProgIdChrome(http_prog_id);
const bool is_default = default_state == shell_integration::IS_DEFAULT;
DefaultBrowserStateValidationResult result;
if (is_default && is_http_prog_id_chrome) {
result = DefaultBrowserStateValidationResult::kTruePositive;
} else if (!is_default && !is_http_prog_id_chrome) {
result = DefaultBrowserStateValidationResult::kTrueNegative;
} else if (is_default && !is_http_prog_id_chrome) {
result = DefaultBrowserStateValidationResult::kFalsePositive;
} else {
result = DefaultBrowserStateValidationResult::kFalseNegative;
}
base::UmaHistogramEnumeration(histogram_name, result);
}
#endif // BUILDFLAG(IS_WIN)
} // namespace
namespace default_browser {
DefaultBrowserManager::ShellDelegate::~ShellDelegate() = default;
// Static
std::unique_ptr<DefaultBrowserManager::ShellDelegate>
DefaultBrowserManager::CreateDefaultDelegate() {
return std::make_unique<ShellDelegateImpl>();
}
DefaultBrowserManager::DefaultBrowserManager(
std::unique_ptr<ShellDelegate> shell_delegate)
: shell_delegate_(std::move(shell_delegate)) {}
DefaultBrowserManager::~DefaultBrowserManager() = default;
// Static
std::unique_ptr<DefaultBrowserController>
DefaultBrowserManager::CreateControllerFor(
DefaultBrowserEntrypointType entrypoint) {
return std::make_unique<DefaultBrowserController>(
std::make_unique<ShellIntegrationDefaultBrowserSetter>(), entrypoint);
}
void DefaultBrowserManager::GetDefaultBrowserState(
DefaultBrowserCheckCompletionCallback callback) {
shell_delegate_->StartCheckIsDefault(
base::BindOnce(&DefaultBrowserManager::OnDefaultBrowserCheckResult,
base::Unretained(this), std::move(callback)));
}
void DefaultBrowserManager::OnDefaultBrowserCheckResult(
default_browser::DefaultBrowserCheckCompletionCallback callback,
default_browser::DefaultBrowserState default_state) {
// Only consider performing the secondary check for telemetry if there was a
// definitive result on default browser state.
if (default_state == shell_integration::IS_DEFAULT ||
default_state == shell_integration::NOT_DEFAULT) {
if (base::FeatureList::IsEnabled(
default_browser::kPerformDefaultBrowserCheckValidations)) {
PerformDefaultBrowserCheckValidations(default_state);
}
}
std::move(callback).Run(default_state);
}
void DefaultBrowserManager::PerformDefaultBrowserCheckValidations(
default_browser::DefaultBrowserState default_state) {
#if BUILDFLAG(IS_WIN)
shell_delegate_->StartCheckDefaultClientProgId(
GURL("http://"),
base::BindOnce(&CompareHttpProgIdWithDefaultState, default_state,
"DefaultBrowser.HttpProgIdAssocValidationResult"));
base::ThreadPool::PostTask(
FROM_HERE, {base::TaskPriority::BEST_EFFORT, base::MayBlock()},
base::BindOnce(
[](default_browser::DefaultBrowserState default_state) {
std::wstring prog_id_from_registry =
GetProgIdFromRegistry(kHttpUserChoiceKeyPath);
CompareHttpProgIdWithDefaultState(
default_state,
"DefaultBrowser.HttpProgIdRegistryValidationResult",
base::WideToUTF16(prog_id_from_registry));
},
default_state));
#endif // BUILDFLAG(IS_WIN)
}
base::CallbackListSubscription
DefaultBrowserManager::RegisterDefaultBrowserChanged(
base::RepeatingClosure callback) {
if (!monitor_) {
monitor_ = std::make_unique<DefaultBrowserMonitor>();
monitor_->StartMonitor();
}
return monitor_->RegisterDefaultBrowserChanged(std::move(callback));
}
} // namespace default_browser