blob: 1c97cfefb6e2ad1c7f362ad8cd791e6235f4e875 [file] [log] [blame]
// 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 <algorithm>
#include "base/containers/contains.h"
#include "base/feature_list.h"
#include "base/strings/string_util.h"
#include "third_party/blink/public/common/manifest/manifest_util.h"
#include "third_party/blink/public/mojom/manifest/manifest.mojom.h"
#include "url/origin.h"
namespace web_app {
// TODO(crbug.com/366323698): Clean this up after m131 hits stable.
BASE_FEATURE(kBlockMlPromotionInNestedPagesNoManifest,
"BlockMlPromotionInNestedPagesNoManifest",
base::FEATURE_ENABLED_BY_DEFAULT);
VisitedManifestManager::VisitedManifestManager(base::Clock* clock)
: clock_(clock) {}
VisitedManifestManager::~VisitedManifestManager() = default;
bool VisitedManifestManager::IsUrlControlledBySeenManifest(
const GURL& site_url) {
if (!base::FeatureList::IsEnabled(kBlockMlPromotionInNestedPagesNoManifest)) {
return false;
}
// This returns the first value 'greater than' the given value. Prefixes MUST
// be equal or less than the candidate string, so the algorithm can iterate
// backwards until it reaches a different origin.
auto index = recent_manifest_scopes_.index();
auto it =
std::ranges::upper_bound(index, site_url, std::less{},
[](const auto& pair) { return pair.first; });
url::Origin site_origin = url::Origin::Create(site_url);
while (it != index.begin()) {
--it;
if (base::StartsWith(site_url.spec(), it->first.spec())) {
recent_manifest_scopes_.Put(it->first, clock_->Now());
return true;
}
if (!site_origin.IsSameOriginWith(it->first)) {
return false;
}
}
return false;
}
void VisitedManifestManager::OnManifestSeen(
const blink::mojom::Manifest& manifest) {
if (!base::FeatureList::IsEnabled(kBlockMlPromotionInNestedPagesNoManifest)) {
return;
}
CHECK(!blink::IsEmptyManifest(manifest));
// Only consider manifests that have specified a `start_url`, as every page
// has a default manifest.
if (!manifest.has_valid_specified_start_url) {
return;
}
recent_manifest_scopes_.Put(manifest.scope, clock_->Now());
}
void VisitedManifestManager::ClearSeenScopes(const base::Time& begin_time,
const base::Time& end_time) {
if (!base::FeatureList::IsEnabled(kBlockMlPromotionInNestedPagesNoManifest)) {
return;
}
auto it = recent_manifest_scopes_.begin();
while (it != recent_manifest_scopes_.end()) {
if (it->second >= begin_time && it->second <= end_time) {
it = recent_manifest_scopes_.Erase(it);
} else {
++it;
}
}
}
} // namespace web_app