| // Copyright 2019 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/public/test/back_forward_cache_util.h" |
| |
| #include <algorithm> |
| #include <map> |
| #include <set> |
| |
| #include "base/containers/contains.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "content/browser/renderer_host/back_forward_cache_impl.h" |
| #include "content/common/features.h" |
| #include "content/public/browser/global_routing_id.h" |
| #include "content/public/browser/navigation_controller.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/common/content_features.h" |
| |
| namespace content { |
| namespace { |
| |
| // TODO(crbug.com/40216768): Remove the default parameters from the |
| // kBackForwardCache feature and remove the complex parameter merging code. |
| std::vector<base::test::FeatureRefAndParams> |
| GetDefaultEnabledBackForwardCacheFeaturesAndParams( |
| const bool ignore_outstanding_network_request) { |
| std::vector<base::test::FeatureRefAndParams> default_features_and_params; |
| if (ignore_outstanding_network_request) { |
| default_features_and_params.push_back( |
| {features::kBackForwardCache, |
| {{// BackForwardCache will not be blocked by outstanding network |
| // requests. |
| // |
| // Example Test Failure (https://crbug.com/1324788): |
| // Navigating quickly between cached pages can fail flakily with: |
| // CanStorePageNow: <URL> : No: blocklisted features: outstanding |
| // network request (others) |
| "ignore_outstanding_network_request_for_testing", "true"}}}); |
| } else { |
| default_features_and_params.push_back({features::kBackForwardCache, {{}}}); |
| } |
| |
| default_features_and_params.push_back( |
| {features::kBackForwardCacheTimeToLiveControl, |
| {{// Sets a very long TTL before expiration (longer than the test |
| // timeout), so tests that are expecting deletion don't pass when |
| // they shouldn't. |
| "time_to_live_in_seconds", "3600" /* 1 hour */}}}); |
| return default_features_and_params; |
| } |
| |
| std::vector<base::test::FeatureRefAndParams> Merge( |
| const std::vector<base::test::FeatureRefAndParams>& |
| default_features_and_params, |
| const std::vector<base::test::FeatureRefAndParams>& |
| additional_features_and_params) { |
| std::vector<base::test::FeatureRefAndParams> final_features_and_params; |
| |
| // TODO(crbug.com/40216768): Consider move the below logic to |
| // base/test/scoped_feature_list.h. |
| // Go over the additional features/params - if they match a default feature, |
| // make a new featureparam with the combined features, otherwise just add the |
| // additional feature as is. |
| for (const auto& feature_and_params : additional_features_and_params) { |
| auto default_feature_and_param = std::ranges::find( |
| default_features_and_params, feature_and_params.feature->name, |
| [](const base::test::FeatureRefAndParams default_feature) { |
| return default_feature.feature->name; |
| }); |
| if (default_feature_and_param != default_features_and_params.end()) { |
| base::FieldTrialParams combined_params; |
| combined_params.insert(default_feature_and_param->params.begin(), |
| default_feature_and_param->params.end()); |
| combined_params.insert(feature_and_params.params.begin(), |
| feature_and_params.params.end()); |
| final_features_and_params.emplace_back(*feature_and_params.feature, |
| combined_params); |
| } else { |
| final_features_and_params.emplace_back(feature_and_params); |
| } |
| } |
| // Add any default features we didn't have additional params for. |
| for (const auto& feature_and_params : default_features_and_params) { |
| if (!base::Contains( |
| final_features_and_params, feature_and_params.feature->name, |
| [](const base::test::FeatureRefAndParams default_feature) { |
| return default_feature.feature->name; |
| })) { |
| final_features_and_params.emplace_back(feature_and_params); |
| } |
| } |
| |
| return final_features_and_params; |
| } |
| |
| } // namespace |
| |
| class BackForwardCacheDisabledTester::Impl |
| : public BackForwardCacheTestDelegate { |
| public: |
| bool IsDisabledForFrameWithReason(GlobalRenderFrameHostId id, |
| BackForwardCache::DisabledReason reason) { |
| return disable_reasons_[id].count(reason) != 0; |
| } |
| |
| void OnDisabledForFrameWithReason( |
| GlobalRenderFrameHostId id, |
| BackForwardCache::DisabledReason reason) override { |
| disable_reasons_[id].insert(reason); |
| } |
| |
| private: |
| std::map<GlobalRenderFrameHostId, std::set<BackForwardCache::DisabledReason>> |
| disable_reasons_; |
| }; |
| |
| BackForwardCacheDisabledTester::BackForwardCacheDisabledTester() |
| : impl_(std::make_unique<Impl>()) {} |
| |
| BackForwardCacheDisabledTester::~BackForwardCacheDisabledTester() {} |
| |
| bool BackForwardCacheDisabledTester::IsDisabledForFrameWithReason( |
| int process_id, |
| int frame_routing_id, |
| BackForwardCache::DisabledReason reason) { |
| return impl_->IsDisabledForFrameWithReason( |
| GlobalRenderFrameHostId{process_id, frame_routing_id}, reason); |
| } |
| |
| void DisableBackForwardCacheForTesting( |
| WebContents* web_contents, |
| BackForwardCache::DisableForTestingReason reason) { |
| // Used by tests. Disables BackForwardCache for a given WebContents. |
| web_contents->GetController().GetBackForwardCache().DisableForTesting(reason); |
| } |
| |
| std::vector<base::test::FeatureRefAndParams> |
| GetDefaultEnabledBackForwardCacheFeaturesForTesting( |
| const bool ignore_outstanding_network_request) { |
| return GetDefaultEnabledBackForwardCacheFeaturesForTesting( |
| {}, ignore_outstanding_network_request); |
| } |
| |
| std::vector<base::test::FeatureRefAndParams> |
| GetDefaultEnabledBackForwardCacheFeaturesForTesting( |
| const std::vector<base::test::FeatureRefAndParams>& |
| additional_features_and_params, |
| const bool ignore_outstanding_network_request) { |
| auto default_features_and_params = |
| GetDefaultEnabledBackForwardCacheFeaturesAndParams( |
| ignore_outstanding_network_request); |
| |
| return Merge(default_features_and_params, additional_features_and_params); |
| } |
| |
| std::vector<base::test::FeatureRefAndParams> |
| GetDefaultEnabledBackForwardCacheFeaturesForTesting( |
| const std::vector<base::test::FeatureRefAndParams>& |
| additional_features_and_params, |
| const size_t cache_size, |
| const size_t foreground_cache_size, |
| const bool ignore_outstanding_network_request) { |
| auto default_features_and_params = |
| GetDefaultEnabledBackForwardCacheFeaturesAndParams( |
| ignore_outstanding_network_request); |
| default_features_and_params.push_back( |
| {kBackForwardCacheSize, |
| {{"cache_size", base::NumberToString(cache_size)}, |
| {"foreground_cache_size", |
| base::NumberToString(foreground_cache_size)}}}); |
| |
| return Merge(default_features_and_params, additional_features_and_params); |
| } |
| |
| std::vector<base::test::FeatureRefAndParams> |
| GetBasicBackForwardCacheFeatureForTesting() { |
| return GetBasicBackForwardCacheFeatureForTesting({}); |
| } |
| |
| std::vector<base::test::FeatureRefAndParams> |
| GetBasicBackForwardCacheFeatureForTesting( |
| const std::vector<base::test::FeatureRefAndParams>& |
| additional_features_and_params) { |
| std::vector<base::test::FeatureRefAndParams> default_features_and_params = { |
| {features::kBackForwardCache, {}}}; |
| |
| return Merge(default_features_and_params, additional_features_and_params); |
| } |
| |
| std::vector<base::test::FeatureRef> |
| GetDefaultDisabledBackForwardCacheFeaturesForTesting() { |
| return GetDefaultDisabledBackForwardCacheFeaturesForTesting({}); |
| } |
| |
| std::vector<base::test::FeatureRef> |
| GetDefaultDisabledBackForwardCacheFeaturesForTesting( |
| const std::vector<base::test::FeatureRef>& additional_features) { |
| // Allows BackForwardCache for all devices regardless of their memory, |
| // including lower memory Android devices. |
| std::vector<base::test::FeatureRef> final_features = { |
| features::kBackForwardCacheMemoryControls}; |
| |
| for (auto additional_feature : additional_features) { |
| if (!base::Contains(final_features, additional_feature->name, |
| [](const base::test::FeatureRef default_feature) { |
| return default_feature->name; |
| })) { |
| final_features.emplace_back(additional_feature); |
| } |
| } |
| return final_features; |
| } |
| |
| void InitBackForwardCacheFeature(base::test::ScopedFeatureList* feature_list, |
| bool enable_back_forward_cache) { |
| if (enable_back_forward_cache) { |
| feature_list->InitWithFeaturesAndParameters( |
| GetBasicBackForwardCacheFeatureForTesting( |
| {{kBackForwardCacheNoTimeEviction, {}}, |
| {features::kBackForwardCacheMemoryControls, {}}}), |
| {}); |
| } else { |
| feature_list->InitAndDisableFeature(features::kBackForwardCache); |
| } |
| } |
| |
| } // namespace content |