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
141
142
143
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
//! Quiescence: the report a surface makes of its pending work, and the default
//! policy for "settled".
//!
//! Every automation harness that polls-and-sleeps does so because it cannot ask
//! the engine what is still in flight. The engine can answer per source, and the
//! per-source signals already exist — this module adds no mechanism, only the
//! common vocabulary the host uses to join them:
//!
//! - loads: `LoadingState` (this crate) — terminal at `Done` / `Failed`
//! - script microtasks: `script-engine-api`'s `pump` (`Quiescent` / `Pending`)
//! - timers: the runtime's `next_timer_delay()`
//! - animation-frame callbacks: the runtime's `has_animation_frame_callbacks()`
//! - declared animations: layout's `has_active_animations()`
//!
//! Only the host sees all of these at once, so the host assembles a
//! [`PendingWork`] per surface; consumers (a test harness, a WebDriver adapter,
//! an agent's observation) read it instead of sleeping.
//!
//! ## Settling vs perpetual sources
//!
//! The default [`settled`](PendingWork::settled) policy counts only work that
//! *finishes by itself*: loads, microtasks, and dirty layout. The other sources
//! are reported but do not block, each for a stated reason:
//!
//! - **Timers.** A page with `setTimeout(fn, 30_000)` is not "busy" for those
//! thirty seconds. WebDriver's own document-readiness never waits on timers,
//! and a harness that did would turn every long-poll page into a hang.
//! - **Animation-frame callbacks.** A one-shot rAF (schedule a measurement)
//! settles next frame, but a rAF *loop* — every game loop, every physics
//! surface — re-requests forever, and the two are statically
//! indistinguishable. Blocking on rAF turns "the orrery is breathing" into
//! "the harness never returns".
//! - **Declared animations.** CSS transitions/animations run on the engine's
//! clock and may be infinite (`animation-iteration-count: infinite`).
//!
//! For those, the tool is a condition-wait ("element exists", "attribute
//! equals"), not a broader settle. A caller with cause to block on the
//! perpetual sources opts in explicitly via [`fully_idle`](PendingWork::fully_idle)
//! and owns the hang risk it is accepting.
use MallocSizeOf;
use ;
/// One surface's pending work, by source, at the moment of asking.
///
/// A snapshot, not a subscription: settling is level-triggered (ask again), so a
/// harness loop is "apply step, ask until settled, assert" with no sleep in it.
/// Common-minimum quiescence query, per surface. Implemented host-side (only the
/// host sees loads, script, and layout at once); read by harnesses, protocol
/// adapters, and agent observation assembly.