1use serde::{Deserialize, Serialize};
2
3use crate::fingerprint::*;
4use crate::seed::PersonaSeed;
5
6#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
8pub struct Viewport {
9 pub width: u32,
10 pub height: u32,
11 pub device_scale_factor: u32,
12}
13
14impl Default for Viewport {
15 fn default() -> Self {
16 Self {
17 width: 1440,
18 height: 900,
19 device_scale_factor: 1,
20 }
21 }
22}
23
24#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
26pub struct ScreenConfig {
27 #[serde(default, skip_serializing_if = "Option::is_none")]
28 pub width: Option<u32>,
29 #[serde(default, skip_serializing_if = "Option::is_none")]
30 pub height: Option<u32>,
31 #[serde(default, skip_serializing_if = "Option::is_none")]
32 pub avail_width: Option<u32>,
33 #[serde(default, skip_serializing_if = "Option::is_none")]
34 pub avail_height: Option<u32>,
35 #[serde(default, skip_serializing_if = "Option::is_none")]
36 pub avail_left: Option<i32>,
37 #[serde(default, skip_serializing_if = "Option::is_none")]
38 pub avail_top: Option<i32>,
39 #[serde(default, skip_serializing_if = "Option::is_none")]
40 pub left: Option<i32>,
41 #[serde(default, skip_serializing_if = "Option::is_none")]
42 pub top: Option<i32>,
43 #[serde(default, skip_serializing_if = "Option::is_none")]
44 pub color_depth: Option<u32>,
45 #[serde(default, skip_serializing_if = "Option::is_none")]
46 pub pixel_depth: Option<u32>,
47 #[serde(default, skip_serializing_if = "Option::is_none")]
48 pub is_extended: Option<bool>,
49 #[serde(default, skip_serializing_if = "Option::is_none")]
50 pub orientation_type: Option<String>,
51 #[serde(default, skip_serializing_if = "Option::is_none")]
52 pub orientation_angle: Option<u16>,
53}
54
55#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
57pub struct WindowConfig {
58 #[serde(default, skip_serializing_if = "Option::is_none")]
59 pub outer_width: Option<u32>,
60 #[serde(default, skip_serializing_if = "Option::is_none")]
61 pub outer_height: Option<u32>,
62 #[serde(default, skip_serializing_if = "Option::is_none")]
63 pub screen_x: Option<i32>,
64 #[serde(default, skip_serializing_if = "Option::is_none")]
65 pub screen_y: Option<i32>,
66}
67
68#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
70pub struct CssConfig {
71 #[serde(default, skip_serializing_if = "Option::is_none")]
72 pub moz_prefix_enabled: Option<bool>,
73 #[serde(default, skip_serializing_if = "Option::is_none")]
74 pub webkit_prefix_enabled: Option<bool>,
75}
76
77#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
79pub struct GraphicsConfig {
80 #[serde(default, skip_serializing_if = "Option::is_none")]
81 pub webgl_vendor: Option<String>,
82 #[serde(default, skip_serializing_if = "Option::is_none")]
83 pub webgl_renderer: Option<String>,
84 #[serde(default, skip_serializing_if = "Option::is_none")]
85 pub webgl_masked_vendor: Option<String>,
86 #[serde(default, skip_serializing_if = "Option::is_none")]
87 pub webgl_masked_renderer: Option<String>,
88}
89
90#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
92pub struct NavigatorConfig {
93 #[serde(default, skip_serializing_if = "Option::is_none")]
94 pub app_version: Option<String>,
95 #[serde(default, skip_serializing_if = "Option::is_none")]
96 pub platform: Option<String>,
97 #[serde(default, skip_serializing_if = "Option::is_none")]
98 pub language: Option<String>,
99 #[serde(default, skip_serializing_if = "Option::is_none")]
100 pub languages: Option<Vec<String>>,
101 #[serde(default, skip_serializing_if = "Option::is_none")]
102 pub hardware_concurrency: Option<u32>,
103 #[serde(default, skip_serializing_if = "Option::is_none")]
104 pub device_memory_gb: Option<u32>,
105 #[serde(default, skip_serializing_if = "Option::is_none")]
106 pub max_touch_points: Option<u32>,
107 #[serde(default, skip_serializing_if = "Option::is_none")]
108 pub do_not_track: Option<String>,
109 #[serde(default, skip_serializing_if = "Option::is_none")]
110 pub global_privacy_control: Option<bool>,
111 #[serde(default, skip_serializing_if = "Option::is_none")]
112 pub expose_global_privacy_control: Option<bool>,
113 #[serde(default, skip_serializing_if = "Option::is_none")]
114 pub permissions_enabled: Option<bool>,
115 #[serde(default, skip_serializing_if = "Option::is_none")]
116 pub bluetooth_enabled: Option<bool>,
117 #[serde(default, skip_serializing_if = "Option::is_none")]
118 pub bluetooth_available: Option<bool>,
119 #[serde(default, skip_serializing_if = "Option::is_none")]
120 pub media_devices_enabled: Option<bool>,
121 #[serde(default, skip_serializing_if = "Option::is_none")]
122 pub webgpu_enabled: Option<bool>,
123 #[serde(default, skip_serializing_if = "Option::is_none")]
124 pub offscreen_canvas_enabled: Option<bool>,
125 #[serde(default, skip_serializing_if = "Option::is_none")]
126 pub service_worker_enabled: Option<bool>,
127 #[serde(default, skip_serializing_if = "Option::is_none")]
128 pub ua_platform_version: Option<String>,
129 #[serde(default, skip_serializing_if = "Option::is_none")]
130 pub ua_architecture: Option<String>,
131 #[serde(default, skip_serializing_if = "Option::is_none")]
132 pub ua_bitness: Option<String>,
133 #[serde(default, skip_serializing_if = "Option::is_none")]
134 pub ua_model: Option<String>,
135 #[serde(default, skip_serializing_if = "Option::is_none")]
136 pub vendor: Option<String>,
137 #[serde(default, skip_serializing_if = "Option::is_none")]
138 pub product_sub: Option<String>,
139 #[serde(default, skip_serializing_if = "Option::is_none")]
140 pub pdf_viewer_enabled: Option<bool>,
141}
142
143#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
145pub struct MediaConfig {
146 #[serde(default, skip_serializing_if = "Option::is_none")]
147 pub speech_voices: Option<Vec<String>>,
148 #[serde(default, skip_serializing_if = "Option::is_none")]
149 pub audio_inputs: Option<u32>,
150 #[serde(default, skip_serializing_if = "Option::is_none")]
151 pub video_inputs: Option<u32>,
152 #[serde(default, skip_serializing_if = "Option::is_none")]
153 pub audio_outputs: Option<u32>,
154}
155
156#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
158pub struct FontConfig {
159 #[serde(default, skip_serializing_if = "Option::is_none")]
160 pub families: Option<Vec<String>>,
161 #[serde(default, skip_serializing_if = "Option::is_none")]
162 pub seed: Option<PersonaSeed>,
163}
164
165#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
167pub struct CanvasConfig {
168 #[serde(default, skip_serializing_if = "Option::is_none")]
169 pub noise_enabled: Option<bool>,
170 #[serde(default, skip_serializing_if = "Option::is_none")]
171 pub noise_amplitude: Option<u8>,
172 #[serde(default, skip_serializing_if = "Option::is_none")]
173 pub blink_low_entropy_probe: Option<bool>,
174 #[serde(default, skip_serializing_if = "Option::is_none")]
175 pub seed: Option<PersonaSeed>,
176}
177
178#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
180pub struct DomRectConfig {
181 #[serde(default, skip_serializing_if = "Option::is_none")]
182 pub enabled: Option<bool>,
183 #[serde(default, skip_serializing_if = "Option::is_none")]
184 pub quantization_steps_per_px: Option<u32>,
185 #[serde(default, skip_serializing_if = "Option::is_none")]
186 pub fill_empty_client_rects: Option<bool>,
187 #[serde(default, skip_serializing_if = "Option::is_none")]
188 pub seed: Option<PersonaSeed>,
189}
190
191#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
193pub struct EngineConfig {
194 #[serde(default, skip_serializing_if = "Option::is_none")]
195 pub enabled: Option<bool>,
196 #[serde(default, skip_serializing_if = "Option::is_none")]
197 pub to_fixed_range_error_message: Option<String>,
198 #[serde(default, skip_serializing_if = "Option::is_none")]
199 pub array_constructor_source: Option<String>,
200}
201
202impl EngineConfig {
203 pub(crate) fn apply_to(&self, engine: &mut EngineFingerprint) {
204 if let Some(enabled) = self.enabled {
205 engine.enabled = enabled;
206 }
207 if let Some(message) = self
208 .to_fixed_range_error_message
209 .as_ref()
210 .filter(|value| !value.is_empty())
211 {
212 engine.to_fixed_range_error_message = message.clone();
213 }
214 if let Some(source) = self
215 .array_constructor_source
216 .as_ref()
217 .filter(|value| !value.is_empty())
218 {
219 engine.array_constructor_source = source.clone();
220 }
221 }
222}
223
224impl DomRectConfig {
225 pub(crate) fn apply_to(&self, domrect: &mut DomRectFingerprint) {
226 if let Some(enabled) = self.enabled {
227 domrect.enabled = enabled;
228 }
229 if let Some(quantization_steps_per_px) = self.quantization_steps_per_px {
230 domrect.quantization_steps_per_px = quantization_steps_per_px;
231 }
232 if let Some(fill_empty_client_rects) = self.fill_empty_client_rects {
233 domrect.fill_empty_client_rects = fill_empty_client_rects;
234 }
235 if let Some(seed) = self.seed.as_ref() {
236 domrect.seed = seed.clone();
237 }
238 }
239}
240
241impl CanvasConfig {
242 pub(crate) fn apply_to(&self, canvas: &mut CanvasFingerprint) {
243 if let Some(noise_enabled) = self.noise_enabled {
244 canvas.noise_enabled = noise_enabled;
245 }
246 if let Some(noise_amplitude) = self.noise_amplitude {
247 canvas.noise_amplitude = noise_amplitude;
248 }
249 if let Some(blink_low_entropy_probe) = self.blink_low_entropy_probe {
250 canvas.blink_low_entropy_probe = blink_low_entropy_probe;
251 }
252 if let Some(seed) = self.seed.as_ref() {
253 canvas.seed = seed.clone();
254 }
255 }
256}
257
258impl MediaConfig {
259 pub(crate) fn apply_to(&self, media: &mut MediaFingerprint) {
260 if let Some(speech_voices) = self
261 .speech_voices
262 .as_ref()
263 .filter(|voices| !voices.is_empty())
264 {
265 media.speech_voices = speech_voices.clone();
266 }
267 if let Some(audio_inputs) = self.audio_inputs {
268 media.audio_inputs = audio_inputs;
269 }
270 if let Some(video_inputs) = self.video_inputs {
271 media.video_inputs = video_inputs;
272 }
273 if let Some(audio_outputs) = self.audio_outputs {
274 media.audio_outputs = audio_outputs;
275 }
276 }
277}
278
279impl FontConfig {
280 pub(crate) fn apply_to(&self, fonts: &mut FontFingerprint) {
281 if let Some(families) = self
282 .families
283 .as_ref()
284 .map(|families| {
285 families
286 .iter()
287 .map(|family| family.trim())
288 .filter(|family| !family.is_empty())
289 .map(ToOwned::to_owned)
290 .collect::<Vec<_>>()
291 })
292 .filter(|families| !families.is_empty())
293 {
294 fonts.families = families;
295 }
296 if let Some(seed) = self.seed.as_ref() {
297 fonts.seed = seed.clone();
298 }
299 }
300}
301
302impl NavigatorConfig {
303 pub(crate) fn apply_to(&self, js: &mut JsFingerprint) {
304 if let Some(app_version) = self.app_version.as_ref().filter(|value| !value.is_empty()) {
305 js.app_version = app_version.clone();
306 }
307 if let Some(platform) = self.platform.as_ref().filter(|value| !value.is_empty()) {
308 js.platform = platform.clone();
309 }
310 if let Some(language) = self.language.as_ref().filter(|value| !value.is_empty()) {
311 js.language = language.clone();
312 }
313 if let Some(languages) = self.languages.as_ref().filter(|values| !values.is_empty()) {
314 js.languages = languages.clone();
315 if self.language.is_none()
316 && let Some(language) = js.languages.first()
317 {
318 js.language = language.clone();
319 }
320 }
321 if let Some(hardware_concurrency) = self.hardware_concurrency {
322 js.hardware_concurrency = hardware_concurrency;
323 }
324 if let Some(device_memory_gb) = self.device_memory_gb {
325 js.device_memory_gb = device_memory_gb;
326 }
327 if let Some(max_touch_points) = self.max_touch_points {
328 js.max_touch_points = max_touch_points;
329 }
330 if let Some(do_not_track) = self.do_not_track.as_ref() {
331 js.do_not_track = do_not_track.clone();
332 }
333 if let Some(global_privacy_control) = self.global_privacy_control {
334 js.global_privacy_control = global_privacy_control;
335 }
336 if let Some(expose_global_privacy_control) = self.expose_global_privacy_control {
337 js.expose_global_privacy_control = expose_global_privacy_control;
338 }
339 if let Some(permissions_enabled) = self.permissions_enabled {
340 js.permissions_enabled = permissions_enabled;
341 }
342 if let Some(bluetooth_enabled) = self.bluetooth_enabled {
343 js.bluetooth_enabled = bluetooth_enabled;
344 }
345 if let Some(bluetooth_available) = self.bluetooth_available {
346 js.bluetooth_available = bluetooth_available;
347 }
348 if let Some(media_devices_enabled) = self.media_devices_enabled {
349 js.media_devices_enabled = media_devices_enabled;
350 }
351 if let Some(webgpu_enabled) = self.webgpu_enabled {
352 js.webgpu_enabled = webgpu_enabled;
353 }
354 if let Some(offscreen_canvas_enabled) = self.offscreen_canvas_enabled {
355 js.offscreen_canvas_enabled = offscreen_canvas_enabled;
356 }
357 if let Some(service_worker_enabled) = self.service_worker_enabled {
358 js.service_worker_enabled = service_worker_enabled;
359 }
360 if let Some(ua_platform_version) = self
361 .ua_platform_version
362 .as_ref()
363 .filter(|value| !value.is_empty())
364 {
365 js.ua_platform_version = ua_platform_version.clone();
366 }
367 if let Some(ua_architecture) = self
368 .ua_architecture
369 .as_ref()
370 .filter(|value| !value.is_empty())
371 {
372 js.ua_architecture = ua_architecture.clone();
373 }
374 if let Some(ua_bitness) = self.ua_bitness.as_ref().filter(|value| !value.is_empty()) {
375 js.ua_bitness = ua_bitness.clone();
376 }
377 if let Some(ua_model) = self.ua_model.as_ref() {
378 js.ua_model = ua_model.clone();
379 }
380 if let Some(vendor) = self.vendor.as_ref().filter(|value| !value.is_empty()) {
381 js.vendor = vendor.clone();
382 }
383 if let Some(product_sub) = self.product_sub.as_ref().filter(|value| !value.is_empty()) {
384 js.product_sub = product_sub.clone();
385 }
386 if let Some(pdf_viewer_enabled) = self.pdf_viewer_enabled {
387 js.pdf_viewer_enabled = pdf_viewer_enabled;
388 }
389 }
390}
391
392impl GraphicsConfig {
393 pub(crate) fn apply_to(&self, graphics: &mut GraphicsFingerprint) {
394 if let Some(webgl_vendor) = self.webgl_vendor.as_ref().filter(|value| !value.is_empty()) {
395 graphics.webgl_vendor = webgl_vendor.clone();
396 }
397 if let Some(webgl_renderer) = self
398 .webgl_renderer
399 .as_ref()
400 .filter(|value| !value.is_empty())
401 {
402 graphics.webgl_renderer = webgl_renderer.clone();
403 }
404 if let Some(webgl_masked_vendor) = self
405 .webgl_masked_vendor
406 .as_ref()
407 .filter(|value| !value.is_empty())
408 {
409 graphics.webgl_masked_vendor = webgl_masked_vendor.clone();
410 }
411 if let Some(webgl_masked_renderer) = self
412 .webgl_masked_renderer
413 .as_ref()
414 .filter(|value| !value.is_empty())
415 {
416 graphics.webgl_masked_renderer = webgl_masked_renderer.clone();
417 }
418 }
419}
420
421impl ScreenConfig {
422 pub(crate) fn apply_to(&self, screen: &mut ScreenFingerprint) {
423 if let Some(width) = self.width {
424 screen.width = width;
425 }
426 if let Some(height) = self.height {
427 screen.height = height;
428 }
429 if let Some(avail_width) = self.avail_width {
430 screen.avail_width = avail_width;
431 }
432 if let Some(avail_height) = self.avail_height {
433 screen.avail_height = avail_height;
434 }
435 if let Some(avail_left) = self.avail_left {
436 screen.avail_left = avail_left;
437 }
438 if let Some(avail_top) = self.avail_top {
439 screen.avail_top = avail_top;
440 }
441 if let Some(left) = self.left {
442 screen.left = left;
443 }
444 if let Some(top) = self.top {
445 screen.top = top;
446 }
447 if let Some(color_depth) = self.color_depth {
448 screen.color_depth = color_depth;
449 }
450 if let Some(pixel_depth) = self.pixel_depth {
451 screen.pixel_depth = pixel_depth;
452 }
453 if let Some(is_extended) = self.is_extended {
454 screen.is_extended = is_extended;
455 }
456 if let Some(orientation_type) = self
457 .orientation_type
458 .as_ref()
459 .filter(|value| !value.is_empty())
460 {
461 screen.orientation_type = orientation_type.clone();
462 }
463 if let Some(orientation_angle) = self.orientation_angle {
464 screen.orientation_angle = orientation_angle;
465 }
466 }
467}
468
469impl WindowConfig {
470 pub(crate) fn apply_to(&self, window: &mut WindowFingerprint) {
471 if let Some(outer_width) = self.outer_width {
472 window.outer_width = outer_width;
473 }
474 if let Some(outer_height) = self.outer_height {
475 window.outer_height = outer_height;
476 }
477 if let Some(screen_x) = self.screen_x {
478 window.screen_x = screen_x;
479 }
480 if let Some(screen_y) = self.screen_y {
481 window.screen_y = screen_y;
482 }
483 }
484}
485
486impl CssConfig {
487 pub(crate) fn apply_to(&self, css: &mut CssFingerprint) {
488 if let Some(moz_prefix_enabled) = self.moz_prefix_enabled {
489 css.moz_prefix_enabled = moz_prefix_enabled;
490 }
491 if let Some(webkit_prefix_enabled) = self.webkit_prefix_enabled {
492 css.webkit_prefix_enabled = webkit_prefix_enabled;
493 }
494 }
495}