| // 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/web_applications/visited_manifest_manager.h" |
| |
| #include "base/feature_list.h" |
| #include "base/strings/strcat.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/test/simple_test_clock.h" |
| #include "base/time/time.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/blink/public/mojom/manifest/manifest.mojom.h" |
| |
| namespace web_app { |
| namespace { |
| |
| GURL GetExampleUrl(int i) { |
| return GURL( |
| base::StrCat({"https://www.example", base::ToString(i), ".com/"})); |
| } |
| |
| blink::mojom::ManifestPtr CreateManifestWithScope(const GURL& scope) { |
| blink::mojom::ManifestPtr manifest = blink::mojom::Manifest::New(); |
| manifest->start_url = GURL(scope.GetWithoutFilename().spec() + "index.html"); |
| manifest->id = manifest->start_url; |
| manifest->scope = scope; |
| manifest->has_valid_specified_start_url = true; |
| return manifest; |
| } |
| |
| blink::mojom::ManifestPtr CreatManifestWithScopeNoStartUrl(const GURL& scope) { |
| blink::mojom::ManifestPtr manifest = CreateManifestWithScope(scope); |
| manifest->has_valid_specified_start_url = false; |
| return manifest; |
| } |
| |
| blink::mojom::ManifestPtr CreateExampleManifest(int i) { |
| return CreateManifestWithScope(GetExampleUrl(i)); |
| } |
| |
| class VisitedManagerEnabledTest : public testing::Test { |
| private: |
| base::test::ScopedFeatureList enable_feature{ |
| kBlockMlPromotionInNestedPagesNoManifest}; |
| }; |
| |
| TEST_F(VisitedManagerEnabledTest, ControlledChecks) { |
| VisitedManifestManager manager; |
| |
| manager.OnManifestSeen( |
| *CreateManifestWithScope(GURL("https://www.example0.com/pre/"))); |
| manager.OnManifestSeen( |
| *CreateManifestWithScope(GURL("https://www.example1.com/seen/"))); |
| manager.OnManifestSeen( |
| *CreateManifestWithScope(GURL("https://www.example2.com/seeo/"))); |
| |
| EXPECT_TRUE(manager.IsUrlControlledBySeenManifest( |
| GURL("https://www.example1.com/seen/"))); |
| EXPECT_TRUE(manager.IsUrlControlledBySeenManifest( |
| GURL("https://www.example1.com/seen/nest"))); |
| EXPECT_TRUE(manager.IsUrlControlledBySeenManifest( |
| GURL("https://www.example1.com/seen/nested/"))); |
| // Before 'seen/' in index ordering, and matches path on other origin |
| EXPECT_FALSE(manager.IsUrlControlledBySeenManifest( |
| GURL("https://www.example1.com/pre/"))); |
| EXPECT_FALSE(manager.IsUrlControlledBySeenManifest( |
| GURL("https://www.example1.com/seen"))); |
| // After 'seen/' in index ordering, and matches path on other origin |
| EXPECT_FALSE(manager.IsUrlControlledBySeenManifest( |
| GURL("https://www.example1.com/seeo/"))); |
| } |
| |
| TEST_F(VisitedManagerEnabledTest, ClearDateRange) { |
| base::SimpleTestClock clock; |
| VisitedManifestManager manager(&clock); |
| |
| base::Time start = base::Time::Now(); |
| clock.SetNow(start); |
| manager.OnManifestSeen( |
| *CreateManifestWithScope(GURL("https://www.example0.com/"))); |
| |
| clock.Advance(base::Minutes(10)); |
| manager.OnManifestSeen( |
| *CreateManifestWithScope(GURL("https://www.example1.com/"))); |
| |
| clock.Advance(base::Minutes(10)); |
| manager.OnManifestSeen( |
| *CreateManifestWithScope(GURL("https://www.example2.com/"))); |
| |
| clock.Advance(base::Minutes(10)); |
| manager.OnManifestSeen( |
| *CreateManifestWithScope(GURL("https://www.example3.com/"))); |
| |
| // This should clear examples 1 and 2. |
| manager.ClearSeenScopes(start + base::Minutes(10), start + base::Minutes(20)); |
| |
| base::Time check_time = clock.Now(); |
| |
| EXPECT_TRUE( |
| manager.IsUrlControlledBySeenManifest(GURL("https://www.example0.com/"))); |
| EXPECT_FALSE( |
| manager.IsUrlControlledBySeenManifest(GURL("https://www.example1.com/"))); |
| EXPECT_FALSE( |
| manager.IsUrlControlledBySeenManifest(GURL("https://www.example2.com/"))); |
| EXPECT_TRUE( |
| manager.IsUrlControlledBySeenManifest(GURL("https://www.example3.com/"))); |
| |
| // Checking the entries also updates the time, so clearing at the check_time |
| // should clear those entries too. |
| manager.ClearSeenScopes(check_time, check_time); |
| |
| EXPECT_FALSE( |
| manager.IsUrlControlledBySeenManifest(GURL("https://www.example0.com/"))); |
| EXPECT_FALSE( |
| manager.IsUrlControlledBySeenManifest(GURL("https://www.example3.com/"))); |
| } |
| |
| TEST_F(VisitedManagerEnabledTest, TestLru) { |
| VisitedManifestManager manager; |
| // Fill up the cache. Eviction should start from 0, as that is the first one |
| // that was added. |
| for (int i = 0; i < VisitedManifestManager::kMaxScopesToSave; ++i) { |
| manager.OnManifestSeen(*CreateExampleManifest(i)); |
| } |
| // Check they are all present, in order again which should keep the same |
| // ordering. |
| for (int i = 0; i < VisitedManifestManager::kMaxScopesToSave; ++i) { |
| EXPECT_TRUE(manager.IsUrlControlledBySeenManifest(GetExampleUrl(i))); |
| } |
| // Test that is '0' was evicted after adding one more. |
| manager.OnManifestSeen( |
| *CreateExampleManifest(VisitedManifestManager::kMaxScopesToSave)); |
| EXPECT_FALSE(manager.IsUrlControlledBySeenManifest(GetExampleUrl(0))); |
| |
| // Test `OnManifestSeen` and `IsUrlControlledBySeenManifest` both move the |
| // entry to the top of the queue. '1' and '2' will not be deleted, instead '3' |
| // and '4' will when adding two more manifests. |
| manager.OnManifestSeen(*CreateExampleManifest(1)); |
| EXPECT_TRUE(manager.IsUrlControlledBySeenManifest(GetExampleUrl(2))); |
| manager.OnManifestSeen( |
| *CreateExampleManifest(VisitedManifestManager::kMaxScopesToSave + 1)); |
| manager.OnManifestSeen( |
| *CreateExampleManifest(VisitedManifestManager::kMaxScopesToSave + 2)); |
| EXPECT_FALSE(manager.IsUrlControlledBySeenManifest(GetExampleUrl(3))); |
| EXPECT_FALSE(manager.IsUrlControlledBySeenManifest(GetExampleUrl(4))); |
| |
| // Final check that all is correct. |
| for (int i = 0; i < VisitedManifestManager::kMaxScopesToSave + 3; ++i) { |
| bool is_controlled = |
| manager.IsUrlControlledBySeenManifest(GetExampleUrl(i)); |
| if (i == 0 || i == 3 || i == 4) { |
| EXPECT_FALSE(is_controlled) << i; |
| } else { |
| EXPECT_TRUE(is_controlled) << i; |
| } |
| } |
| } |
| |
| TEST_F(VisitedManagerEnabledTest, TestInvalidManifests) { |
| VisitedManifestManager manager; |
| manager.OnManifestSeen( |
| *CreatManifestWithScopeNoStartUrl(GURL("https://www.example0.com/"))); |
| EXPECT_FALSE( |
| manager.IsUrlControlledBySeenManifest(GURL("https://www.example0.com/"))); |
| } |
| |
| TEST(VisitedManagerDisabledTest, DisabledFeature) { |
| // Ensure that we can disable the feature and have things not crash, and |
| // always returns 'false'. |
| base::test::ScopedFeatureList disable; |
| disable.InitAndDisableFeature(kBlockMlPromotionInNestedPagesNoManifest); |
| base::SimpleTestClock clock; |
| VisitedManifestManager manager(&clock); |
| |
| base::Time start = base::Time::Now(); |
| clock.SetNow(start); |
| manager.OnManifestSeen( |
| *CreateManifestWithScope(GURL("https://www.example0.com/"))); |
| manager.OnManifestSeen( |
| *CreateManifestWithScope(GURL("https://www.example1.com/"))); |
| |
| base::Time check_time = clock.Now(); |
| |
| EXPECT_FALSE( |
| manager.IsUrlControlledBySeenManifest(GURL("https://www.example0.com/"))); |
| EXPECT_FALSE( |
| manager.IsUrlControlledBySeenManifest(GURL("https://www.example1.com/"))); |
| |
| manager.ClearSeenScopes(start, check_time); |
| } |
| |
| } // namespace |
| } // namespace web_app |