blob: fc446b835408f07c2ebe7d8030762575bd4d6f67 [file] [log] [blame]
// Copyright 2019 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 "components/previews/content/previews_optimization_guide_decider.h"
#include <utility>
#include "components/optimization_guide/hints_processing_util.h"
#include "components/optimization_guide/optimization_guide_decider.h"
#include "components/optimization_guide/proto/hints.pb.h"
#include "components/previews/content/previews_user_data.h"
#include "components/previews/core/previews_experiments.h"
#include "components/previews/core/previews_switches.h"
#include "content/public/browser/navigation_handle.h"
namespace previews {
namespace {
// The default max size of the cache holding resource loading hints by URL.
size_t kDefaultMaxResourceLoadingHintsCacheSize = 10;
// Returns base::nullopt if |previews_type| can't be converted.
base::Optional<optimization_guide::proto::OptimizationType>
ConvertPreviewsTypeToOptimizationType(PreviewsType previews_type) {
switch (previews_type) {
case PreviewsType::NONE:
return optimization_guide::proto::OPTIMIZATION_NONE;
case PreviewsType::NOSCRIPT:
return optimization_guide::proto::NOSCRIPT;
case PreviewsType::UNSPECIFIED:
return optimization_guide::proto::TYPE_UNSPECIFIED;
case PreviewsType::RESOURCE_LOADING_HINTS:
return optimization_guide::proto::RESOURCE_LOADING;
case PreviewsType::LITE_PAGE_REDIRECT:
return optimization_guide::proto::LITE_PAGE_REDIRECT;
case PreviewsType::DEFER_ALL_SCRIPT:
return optimization_guide::proto::DEFER_ALL_SCRIPT;
default:
return base::nullopt;
}
}
// Returns the optimization types to register with the Optimization Guide
// Decider based on which Previews are enabled for the session.
std::unordered_set<optimization_guide::proto::OptimizationType>
GetOptimizationTypesToRegister() {
std::unordered_set<optimization_guide::proto::OptimizationType>
optimization_types;
if (params::IsNoScriptPreviewsEnabled())
optimization_types.insert(optimization_guide::proto::NOSCRIPT);
if (params::IsResourceLoadingHintsEnabled())
optimization_types.insert(optimization_guide::proto::RESOURCE_LOADING);
if (params::IsLitePageServerPreviewsEnabled())
optimization_types.insert(optimization_guide::proto::LITE_PAGE_REDIRECT);
if (params::IsDeferAllScriptPreviewsEnabled())
optimization_types.insert(optimization_guide::proto::DEFER_ALL_SCRIPT);
return optimization_types;
}
// Parses |resource_loading_hints| and returns a vector of resource patterns
// that can be blocked.
std::vector<std::string> GetResourcePatternsToBlock(
const google::protobuf::RepeatedPtrField<
optimization_guide::proto::ResourceLoadingHint>&
resource_loading_hints) {
std::vector<std::string> resource_patterns_to_block;
for (const auto& resource_loading_hint : resource_loading_hints) {
if (!resource_loading_hint.resource_pattern().empty() &&
resource_loading_hint.loading_optimization_type() ==
optimization_guide::proto::LOADING_BLOCK_RESOURCE) {
resource_patterns_to_block.push_back(
resource_loading_hint.resource_pattern());
}
}
return resource_patterns_to_block;
}
} // namespace
PreviewsOptimizationGuideDecider::PreviewsOptimizationGuideDecider(
optimization_guide::OptimizationGuideDecider* optimization_guide_decider)
: optimization_guide_decider_(optimization_guide_decider),
resource_loading_hints_cache_(kDefaultMaxResourceLoadingHintsCacheSize),
registered_optimization_types_(GetOptimizationTypesToRegister()) {
DCHECK(optimization_guide_decider_);
optimization_guide_decider_->RegisterOptimizationTypes(
std::vector<optimization_guide::proto::OptimizationType>(
registered_optimization_types_.begin(),
registered_optimization_types_.end()));
}
PreviewsOptimizationGuideDecider::~PreviewsOptimizationGuideDecider() = default;
bool PreviewsOptimizationGuideDecider::IsReady() const {
// TODO(crbug/969558): Figure out what to do here. The behavior in the old
// version was to check if the hints were initialized, but that detail is
// opaque to us at this level.
return true;
}
bool PreviewsOptimizationGuideDecider::CanApplyPreview(
PreviewsUserData* previews_data,
content::NavigationHandle* navigation_handle,
PreviewsType type) {
// See if we need to bypass the lite page redirect blacklist.
if (type == PreviewsType::LITE_PAGE_REDIRECT &&
params::LitePageRedirectPreviewIgnoresOptimizationGuideFilter()) {
return true;
}
base::Optional<optimization_guide::proto::OptimizationType>
optimization_type = ConvertPreviewsTypeToOptimizationType(type);
if (!optimization_type.has_value())
return false;
// See if we can apply the optimization. Note that
// |optimization_guide_decider_| also ensures that the current browser
// conditions match a painful page load as a prerequisite for returning true.
optimization_guide::OptimizationMetadata optimization_metadata;
optimization_guide::OptimizationGuideDecision decision =
optimization_guide_decider_->CanApplyOptimization(
navigation_handle,
optimization_guide::OptimizationTarget::kPainfulPageLoad,
*optimization_type, &optimization_metadata);
// Return false if we are even unsure if we can apply the optimization (i.e.
// hint not loaded yet or just not applicable).
if (decision != optimization_guide::OptimizationGuideDecision::kTrue)
return false;
// If we can apply it, populate information from metadata.
if (previews_data &&
optimization_metadata.previews_metadata.has_inflation_percent()) {
previews_data->set_data_savings_inflation_percent(
optimization_metadata.previews_metadata.inflation_percent());
}
if (optimization_metadata.previews_metadata.resource_loading_hints_size() >
0) {
resource_loading_hints_cache_.Put(
navigation_handle->GetURL(),
GetResourcePatternsToBlock(
optimization_metadata.previews_metadata.resource_loading_hints()));
}
return true;
}
bool PreviewsOptimizationGuideDecider::MaybeLoadOptimizationHints(
content::NavigationHandle* navigation_handle,
base::OnceClosure callback) {
// We use this method as a way of enforcing some sort of preview ordering.
// Thus, we check if we can potentially apply any of the client-side previews,
// and if any of them potentially can be applied, then we return true.
const std::vector<optimization_guide::proto::OptimizationType>
optimization_types_to_check = {
optimization_guide::proto::DEFER_ALL_SCRIPT,
optimization_guide::proto::RESOURCE_LOADING,
optimization_guide::proto::NOSCRIPT};
bool might_have_hint = false;
for (const auto optimization_type : optimization_types_to_check) {
// Don't check for the hint if the optimization type is not enabled.
if (registered_optimization_types_.find(optimization_type) ==
registered_optimization_types_.end()) {
continue;
}
if (optimization_guide_decider_->CanApplyOptimization(
navigation_handle,
optimization_guide::OptimizationTarget::kPainfulPageLoad,
optimization_type,
/*optimization_metadata=*/nullptr) !=
optimization_guide::OptimizationGuideDecision::kFalse) {
might_have_hint = true;
break;
}
}
std::move(callback).Run();
return might_have_hint;
}
bool PreviewsOptimizationGuideDecider::GetResourceLoadingHints(
const GURL& url,
std::vector<std::string>* out_resource_patterns_to_block) {
auto rlh_it = resource_loading_hints_cache_.Get(url);
if (rlh_it == resource_loading_hints_cache_.end())
return false;
*out_resource_patterns_to_block = rlh_it->second;
return true;
}
void PreviewsOptimizationGuideDecider::LogHintCacheMatch(
const GURL& url,
bool is_committed) const {
// Intentionally unimplemented: This is handled directly by the
// OptimizationGuideDecider.
}
void PreviewsOptimizationGuideDecider::ClearFetchedHints() {
// Intentionally unimplemented: This is handled directly by the
// OptimizationGuideDecider.
}
} // namespace previews