| // 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 CONTENT_RENDERER_RENDERER_NAVIGATION_METRICS_MANAGER_H_ |
| #define CONTENT_RENDERER_RENDERER_NAVIGATION_METRICS_MANAGER_H_ |
| |
| #include <map> |
| |
| #include "base/time/time.h" |
| #include "base/timer/timer.h" |
| #include "base/unguessable_token.h" |
| #include "content/common/content_export.h" |
| #include "url/gurl.h" |
| |
| namespace content { |
| |
| // This class collects start and end times of different events pertaining to |
| // navigations in the renderer process. These events include RenderView/WebView |
| // creation, proxy creation, (possibly provisional) frame creation, and |
| // navigation commit. The main goal is to output a trace with the renderer's |
| // view of navigation events, as well as to record metrics for durations of |
| // those events. This is done by calling `RecordTraceEventsAndMetrics()` when a |
| // navigation finishes committing. The |
| // RendererNavigationMetricsManager::Timeline struct below keeps all the |
| // necessary timestamps for a single navigation. |
| // |
| // Multiple navigations may be in progress in the same renderer process, so this |
| // class keeps a map of Timelines keyed by "navigation metrics tokens", which |
| // are passed in by the browser process in IPCs related to navigations. Each |
| // navigation has a unique navigation metrics token, with the source of truth |
| // stored in the NavigationRequest. |
| // |
| // If a navigation starts creating view/frame/proxy objects but ends up not |
| // committing in this process, such as after a cross-process redirect or if the |
| // navigation is explicitly canceled by the user or another navigation, its |
| // timeline data is cleaned up lazily. In particular, a navigation's timestamps |
| // are cleaned up if the navigation hasn't committed after a 5-minute timeout - |
| // see note in RendererNavigationMetricsManager::GetOrCreateTimeline() about how |
| // this works, and why using explicit cancellation signals is hard. The goal is |
| // to change this into explicit cleanup in the future by improving signals for |
| // navigation cancellations and making them aware of navigation metrics tokens. |
| // |
| // There is one global RendererNavigationMetricsManager object in each renderer |
| // process, accessible via the `Instance()` method below. |
| class CONTENT_EXPORT RendererNavigationMetricsManager { |
| public: |
| RendererNavigationMetricsManager(); |
| |
| static RendererNavigationMetricsManager& Instance(); |
| |
| // This struct keeps a set of timestamps for performing a particular |
| // navigation in the renderer process. It is created when the very first IPC |
| // associated with this navigation is processed, which is typically |
| // CreateView or CreateFrame. |
| struct Timeline { |
| Timeline(); |
| ~Timeline(); |
| |
| // The time at which this navigation was started, without any beforeunload |
| // adjustments. Note that navigation start might have happened in another |
| // process (e.g., browser process or another renderer process). |
| base::TimeTicks navigation_start; |
| |
| // Helper struct that holds a start/end time for a particular event. |
| struct TimelineEvent { |
| base::TimeTicks start; |
| base::TimeTicks end; |
| }; |
| |
| // A set of <start, end> timestamps for processing all CreateView IPCs for |
| // this navigation. These IPCs set up the blink::WebView and main frame |
| // frame or proxy for the navigating frame and its opener Pages/FrameTrees, |
| // if any. |
| std::vector<TimelineEvent> create_view_events; |
| |
| // A set of <start, end> timestamps for processing all CreateRemoteChildren |
| // IPCs that create all necessary subframe proxies for this navigation. |
| // There is one pair of timestamps for each Page/FrameTree that needed |
| // subframe proxies, including the navigating frame's page as well as |
| // its opener chain. |
| std::vector<TimelineEvent> create_remote_children_events; |
| |
| // Start and end times for the CreateFrame IPC. This should exist for most |
| // navigations, but could end up nullopt when a navigation stays in the same |
| // RenderFrame, such as for same-document navigations or navigations out of |
| // an initial blank document. |
| std::optional<TimelineEvent> create_frame_event; |
| |
| // The time at which the CommitNavigation IPC was sent to this renderer |
| // process from the browser process. |
| base::TimeTicks commit_sent; |
| |
| // The time at which this navigation started processing the CommitNavigation |
| // IPC. |
| base::TimeTicks commit_start; |
| |
| // The time at which this navigation finished processing the |
| // CommitNavigation IPC. |
| base::TimeTicks commit_end; |
| |
| // A timer that's set at the time this Timeline object is created (i.e., |
| // when the very first IPC for this navigation is processed), to clean up |
| // the Timeline if this navigation ends up never committing. See a note |
| // about this in RendererNavigationMetricsManager::GetOrCreateTimeline(). |
| base::OneShotTimer lazy_cleanup_timer_; |
| |
| // Whether this Timeline is for the first navigation in this renderer |
| // process. This is needed to report a single zero-sized |
| // WaitingForProcessReady event in cases that the renderer is ready before |
| // the first navigation begins, to give a sense of how often a process |
| // is ready to go when it's needed for a navigation. |
| bool is_first_navigation_in_this_process; |
| |
| // Whether this navigation was in a main frame, as defined by |
| // RenderFrameImpl::IsMainFrame(). Useful for recording metrics for main |
| // frames only. Note that this is not limited to outermost or primary main |
| // frames. |
| bool is_main_frame; |
| }; |
| |
| // The following methods are called to add timestamps for processing the |
| // CreateView, CreateRemoteChildren, and CreateFrame IPCs, for a navigation |
| // identified by `navigation_metrics_token`. Note that there might be multiple |
| // CreateView and CreateRemoteChildren IPCs per navigation, since one is sent |
| // for each page/FrameTree, and there could be multiple pages involved with |
| // opener chains. In this case, AddCreateViewEvent and |
| // AddCreateRemoteChildrenEvent might be called multiple times and will record |
| // a list of <start,end> timestamps. |
| // |
| // The normal expected sequence of these events is: |
| // - each page (if any) on the opener chain will generate a CreateView event |
| // followed by a CreateRemoteChildren event for any subframe proxies on that |
| // page. |
| // - the page with the navigating frame will generate an optional CreateView |
| // event (if a main frame/proxy needs to be created), followed by an |
| // optional CreateRemoteChildren event (if any subframe proxies need to be |
| // created). |
| // - the navigating frame will generate an optional CreateFrame event (when it |
| // navigates in a new RenderFrame). |
| // |
| // Note that a navigation could involve no CreateView IPCs at all (e.g., in |
| // same-origin navigations). It could also involve no CreateFrame IPCs if the |
| // navigation is staying in a previous RenderFrame, such as when performing a |
| // same-document navigation, or navigating out of an initial blank document |
| // where RenderDocument is not used. |
| void AddCreateViewEvent( |
| const std::optional<base::UnguessableToken>& navigation_metrics_token, |
| const base::TimeTicks& start_time, |
| const base::TimeDelta& elapsed_time); |
| void AddCreateRemoteChildrenEvent( |
| const std::optional<base::UnguessableToken>& navigation_metrics_token, |
| const base::TimeTicks& start_time, |
| const base::TimeDelta& elapsed_time); |
| void AddCreateFrameEvent( |
| const std::optional<base::UnguessableToken>& navigation_metrics_token, |
| const base::TimeTicks& start_time, |
| const base::TimeDelta& elapsed_time); |
| |
| // Set the time at which the renderer process started processing the |
| // CommitNavigation IPC for the navigation identified by |
| // `navigation_metrics_token`. This is not guaranteed to happen for all |
| // navigations - in particular, renderer-initiated same-document navigations |
| // and synchronous about:blank navigations do not involve a commit IPC from |
| // the browser process. |
| // |
| // TODO(crbug.com/415821826): Consider still calling this at the start of |
| // commit for those cases and recording a timeline for them as well. |
| void MarkCommitStart(const base::UnguessableToken& navigation_metrics_token); |
| |
| // This is called when a navigation to `url` and identified by |
| // `navigation_metrics_token` has finished committing in the renderer process. |
| // This is a signal for this class to generate trace events and metrics using |
| // all the timestamps collected so far for it. `navigation_start_time` |
| // identifies the time at which this navigation was started, possibly in |
| // another process. `commit_sent_time` is the time at which the |
| // CommitNavigation IPC was sent by the browser process to this renderer |
| // process. |
| void ProcessNavigationCommit( |
| const base::UnguessableToken& navigation_metrics_token, |
| const GURL& url, |
| const base::TimeTicks& navigation_start_time, |
| const base::TimeTicks& commit_sent_time, |
| bool is_main_frame); |
| |
| private: |
| ~RendererNavigationMetricsManager(); |
| |
| Timeline& GetOrCreateTimeline( |
| const base::UnguessableToken& navigation_metrics_token); |
| |
| // Records trace events and metrics for a particular navigation, using |
| // timestamps in the provided `timeline`. `url` is used to log a trace event |
| // that contains the navigation's final URL for convenience. |
| void RecordTraceEventsAndMetrics( |
| const RendererNavigationMetricsManager::Timeline& timeline, |
| const GURL& url); |
| |
| // A map of navigation metrics tokens to corresponding Timeline objects. |
| std::map<base::UnguessableToken, Timeline> timelines_; |
| |
| // Whether or not a first navigation in this renderer process has started. |
| // This is used to report a single zero-sized WaitingForProcessReady per |
| // process in any cases that the process was ready before the first |
| // navigation, which indicates how often the process is ready to go when it's |
| // needed for a navigation. |
| bool has_first_navigation_started_ = false; |
| }; |
| |
| } // namespace content |
| |
| #endif // CONTENT_RENDERER_RENDERER_NAVIGATION_METRICS_MANAGER_H_ |