File: observation_delay_controller.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (140 lines) | stat: -rw-r--r-- 4,355 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// 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.

#include "chrome/browser/actor/tools/observation_delay_controller.h"

#include "base/functional/bind.h"
#include "base/task/sequenced_task_runner.h"
#include "base/time/time.h"
#include "chrome/browser/actor/execution_engine.h"
#include "chrome/browser/actor/tools/tool_callbacks.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"

namespace actor {

using ::content::RenderFrameHost;
using ::content::WebContents;
using ::content::WebContentsObserver;

namespace {
// This timeout is long but based on the NavigationToLoadEventFired UMA. This
// should be tuned with real world usage.
constexpr base::TimeDelta kCompletionTimeout = base::Seconds(10);
}  // namespace

ObservationDelayController::ObservationDelayController(
    content::RenderFrameHost& target_frame)
    : content::WebContentsObserver(
          WebContents::FromRenderFrameHost(&target_frame)) {
  CHECK(web_contents());
}

ObservationDelayController::~ObservationDelayController() = default;

void ObservationDelayController::Wait(
    AggregatedJournal::PendingAsyncEntry& parent_journal_entry,
    ReadyCallback callback) {
  journal_entry_ = parent_journal_entry.GetJournal().CreatePendingAsyncEntry(
      GURL::EmptyGURL(), parent_journal_entry.GetTaskId(), "ObservationDelay",
      StateToString(state_));

  switch (state_) {
    case State::kWaitingForLoadStart:
    case State::kWaitingForLoadStop:
    case State::kWaitingForVisualUpdate: {
      ready_callback_ = std::move(callback);
      PostFinishedTask(base::BindOnce(&ObservationDelayController::Timeout,
                                      weak_ptr_factory_.GetWeakPtr()),
                       kCompletionTimeout);

      // If no navigating load was started, simply force and wait for a new
      // frame to be presented.
      if (state_ == State::kWaitingForLoadStart) {
        WaitForVisualStateUpdate();
      }
      break;
    }
    case State::kDone: {
      PostFinishedTask(std::move(callback));
      journal_entry_->EndEntry("Done");
      break;
    }
  }

  CHECK(callback.is_null());
}

void ObservationDelayController::DidStartLoading() {
  if (state_ != State::kWaitingForLoadStart) {
    return;
  }

  state_ = State::kWaitingForLoadStop;
}

void ObservationDelayController::DidStopLoading() {
  if (state_ != State::kWaitingForLoadStop) {
    return;
  }

  // If we aren't waiting, then this new state will be logged when
  // we actually wait.
  if (journal_entry_) {
    journal_entry_->GetJournal().Log(GURL::EmptyGURL(),
                                     journal_entry_->GetTaskId(),
                                     "ObservationDelay", "Done Loading");
  }
  WaitForVisualStateUpdate();
}

void ObservationDelayController::WaitForVisualStateUpdate() {
  state_ = State::kWaitingForVisualUpdate;

  // TODO(crbug.com/414662842): This should probably ensure an update from
  // all/selected OOPIFS?
  web_contents()->GetPrimaryMainFrame()->InsertVisualStateCallback(
      base::BindOnce(&ObservationDelayController::VisualStateUpdated,
                     weak_ptr_factory_.GetWeakPtr()));
}

void ObservationDelayController::VisualStateUpdated(bool /*success*/) {
  if (state_ != State::kWaitingForVisualUpdate) {
    return;
  }

  state_ = State::kDone;

  // It's possible the ready state has been reached before Wait has been
  // called. In that case, the callback will be posted when Wait is called.
  if (ready_callback_) {
    PostFinishedTask(std::move(ready_callback_));
    journal_entry_->EndEntry("Visual Update");
  }
}

void ObservationDelayController::Timeout() {
  state_ = State::kDone;
  if (ready_callback_) {
    PostFinishedTask(std::move(ready_callback_));
    journal_entry_->EndEntry("Timeout");
  }
}

std::string_view ObservationDelayController::StateToString(State state) {
  switch (state) {
    case State::kWaitingForLoadStart:
      return "WaitLoadStart";
    case State::kWaitingForLoadStop:
      return "WaitLoadStop";
    case State::kWaitingForVisualUpdate:
      return "WaitVisualUpdate";
    case State::kDone:
      return "Done";
  }
  NOTREACHED();
}

}  // namespace actor