blob: 1f3ecc20e2e2a0b7dcaf7c1630801b5cfcaeb7e9 [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_FRAME_NODE_IMPL_H_
#define COMPONENTS_PERFORMANCE_MANAGER_GRAPH_FRAME_NODE_IMPL_H_
#include <memory>
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/types/pass_key.h"
#include "base/unguessable_token.h"
#include "components/performance_manager/execution_context/execution_context_impl.h"
#include "components/performance_manager/graph/node_attached_data_storage.h"
#include "components/performance_manager/graph/node_base.h"
#include "components/performance_manager/graph/node_inline_data.h"
#include "components/performance_manager/public/graph/frame_node.h"
#include "components/performance_manager/public/graph/node_attached_data.h"
#include "components/performance_manager/public/mojom/coordination_unit.mojom.h"
#include "components/performance_manager/public/mojom/web_memory.mojom.h"
#include "components/performance_manager/public/render_frame_host_proxy.h"
#include "components/performance_manager/resource_attribution/cpu_measurement_data.h"
#include "content/public/browser/browsing_instance_id.h"
#include "content/public/browser/site_instance.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "third_party/blink/public/common/tokens/tokens.h"
#include "url/gurl.h"
#include "url/origin.h"
namespace performance_manager {
class FrameNodeImplDescriber;
// Frame nodes for a tree structure that is described in
// components/performance_manager/public/graph/frame_node.h.
class FrameNodeImpl
: public PublicNodeImpl<FrameNodeImpl, FrameNode>,
public TypedNodeBase<FrameNodeImpl, FrameNode, FrameNodeObserver>,
public mojom::DocumentCoordinationUnit,
public SupportsNodeInlineData<
execution_context::FrameExecutionContext,
resource_attribution::SharedCPUTimeResultData,
// Keep this last to avoid merge conflicts.
NodeAttachedDataStorage> {
public:
static const char kDefaultPriorityReason[];
using TypedNodeBase<FrameNodeImpl, FrameNode, FrameNodeObserver>::FromNode;
// Construct a frame node associated with a `process_node`, a `page_node` and
// optionally with a `parent_frame_node`. For the main frame of `page_node`
// the `parent_frame_node` parameter should be nullptr. For <fencedframe>s,
// and MPArch aware <webview>s, `outer_document_for_inner_frame_root` should
// be set to its outer document, nullptr otherwise. `render_frame_id` is the
// routing id of the frame (from RenderFrameHost::GetRoutingID).
FrameNodeImpl(ProcessNodeImpl* process_node,
PageNodeImpl* page_node,
FrameNodeImpl* parent_frame_node,
FrameNodeImpl* outer_document_for_inner_frame_root,
int render_frame_id,
const blink::LocalFrameToken& frame_token,
content::BrowsingInstanceId browsing_instance_id,
content::SiteInstanceGroupId site_instance_group_id,
bool is_current);
FrameNodeImpl(const FrameNodeImpl&) = delete;
FrameNodeImpl& operator=(const FrameNodeImpl&) = delete;
~FrameNodeImpl() override;
void Bind(mojo::PendingReceiver<mojom::DocumentCoordinationUnit> receiver);
// mojom::DocumentCoordinationUnit implementation.
void SetNetworkAlmostIdle() override;
void SetLifecycleState(LifecycleState state) override;
void SetHasNonEmptyBeforeUnload(bool has_nonempty_beforeunload) override;
void SetIsAdFrame(bool is_ad_frame) override;
void SetHadFormInteraction() override;
void SetHadUserEdits() override;
void OnStartedUsingWebRTC() override;
void OnStoppedUsingWebRTC() override;
void OnNonPersistentNotificationCreated() override;
void OnFirstContentfulPaint(
base::TimeDelta time_since_navigation_start) override;
void OnWebMemoryMeasurementRequested(
mojom::WebMemoryMeasurement::Mode mode,
OnWebMemoryMeasurementRequestedCallback callback) override;
void OnFreezingOriginTrialOptOut() override;
// Partial FrameNode implementation:
const blink::LocalFrameToken& GetFrameToken() const override;
content::BrowsingInstanceId GetBrowsingInstanceId() const override;
content::SiteInstanceGroupId GetSiteInstanceGroupId() const override;
resource_attribution::FrameContext GetResourceContext() const override;
bool IsMainFrame() const override;
LifecycleState GetLifecycleState() const override;
bool HasNonemptyBeforeUnload() const override;
const GURL& GetURL() const override;
const std::optional<url::Origin>& GetOrigin() const override;
bool IsCurrent() const override;
const PriorityAndReason& GetPriorityAndReason() const override;
bool GetNetworkAlmostIdle() const override;
bool IsAdFrame() const override;
bool IsHoldingWebLock() const override;
bool IsHoldingBlockingIndexedDBLock() const override;
bool UsesWebRTC() const override;
bool HadUserActivation() const override;
bool HadFormInteraction() const override;
bool HadUserEdits() const override;
bool IsAudible() const override;
bool IsCapturingMediaStream() const override;
bool HasFreezingOriginTrialOptOut() const override;
ViewportIntersection GetViewportIntersection() const override;
Visibility GetVisibility() const override;
bool IsIntersectingLargeArea() const override;
bool IsImportant() const override;
const RenderFrameHostProxy& GetRenderFrameHostProxy() const override;
base::ByteCount GetResidentSetEstimate() const override;
base::ByteCount GetPrivateFootprintEstimate() const override;
// Getters for const properties.
FrameNodeImpl* parent_frame_node() const;
FrameNodeImpl* parent_or_outer_document_or_embedder() const;
PageNodeImpl* page_node() const;
ProcessNodeImpl* process_node() const;
int render_frame_id() const;
// Getters for non-const properties. These are not thread safe.
NodeSetView<FrameNodeImpl*> child_frame_nodes() const;
NodeSetView<PageNodeImpl*> opened_page_nodes() const;
NodeSetView<PageNodeImpl*> embedded_page_nodes() const;
NodeSetView<WorkerNodeImpl*> child_worker_nodes() const;
// Setters are not thread safe.
// Updates the IsCurrent() property on both `previous_frame_node` and
// `current_frame_node` and sends a single notification to FrameNodeObservers.
static void UpdateCurrentFrame(FrameNodeImpl* previous_frame_node,
FrameNodeImpl* current_frame_node,
GraphImpl* graph);
void SetHadUserActivation();
void SetIsHoldingWebLock(bool is_holding_weblock);
void SetIsHoldingBlockingIndexedDBLock(
bool is_holding_blocking_indexeddb_lock);
void SetIsAudible(bool is_audible);
void SetIsCapturingMediaStream(bool is_capturing_media_stream);
void SetViewportIntersection(ViewportIntersection viewport_intersection);
void SetInitialVisibility(Visibility visibility);
void SetVisibility(Visibility visibility);
void SetIsIntersectingLargeArea(bool is_intersecting_large_area);
void SetIsImportant(bool is_important);
void SetResidentSetEstimate(base::ByteCount rss_estimate);
void SetPrivateFootprintEstimate(base::ByteCount private_footprint_estimate);
// Invoked when a navigation is committed in the frame.
void OnNavigationCommitted(GURL url,
url::Origin origin,
bool same_document,
bool is_served_from_back_forward_cache);
// Traverses the frame tree and notifies all frames that their embedding
// primary page is about to be discarded.
void OnPrimaryPageAboutToBeDiscarded();
// Invoked by |worker_node| when it starts/stops being a child of this frame.
void AddChildWorker(WorkerNodeImpl* worker_node);
void RemoveChildWorker(WorkerNodeImpl* worker_node);
// Invoked to set the frame priority, and the reason behind it.
void SetPriorityAndReason(const PriorityAndReason& priority_and_reason);
base::WeakPtr<FrameNodeImpl> GetWeakPtr();
void SeverPageRelationshipsAndMaybeReparentForTesting() {
SeverPageRelationshipsAndMaybeReparent();
}
// Implementation details below this point.
// Invoked by opened pages when this frame is set/cleared as their opener.
// See PageNodeImpl::(Set|Clear)OpenerFrameNode.
void AddOpenedPage(base::PassKey<PageNodeImpl> key, PageNodeImpl* page_node);
void RemoveOpenedPage(base::PassKey<PageNodeImpl> key,
PageNodeImpl* page_node);
// Invoked by embedded pages when this frame is set/cleared as their embedder.
// See PageNodeImpl::(Set|Clear)EmbedderFrameNodeAndEmbeddingType.
void AddEmbeddedPage(base::PassKey<PageNodeImpl> key,
PageNodeImpl* page_node);
void RemoveEmbeddedPage(base::PassKey<PageNodeImpl> key,
PageNodeImpl* page_node);
private:
friend class FrameNodeImplDescriber;
friend class ProcessNodeImpl;
// Rest of FrameNode implementation. These are private so that users of the
// impl use the private getters rather than the public interface.
const FrameNode* GetParentFrameNode() const override;
const FrameNode* GetParentOrOuterDocumentOrEmbedder() const override;
const PageNode* GetPageNode() const override;
const ProcessNode* GetProcessNode() const override;
NodeSetView<const FrameNode*> GetChildFrameNodes() const override;
NodeSetView<const PageNode*> GetOpenedPageNodes() const override;
NodeSetView<const PageNode*> GetEmbeddedPageNodes() const override;
NodeSetView<const WorkerNode*> GetChildWorkerNodes() const override;
// Properties associated with a Document, which are reset when a
// different-document navigation is committed in the frame.
// LINT.IfChange(document_prop)
struct DocumentProperties {
DocumentProperties();
~DocumentProperties();
void Reset(FrameNodeImpl* frame_node, GURL url_in, url::Origin origin_in);
// FrameNodeObserver::OnURLChanged/OnOriginChanged() is invoked when the
// URL/origin changes. Not using ObservedProperty here to allow updating
// both properties before notifying observers (see
// `FrameNodeImpl::DocumentProperties::Reset` implementation).
GURL url;
std::optional<url::Origin> origin;
bool has_nonempty_beforeunload = false;
// Network is considered almost idle when there are no more than 2 network
// connections.
ObservedProperty::NotifiesOnlyOnChanges<
bool,
&FrameNodeObserver::OnNetworkAlmostIdleChanged>
network_almost_idle{false};
// Indicates if a form in the frame has been interacted with.
// TODO(crbug.com/40735910): Remove this once HadUserEdits is known to cover
// all existing cases.
ObservedProperty::NotifiesOnlyOnChanges<
bool,
&FrameNodeObserver::OnHadFormInteractionChanged>
had_form_interaction{false};
// Indicates that the user has made edits to the page. This is a superset of
// `had_form_interaction`, but can also represent changes to
// `contenteditable` elements.
ObservedProperty::
NotifiesOnlyOnChanges<bool, &FrameNodeObserver::OnHadUserEditsChanged>
had_user_edits{false};
// Whether the document uses WebRTC.
ObservedProperty::NotifiesOnlyOnChanges<
bool,
&FrameNodeObserver::OnFrameUsesWebRTCChanged>
uses_web_rtc{false};
// Whether the document is opted-out from freezing via origin trial.
ObservedProperty::NotifiesOnlyOnChanges<
bool,
&FrameNodeObserver::OnFrameHasFreezingOriginTrialOptOutChanged>
has_freezing_origin_trial_opt_out{false};
};
// LINT.ThenChange(//components/performance_manager/graph/frame_node_impl.cc:document_prop_reset)
// Invoked by subframes on joining/leaving the graph.
void AddChildFrame(FrameNodeImpl* frame_node);
void RemoveChildFrame(FrameNodeImpl* frame_node);
// NodeBase:
void OnInitializingProperties() override;
void OnInitializingEdges() override;
void OnBeforeLeavingGraph() override;
void OnUninitializingEdges() override;
void CleanUpNodeState() override;
// Helper function to sever all opened/embedded page relationships. This is
// called before destroying the frame node in "OnBeforeLeavingGraph". Note
// that this will reparent embedded pages to this frame's parent so that
// tracking is maintained.
void SeverPageRelationshipsAndMaybeReparent();
// This is not quite the same as GetMainFrame, because there can be multiple
// main frames while the main frame is navigating. This explicitly walks up
// the tree to find the main frame that corresponds to this frame tree node,
// even if it is not current.
FrameNodeImpl* GetFrameTreeRoot() const;
bool HasFrameNodeInAncestors(FrameNodeImpl* frame_node) const;
bool HasFrameNodeInDescendants(FrameNodeImpl* frame_node) const;
bool HasFrameNodeInTree(FrameNodeImpl* frame_node) const;
// Sets the `is_current_` property. Returns true if its value changed as a
// result of this call.
bool SetIsCurrent(bool is_current);
// Updates the inherited `IsIntersectingLargeArea()` property of this frame.
void SetInheritedIsIntersectingLargeArea(bool is_intersecting_large_area);
void SetIsIntersectingLargeAreaImpl(bool is_intersecting_large_area);
mojo::Receiver<mojom::DocumentCoordinationUnit> receiver_{this};
const raw_ptr<FrameNodeImpl, DanglingUntriaged> parent_frame_node_;
const raw_ptr<FrameNodeImpl, DanglingUntriaged>
outer_document_for_inner_frame_root_;
const raw_ptr<PageNodeImpl, DanglingUntriaged> page_node_;
const raw_ptr<ProcessNodeImpl, DanglingUntriaged> process_node_;
// The routing id of the frame.
const int render_frame_id_;
// This is the unique token for this frame instance as per e.g.
// RenderFrameHost::GetFrameToken().
const blink::LocalFrameToken frame_token_;
// The unique ID of the BrowsingInstance this frame belongs to. Frames in the
// same BrowsingInstance are allowed to script each other at least
// asynchronously (if cross-site), and sometimes synchronously (if same-site,
// and thus same SiteInstance).
const content::BrowsingInstanceId browsing_instance_id_;
// The unique ID of the SiteInstanceGroup this frame belongs to. Frames in the
// same SiteInstanceGroup are in the same process and exist as LocalFrames in
// the same blink::FrameTree. Frames with the same |site_instance_group_id_|
// will also have the same |browsing_instance_id_|.
const content::SiteInstanceGroupId site_instance_group_id_;
// A proxy object that lets the underlying RFH be safely dereferenced on the
// UI thread.
const RenderFrameHostProxy render_frame_host_proxy_;
NodeSet child_frame_nodes_;
// The set of pages that have been opened by this frame.
NodeSet opened_page_nodes_;
// The set of pages that have been embedded by this frame.
NodeSet embedded_page_nodes_;
base::ByteCount resident_set_estimate_;
base::ByteCount private_footprint_estimate_;
// Does *not* change when a navigation is committed.
ObservedProperty::NotifiesOnlyOnChanges<
LifecycleState,
&FrameNodeObserver::OnFrameLifecycleStateChanged>
lifecycle_state_{LifecycleState::kRunning};
// Indicates if the frame has been interacted with.
ObservedProperty::NotifiesOnlyOnChanges<
bool,
&FrameNodeObserver::OnHadUserActivationChanged>
had_user_activation_{false};
ObservedProperty::
NotifiesOnlyOnChanges<bool, &FrameNodeObserver::OnIsAdFrameChanged>
is_ad_frame_{false};
// Locks held by a frame are tracked independently from navigation
// (specifically, a few tasks must run in the Web Lock and IndexedDB
// subsystems after a navigation for locks to be released).
ObservedProperty::NotifiesOnlyOnChanges<
bool,
&FrameNodeObserver::OnFrameIsHoldingWebLockChanged>
is_holding_weblock_{false};
ObservedProperty::NotifiesOnlyOnChanges<
bool,
&FrameNodeObserver::OnFrameIsHoldingBlockingIndexedDBLockChanged>
is_holding_blocking_indexeddb_lock_{false};
bool is_current_{false};
// Properties associated with a Document, which are reset when a
// different-document navigation is committed in the frame.
//
// TODO(fdoray): Cleanup this once there is a 1:1 mapping between
// RenderFrameHost and Document https://crbug.com/936696.
DocumentProperties document_;
// The child workers of this frame.
NodeSet child_worker_nodes_;
// Frame priority information. Set via ExecutionContextPriorityDecorator.
ObservedProperty::NotifiesOnlyOnChangesWithPreviousValue<
PriorityAndReason,
&FrameNodeObserver::OnPriorityAndReasonChanged>
priority_and_reason_{PriorityAndReason(base::TaskPriority::LOWEST,
kDefaultPriorityReason)};
// Indicates if the frame is audible. This is tracked independently of a
// document, and if a document swap occurs the audio stream monitor machinery
// will keep this up to date.
ObservedProperty::
NotifiesOnlyOnChanges<bool, &FrameNodeObserver::OnIsAudibleChanged>
is_audible_{false};
// Indicates if the frame is capturing at least one media stream.
ObservedProperty::NotifiesOnlyOnChanges<
bool,
&FrameNodeObserver::OnIsCapturingMediaStreamChanged>
is_capturing_media_stream_{false};
// Indicates the intersection between the frame and the viewport.
//
// Note that calling `GetViewportIntersection()` will always returns
// ViewportIntersection::kIntersecting for the outermost main frame. This is
// because it always occupies the entirety of the viewport, so there is no
// point in tracking it.
ObservedProperty::NotifiesOnlyOnChanges<
ViewportIntersection,
&FrameNodeObserver::OnViewportIntersectionChanged>
viewport_intersection_{ViewportIntersection::kUnknown};
// Indicates if the frame is visible. This is maintained by the
// FrameVisibilityDecorator.
ObservedProperty::NotifiesOnlyOnChangesWithPreviousValue<
Visibility,
&FrameNodeObserver::OnFrameVisibilityChanged>
visibility_{Visibility::kUnknown};
// Indicates if this frame intersects with a large area of the viewport.
// Defaults to true when its value is unknown.
bool is_intersecting_large_area_ = true;
// Indicates that SetIsIntersectingLargeArea() was called for this frame. This
// is only called for local root frames and means this frame should not
// inherit the value of its parent.
bool has_is_intersecting_large_area_updates_ = false;
ObservedProperty::
NotifiesOnlyOnChanges<bool, &FrameNodeObserver::OnIsImportantChanged>
is_important_{true};
base::WeakPtrFactory<FrameNodeImpl> weak_factory_
GUARDED_BY_CONTEXT(sequence_checker_){this};
};
} // namespace performance_manager
#endif // COMPONENTS_PERFORMANCE_MANAGER_GRAPH_FRAME_NODE_IMPL_H_