blob: 1c97cfefb6e2ad1c7f362ad8cd791e6235f4e875 [file] [log] [blame]
Daniel Murphy2e5932d2024-09-18 16:23:391// Copyright 2024 The Chromium Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/web_applications/visited_manifest_manager.h"
6
7#include <algorithm>
8
9#include "base/containers/contains.h"
10#include "base/feature_list.h"
11#include "base/strings/string_util.h"
12#include "third_party/blink/public/common/manifest/manifest_util.h"
13#include "third_party/blink/public/mojom/manifest/manifest.mojom.h"
14#include "url/origin.h"
15
16namespace web_app {
17
18// TODO(crbug.com/366323698): Clean this up after m131 hits stable.
19BASE_FEATURE(kBlockMlPromotionInNestedPagesNoManifest,
20 "BlockMlPromotionInNestedPagesNoManifest",
21 base::FEATURE_ENABLED_BY_DEFAULT);
22
23VisitedManifestManager::VisitedManifestManager(base::Clock* clock)
24 : clock_(clock) {}
25VisitedManifestManager::~VisitedManifestManager() = default;
26
27bool VisitedManifestManager::IsUrlControlledBySeenManifest(
28 const GURL& site_url) {
29 if (!base::FeatureList::IsEnabled(kBlockMlPromotionInNestedPagesNoManifest)) {
30 return false;
31 }
32 // This returns the first value 'greater than' the given value. Prefixes MUST
33 // be equal or less than the candidate string, so the algorithm can iterate
34 // backwards until it reaches a different origin.
35 auto index = recent_manifest_scopes_.index();
36 auto it =
37 std::ranges::upper_bound(index, site_url, std::less{},
38 [](const auto& pair) { return pair.first; });
39
40 url::Origin site_origin = url::Origin::Create(site_url);
41 while (it != index.begin()) {
42 --it;
43 if (base::StartsWith(site_url.spec(), it->first.spec())) {
44 recent_manifest_scopes_.Put(it->first, clock_->Now());
45 return true;
46 }
47 if (!site_origin.IsSameOriginWith(it->first)) {
48 return false;
49 }
50 }
51 return false;
52}
53
54void VisitedManifestManager::OnManifestSeen(
55 const blink::mojom::Manifest& manifest) {
56 if (!base::FeatureList::IsEnabled(kBlockMlPromotionInNestedPagesNoManifest)) {
57 return;
58 }
59 CHECK(!blink::IsEmptyManifest(manifest));
60 // Only consider manifests that have specified a `start_url`, as every page
61 // has a default manifest.
62 if (!manifest.has_valid_specified_start_url) {
63 return;
64 }
65 recent_manifest_scopes_.Put(manifest.scope, clock_->Now());
66}
67
68void VisitedManifestManager::ClearSeenScopes(const base::Time& begin_time,
69 const base::Time& end_time) {
70 if (!base::FeatureList::IsEnabled(kBlockMlPromotionInNestedPagesNoManifest)) {
71 return;
72 }
73 auto it = recent_manifest_scopes_.begin();
74 while (it != recent_manifest_scopes_.end()) {
75 if (it->second >= begin_time && it->second <= end_time) {
76 it = recent_manifest_scopes_.Erase(it);
77 } else {
78 ++it;
79 }
80 }
81}
82
83} // namespace web_app