910e62b5创建于 1月15日历史提交
// 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 CHROME_RENDERER_ACTOR_PAGE_STABILITY_MONITOR_H_
#define CHROME_RENDERER_ACTOR_PAGE_STABILITY_MONITOR_H_

#include <string_view>

#include "base/functional/callback.h"
#include "base/functional/callback_forward.h"
#include "base/memory/raw_ref.h"
#include "base/memory/weak_ptr.h"
#include "base/task/delayed_task_handle.h"
#include "base/time/time.h"
#include "chrome/common/actor.mojom.h"
#include "chrome/common/actor/task_id.h"
#include "chrome/renderer/actor/journal.h"
#include "content/public/renderer/render_frame_observer.h"
#include "mojo/public/cpp/bindings/receiver.h"

namespace content {
class RenderFrame;
}  // namespace content

namespace actor {

class Journal;
class NetworkAndMainThreadStabilityMonitor;
class PageStabilityMetrics;
class PaintStabilityMonitor;

// Helper class for monitoring page stability after tool usage. Its lifetime
// must not outlive the RenderFrame it is observing. This object is single-use,
// i.e. NotifyWhenStable can only be called once.
class PageStabilityMonitor : public content::RenderFrameObserver,
                             public mojom::PageStabilityMonitor {
 public:
  // Constructs the monitor and takes a baseline observation of the document in
  // the given RenderFrame. If `supports_paint_stability` is true, paint
  // stability will be included in page stability heuristics if the `frame`
  // supports it.
  PageStabilityMonitor(content::RenderFrame& frame,
                       bool supports_paint_stability,
                       TaskId task_id,
                       Journal& journal);

  ~PageStabilityMonitor() override;

  // RenderFrameObserver
  void DidCommitProvisionalLoad(ui::PageTransition transition) override;
  void DidFailProvisionalLoad() override;
  void DidSetPageLifecycleState(
      blink::BFCacheStateChange bfcache_change) override;
  void OnDestruct() override;

  // mojom::PageStabilityMonitor:
  // Invokes the given callback when the page is deemed stable enough for an
  // observation to take place or when the document is no longer active.
  //
  // `observation_delay` is the amount of time to wait when observing tool
  // execution before starting to wait for page stability.
  void NotifyWhenStable(base::TimeDelta observation_delay,
                        NotifyWhenStableCallback callback) override;

  void Bind(mojo::PendingReceiver<mojom::PageStabilityMonitor> receiver);

 private:
  friend class PageStabilityMetrics;

  enum class State {
    kInitial,

    // If a tool specifies an execution delay, wait in this state before
    // starting monitoring.
    kMonitorStartDelay,

    // Before starting the monitor, if a navigation is in-progress, wait for it
    // to commit or fail.
    kWaitForNavigation,

    // Entry point into the state machine. Decides which state to start in.
    kStartMonitoring,

    //  The NetworkAndMainThreadStabilityMonitor or PaintStabilityMonitor has
    //  determined that the page stability has been reached. If
    //  `kGlicActorPageStabilityMinWait` is set, the callback passed to
    // NotifyWhenStable() may be delayed until the said amount of time is
    // reached.
    kMonitorCompleted,

    // Timeout state - this just logs and and moves to invoke callback state.
    kTimeout,

    // Delay the callback until the min wait time is reached.
    kDelayCallback,

    // Invoke the callback passed to NotifyWhenStable and cleanup.
    kInvokeCallback,

    // The render frame is about to be deleted (e.g. because of a navigation to
    // a new RenderFrame).
    kRenderFrameGoingAway,

    // The mojo pipeline gets disconnected. This just moves to kDone.
    kMojoDisconnected,

    kDone
  } state_ = State::kInitial;
  static std::string_view StateToString(State state);

  friend std::ostream& operator<<(std::ostream& o,
                                  const PageStabilityMonitor::State& state);

  // Synchronously moves to the given state.
  void MoveToState(State new_state);

  // Returns a closure that synchronously moves to the given state. This avoids
  // the extra scheduling hop of `PostMoveToStateClosure`, which is useful if
  // the closure is already being scheduled to run in a separate task.
  base::OnceClosure MoveToStateClosure(State new_state);

  // Helper that provides a closure that invokes MoveToState with the given
  // State on the default task queue for the sequence that created this object.
  base::OnceClosure PostMoveToStateClosure(
      State new_state,
      base::TimeDelta delay = base::TimeDelta());

  base::OnceCallback<base::DelayedTaskHandle()>
  PostCancelableMoveToStateClosure(State new_state,
                                   base::TimeDelta delay = base::TimeDelta());

  void DCheckStateTransition(State old_state, State new_state);

  void OnPaintStabilityReached();
  void OnNetworkAndMainThreadIdle();
  void OnRenderFrameGoingAway();
  void OnMojoDisconnected();
  void OnTimeout();

  void StopMonitoring();
  void Teardown();

  base::OnceClosure is_stable_callback_;

  std::unique_ptr<Journal::PendingAsyncEntry> journal_entry_;

  // Amount of time to delay before monitoring begins.
  base::TimeDelta monitoring_start_delay_;

  // The time at which monitoring begins.
  base::TimeTicks start_monitoring_time_;

  // A navigation may commit while waiting to start monitoring. Cancel the task
  // and don't move to `kStartMonitoring` when the delay expires in this case.
  base::DelayedTaskHandle start_monitoring_delayed_handle_;

  // Amount of time to delay before invoking the callback.
  base::TimeDelta callback_invoke_delay_;

  TaskId task_id_;

  base::raw_ref<Journal> journal_;

  std::unique_ptr<PageStabilityMetrics> metrics_;

  // This will be null if paint stability monitoring is disabled, or if we're
  // monitoring an unsupported interaction. This must be destroyed before
  // `journal_entry_` to avoid a dangling pointer.
  std::unique_ptr<PaintStabilityMonitor> paint_stability_monitor_;

  // This must be destroyed before `journal_` to avoid a dangling pointer.
  std::unique_ptr<NetworkAndMainThreadStabilityMonitor>
      network_and_main_thread_stability_monitor_;

  bool render_frame_did_go_away_ = false;

  bool monitoring_complete_ = false;

  mojo::Receiver<mojom::PageStabilityMonitor> receiver_{this};

  base::WeakPtrFactory<PageStabilityMonitor> weak_ptr_factory_{this};
};

}  // namespace actor

#endif  // CHROME_RENDERER_ACTOR_PAGE_STABILITY_MONITOR_H_