blob: fdcc05897e0f038d8417445a5f4466705d271883 [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/containers/flat_set.h"
#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/graph/node_base.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 "content/public/browser/browsing_instance_id.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"
namespace performance_manager {
class FrameNodeImplDescriber;
namespace execution_context {
class ExecutionContextAccess;
} // namespace execution_context
// 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:
static const char kDefaultPriorityReason[];
static constexpr NodeTypeEnum Type() { return NodeTypeEnum::kFrame; }
// 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,
// `outer_document_for_fenced_frame` 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_fenced_frame,
int render_frame_id,
const blink::LocalFrameToken& frame_token,
content::BrowsingInstanceId browsing_instance_id,
content::SiteInstanceId site_instance_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 OnNonPersistentNotificationCreated() override;
void OnFirstContentfulPaint(
base::TimeDelta time_since_navigation_start) override;
void OnWebMemoryMeasurementRequested(
mojom::WebMemoryMeasurement::Mode mode,
OnWebMemoryMeasurementRequestedCallback callback) override;
// Partial FrameNode implementation:
const blink::LocalFrameToken& GetFrameToken() const override;
content::BrowsingInstanceId GetBrowsingInstanceId() const override;
content::SiteInstanceId GetSiteInstanceId() 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;
bool IsCurrent() const override;
const PriorityAndReason& GetPriorityAndReason() const override;
bool GetNetworkAlmostIdle() const override;
bool IsAdFrame() const override;
bool IsHoldingWebLock() const override;
bool IsHoldingIndexedDBLock() const override;
bool HadFormInteraction() const override;
bool HadUserEdits() const override;
bool IsAudible() const override;
bool IsCapturingMediaStream() const override;
std::optional<bool> IntersectsViewport() const override;
Visibility GetVisibility() const override;
const RenderFrameHostProxy& GetRenderFrameHostProxy() const override;
uint64_t GetResidentSetKbEstimate() const override;
uint64_t GetPrivateFootprintKbEstimate() const override;
// Getters for const properties.
FrameNodeImpl* parent_frame_node() const;
FrameNodeImpl* parent_or_outer_document_or_embedder() const;
FrameNodeImpl* outer_document_for_fenced_frame() const;
PageNodeImpl* page_node() const;
ProcessNodeImpl* process_node() const;
int render_frame_id() const;
// Getters for non-const properties. These are not thread safe.
const base::flat_set<raw_ptr<FrameNodeImpl, CtnExperimental>>&
child_frame_nodes() const;
const base::flat_set<raw_ptr<PageNodeImpl, CtnExperimental>>&
opened_page_nodes() const;
const base::flat_set<raw_ptr<PageNodeImpl, CtnExperimental>>&
embedded_page_nodes() const;
const base::flat_set<raw_ptr<WorkerNodeImpl, CtnExperimental>>&
child_worker_nodes() const;
// Setters are not thread safe.
void SetIsCurrent(bool is_current);
void SetIsHoldingWebLock(bool is_holding_weblock);
void SetIsHoldingIndexedDBLock(bool is_holding_indexeddb_lock);
void SetIsAudible(bool is_audible);
void SetIsCapturingMediaStream(bool is_capturing_media_stream);
void SetIntersectsViewport(bool intersects_viewport);
void SetInitialVisibility(Visibility visibility);
void SetVisibility(Visibility visibility);
void SetResidentSetKbEstimate(uint64_t rss_estimate);
void SetPrivateFootprintKbEstimate(uint64_t private_footprint_estimate);
// Invoked when a navigation is committed in the frame.
void OnNavigationCommitted(const GURL& url, bool same_document);
// 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> GetWeakPtrOnUIThread();
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);
// Used by the ExecutionContextRegistry mechanism.
std::unique_ptr<NodeAttachedData>* GetExecutionContextStorage(
base::PassKey<execution_context::ExecutionContextAccess> key) {
return &execution_context_;
}
private:
friend class ExecutionContextPriorityAccess;
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;
bool VisitChildFrameNodes(const FrameNodeVisitor& visitor) const override;
const base::flat_set<const FrameNode*> GetChildFrameNodes() const override;
bool VisitOpenedPageNodes(const PageNodeVisitor& visitor) const override;
const base::flat_set<const PageNode*> GetOpenedPageNodes() const override;
bool VisitEmbeddedPageNodes(const PageNodeVisitor& visitor) const override;
const base::flat_set<const PageNode*> GetEmbeddedPageNodes() const override;
const base::flat_set<const WorkerNode*> GetChildWorkerNodes() const override;
bool VisitChildDedicatedWorkers(
const WorkerNodeVisitor& visitor) const override;
// Properties associated with a Document, which are reset when a
// different-document navigation is committed in the frame.
struct DocumentProperties {
DocumentProperties();
~DocumentProperties();
void Reset(FrameNodeImpl* frame_node, const GURL& url_in);
ObservedProperty::NotifiesOnlyOnChangesWithPreviousValue<
GURL,
const GURL&,
&FrameNodeObserver::OnURLChanged>
url;
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};
};
// Invoked by subframes on joining/leaving the graph.
void AddChildFrame(FrameNodeImpl* frame_node);
void RemoveChildFrame(FrameNodeImpl* frame_node);
// NodeBase:
void OnJoiningGraph() override;
void OnBeforeLeavingGraph() override;
void RemoveNodeAttachedData() 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;
mojo::Receiver<mojom::DocumentCoordinationUnit> receiver_{this};
const raw_ptr<FrameNodeImpl, DanglingUntriaged> parent_frame_node_;
const raw_ptr<FrameNodeImpl, DanglingUntriaged>
outer_document_for_fenced_frame_;
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 SiteInstance this frame belongs to. Frames in the
// same SiteInstance may sychronously script each other. Frames with the
// same |site_instance_id_| will also have the same |browsing_instance_id_|.
const content::SiteInstanceId site_instance_id_;
// A proxy object that lets the underlying RFH be safely dereferenced on the
// UI thread.
const RenderFrameHostProxy render_frame_host_proxy_;
base::flat_set<raw_ptr<FrameNodeImpl, CtnExperimental>> child_frame_nodes_;
// The set of pages that have been opened by this frame.
base::flat_set<raw_ptr<PageNodeImpl, CtnExperimental>> opened_page_nodes_;
// The set of pages that have been embedded by this frame.
base::flat_set<raw_ptr<PageNodeImpl, CtnExperimental>> embedded_page_nodes_;
uint64_t resident_set_kb_estimate_ = 0;
uint64_t private_footprint_kb_estimate_ = 0;
// Does *not* change when a navigation is committed.
ObservedProperty::NotifiesOnlyOnChanges<
LifecycleState,
&FrameNodeObserver::OnFrameLifecycleStateChanged>
lifecycle_state_{LifecycleState::kRunning};
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::OnFrameIsHoldingIndexedDBLockChanged>
is_holding_indexeddb_lock_{false};
ObservedProperty::
NotifiesOnlyOnChanges<bool, &FrameNodeObserver::OnIsCurrentChanged>
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.
base::flat_set<raw_ptr<WorkerNodeImpl, CtnExperimental>> child_worker_nodes_;
// Frame priority information. Set via ExecutionContextPriorityDecorator.
ObservedProperty::NotifiesOnlyOnChangesWithPreviousValue<
PriorityAndReason,
const 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 if the frame intersects with the viewport.
//
// Note that this property is always invalid for a main frame. This is because
// the main frame always occupies the entirety of the viewport so there is no
// point in tracking it. To avoid programming mistakes, it is forbidden to
// query this property for the main frame.
ObservedProperty::NotifiesOnlyOnChanges<
std::optional<bool>,
&FrameNodeObserver::OnIntersectsViewportChanged>
intersects_viewport_;
// Indicates if the frame is visible. This is maintained by the
// FrameVisibilityDecorator.
ObservedProperty::NotifiesOnlyOnChangesWithPreviousValue<
Visibility,
Visibility,
&FrameNodeObserver::OnFrameVisibilityChanged>
visibility_{Visibility::kUnknown};
// Inline storage for ExecutionContext.
std::unique_ptr<NodeAttachedData> execution_context_;
base::WeakPtr<FrameNodeImpl> weak_this_;
base::WeakPtrFactory<FrameNodeImpl> weak_factory_
GUARDED_BY_CONTEXT(sequence_checker_){this};
};
} // namespace performance_manager
#endif // COMPONENTS_PERFORMANCE_MANAGER_GRAPH_FRAME_NODE_IMPL_H_