Commit 11202527 authored by Doug Arnett's avatar Doug Arnett Committed by Commit Bot

Adds HintCache to hold PageHint based Cacao hints

This only holds the hint data for now. Another CL to follow will use it.
Moves the recently added activation list into the hint cache as well.

Bug: 867651
Change-Id: I188cab0b8539439d859186eecc8185a66ba34efb
Reviewed-on: https://chromium-review.googlesource.com/1150739
Commit-Queue: Doug Arnett <dougarnett@chromium.org>
Reviewed-by: default avatarTarun Bansal <tbansal@chromium.org>
Cr-Commit-Position: refs/heads/master@{#579842}
parent 23823a1d
...@@ -4,8 +4,8 @@ ...@@ -4,8 +4,8 @@
static_library("content") { static_library("content") {
sources = [ sources = [
"activation_list.cc", "hint_cache.cc",
"activation_list.h", "hint_cache.h",
"previews_content_util.cc", "previews_content_util.cc",
"previews_content_util.h", "previews_content_util.h",
"previews_decider_impl.cc", "previews_decider_impl.cc",
...@@ -34,7 +34,7 @@ static_library("content") { ...@@ -34,7 +34,7 @@ static_library("content") {
source_set("unit_tests") { source_set("unit_tests") {
testonly = true testonly = true
sources = [ sources = [
"activation_list_unittest.cc", "hint_cache_unittest.cc",
"previews_content_util_unittest.cc", "previews_content_util_unittest.cc",
"previews_decider_impl_unittest.cc", "previews_decider_impl_unittest.cc",
"previews_optimization_guide_unittest.cc", "previews_optimization_guide_unittest.cc",
......
// Copyright 2018 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/activation_list.h"
#include <set>
#include "base/metrics/histogram_macros.h"
#include "url/gurl.h"
namespace previews {
ActivationList::ActivationList(const std::vector<std::string>& url_suffixes) {
url_matcher_ = std::make_unique<url_matcher::URLMatcher>();
url_matcher::URLMatcherConditionSet::Vector all_conditions;
url_matcher::URLMatcherConditionFactory* condition_factory =
url_matcher_->condition_factory();
int id = 0;
for (const auto& url_suffix : url_suffixes) {
DCHECK(!url_suffix.empty());
DCHECK_EQ(std::string::npos, url_suffix.find("/"));
url_matcher::URLMatcherCondition condition =
condition_factory->CreateHostSuffixCondition(url_suffix);
all_conditions.push_back(new url_matcher::URLMatcherConditionSet(
id, std::set<url_matcher::URLMatcherCondition>{condition}));
++id;
}
url_matcher_->AddConditionSets(all_conditions);
DETACH_FROM_SEQUENCE(sequence_checker_);
}
ActivationList::~ActivationList() {}
bool ActivationList::IsHostWhitelistedAtNavigation(const GURL& url,
PreviewsType type) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(PreviewsType::RESOURCE_LOADING_HINTS, type);
DCHECK(url.is_valid());
DCHECK(url.SchemeIsHTTPOrHTTPS());
if (!url_matcher_)
return false;
const std::set<url_matcher::URLMatcherConditionSet::ID>& matches =
url_matcher_->MatchURL(url);
// Only consider the first match in iteration order as it takes precedence
// if there are multiple matches.
return (matches.begin() != matches.end());
}
} // namespace previews
\ No newline at end of file
// Copyright 2018 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.
#ifndef COMPONENTS_PREVIEWS_CONTENT_ACTIVATION_LIST_H_
#define COMPONENTS_PREVIEWS_CONTENT_ACTIVATION_LIST_H_
#include <memory>
#include <string>
#include <vector>
#include "base/macros.h"
#include "base/sequence_checker.h"
#include "components/optimization_guide/proto/hints.pb.h"
#include "components/previews/core/previews_experiments.h"
#include "components/previews/core/previews_user_data.h"
#include "components/url_matcher/url_matcher.h"
class GURL;
namespace previews {
// Holds the activation list of hosts extracted from the server hints
// configuration.
class ActivationList {
public:
// Adds |url_suffixes| to |url_matcher_| for resource loading hints
// optimization.
explicit ActivationList(const std::vector<std::string>& url_suffixes);
~ActivationList();
// Whether |url| is whitelisted for previews type |type|. The method may
// make the decision based only on a partial comparison (e.g., only the
// hostname of |url|). As such, the method may return true even if |type|
// can't be used for the previews.
bool IsHostWhitelistedAtNavigation(const GURL& url, PreviewsType type) const;
private:
// The URLMatcher used to match whether a URL has any previews whitelisted
// with it.
std::unique_ptr<url_matcher::URLMatcher> url_matcher_;
SEQUENCE_CHECKER(sequence_checker_);
DISALLOW_COPY_AND_ASSIGN(ActivationList);
};
} // namespace previews
#endif // COMPONENTS_PREVIEWS_CONTENT_ACTIVATION_LIST_H_
// Copyright 2018 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/activation_list.h"
#include <string>
#include <vector>
#include "base/macros.h"
#include "components/previews/core/previews_experiments.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace previews {
namespace {
class ActivationListTest : public testing::Test {
public:
ActivationListTest() {}
~ActivationListTest() override {}
protected:
std::unique_ptr<ActivationList> page_host_activation_list_;
DISALLOW_COPY_AND_ASSIGN(ActivationListTest);
};
TEST_F(ActivationListTest, TestInitialization) {
std::vector<std::string> keys;
keys.push_back("example.com");
keys.push_back("m.foo.com");
page_host_activation_list_ = std::make_unique<ActivationList>(keys);
struct {
GURL url;
bool expect_whitelisted;
} test_cases[] = {
{GURL("https://www.example.com"), true},
{GURL("https://www.example.co.in"), false},
{GURL("https://www.example.com/index.html"), true},
{GURL("https://www.example.com/a.html"), true},
{GURL("https://www.example.com/foo/a.html"), true},
{GURL("https://m.example.com"), true},
{GURL("https://m.example.com/foo.html"), true},
{GURL("https://m.example.com/foo/bar/baz.html"), true},
{GURL("https://example.com"), true},
{GURL("https://example2.com"), false},
{GURL("https://m.example2.com"), false},
{GURL("https://example2.com/foo.html"), false},
{GURL("https://m.foo.com"), true},
{GURL("https://en.m.foo.com"), true},
{GURL("https://m.en.foo.com"), false},
{GURL("https://m.foo.com/foo.html"), true},
{GURL("https://m.foo.com/foo/bar/baz.html"), true},
{GURL("https://foo.com"), false},
{GURL("https://en.foo.com"), false},
{GURL("https://m.foo2.com"), false},
{GURL("https://en.foo2.com"), false},
};
for (const auto& test : test_cases) {
EXPECT_TRUE(test.url.is_valid());
EXPECT_EQ(test.expect_whitelisted,
page_host_activation_list_->IsHostWhitelistedAtNavigation(
test.url, PreviewsType::RESOURCE_LOADING_HINTS))
<< " url=" << test.url.spec();
}
}
} // namespace
} // namespace previews
\ No newline at end of file
// Copyright 2018 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/hint_cache.h"
#include "url/gurl.h"
namespace previews {
// Realistic minimum length of a host suffix.
const int kMinHostSuffix = 6; // eg., abc.tv
HintCache::HintCache() {
DETACH_FROM_SEQUENCE(sequence_checker_);
}
HintCache::~HintCache() {}
bool HintCache::HasHint(const std::string& host) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return !DetermineHostSuffix(host).empty();
}
void HintCache::LoadHint(const std::string& host) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// TODO(dougarnett): Add backing store support to load from.
}
bool HintCache::IsHintLoaded(const std::string& host) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::string host_suffix = DetermineHostSuffix(host);
if (host_suffix.empty()) {
return false;
}
return memory_cache_.find(host_suffix) != memory_cache_.end();
}
const optimization_guide::proto::Hint* HintCache::GetHint(
const std::string& host) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::string host_suffix = DetermineHostSuffix(host);
if (host_suffix.empty()) {
return nullptr;
}
auto it = memory_cache_.find(host_suffix);
if (it != memory_cache_.end()) {
return &it->second;
}
return nullptr;
}
void HintCache::AddHint(const optimization_guide::proto::Hint& hint) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(optimization_guide::proto::HOST_SUFFIX, hint.key_representation());
activation_list_.insert(hint.key());
// TODO(dougarnett): Limit size of memory cache.
memory_cache_[hint.key()] = hint;
}
void HintCache::AddHints(
const std::vector<optimization_guide::proto::Hint>& hints) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
for (auto hint : hints) {
AddHint(hint);
}
}
std::string HintCache::DetermineHostSuffix(const std::string& host) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::string suffix(host);
// Look for longest host name suffix that has a hint. No need to continue
// lookups and substring work once get to a root domain like ".com" or
// ".co.in" (MinHostSuffix length check is a heuristic for that).
while (suffix.length() >= kMinHostSuffix) {
if (activation_list_.find(suffix) != activation_list_.end()) {
return suffix;
}
size_t pos = suffix.find_first_of('.');
if (pos == std::string::npos) {
return std::string();
}
suffix = suffix.substr(pos + 1);
}
return std::string();
}
} // namespace previews
// Copyright 2018 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.
#ifndef COMPONENTS_PREVIEWS_CONTENT_HINT_CACHE_H_
#define COMPONENTS_PREVIEWS_CONTENT_HINT_CACHE_H_
#include <map>
#include <string>
#include <unordered_set>
#include <vector>
#include "base/macros.h"
#include "base/sequence_checker.h"
#include "components/optimization_guide/proto/hints.pb.h"
namespace previews {
// Holds a set of optimization hints received from the Cacao service.
// This may include hints received from the ComponentUpdater and hints
// fetched from a Cacao Optimization Guide Service API. It will hold
// the host suffix keys of all cached hints (aka, activation list). It
// will manage a set of the associated hints details in memory and all
// of the hints in a persisted backing store.
class HintCache {
public:
HintCache();
~HintCache();
// Returns whether the cache has a hint data for |host| locally (whether
// in memory or persisted on disk).
bool HasHint(const std::string& host) const;
// Requests that hint data for |host| be loaded from disk.
void LoadHint(const std::string& host);
// Returns whether there is hint data available for |host| in memory.
bool IsHintLoaded(const std::string& host);
// Returns the hint data for |host| found in memory, otherwise nullptr.
const optimization_guide::proto::Hint* GetHint(const std::string& host) const;
// Adds |hint| to the cache.
void AddHint(const optimization_guide::proto::Hint& hint);
// Adds |hints| to the cache.
void AddHints(const std::vector<optimization_guide::proto::Hint>& hints);
private:
// Returns the most specific host suffix of the host name that is present
// in the activation list.
std::string DetermineHostSuffix(const std::string& host) const;
// The set of host suffixes that have Hint data.
std::unordered_set<std::string> activation_list_;
// The in-memory cache of hints. Maps host suffix to Hint proto.
// TODO(dougarnett): Add MRU subset support (mru_cache) with backing store.
std::map<std::string, optimization_guide::proto::Hint> memory_cache_;
// TODO(dougarnett): Add a backing store with all hints.
SEQUENCE_CHECKER(sequence_checker_);
DISALLOW_COPY_AND_ASSIGN(HintCache);
};
} // namespace previews
#endif // COMPONENTS_PREVIEWS_CONTENT_HINT_CACHE_H_
// Copyright 2018 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/hint_cache.h"
#include <string>
#include <vector>
#include "base/macros.h"
#include "components/previews/core/previews_experiments.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace previews {
namespace {
class HintCacheTest : public testing::Test {
public:
HintCacheTest() {}
~HintCacheTest() override {}
DISALLOW_COPY_AND_ASSIGN(HintCacheTest);
};
TEST_F(HintCacheTest, TestMemoryCache) {
HintCache hint_cache;
optimization_guide::proto::Hint hint1;
hint1.set_key("subdomain.domain.org");
hint1.set_key_representation(optimization_guide::proto::HOST_SUFFIX);
optimization_guide::proto::Hint hint2;
hint2.set_key("host.domain.org");
hint2.set_key_representation(optimization_guide::proto::HOST_SUFFIX);
optimization_guide::proto::Hint hint3;
hint3.set_key("otherhost.subdomain.domain.org");
hint3.set_key_representation(optimization_guide::proto::HOST_SUFFIX);
hint_cache.AddHint(hint1);
hint_cache.AddHint(hint2);
hint_cache.AddHint(hint3);
// Not matched
EXPECT_FALSE(hint_cache.HasHint("domain.org"));
EXPECT_FALSE(hint_cache.HasHint("othersubdomain.domain.org"));
// Matched
EXPECT_TRUE(hint_cache.HasHint("otherhost.subdomain.domain.org"));
EXPECT_TRUE(hint_cache.HasHint("host.subdomain.domain.org"));
EXPECT_TRUE(hint_cache.HasHint("subhost.host.subdomain.domain.org"));
// Cached in memory
EXPECT_TRUE(hint_cache.IsHintLoaded("host.domain.org"));
EXPECT_TRUE(hint_cache.IsHintLoaded("otherhost.subdomain.domain.org"));
EXPECT_TRUE(hint_cache.IsHintLoaded("host.subdomain.domain.org"));
EXPECT_TRUE(hint_cache.IsHintLoaded("subhost.host.subdomain.domain.org"));
// Matched key
EXPECT_EQ(hint2.key(), hint_cache.GetHint("host.domain.org")->key());
EXPECT_EQ(hint3.key(),
hint_cache.GetHint("otherhost.subdomain.domain.org")->key());
EXPECT_EQ(hint1.key(),
hint_cache.GetHint("host.subdomain.domain.org")->key());
}
} // namespace
} // namespace previews
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "base/optional.h" #include "base/optional.h"
#include "components/optimization_guide/optimization_guide_service_observer.h" #include "components/optimization_guide/optimization_guide_service_observer.h"
#include "components/previews/core/previews_features.h" #include "components/previews/core/previews_features.h"
#include "url/gurl.h"
namespace previews { namespace previews {
...@@ -69,6 +70,14 @@ bool CreateSentinelFile(const base::FilePath& sentinel_path, ...@@ -69,6 +70,14 @@ bool CreateSentinelFile(const base::FilePath& sentinel_path,
return true; return true;
} }
// Deletes the sentinel file. This should be done once processing the
// configuration is complete and should be done in the background (e.g.,
// same task as Hints.CreateFromConfig).
void DeleteSentinelFile(const base::FilePath& sentinel_path) {
if (!base::DeleteFile(sentinel_path, false /* recursive */))
DLOG(ERROR) << "Error deleting sentinel file";
}
// Enumerates the possible outcomes of processing previews hints. Used in UMA // Enumerates the possible outcomes of processing previews hints. Used in UMA
// histograms, so the order of enumerators should not be changed. // histograms, so the order of enumerators should not be changed.
// //
...@@ -97,20 +106,39 @@ ConvertProtoOptimizationTypeToPreviewsOptimizationType( ...@@ -97,20 +106,39 @@ ConvertProtoOptimizationTypeToPreviewsOptimizationType(
} }
} }
// Returns whether any features using page hints are enabled.
bool ShouldProcessPageHints() {
return previews::params::IsResourceLoadingHintsEnabled();
}
bool IsDisabledExperimentalOptimization(
const optimization_guide::proto::Optimization& optimization) {
// If this optimization has been marked with an experiment name, consider it
// disabled unless an experiment with that name is running. Experiment names
// are configured with the experiment_name parameter to the
// kOptimizationHintsExperiments feature.
//
// If kOptimizationHintsExperiments is disabled, getting the param value
// returns an empty string. Since experiment names are not allowed to be
// empty strings, all experiments will be disabled if the feature is
// disabled.
if (optimization.has_experiment_name() &&
!optimization.experiment_name().empty() &&
optimization.experiment_name() !=
base::GetFieldTrialParamValueByFeature(
features::kOptimizationHintsExperiments,
features::kOptimizationHintsExperimentNameParam)) {
return true;
}
return false;
}
void RecordProcessHintsResult(PreviewsProcessHintsResult result) { void RecordProcessHintsResult(PreviewsProcessHintsResult result) {
UMA_HISTOGRAM_ENUMERATION("Previews.ProcessHintsResult", UMA_HISTOGRAM_ENUMERATION("Previews.ProcessHintsResult",
static_cast<int>(result), static_cast<int>(result),
static_cast<int>(PreviewsProcessHintsResult::MAX)); static_cast<int>(PreviewsProcessHintsResult::MAX));
} }
// Deletes the sentinel file. This should be done once processing the
// configuration is complete and should be done in the background (e.g.,
// same task as Hints.CreateFromConfig).
void DeleteSentinelFile(const base::FilePath& sentinel_path) {
if (!base::DeleteFile(sentinel_path, false /* recursive */))
DLOG(ERROR) << "Error deleting sentinel file";
}
} // namespace } // namespace
PreviewsHints::PreviewsHints() { PreviewsHints::PreviewsHints() {
...@@ -145,8 +173,6 @@ std::unique_ptr<PreviewsHints> PreviewsHints::CreateFromConfig( ...@@ -145,8 +173,6 @@ std::unique_ptr<PreviewsHints> PreviewsHints::CreateFromConfig(
url_matcher::URLMatcherConditionSet::Vector all_conditions; url_matcher::URLMatcherConditionSet::Vector all_conditions;
std::set<std::string> seen_host_suffixes; std::set<std::string> seen_host_suffixes;
std::vector<std::string> activation_list;
// Process hint configuration. // Process hint configuration.
for (const auto hint : config.hints()) { for (const auto hint : config.hints()) {
// We only support host suffixes at the moment. Skip anything else. // We only support host suffixes at the moment. Skip anything else.
...@@ -167,24 +193,11 @@ std::unique_ptr<PreviewsHints> PreviewsHints::CreateFromConfig( ...@@ -167,24 +193,11 @@ std::unique_ptr<PreviewsHints> PreviewsHints::CreateFromConfig(
seen_host_suffixes.insert(hint.key()); seen_host_suffixes.insert(hint.key());
// Create whitelist condition set out of the optimizations that are // Create whitelist condition set out of the optimizations that are
// whitelisted for the host suffix. // whitelisted for the host suffix at the top level (i.e., not within
// PageHints).
std::set<std::pair<PreviewsType, int>> whitelisted_optimizations; std::set<std::pair<PreviewsType, int>> whitelisted_optimizations;
for (const auto optimization : hint.whitelisted_optimizations()) { for (const auto optimization : hint.whitelisted_optimizations()) {
// If this optimization has been marked with an experiment name, skip it if (IsDisabledExperimentalOptimization(optimization)) {
// unless an experiment with that name is running. Experiment names are
// configured with the experiment_name parameter to the
// kOptimizationHintsExperiments feature.
//
// If kOptimizationHintsExperiments is disabled, getting the param value
// returns an empty string. Since experiment names are not allowed to be
// empty strings, all experiments will be disabled if the feature is
// disabled.
if (optimization.has_experiment_name() &&
!optimization.experiment_name().empty() &&
optimization.experiment_name() !=
base::GetFieldTrialParamValueByFeature(
features::kOptimizationHintsExperiments,
features::kOptimizationHintsExperimentNameParam)) {
continue; continue;
} }
base::Optional<PreviewsType> previews_type = base::Optional<PreviewsType> previews_type =
...@@ -193,10 +206,6 @@ std::unique_ptr<PreviewsHints> PreviewsHints::CreateFromConfig( ...@@ -193,10 +206,6 @@ std::unique_ptr<PreviewsHints> PreviewsHints::CreateFromConfig(
if (previews_type.has_value()) { if (previews_type.has_value()) {
whitelisted_optimizations.insert(std::make_pair( whitelisted_optimizations.insert(std::make_pair(
previews_type.value(), optimization.inflation_percent())); previews_type.value(), optimization.inflation_percent()));
if (previews_type == previews::PreviewsType::RESOURCE_LOADING_HINTS) {
activation_list.push_back(hint.key());
}
} }
} }
url_matcher::URLMatcherCondition condition = url_matcher::URLMatcherCondition condition =
...@@ -205,9 +214,13 @@ std::unique_ptr<PreviewsHints> PreviewsHints::CreateFromConfig( ...@@ -205,9 +214,13 @@ std::unique_ptr<PreviewsHints> PreviewsHints::CreateFromConfig(
id, std::set<url_matcher::URLMatcherCondition>{condition})); id, std::set<url_matcher::URLMatcherCondition>{condition}));
hints->whitelist_[id] = whitelisted_optimizations; hints->whitelist_[id] = whitelisted_optimizations;
id++; id++;
// Cache hints that have PageHints.
if (ShouldProcessPageHints() && !hint.page_hints().empty()) {
hints->initial_hints_.push_back(hint);
}
} }
hints->url_matcher_.AddConditionSets(all_conditions); hints->url_matcher_.AddConditionSets(all_conditions);
hints->activation_list_ = std::make_unique<ActivationList>(activation_list);
// Completed processing hints data without crashing so clear sentinel. // Completed processing hints data without crashing so clear sentinel.
DeleteSentinelFile(sentinel_path); DeleteSentinelFile(sentinel_path);
...@@ -218,6 +231,16 @@ std::unique_ptr<PreviewsHints> PreviewsHints::CreateFromConfig( ...@@ -218,6 +231,16 @@ std::unique_ptr<PreviewsHints> PreviewsHints::CreateFromConfig(
return hints; return hints;
} }
void PreviewsHints::Initialize() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!initial_hints_.empty()) {
if (!hint_cache_)
hint_cache_ = std::make_unique<HintCache>();
hint_cache_->AddHints(initial_hints_);
initial_hints_.clear();
}
}
bool PreviewsHints::IsWhitelisted(const GURL& url, bool PreviewsHints::IsWhitelisted(const GURL& url,
PreviewsType type, PreviewsType type,
int* out_inflation_percent) { int* out_inflation_percent) {
...@@ -247,18 +270,20 @@ bool PreviewsHints::IsWhitelisted(const GURL& url, ...@@ -247,18 +270,20 @@ bool PreviewsHints::IsWhitelisted(const GURL& url,
return true; return true;
} }
} }
// TODO(dougarnett): Check the hint cache for whitelisted optmizations too.
return false; return false;
} }
bool PreviewsHints::IsHostWhitelistedAtNavigation( bool PreviewsHints::MaybeLoadOptimizationHints(const GURL& url) const {
const GURL& url,
previews::PreviewsType type) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!activation_list_) if (!hint_cache_ || !url.has_host())
return false; return false;
return activation_list_->IsHostWhitelistedAtNavigation(url, type); // TODO(dougarnett): Request loading of hints if not cached in memory.
return hint_cache_->HasHint(url.host());
} }
} // namespace previews } // namespace previews
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
#include "base/macros.h" #include "base/macros.h"
#include "base/sequence_checker.h" #include "base/sequence_checker.h"
#include "components/optimization_guide/proto/hints.pb.h" #include "components/optimization_guide/proto/hints.pb.h"
#include "components/previews/content/activation_list.h" #include "components/previews/content/hint_cache.h"
#include "components/previews/content/previews_hints.h" #include "components/previews/content/previews_hints.h"
#include "components/previews/core/previews_user_data.h" #include "components/previews/core/previews_user_data.h"
#include "components/url_matcher/url_matcher.h" #include "components/url_matcher/url_matcher.h"
...@@ -35,18 +35,19 @@ class PreviewsHints { ...@@ -35,18 +35,19 @@ class PreviewsHints {
const optimization_guide::proto::Configuration& config, const optimization_guide::proto::Configuration& config,
const optimization_guide::ComponentInfo& info); const optimization_guide::ComponentInfo& info);
void Initialize();
// Whether the URL is whitelisted for the given previews type. If so, // Whether the URL is whitelisted for the given previews type. If so,
// |out_inflation_percent| will be populated if meta data available for it. // |out_inflation_percent| will be populated if meta data available for it.
// Note: this is top-level whitelist check which does not include checking
// within PageHints.
bool IsWhitelisted(const GURL& url, bool IsWhitelisted(const GURL& url,
PreviewsType type, PreviewsType type,
int* out_inflation_percent); int* out_inflation_percent);
// Whether |url| is whitelisted for previews type |type|. The method may // Returns whether |url| may have PageHints and triggers asynchronous load
// make the decision based only on a partial comparison (e.g., only the // of such hints are not currently available synchronously.
// hostname of |url|). As such, the method may return true even if |type| bool MaybeLoadOptimizationHints(const GURL& url) const;
// can't be used for the previews.
bool IsHostWhitelistedAtNavigation(const GURL& url,
previews::PreviewsType type) const;
private: private:
PreviewsHints(); PreviewsHints();
...@@ -55,9 +56,8 @@ class PreviewsHints { ...@@ -55,9 +56,8 @@ class PreviewsHints {
// it. // it.
url_matcher::URLMatcher url_matcher_; url_matcher::URLMatcher url_matcher_;
// Holds the activation list of hosts extracted from the server hints // Holds the hint cache (if any optimizations using it are enabled).
// configuration. std::unique_ptr<HintCache> hint_cache_;
std::unique_ptr<ActivationList> activation_list_;
// A map from the condition set ID to associated whitelist Optimization // A map from the condition set ID to associated whitelist Optimization
// details. // details.
...@@ -65,6 +65,8 @@ class PreviewsHints { ...@@ -65,6 +65,8 @@ class PreviewsHints {
std::set<std::pair<PreviewsType, int>>> std::set<std::pair<PreviewsType, int>>>
whitelist_; whitelist_;
std::vector<optimization_guide::proto::Hint> initial_hints_;
SEQUENCE_CHECKER(sequence_checker_); SEQUENCE_CHECKER(sequence_checker_);
DISALLOW_COPY_AND_ASSIGN(PreviewsHints); DISALLOW_COPY_AND_ASSIGN(PreviewsHints);
...@@ -72,4 +74,4 @@ class PreviewsHints { ...@@ -72,4 +74,4 @@ class PreviewsHints {
} // namespace previews } // namespace previews
#endif // COMPONENTS_PREVIEWS_CONTENT_PREVIEWS_HINTS_H_ #endif // COMPONENTS_PREVIEWS_CONTENT_PREVIEWS_HINTS_H_
\ No newline at end of file
...@@ -50,14 +50,13 @@ bool PreviewsOptimizationGuide::IsWhitelisted(const net::URLRequest& request, ...@@ -50,14 +50,13 @@ bool PreviewsOptimizationGuide::IsWhitelisted(const net::URLRequest& request,
return true; return true;
} }
bool PreviewsOptimizationGuide::IsHostWhitelistedAtNavigation( bool PreviewsOptimizationGuide::MaybeLoadOptimizationHints(
const GURL& url, const net::URLRequest& request) const {
previews::PreviewsType type) const {
DCHECK(io_task_runner_->BelongsToCurrentThread()); DCHECK(io_task_runner_->BelongsToCurrentThread());
if (!hints_) if (!hints_)
return false; return false;
return hints_->IsHostWhitelistedAtNavigation(url, type); return hints_->MaybeLoadOptimizationHints(request.url());
} }
void PreviewsOptimizationGuide::OnHintsProcessed( void PreviewsOptimizationGuide::OnHintsProcessed(
...@@ -76,6 +75,8 @@ void PreviewsOptimizationGuide::UpdateHints( ...@@ -76,6 +75,8 @@ void PreviewsOptimizationGuide::UpdateHints(
std::unique_ptr<PreviewsHints> hints) { std::unique_ptr<PreviewsHints> hints) {
DCHECK(io_task_runner_->BelongsToCurrentThread()); DCHECK(io_task_runner_->BelongsToCurrentThread());
hints_ = std::move(hints); hints_ = std::move(hints);
if (hints_)
hints_->Initialize();
} }
} // namespace previews } // namespace previews
...@@ -15,8 +15,6 @@ ...@@ -15,8 +15,6 @@
#include "components/previews/content/previews_optimization_guide.h" #include "components/previews/content/previews_optimization_guide.h"
#include "components/previews/core/previews_experiments.h" #include "components/previews/core/previews_experiments.h"
class GURL;
namespace net { namespace net {
class URLRequest; class URLRequest;
} // namespace net } // namespace net
...@@ -48,12 +46,11 @@ class PreviewsOptimizationGuide ...@@ -48,12 +46,11 @@ class PreviewsOptimizationGuide
virtual bool IsWhitelisted(const net::URLRequest& request, virtual bool IsWhitelisted(const net::URLRequest& request,
PreviewsType type) const; PreviewsType type) const;
// Whether |url| is whitelisted for previews type |type|. The method may // Returns whether |request| may have associated optimization hints
// make the decision based only on a partial comparison (e.g., only the // (specifically, PageHints). If so, but the hints are not available
// hostname of |url|). As such, the method may return true even if |type| // synchronously, this method will request that they be loaded (from disk or
// can't be used for the previews. // network).
bool IsHostWhitelistedAtNavigation(const GURL& url, bool MaybeLoadOptimizationHints(const net::URLRequest& request) const;
previews::PreviewsType type) const;
// optimization_guide::OptimizationGuideServiceObserver implementation: // optimization_guide::OptimizationGuideServiceObserver implementation:
void OnHintsProcessed( void OnHintsProcessed(
......
...@@ -102,6 +102,8 @@ class PreviewsOptimizationGuideTest : public testing::Test { ...@@ -102,6 +102,8 @@ class PreviewsOptimizationGuideTest : public testing::Test {
void DoExperimentFlagTest(base::Optional<std::string> experiment_name, void DoExperimentFlagTest(base::Optional<std::string> experiment_name,
bool expect_enabled); bool expect_enabled);
void InitializeResourceLoadingHints();
private: private:
base::test::ScopedTaskEnvironment scoped_task_environment_; base::test::ScopedTaskEnvironment scoped_task_environment_;
base::ScopedTempDir temp_dir_; base::ScopedTempDir temp_dir_;
...@@ -156,14 +158,6 @@ TEST_F(PreviewsOptimizationGuideTest, ...@@ -156,14 +158,6 @@ TEST_F(PreviewsOptimizationGuideTest,
EXPECT_FALSE( EXPECT_FALSE(
guide()->IsWhitelisted(*CreateRequestWithURL(GURL("https://google.com")), guide()->IsWhitelisted(*CreateRequestWithURL(GURL("https://google.com")),
PreviewsType::NOSCRIPT)); PreviewsType::NOSCRIPT));
EXPECT_FALSE(guide()->IsHostWhitelistedAtNavigation(
GURL("https://m.facebook.com"), PreviewsType::RESOURCE_LOADING_HINTS));
EXPECT_FALSE(guide()->IsHostWhitelistedAtNavigation(
GURL("https://m.twitter.com/example"),
PreviewsType::RESOURCE_LOADING_HINTS));
EXPECT_FALSE(guide()->IsHostWhitelistedAtNavigation(
GURL("https://google.com"), PreviewsType::RESOURCE_LOADING_HINTS));
} }
// Test when resource loading hints are enabled. // Test when resource loading hints are enabled.
...@@ -205,17 +199,6 @@ TEST_F(PreviewsOptimizationGuideTest, ...@@ -205,17 +199,6 @@ TEST_F(PreviewsOptimizationGuideTest,
EXPECT_FALSE( EXPECT_FALSE(
guide()->IsWhitelisted(*CreateRequestWithURL(GURL("https://google.com")), guide()->IsWhitelisted(*CreateRequestWithURL(GURL("https://google.com")),
PreviewsType::RESOURCE_LOADING_HINTS)); PreviewsType::RESOURCE_LOADING_HINTS));
EXPECT_TRUE(guide()->IsHostWhitelistedAtNavigation(
GURL("https://m.facebook.com"), PreviewsType::RESOURCE_LOADING_HINTS));
EXPECT_TRUE(guide()->IsHostWhitelistedAtNavigation(
GURL("https://m.facebook.com/example.html"),
PreviewsType::RESOURCE_LOADING_HINTS));
EXPECT_TRUE(guide()->IsHostWhitelistedAtNavigation(
GURL("https://m.twitter.com/example"),
PreviewsType::RESOURCE_LOADING_HINTS));
EXPECT_FALSE(guide()->IsHostWhitelistedAtNavigation(
GURL("https://google.com"), PreviewsType::RESOURCE_LOADING_HINTS));
} }
// Test when both NoScript and resource loading hints are enabled. // Test when both NoScript and resource loading hints are enabled.
...@@ -260,16 +243,6 @@ TEST_F( ...@@ -260,16 +243,6 @@ TEST_F(
EXPECT_FALSE( EXPECT_FALSE(
guide()->IsWhitelisted(*CreateRequestWithURL(GURL("https://google.com")), guide()->IsWhitelisted(*CreateRequestWithURL(GURL("https://google.com")),
PreviewsType::RESOURCE_LOADING_HINTS)); PreviewsType::RESOURCE_LOADING_HINTS));
EXPECT_FALSE(guide()->IsHostWhitelistedAtNavigation(
GURL("https://m.facebook.com"), PreviewsType::RESOURCE_LOADING_HINTS));
EXPECT_TRUE(guide()->IsHostWhitelistedAtNavigation(
GURL("https://m.twitter.com/example"),
PreviewsType::RESOURCE_LOADING_HINTS));
EXPECT_TRUE(guide()->IsHostWhitelistedAtNavigation(
GURL("https://m.twitter.com"), PreviewsType::RESOURCE_LOADING_HINTS));
EXPECT_FALSE(guide()->IsHostWhitelistedAtNavigation(
GURL("https://google.com"), PreviewsType::RESOURCE_LOADING_HINTS));
} }
// This is a helper function for testing the experiment flags on the config for // This is a helper function for testing the experiment flags on the config for
...@@ -327,16 +300,11 @@ void PreviewsOptimizationGuideTest::DoExperimentFlagTest( ...@@ -327,16 +300,11 @@ void PreviewsOptimizationGuideTest::DoExperimentFlagTest(
EXPECT_TRUE(guide()->IsWhitelisted( EXPECT_TRUE(guide()->IsWhitelisted(
*CreateRequestWithURL(GURL("https://m.twitter.com/example")), *CreateRequestWithURL(GURL("https://m.twitter.com/example")),
PreviewsType::NOSCRIPT)); PreviewsType::NOSCRIPT));
EXPECT_FALSE(guide()->IsHostWhitelistedAtNavigation(
GURL("https://m.twitter.com/example"),
PreviewsType::RESOURCE_LOADING_HINTS));
// Google (which is not configured at all) should always have both NOSCRIPT // Google (which is not configured at all) should always have both NOSCRIPT
// and RESOURCE_LOADING_HINTS disabled. // and RESOURCE_LOADING_HINTS disabled.
EXPECT_FALSE( EXPECT_FALSE(
guide()->IsWhitelisted(*CreateRequestWithURL(GURL("https://google.com")), guide()->IsWhitelisted(*CreateRequestWithURL(GURL("https://google.com")),
PreviewsType::NOSCRIPT)); PreviewsType::NOSCRIPT));
EXPECT_FALSE(guide()->IsHostWhitelistedAtNavigation(
GURL("https://google.com"), PreviewsType::RESOURCE_LOADING_HINTS));
} }
TEST_F(PreviewsOptimizationGuideTest, TEST_F(PreviewsOptimizationGuideTest,
...@@ -610,22 +578,50 @@ TEST_F(PreviewsOptimizationGuideTest, IsWhitelistedWithMultipleHintMatches) { ...@@ -610,22 +578,50 @@ TEST_F(PreviewsOptimizationGuideTest, IsWhitelistedWithMultipleHintMatches) {
CreateRequestWithURL(GURL("https://outdoor.sports.yahoo.com")); CreateRequestWithURL(GURL("https://outdoor.sports.yahoo.com"));
// Uses "sports.yahoo.com" match before "yahoo.com" match. // Uses "sports.yahoo.com" match before "yahoo.com" match.
EXPECT_FALSE(guide()->IsWhitelisted(*request5, PreviewsType::NOSCRIPT)); EXPECT_FALSE(guide()->IsWhitelisted(*request5, PreviewsType::NOSCRIPT));
}
EXPECT_FALSE(guide()->IsHostWhitelistedAtNavigation( // This is a helper function for initializing some ResourceLoading hints.
GURL("https://yahoo.com"), PreviewsType::RESOURCE_LOADING_HINTS)); void PreviewsOptimizationGuideTest::InitializeResourceLoadingHints() {
EXPECT_FALSE(guide()->IsHostWhitelistedAtNavigation( optimization_guide::proto::Configuration config;
GURL("https://m.facebook.com"), PreviewsType::RESOURCE_LOADING_HINTS)); optimization_guide::proto::Hint* hint1 = config.add_hints();
EXPECT_FALSE(guide()->IsHostWhitelistedAtNavigation( hint1->set_key("somedomain.org");
GURL("https://m.twitter.com/example"), hint1->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
PreviewsType::RESOURCE_LOADING_HINTS)); optimization_guide::proto::PageHint* page_hint1 = hint1->add_page_hints();
EXPECT_FALSE(guide()->IsHostWhitelistedAtNavigation( page_hint1->set_page_pattern("/news/");
GURL("https://google.com"), PreviewsType::RESOURCE_LOADING_HINTS)); optimization_guide::proto::Optimization* optimization1 =
EXPECT_FALSE(guide()->IsHostWhitelistedAtNavigation( hint1->add_whitelisted_optimizations();
GURL("https://outdoor.sports.yahoo.com"), optimization1->set_optimization_type(
PreviewsType::RESOURCE_LOADING_HINTS)); optimization_guide::proto::RESOURCE_LOADING);
EXPECT_FALSE(guide()->IsHostWhitelistedAtNavigation( ProcessHints(config, "2.0.0");
GURL("https://outdoor.sports.yahoo.com/index.html"),
PreviewsType::RESOURCE_LOADING_HINTS)); RunUntilIdle();
}
TEST_F(PreviewsOptimizationGuideTest, MaybeLoadOptimizationHints) {
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndEnableFeature(features::kResourceLoadingHints);
InitializeResourceLoadingHints();
EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
*CreateRequestWithURL(GURL("https://somedomain.org/"))));
EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
*CreateRequestWithURL(GURL("https://www.somedomain.org"))));
EXPECT_FALSE(guide()->MaybeLoadOptimizationHints(
*CreateRequestWithURL(GURL("https://www.unknown.com"))));
}
TEST_F(PreviewsOptimizationGuideTest,
MaybeLoadOptimizationHintsWithoutEnabledPageHintsFeature) {
// Without PageHints-oriented feature enabled, never see
// enabled, the optimization should be disabled.
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndDisableFeature(features::kResourceLoadingHints);
InitializeResourceLoadingHints();
EXPECT_FALSE(guide()->MaybeLoadOptimizationHints(
*CreateRequestWithURL(GURL("https://www.somedomain.org"))));
} }
TEST_F(PreviewsOptimizationGuideTest, RemoveObserverCalledAtDestruction) { TEST_F(PreviewsOptimizationGuideTest, RemoveObserverCalledAtDestruction) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment