blob: 59c212002a4d15cd219b1719309af48fb145b787 [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_PERFORMANCE_MANAGER_POLICIES_DISCARD_ELIGIBILITY_POLICY_H_
#define CHROME_BROWSER_PERFORMANCE_MANAGER_POLICIES_DISCARD_ELIGIBILITY_POLICY_H_
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "base/time/time.h"
#include "chrome/browser/performance_manager/policies/cannot_discard_reason.h"
#include "chrome/browser/resource_coordinator/lifecycle_unit_state.mojom-shared.h"
#include "components/performance_manager/public/graph/graph.h"
#include "components/performance_manager/public/graph/graph_registered.h"
#include "components/performance_manager/public/graph/node_data_describer.h"
#include "components/performance_manager/public/graph/page_node.h"
namespace url_matcher {
class URLMatcher;
} // namespace url_matcher
namespace performance_manager::policies {
#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
inline constexpr base::TimeDelta kNonVisiblePagesUrgentProtectionTime =
base::TimeDelta();
#else
// Time during which non visible pages are protected from urgent discarding
// (not on ChromeOS).
inline constexpr base::TimeDelta kNonVisiblePagesUrgentProtectionTime =
base::Minutes(10);
#endif
#if BUILDFLAG(IS_ANDROID)
// TODO(crbug.com/412839833): kTabAudioProtectionTime may be needed on Android
// as well.
inline constexpr base::TimeDelta kTabAudioProtectionTime = base::TimeDelta();
#else
// Time during which a tab cannot be discarded after having played audio.
inline constexpr base::TimeDelta kTabAudioProtectionTime = base::Minutes(1);
#endif
// Whether a page can be discarded.
enum class CanDiscardResult {
// The page can be discarded. The user should experience minimal disruption
// from discarding.
kEligible,
// The page can be discarded. The user will likely find discarding disruptive.
kProtected,
// The page cannot be discarded.
kDisallowed,
};
// Caches page node properties to facilitate sorting.
class PageNodeSortProxy {
public:
PageNodeSortProxy(base::WeakPtr<const PageNode> page_node,
CanDiscardResult can_discard_result,
bool is_visible,
bool is_focused,
base::TimeTicks last_visibility_change_time);
PageNodeSortProxy(PageNodeSortProxy&&);
PageNodeSortProxy& operator=(PageNodeSortProxy&&);
~PageNodeSortProxy();
base::WeakPtr<const PageNode> page_node() const { return page_node_; }
bool is_disallowed() const {
return can_discard_result_ == CanDiscardResult::kDisallowed;
}
bool is_protected() const {
return can_discard_result_ == CanDiscardResult::kProtected;
}
bool is_visible() const { return is_visible_; }
bool is_focused() const { return is_focused_; }
base::TimeTicks last_visibility_change_time() const {
return last_visibility_change_time_;
}
// Returns true if the rhs is more important.
bool operator<(const PageNodeSortProxy& rhs) const {
if (is_disallowed() != rhs.is_disallowed()) {
return rhs.is_disallowed();
}
if (is_focused_ != rhs.is_focused_) {
return rhs.is_focused_;
}
if (is_visible_ != rhs.is_visible_) {
return rhs.is_visible_;
}
if (is_protected() != rhs.is_protected()) {
return rhs.is_protected();
}
return last_visibility_change_time_ < rhs.last_visibility_change_time_;
}
private:
base::WeakPtr<const PageNode> page_node_;
CanDiscardResult can_discard_result_;
bool is_visible_;
bool is_focused_;
base::TimeTicks last_visibility_change_time_;
};
// DiscardEligibilityPolicy decides which PageNode is eligigle for tab
// discarding.
class DiscardEligibilityPolicy
: public GraphOwnedAndRegistered<DiscardEligibilityPolicy>,
public NodeDataDescriberDefaultImpl,
public PageNodeObserver {
public:
// Export discard reason in the public interface.
using DiscardReason = ::mojom::LifecycleUnitDiscardReason;
DiscardEligibilityPolicy();
~DiscardEligibilityPolicy() override;
DiscardEligibilityPolicy(const DiscardEligibilityPolicy& other) = delete;
DiscardEligibilityPolicy& operator=(const DiscardEligibilityPolicy&) = delete;
// PageNodeObserver:
void OnMainFrameDocumentChanged(const PageNode* page_node) override;
base::WeakPtr<DiscardEligibilityPolicy> GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
void SetNoDiscardPatternsForProfile(const std::string& browser_context_id,
const std::vector<std::string>& patterns);
void ClearNoDiscardPatternsForProfile(const std::string& browser_context_id);
// Indicates if `page_node` can be urgently discarded, using a list of
// criteria depending on `discard_reason`. If `minimum_time_in_background` is
// non-zero, the page will not be discarded if it has not spent at least
// `minimum_time_in_background` in the not-visible state.
CanDiscardResult CanDiscard(
const PageNode* page_node,
DiscardReason discard_reason,
base::TimeDelta minimum_time_in_background =
kNonVisiblePagesUrgentProtectionTime,
std::vector<CannotDiscardReason>* cannot_discard_reasons = nullptr) const;
// This must be called from PageDiscardingHelper or from test only.
static void AddDiscardAttemptMarker(PageNode* page_node);
static void RemovesDiscardAttemptMarkerForTesting(PageNode* page_node);
// Sets an additional callback that should be invoked whenever the
// SetNoDiscardPatternsForProfile() or ClearNoDiscardPatternsForProfile()
// methosd is called, with the method's `browser_context_id` argument.
void SetOptOutPolicyChangedCallback(
base::RepeatingCallback<void(std::string_view)> callback);
bool IsPageOptedOutOfDiscarding(const std::string& browser_context_id,
const GURL& url) const;
private:
void OnPassedToGraph(Graph* graph) override;
void OnTakenFromGraph(Graph* graph) override;
// NodeDataDescriber implementation:
base::Value::Dict DescribePageNodeData(const PageNode* node) const override;
std::map<std::string, std::unique_ptr<url_matcher::URLMatcher>>
profiles_no_discard_patterns_ GUARDED_BY_CONTEXT(sequence_checker_);
base::RepeatingCallback<void(std::string_view)>
opt_out_policy_changed_callback_ GUARDED_BY_CONTEXT(sequence_checker_);
SEQUENCE_CHECKER(sequence_checker_);
base::WeakPtrFactory<DiscardEligibilityPolicy> weak_factory_{this};
};
} // namespace performance_manager::policies
#endif // CHROME_BROWSER_PERFORMANCE_MANAGER_POLICIES_DISCARD_ELIGIBILITY_POLICY_H_