blob: 70c1d9761f038271f9162a79a28c3b0fdddb4553 [file] [log] [blame]
// Copyright 2024 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/tpcd/support/top_level_trial_service.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/document_user_data.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/origin_trials_controller_delegate.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/storage_partition.h"
#include "net/base/features.h"
#include "services/network/public/mojom/cookie_manager.mojom.h"
namespace tpcd::trial {
namespace {
const char kTrialName[] = "TopLevelTpcd";
} // namespace
TopLevelTrialService::TopLevelTrialService(
content::BrowserContext* browser_context)
: browser_context_(browser_context) {
ot_controller_ = browser_context->GetOriginTrialsControllerDelegate();
if (ot_controller_) {
ot_controller_->AddObserver(this);
}
}
TopLevelTrialService::~TopLevelTrialService() = default;
void TopLevelTrialService::Shutdown() {
if (ot_controller_) {
ot_controller_->RemoveObserver(this);
}
ot_controller_ = nullptr;
browser_context_ = nullptr;
}
void TopLevelTrialService::UpdateTopLevelTrialSettingsForTesting(
const url::Origin& origin,
bool match_subdomains,
bool enabled) {
UpdateTopLevelTrialSettings(origin, match_subdomains, enabled);
}
void TopLevelTrialService::UpdateTopLevelTrialSettings(
const url::Origin& origin,
bool includes_subdomains,
bool enabled) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
HostContentSettingsMap* settings_map =
HostContentSettingsMapFactory::GetForProfile(browser_context_);
CHECK(settings_map);
const GURL origin_as_url = origin.GetURL();
// Check for an existing `TOP_LEVEL_TPCD_TRIAL` setting that allows origin.
content_settings::SettingInfo existing_setting_info;
bool setting_exists =
(settings_map->GetContentSetting(
origin_as_url, origin_as_url,
ContentSettingsType::TOP_LEVEL_TPCD_TRIAL,
&existing_setting_info) == CONTENT_SETTING_ALLOW) &&
(existing_setting_info.primary_pattern.HasDomainWildcard() ==
includes_subdomains) &&
!existing_setting_info.primary_pattern.MatchesAllHosts();
// If the trial status matches existing settings, there is no need to
// update `settings_map`.
if (enabled == setting_exists) {
return;
}
ContentSettingsPattern primary_setting_pattern;
ContentSettingsPattern secondary_setting_pattern =
ContentSettingsPattern::Wildcard();
if (includes_subdomains) {
primary_setting_pattern = ContentSettingsPattern::FromURL(origin_as_url);
} else {
// In this case, the combination of `primary_setting_pattern` and
// `secondary_setting_pattern` is equivalent to
// `ContentSettingsType::TOP_LEVEL_TPCD_TRIAL`'s default scope
// (`TOP_ORIGIN_ONLY_SCOPE`).
primary_setting_pattern =
ContentSettingsPattern::FromURLNoWildcard(origin_as_url);
}
if (enabled) {
settings_map->SetContentSettingCustomScope(
primary_setting_pattern, secondary_setting_pattern,
ContentSettingsType::TOP_LEVEL_TPCD_TRIAL, CONTENT_SETTING_ALLOW);
} else {
CHECK(setting_exists);
// Remove settings for expired/unused pairs to avoid memory bloat.
auto matches_pair =
[&](const ContentSettingPatternSource& setting) -> bool {
return (setting.primary_pattern ==
existing_setting_info.primary_pattern) &&
(setting.secondary_pattern ==
existing_setting_info.secondary_pattern);
};
settings_map->ClearSettingsForOneTypeWithPredicate(
ContentSettingsType::TOP_LEVEL_TPCD_TRIAL, matches_pair);
}
ContentSettingsForOneType trial_settings =
settings_map->GetSettingsForOneType(
ContentSettingsType::TOP_LEVEL_TPCD_TRIAL);
browser_context_->GetDefaultStoragePartition()
->GetCookieManagerForBrowserProcess()
->SetContentSettings(ContentSettingsType::TOP_LEVEL_TPCD_TRIAL,
std::move(trial_settings), base::NullCallback());
}
void TopLevelTrialService::ClearTopLevelTrialSettings() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
HostContentSettingsMap* settings_map =
HostContentSettingsMapFactory::GetForProfile(browser_context_);
CHECK(settings_map);
settings_map->ClearSettingsForOneType(
ContentSettingsType::TOP_LEVEL_TPCD_TRIAL);
}
void TopLevelTrialService::OnStatusChanged(const url::Origin& origin,
const std::string& partition_site,
bool includes_subdomains,
bool enabled) {
// TopLevelTpcd is a first-party trial, so the |partition_site| can be ignored
// (and should always be same-site with the |origin| anyway).
UpdateTopLevelTrialSettings(origin, includes_subdomains, enabled);
}
void TopLevelTrialService::OnPersistedTokensCleared() {
ClearTopLevelTrialSettings();
}
std::string TopLevelTrialService::trial_name() {
return kTrialName;
}
} // namespace tpcd::trial