| // Copyright 2023 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/browser/preloading/preloading_config.h" |
| |
| #include <string_view> |
| |
| #include "base/json/json_reader.h" |
| #include "base/metrics/field_trial_params.h" |
| #include "base/values.h" |
| #include "content/browser/preloading/preloading.h" |
| #include "content/common/features.h" |
| #include "content/public/browser/preloading.h" |
| |
| namespace content { |
| |
| namespace { |
| |
| // Allows configuring preloading features via a JSON string. This string should |
| // contain a JSON array of objects. Each object should specify a preloading_type |
| // key (a string to specify which preloading type is being configured) and a |
| // predictor key (a string to specify which predictor is being configured). Then |
| // each object can specify some parameters to tune. Supported parameters are: |
| // * holdback: whether this preloading_type, predictor combination should be |
| // held back for counterfactual evaluation. |
| // * sampling_likelihood: the fraction of preloading attempts that will be |
| // logged in UKM. See crbug.com/1411841#c3 to see how the sampling_likelihood |
| // default values are determined. |
| constexpr base::FeatureParam<std::string> kPreloadingConfigParam{ |
| &features::kPreloadingConfig, "preloading_config", R"( |
| [{ |
| "preloading_type": "NoStatePrefetch", |
| "preloading_predictor": "LinkRel", |
| "sampling_likelihood": 0.006667 |
| }, { |
| "preloading_type": "Preconnect", |
| "preloading_predictor": "PointerDownOnAnchor", |
| "sampling_likelihood": 0.000113 |
| }, { |
| "preloading_type": "Prefetch", |
| "preloading_predictor": "DefaultSearchEngine", |
| "sampling_likelihood": 0.006505 |
| }, { |
| "preloading_type": "Prefetch", |
| "preloading_predictor": "OmniboxMousePredictor", |
| "sampling_likelihood": 1.000000 |
| }, { |
| "preloading_type": "Prefetch", |
| "preloading_predictor": "OmniboxSearchPredictor", |
| "sampling_likelihood": 1.000000 |
| }, { |
| "preloading_type": "Prefetch", |
| "preloading_predictor": "OmniboxTouchDownPredirector", |
| "sampling_likelihood": 0.011505 |
| }, { |
| "preloading_type": "Prefetch", |
| "preloading_predictor": "SpeculationRules", |
| "sampling_likelihood": 0.001356 |
| }, { |
| "preloading_type": "Prefetch", |
| "preloading_predictor": "SpeculationRulesFromIsolatedWorld", |
| "sampling_likelihood": 1.000000 |
| }, { |
| "preloading_type": "Prefetch", |
| "preloading_predictor": "UrlPointerDownOnAnchor", |
| "sampling_likelihood": 0.027996 |
| }, { |
| "preloading_type": "Prefetch", |
| "preloading_predictor": "UrlPointerHoverOnAnchor", |
| "sampling_likelihood": 0.029942 |
| }, { |
| "preloading_type": "Prerender", |
| "preloading_predictor": "BackButtonHover", |
| "sampling_likelihood": 0.007192 |
| }, { |
| "preloading_type": "Prerender", |
| "preloading_predictor": "BackGestureNavigation", |
| "sampling_likelihood": 0.232383 |
| }, { |
| "preloading_type": "Prerender", |
| "preloading_predictor": "DefaultSearchEngine", |
| "sampling_likelihood": 0.006344 |
| }, { |
| "preloading_type": "Prerender", |
| "preloading_predictor": "MouseBackButton", |
| "sampling_likelihood": 0.076308 |
| }, { |
| "preloading_type": "Prerender", |
| "preloading_predictor": "MouseHoverOrMouseDownOnBookmarkBar", |
| "sampling_likelihood": 0.023874 |
| }, { |
| "preloading_type": "Prerender", |
| "preloading_predictor": "MouseHoverOrMouseDownOnNewTabPage", |
| "sampling_likelihood": 0.033195 |
| }, { |
| "preloading_type": "Prerender", |
| "preloading_predictor": "OmniboxDirectURLInput", |
| "sampling_likelihood": 0.005746 |
| }, { |
| "preloading_type": "Prerender", |
| "preloading_predictor": "SpeculationRules", |
| "sampling_likelihood": 0.067464 |
| }, { |
| "preloading_type": "Prerender", |
| "preloading_predictor": "SpeculationRulesFromIsolatedWorld", |
| "sampling_likelihood": 1.000000 |
| }, { |
| "preloading_type": "Prerender", |
| "preloading_predictor": "TouchOnNewTabPage", |
| "sampling_likelihood": 0.034646 |
| }, { |
| "preloading_type": "Prerender", |
| "preloading_predictor": "UrlPointerDownOnAnchor", |
| "sampling_likelihood": 0.194379 |
| }, { |
| "preloading_type": "Prerender", |
| "preloading_predictor": "UrlPointerHoverOnAnchor", |
| "sampling_likelihood": 0.788890 |
| }] |
| )"}; |
| |
| static PreloadingConfig* g_config_override = nullptr; |
| |
| } // namespace |
| |
| PreloadingConfig& PreloadingConfig::GetInstance() { |
| static base::NoDestructor<PreloadingConfig> config; |
| static bool initialized = false; |
| if (!initialized) { |
| config->ParseConfig(); |
| initialized = true; |
| } |
| |
| if (g_config_override) { |
| return *g_config_override; |
| } |
| return *config; |
| } |
| |
| PreloadingConfig::PreloadingConfig() = default; |
| |
| PreloadingConfig* PreloadingConfig::OverrideForTesting( |
| PreloadingConfig* config_override) { |
| raw_ptr<PreloadingConfig> old_override = g_config_override; |
| g_config_override = config_override; |
| return old_override; |
| } |
| |
| void PreloadingConfig::ParseConfig() { |
| entries_.clear(); |
| |
| if (!base::FeatureList::IsEnabled(features::kPreloadingConfig)) { |
| return; |
| } |
| // Throughout parsing the config, if we fail to parse, we silently skip the |
| // config and use the default values. |
| std::optional<base::Value> config_value = |
| base::JSONReader::Read(kPreloadingConfigParam.Get()); |
| if (!config_value) { |
| return; |
| } |
| base::Value::List* entries = config_value->GetIfList(); |
| if (!entries) { |
| return; |
| } |
| |
| for (const base::Value& entry : *entries) { |
| const base::Value::Dict* config_dict = entry.GetIfDict(); |
| DCHECK(config_dict); |
| if (!config_dict) { |
| continue; |
| } |
| |
| const std::string* preloading_type = |
| config_dict->FindString("preloading_type"); |
| DCHECK(preloading_type); |
| if (!preloading_type) { |
| continue; |
| } |
| |
| const std::string* preloading_predictor = |
| config_dict->FindString("preloading_predictor"); |
| DCHECK(preloading_predictor); |
| if (!preloading_predictor) { |
| continue; |
| } |
| |
| entries_.emplace(Key(*preloading_type, *preloading_predictor), |
| Entry::FromDict(config_dict)); |
| } |
| } |
| |
| PreloadingConfig::~PreloadingConfig() = default; |
| |
| bool PreloadingConfig::ShouldHoldback(PreloadingType preloading_type, |
| PreloadingPredictor predictor) { |
| Entry entry = entries_[Key::FromEnums(preloading_type, predictor)]; |
| return entry.holdback_; |
| } |
| |
| void PreloadingConfig::SetHoldbackForTesting(PreloadingType preloading_type, |
| PreloadingPredictor predictor, |
| bool holdback) { |
| Entry entry; |
| entry.holdback_ = holdback; |
| entries_.emplace( |
| Key(PreloadingTypeToString(preloading_type), predictor.name()), entry); |
| } |
| |
| void PreloadingConfig::SetHoldbackForTesting(std::string_view preloading_type, |
| std::string_view predictor, |
| bool holdback) { |
| Entry entry; |
| entry.holdback_ = holdback; |
| entries_.emplace(Key(preloading_type, predictor), entry); |
| } |
| |
| double PreloadingConfig::SamplingLikelihood(PreloadingType preloading_type, |
| PreloadingPredictor predictor) { |
| Entry entry = entries_[Key::FromEnums(preloading_type, predictor)]; |
| return entry.sampling_likelihood_; |
| } |
| |
| PreloadingConfig::Key::Key(std::string_view preloading_type, |
| std::string_view predictor) |
| : preloading_type_(preloading_type), predictor_(predictor) {} |
| |
| PreloadingConfig::Key PreloadingConfig::Key::FromEnums( |
| PreloadingType preloading_type, |
| PreloadingPredictor predictor) { |
| return Key(PreloadingTypeToString(preloading_type), predictor.name()); |
| } |
| |
| PreloadingConfig::Entry PreloadingConfig::Entry::FromDict( |
| const base::Value::Dict* dict) { |
| Entry entry; |
| std::optional<bool> holdback = dict->FindBool("holdback"); |
| if (holdback) { |
| entry.holdback_ = *holdback; |
| } |
| std::optional<double> sampling_likelihood = |
| dict->FindDouble("sampling_likelihood"); |
| if (sampling_likelihood) { |
| entry.sampling_likelihood_ = *sampling_likelihood; |
| } |
| return entry; |
| } |
| |
| bool PreloadingConfig::KeyCompare::operator()( |
| const PreloadingConfig::Key& lhs, |
| const PreloadingConfig::Key& rhs) const { |
| return std::tie(lhs.preloading_type_, lhs.predictor_) < |
| std::tie(rhs.preloading_type_, rhs.predictor_); |
| } |
| |
| } // namespace content |