blob: 5db306b80b1de88f2a87237e1b649f7d2bd57d0d [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_PERFORMANCE_MANAGER_GRAPH_PAGE_NODE_IMPL_H_
#define COMPONENTS_PERFORMANCE_MANAGER_GRAPH_PAGE_NODE_IMPL_H_
#include <memory>
#include <optional>
#include <string>
#include "base/containers/enum_set.h"
#include "base/containers/flat_set.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "base/types/pass_key.h"
#include "base/types/token_type.h"
#include "build/build_config.h"
#include "components/performance_manager/decorators/page_aggregator_data.h"
#include "components/performance_manager/decorators/page_load_tracker_decorator_data.h"
#include "components/performance_manager/decorators/site_data_node_data.h"
#include "components/performance_manager/freezing/frozen_data.h"
#include "components/performance_manager/graph/node_attached_data_storage.h"
#include "components/performance_manager/graph/node_base.h"
#include "components/performance_manager/public/graph/page_node.h"
#include "components/performance_manager/resource_attribution/cpu_measurement_data.h"
#include "components/performance_manager/scenarios/loading_scenario_data.h"
#include "third_party/perfetto/include/perfetto/tracing/track.h"
#include "url/gurl.h"
namespace performance_manager {
class FrameNodeImpl;
class FrozenFrameAggregator;
// The starting state of various boolean properties of the PageNode.
enum class PagePropertyFlag {
kIsVisible, // initializes PageNode::IsVisible()
kMin = kIsVisible,
kIsAudible, // initializes PageNode::IsAudible()
kHasPictureInPicture, // initializes PageNode::HasPictureInPicture()
kIsOffTheRecord, // initializes PageNode::IsOffTheRecord()
kMax = kIsOffTheRecord,
};
using PagePropertyFlags = base::
EnumSet<PagePropertyFlag, PagePropertyFlag::kMin, PagePropertyFlag::kMax>;
class PageNodeImpl
: public PublicNodeImpl<PageNodeImpl, PageNode>,
public TypedNodeBase<PageNodeImpl, PageNode, PageNodeObserver>,
public SupportsNodeInlineData<
PageLoadTrackerDecoratorData,
PageAggregatorData,
SiteDataNodeData,
FrozenData,
LoadingScenarioPageFrameCounts,
resource_attribution::SharedCPUTimeResultData,
// Keep this last to avoid merge conflicts.
NodeAttachedDataStorage> {
public:
using PassKey = base::PassKey<PageNodeImpl>;
// A unique token to identify the PageNode and its associated WebContents for
// the lifetime of the browser. Most node types use an existing unique
// identifier for this (eg. FrameNode uses content::GlobalRenderFrameHostId,
// WorkerNode uses blink::WorkerToken) but WebContents has no id to use.
using PageToken = base::TokenType<class PageTokenTag>;
using TypedNodeBase<PageNodeImpl, PageNode, PageNodeObserver>::FromNode;
PageNodeImpl(base::WeakPtr<content::WebContents> web_contents,
const std::string& browser_context_id,
const GURL& visible_url,
PagePropertyFlags initial_properties,
base::TimeTicks visibility_change_time);
PageNodeImpl(const PageNodeImpl&) = delete;
PageNodeImpl& operator=(const PageNodeImpl&) = delete;
~PageNodeImpl() override;
// Partial PageNode implementation:
const std::string& GetBrowserContextID() const override;
resource_attribution::PageContext GetResourceContext() const override;
PageType GetType() const override;
bool IsFocused() const override;
bool IsVisible() const override;
base::TimeTicks GetLastVisibilityChangeTime() const override;
bool IsAudible() const override;
std::optional<base::TimeDelta> GetTimeSinceLastAudibleChange() const override;
bool HasPictureInPicture() const override;
bool HasFreezingOriginTrialOptOut() const override;
bool IsOffTheRecord() const override;
LoadingState GetLoadingState() const override;
ukm::SourceId GetUkmSourceID() const override;
LifecycleState GetLifecycleState() const override;
bool IsHoldingWebLock() const override;
bool IsHoldingBlockingIndexedDBLock() const override;
bool UsesWebRTC() const override;
int64_t GetNavigationID() const override;
const std::string& GetContentsMimeType() const override;
std::optional<blink::mojom::PermissionStatus>
GetNotificationPermissionStatus() const override;
base::TimeDelta GetTimeSinceLastNavigation() const override;
const GURL& GetMainFrameUrl() const override;
uint64_t EstimateMainFramePrivateFootprintSize() const override;
bool HadFormInteraction() const override;
bool HadUserEdits() const override;
base::WeakPtr<content::WebContents> GetWebContents() const override;
uint64_t EstimateResidentSetSize() const override;
uint64_t EstimatePrivateFootprintSize() const override;
base::WeakPtr<PageNode> GetWeakPtr() override;
base::WeakPtr<const PageNode> GetWeakPtr() const override;
// Returns the unique token for the page node. This function can be called
// from any thread.
const PageToken& page_token() const { return page_token_; }
// Returns a Perfetto track that can record trace events for the page. This
// function can be called from any thread.
const perfetto::NamedTrack& tracing_track() const { return tracing_track_; }
void SetType(PageType type);
void SetIsFocused(bool is_focused);
void SetIsVisible(bool is_visible);
void SetIsAudible(bool is_audible);
void SetHasPictureInPicture(bool has_picture_in_picture);
void SetLoadingState(LoadingState loading_state);
void SetUkmSourceId(ukm::SourceId ukm_source_id);
void OnFaviconUpdated();
void OnTitleUpdated();
void OnAboutToBeDiscarded(base::WeakPtr<PageNode> new_page_node);
// Set main frame information of a restored page before the first navigation
// is committed.
void SetMainFrameRestoredState(
const GURL& url,
blink::mojom::PermissionStatus notification_permission_status);
// Invoked when a main frame navigation is committed.
void OnMainFrameNavigationCommitted(
bool same_document,
base::TimeTicks navigation_committed_time,
int64_t navigation_id,
const GURL& url,
const std::string& contents_mime_type,
std::optional<blink::mojom::PermissionStatus>
notification_permission_status);
// While notification permission status is most often updated on main frame
// navigation, it can also be updated independently from main frame navigation
// when the user grants/revokes the permission.
void OnNotificationPermissionStatusChange(
blink::mojom::PermissionStatus permission_status);
// Accessors.
FrameNodeImpl* opener_frame_node() const;
FrameNodeImpl* embedder_frame_node() const;
FrameNodeImpl* main_frame_node() const;
NodeSetView<FrameNodeImpl*> main_frame_nodes() const;
// Invoked to set/clear the opener of this page.
void SetOpenerFrameNode(FrameNodeImpl* opener);
void ClearOpenerFrameNode();
// Invoked to set/clear the embedder of this page.
void SetEmbedderFrameNode(FrameNodeImpl* embedder);
void ClearEmbedderFrameNode();
void set_has_nonempty_beforeunload(bool has_nonempty_beforeunload);
void SetLifecycleStateForTesting(LifecycleState lifecycle_state) {
SetLifecycleState(lifecycle_state);
}
void SetHasFreezingOriginTrialOptOutForTesting(
bool has_freezing_origin_trial_opt_out) {
SetHasFreezingOriginTrialOptOut(has_freezing_origin_trial_opt_out);
}
void SetIsHoldingWebLockForTesting(bool is_holding_weblock) {
SetIsHoldingWebLock(is_holding_weblock);
}
void SetIsHoldingBlockingIndexedDBLockForTesting(
bool is_holding_blocking_indexeddb_lock) {
SetIsHoldingBlockingIndexedDBLock(is_holding_blocking_indexeddb_lock);
}
void SetUsesWebRTCForTesting(bool uses_webrtc) { SetUsesWebRTC(uses_webrtc); }
void SetHadFormInteractionForTesting(bool had_form_interaction) {
SetHadFormInteraction(had_form_interaction);
}
void SetHadUserEditsForTesting(bool had_user_edits) {
SetHadUserEdits(had_user_edits);
}
// Functions meant to be called by a FrameNodeImpl:
void AddFrame(base::PassKey<FrameNodeImpl>, FrameNodeImpl* frame_node);
void RemoveFrame(base::PassKey<FrameNodeImpl>, FrameNodeImpl* frame_node);
// Function meant to be called by FrozenFrameAggregator.
void SetLifecycleState(base::PassKey<FrozenFrameAggregator>,
LifecycleState lifecycle_state) {
SetLifecycleState(lifecycle_state);
}
// Functions meant to be called by PageAggregatorData:
void SetIsHoldingWebLock(base::PassKey<PageAggregatorData>,
bool is_holding_weblock) {
SetIsHoldingWebLock(is_holding_weblock);
}
void SetIsHoldingBlockingIndexedDBLock(
base::PassKey<PageAggregatorData>,
bool is_holding_blocking_indexeddb_lock) {
SetIsHoldingBlockingIndexedDBLock(is_holding_blocking_indexeddb_lock);
}
void SetUsesWebRTC(base::PassKey<PageAggregatorData>, bool uses_web_rtc) {
SetUsesWebRTC(uses_web_rtc);
}
void SetHadFormInteraction(base::PassKey<PageAggregatorData>,
bool had_form_interaction) {
SetHadFormInteraction(had_form_interaction);
}
void SetHadUserEdits(base::PassKey<PageAggregatorData>, bool had_user_edits) {
SetHadUserEdits(had_user_edits);
}
void SetHasFreezingOriginTrialOptOut(base::PassKey<PageAggregatorData>,
bool has_freezing_origin_trial_opt_out) {
SetHasFreezingOriginTrialOptOut(has_freezing_origin_trial_opt_out);
}
private:
friend class PageNodeImplDescriber;
// Partial PageNode implementation:
const FrameNode* GetOpenerFrameNode() const override;
const FrameNode* GetEmbedderFrameNode() const override;
const FrameNode* GetMainFrameNode() const override;
NodeSetView<const FrameNode*> GetMainFrameNodes() const override;
// NodeBase:
void OnInitializingProperties() override;
void OnBeforeLeavingGraph() override;
void CleanUpNodeState() override;
void SetLifecycleState(LifecycleState lifecycle_state);
void SetIsHoldingWebLock(bool is_holding_weblock);
void SetIsHoldingBlockingIndexedDBLock(
bool is_holding_blocking_indexeddb_lock);
void SetUsesWebRTC(bool uses_web_rtc);
void SetHadFormInteraction(bool had_form_interaction);
void SetHadUserEdits(bool had_user_edits);
void SetHasFreezingOriginTrialOptOut(bool has_freezing_origin_trial_opt_out);
// Emits an instant event recording when the main frame url changed to `url`.
// Also includes `navigation_id` if it's not nullopt.
void EmitMainFrameUrlChangedEvent(
const GURL& url,
std::optional<int64_t> navigation_id = std::nullopt) const;
// Emits the beginning or end of a trace event when the LoadingState changes
// to `loading_state`.
void EmitLoadingTraceEvent(LoadingState loading_state) const;
// The WebContents associated with this page.
const base::WeakPtr<content::WebContents> web_contents_;
// The unique token that identifies this PageNode for the life of the browser.
const PageToken page_token_;
// Perfetto track that can record trace events for the page.
const perfetto::NamedTrack tracing_track_;
// The main frame nodes of this page. There can be more than one main frame
// in a page, among other reasons because during main frame navigation, the
// pending navigation will coexist with the existing main frame until it's
// committed.
NodeSet main_frame_nodes_ GUARDED_BY_CONTEXT(sequence_checker_);
// The total count of frames that tally up to this page.
size_t frame_node_count_ GUARDED_BY_CONTEXT(sequence_checker_) = 0;
// The last time at which the page visibility changed.
base::TimeTicks visibility_change_time_ GUARDED_BY_CONTEXT(sequence_checker_);
// The last time at which the audible property changed, or nullopt if the node
// has never been audible.
std::optional<base::TimeTicks> audible_change_time_
GUARDED_BY_CONTEXT(sequence_checker_);
// The last time at which a main frame navigation was committed.
base::TimeTicks navigation_committed_time_
GUARDED_BY_CONTEXT(sequence_checker_);
// Indicates whether or not this page has a non-empty beforeunload handler.
// This is an aggregation of the same value on each frame in the page's frame
// tree. The aggregation is made at the moment all frames associated with a
// page have transition to frozen.
bool has_nonempty_beforeunload_ GUARDED_BY_CONTEXT(sequence_checker_) = false;
// The URL the main frame last committed, or the initial URL a page was
// initialized with. The latter case is distinguished by a zero navigation ID.
ObservedProperty::
NotifiesOnlyOnChanges<GURL, &PageNodeObserver::OnMainFrameUrlChanged>
main_frame_url_ GUARDED_BY_CONTEXT(sequence_checker_);
// The unique ID of the navigation handle the main frame last committed, or
// zero if the page has never committed a navigation.
int64_t navigation_id_ GUARDED_BY_CONTEXT(sequence_checker_) = 0;
// The MIME type of the content associated with the last committed navigation
// event for the main frame of this page or an empty string if the page has
// never committed a navigation
std::string contents_mime_type_ GUARDED_BY_CONTEXT(sequence_checker_);
// The notification permission status for the last committed main frame
// navigation.
ObservedProperty::NotifiesOnlyOnChangesWithPreviousValue<
std::optional<blink::mojom::PermissionStatus>,
std::optional<blink::mojom::PermissionStatus>,
&PageNodeObserver::OnPageNotificationPermissionStatusChange>
notification_permission_status_ GUARDED_BY_CONTEXT(sequence_checker_);
// The unique ID of the browser context that this page belongs to.
const std::string browser_context_id_;
// The opener of this page, if there is one.
raw_ptr<FrameNodeImpl> opener_frame_node_
GUARDED_BY_CONTEXT(sequence_checker_) = nullptr;
// The embedder of this page, if there is one.
raw_ptr<FrameNodeImpl> embedder_frame_node_
GUARDED_BY_CONTEXT(sequence_checker_) = nullptr;
// The type of the page.
ObservedProperty::NotifiesOnlyOnChangesWithPreviousValue<
PageType,
PageType,
&PageNodeObserver::OnTypeChanged>
type_ GUARDED_BY_CONTEXT(sequence_checker_){PageType::kUnknown};
// Whether or not the page is focused. Driven by browser instrumentation.
ObservedProperty::NotifiesOnlyOnChanges<bool,
&PageNodeObserver::OnIsFocusedChanged>
is_focused_ GUARDED_BY_CONTEXT(sequence_checker_){false};
// Whether or not the page is visible. Driven by browser instrumentation.
// Initialized on construction.
ObservedProperty::NotifiesOnlyOnChanges<bool,
&PageNodeObserver::OnIsVisibleChanged>
is_visible_ GUARDED_BY_CONTEXT(sequence_checker_){false};
// Whether or not the page is audible. Driven by browser instrumentation.
// Initialized on construction.
ObservedProperty::NotifiesOnlyOnChanges<bool,
&PageNodeObserver::OnIsAudibleChanged>
is_audible_ GUARDED_BY_CONTEXT(sequence_checker_){false};
// Whether or not the page is displaying content in picture-in-picture. Driven
// by browser instrumentation. Initialized on construction.
ObservedProperty::NotifiesOnlyOnChanges<
bool,
&PageNodeObserver::OnHasPictureInPictureChanged>
has_picture_in_picture_ GUARDED_BY_CONTEXT(sequence_checker_){false};
// Whether the page is opted-out from freezing via origin trial, i.e. if any
// of its current frames sets the origin trial.
ObservedProperty::NotifiesOnlyOnChanges<
bool,
&PageNodeObserver::OnPageHasFreezingOriginTrialOptOutChanged>
has_freezing_origin_trial_opt_out_ GUARDED_BY_CONTEXT(sequence_checker_){
false};
const bool is_off_the_record_;
// The loading state. This is driven by instrumentation in the browser
// process.
ObservedProperty::NotifiesOnlyOnChangesWithPreviousValue<
LoadingState,
LoadingState,
&PageNodeObserver::OnLoadingStateChanged>
loading_state_ GUARDED_BY_CONTEXT(sequence_checker_){
LoadingState::kLoadingNotStarted};
// The UKM source ID associated with the URL of the main frame of this page.
ObservedProperty::NotifiesOnlyOnChanges<
ukm::SourceId,
&PageNodeObserver::OnUkmSourceIdChanged>
ukm_source_id_ GUARDED_BY_CONTEXT(sequence_checker_){
ukm::kInvalidSourceId};
// The lifecycle state of this page. This is aggregated from the lifecycle
// state of each frame in the frame tree.
ObservedProperty::NotifiesOnlyOnChanges<
LifecycleState,
&PageNodeObserver::OnPageLifecycleStateChanged>
lifecycle_state_ GUARDED_BY_CONTEXT(sequence_checker_){
LifecycleState::kRunning};
// Indicates if at least one frame of the page is currently holding a WebLock.
ObservedProperty::NotifiesOnlyOnChanges<
bool,
&PageNodeObserver::OnPageIsHoldingWebLockChanged>
is_holding_weblock_ GUARDED_BY_CONTEXT(sequence_checker_){false};
// Indicates if at least one frame of the page is currently holding an
// IndexedDB lock that is blocking another client.
ObservedProperty::NotifiesOnlyOnChanges<
bool,
&PageNodeObserver::OnPageIsHoldingBlockingIndexedDBLockChanged>
is_holding_blocking_indexeddb_lock_ GUARDED_BY_CONTEXT(sequence_checker_){
false};
// Indicates if at least one frame of the page currently uses WebRTC.
ObservedProperty::
NotifiesOnlyOnChanges<bool, &PageNodeObserver::OnPageUsesWebRTCChanged>
uses_web_rtc_ GUARDED_BY_CONTEXT(sequence_checker_){false};
// Indicates if at least one frame of the page has received some form
// interactions.
ObservedProperty::NotifiesOnlyOnChanges<
bool,
&PageNodeObserver::OnHadFormInteractionChanged>
had_form_interaction_ GUARDED_BY_CONTEXT(sequence_checker_){false};
// Indicates if at least one frame of the page has received some
// user-initiated edits.
ObservedProperty::
NotifiesOnlyOnChanges<bool, &PageNodeObserver::OnHadUserEditsChanged>
had_user_edits_ GUARDED_BY_CONTEXT(sequence_checker_){false};
base::WeakPtrFactory<PageNodeImpl> weak_factory_
GUARDED_BY_CONTEXT(sequence_checker_){this};
};
} // namespace performance_manager
#endif // COMPONENTS_PERFORMANCE_MANAGER_GRAPH_PAGE_NODE_IMPL_H_