chromiumoxide/handler/
emulation.rs

1use chromiumoxide_cdp::cdp::browser_protocol::emulation::{
2    ScreenOrientation, ScreenOrientationType, SetDeviceMetricsOverrideParams,
3    SetTouchEmulationEnabledParams,
4};
5use chromiumoxide_types::Method;
6
7use crate::cmd::CommandChain;
8use crate::handler::viewport::Viewport;
9use std::time::Duration;
10
11#[derive(Default, Debug, Clone, Copy, PartialEq)]
12/// Emulation manager.
13pub struct EmulationManager {
14    /// Whether mobile emulation is enabled.
15    pub emulating_mobile: bool,
16    /// Whether touch input is enabled.
17    pub has_touch: bool,
18    /// Whether a reload is required to apply new emulation settings.
19    pub needs_reload: bool,
20    /// Timeout to apply emulation requests.
21    pub request_timeout: Duration,
22}
23
24impl EmulationManager {
25    /// Creates a new `EmulationManager` with the given request timeout.
26    pub fn new(request_timeout: Duration) -> Self {
27        Self {
28            emulating_mobile: false,
29            has_touch: false,
30            needs_reload: false,
31            request_timeout,
32        }
33    }
34    /// Generates the initial emulation commands based on the provided viewport.
35    ///
36    /// This sets up device metrics and touch emulation, and updates internal flags
37    /// to determine if a page reload is required to apply the changes.
38    pub fn init_commands(&mut self, viewport: &Viewport) -> CommandChain {
39        let mut chains = Vec::with_capacity(2);
40        let set_touch = SetTouchEmulationEnabledParams::new(viewport.emulating_mobile);
41        let orientation = if viewport.is_landscape {
42            ScreenOrientation::new(ScreenOrientationType::LandscapePrimary, 90)
43        } else {
44            ScreenOrientation::new(ScreenOrientationType::PortraitPrimary, 0)
45        };
46
47        if let Ok(set_device) = SetDeviceMetricsOverrideParams::builder()
48            .mobile(viewport.emulating_mobile)
49            .width(viewport.width)
50            .height(viewport.height)
51            .device_scale_factor(viewport.device_scale_factor.unwrap_or(1.))
52            .screen_orientation(orientation)
53            .build()
54        {
55            if let Ok(set_device_value) = serde_json::to_value(&set_device) {
56                chains.push((set_device.identifier(), set_device_value));
57            }
58        }
59
60        if let Ok(set_touch_value) = serde_json::to_value(&set_touch) {
61            chains.push((set_touch.identifier(), set_touch_value));
62        }
63
64        let chain = CommandChain::new(chains, self.request_timeout);
65
66        self.needs_reload = self.emulating_mobile != viewport.emulating_mobile
67            || self.has_touch != viewport.has_touch;
68        chain
69    }
70}