blob: 5c6d8705ff8e6fe59cbea53a5691e2e3ea383c78 [file] [log] [blame]
// Copyright 2020 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_PAGE_DISCARDING_HELPER_H_
#define CHROME_BROWSER_PERFORMANCE_MANAGER_POLICIES_PAGE_DISCARDING_HELPER_H_
#include <optional>
#include "base/functional/callback_forward.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "chrome/browser/performance_manager/mechanisms/page_discarder.h"
#include "chrome/browser/resource_coordinator/lifecycle_unit_state.mojom-shared.h"
#include "components/memory_pressure/reclaim_target.h"
#include "components/memory_pressure/unnecessary_discard_monitor.h"
#include "components/performance_manager/public/decorators/page_live_state_decorator.h"
#include "components/performance_manager/public/features.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 {
namespace mechanism {
class PageDiscarder;
} // namespace mechanism
namespace policies {
#if BUILDFLAG(IS_CHROMEOS)
constexpr base::TimeDelta kNonVisiblePagesUrgentProtectionTime =
base::TimeDelta();
#else
// Time during which non visible pages are protected from urgent discarding
// (not on ChromeOS).
constexpr base::TimeDelta kNonVisiblePagesUrgentProtectionTime =
base::Minutes(10);
#endif
// Time during which a tab cannot be discarded after having played audio.
constexpr base::TimeDelta kTabAudioProtectionTime = base::Minutes(1);
// Caches page node properties to facilitate sorting.
class PageNodeSortProxy {
public:
PageNodeSortProxy(const PageNode* page_node,
bool is_marked,
bool is_visible,
bool is_protected,
bool is_focused,
base::TimeDelta last_visible)
: page_node_(page_node),
is_marked_(is_marked),
is_visible_(is_visible),
is_protected_(is_protected),
is_focused_(is_focused),
last_visible_(last_visible) {}
const PageNode* page_node() const { return page_node_; }
bool is_marked() const { return is_marked_; }
bool is_protected() const { return is_protected_; }
bool is_visible() const { return is_visible_; }
bool is_focused() const { return is_focused_; }
base::TimeDelta last_visible() const { return last_visible_; }
// Returns true if the rhs is more important.
bool operator<(const PageNodeSortProxy& rhs) const {
if (is_marked_ != rhs.is_marked_) {
return rhs.is_marked_;
}
if (is_visible_ != rhs.is_visible_) {
return rhs.is_visible_;
}
if (is_protected_ != rhs.is_protected_) {
return rhs.is_protected_;
}
return last_visible_ > rhs.last_visible_;
}
private:
raw_ptr<const PageNode> page_node_;
bool is_marked_;
bool is_visible_;
bool is_protected_;
bool is_focused_;
// Delta between current time and last visibility change time.
base::TimeDelta last_visible_;
};
// Helper class to be used by the policies that want to discard tabs.
//
// This is a GraphRegistered object and should be accessed via
// PageDiscardingHelper::GetFromGraph(graph()).
class PageDiscardingHelper
: public GraphOwnedAndRegistered<PageDiscardingHelper>,
public NodeDataDescriberDefaultImpl {
public:
enum class CanDiscardResult {
// Discarding eligible nodes is hard to notice for user.
kEligible,
// Discarding protected nodes is noticeable to user.
kProtected,
// Marked nodes can never be discarded.
kMarked,
};
// Export discard reason in the public interface.
using DiscardReason = ::mojom::LifecycleUnitDiscardReason;
PageDiscardingHelper();
~PageDiscardingHelper() override;
PageDiscardingHelper(const PageDiscardingHelper& other) = delete;
PageDiscardingHelper& operator=(const PageDiscardingHelper&) = delete;
// Selects a tab to discard and posts to the UI thread to discard it. This
// will try to discard a tab until there's been a successful discard or until
// there's no more discard candidate.
// `minimum_time_in_background` is passed to `CanDiscard()`, see the comment
// there about its usage.
void DiscardAPage(base::OnceCallback<void(bool)> post_discard_cb,
DiscardReason discard_reason,
base::TimeDelta minimum_time_in_background =
kNonVisiblePagesUrgentProtectionTime);
// Discards multiple tabs to meet the reclaim target based and posts to the UI
// thread to discard these tabs. Retries discarding if all discardings in the
// UI thread fail. If |reclaim_target_kb| is nullopt, only discard one tab. If
// |discard_protected_tabs| is true, protected tabs (CanDiscard() returns
// kProtected) can also be discarded.
// `minimum_time_in_background` is passed to `CanDiscard()`, see the comment
// there about its usage.
void DiscardMultiplePages(
std::optional<memory_pressure::ReclaimTarget> reclaim_target,
bool discard_protected_tabs,
base::OnceCallback<void(bool)> post_discard_cb,
DiscardReason discard_reason,
base::TimeDelta minimum_time_in_background =
kNonVisiblePagesUrgentProtectionTime);
void ImmediatelyDiscardMultiplePages(
const std::vector<const PageNode*>& page_nodes,
DiscardReason discard_reason,
base::OnceCallback<void(bool)> post_discard_cb = base::DoNothing());
void SetNoDiscardPatternsForProfile(const std::string& browser_context_id,
const std::vector<std::string>& patterns);
void ClearNoDiscardPatternsForProfile(const std::string& browser_context_id);
void SetMockDiscarderForTesting(
std::unique_ptr<mechanism::PageDiscarder> discarder);
// 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) const;
static void AddDiscardAttemptMarkerForTesting(PageNode* page_node);
static void RemovesDiscardAttemptMarkerForTesting(PageNode* page_node);
protected:
void OnPassedToGraph(Graph* graph) override;
void OnTakenFromGraph(Graph* graph) override;
// Returns the PageLiveStateDecorator::Data associated with a PageNode.
// Exposed and made virtual to allowed injecting some fake data in tests.
virtual const PageLiveStateDecorator::Data* GetPageNodeLiveStateData(
const PageNode* page_node) const;
private:
bool IsPageOptedOutOfDiscarding(const std::string& browser_context_id,
const GURL& url) const;
// NodeDataDescriber implementation:
base::Value::Dict DescribePageNodeData(const PageNode* node) const override;
// Called after each discard attempt. |success| will indicate whether or not
// the attempt has been successful. |post_discard_cb| will be called once
// there's been at least one successful discard or if there's no more discard
// candidates.
void PostDiscardAttemptCallback(
std::optional<memory_pressure::ReclaimTarget> reclaim_target,
bool discard_protected_tabs,
base::OnceCallback<void(bool)> post_discard_cb,
DiscardReason discard_reason,
base::TimeDelta minimum_time_in_background,
const std::vector<mechanism::PageDiscarder::DiscardEvent>&
discard_events);
// The mechanism used to do the actual discarding.
std::unique_ptr<mechanism::PageDiscarder> page_discarder_;
std::map<std::string, std::unique_ptr<url_matcher::URLMatcher>>
profiles_no_discard_patterns_;
memory_pressure::UnnecessaryDiscardMonitor unnecessary_discard_monitor_;
SEQUENCE_CHECKER(sequence_checker_);
base::WeakPtrFactory<PageDiscardingHelper> weak_factory_{this};
};
} // namespace policies
} // namespace performance_manager
#endif // CHROME_BROWSER_PERFORMANCE_MANAGER_POLICIES_PAGE_DISCARDING_HELPER_H_