zendriver_stealth/persona/
surface.rs1use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
7pub enum Surface {
8 Canvas,
9 Webgl,
10 Audio,
11 Fonts,
12 ClientRects,
13 Webrtc,
14 Hardware,
15 Webgpu,
16}
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
20pub enum Strategy {
21 Native,
22 Seeded,
23 Random,
24 Block,
25 Value,
26}
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30pub enum SurfaceKind {
31 Noise,
32 Value,
33 Policy,
34}
35
36impl Surface {
37 pub fn kind(self) -> SurfaceKind {
38 match self {
39 Surface::Canvas | Surface::Audio | Surface::ClientRects => SurfaceKind::Noise,
40 Surface::Webgl | Surface::Fonts | Surface::Hardware | Surface::Webgpu => {
41 SurfaceKind::Value
42 }
43 Surface::Webrtc => SurfaceKind::Policy,
44 }
45 }
46
47 pub fn default_strategy(self) -> Strategy {
49 match self.kind() {
50 SurfaceKind::Noise => Strategy::Seeded,
51 SurfaceKind::Value => Strategy::Value,
52 SurfaceKind::Policy => Strategy::Block,
53 }
54 }
55
56 pub fn resolve_strategy(self, requested: Option<Strategy>) -> Strategy {
60 let req = match requested {
61 None => return self.default_strategy(),
62 Some(s) => s,
63 };
64 let ok = matches!(
65 (self.kind(), req),
66 (_, Strategy::Native)
67 | (_, Strategy::Block)
68 | (SurfaceKind::Noise, Strategy::Seeded | Strategy::Random)
69 | (SurfaceKind::Value, Strategy::Value)
70 | (SurfaceKind::Policy, Strategy::Value)
71 );
72 if ok {
73 req
74 } else {
75 tracing::warn!(
76 surface = ?self,
77 requested = ?req,
78 "strategy not meaningful for surface kind; using default"
79 );
80 self.default_strategy()
81 }
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
88
89 #[test]
90 fn noise_default_is_seeded() {
91 assert_eq!(Surface::Canvas.resolve_strategy(None), Strategy::Seeded);
92 }
93
94 #[test]
95 fn value_strategy_on_noise_warns_and_falls_back() {
96 assert_eq!(
98 Surface::Canvas.resolve_strategy(Some(Strategy::Value)),
99 Strategy::Seeded
100 );
101 }
102
103 #[test]
104 fn native_and_block_always_pass() {
105 assert_eq!(
106 Surface::Webgl.resolve_strategy(Some(Strategy::Native)),
107 Strategy::Native
108 );
109 assert_eq!(
110 Surface::Audio.resolve_strategy(Some(Strategy::Block)),
111 Strategy::Block
112 );
113 }
114
115 #[test]
116 fn webrtc_default_is_block() {
117 assert_eq!(Surface::Webrtc.resolve_strategy(None), Strategy::Block);
118 }
119
120 #[test]
121 fn webgpu_is_value_kind_default_value() {
122 assert_eq!(Surface::Webgpu.kind(), SurfaceKind::Value);
123 assert_eq!(Surface::Webgpu.resolve_strategy(None), Strategy::Value);
124 }
125
126 #[test]
127 fn webgpu_block_passes() {
128 assert_eq!(
129 Surface::Webgpu.resolve_strategy(Some(Strategy::Block)),
130 Strategy::Block
131 );
132 }
133}