1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
|
// 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 <map>
#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;
void set_always_discard_for_testing(bool always_discard) {
always_discard_for_testing_ = always_discard;
}
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_);
bool always_discard_for_testing_ = false;
SEQUENCE_CHECKER(sequence_checker_);
base::WeakPtrFactory<DiscardEligibilityPolicy> weak_factory_{this};
};
} // namespace performance_manager::policies
#endif // CHROME_BROWSER_PERFORMANCE_MANAGER_POLICIES_DISCARD_ELIGIBILITY_POLICY_H_
|