devtools/actors/watcher/
target_configuration.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! Liberally derived from <https://searchfox.org/mozilla-central/source/devtools/server/actors/target-configuration.js>
6//! This actor manages the configuration flags that the devtools host can apply to the targets.
7
8use std::collections::HashMap;
9
10use embedder_traits::Theme;
11use log::warn;
12use serde::Serialize;
13use serde_json::{Map, Value};
14
15use crate::actor::{Actor, ActorError, ActorRegistry};
16use crate::actors::browsing_context::BrowsingContextActor;
17use crate::actors::tab::TabDescriptorActor;
18use crate::protocol::ClientRequest;
19use crate::{EmptyReplyMsg, RootActor, StreamId};
20
21#[derive(Serialize)]
22#[serde(rename_all = "camelCase")]
23pub struct TargetConfigurationTraits {
24    supported_options: HashMap<&'static str, bool>,
25}
26
27#[derive(Serialize)]
28pub struct TargetConfigurationActorMsg {
29    actor: String,
30    configuration: HashMap<&'static str, bool>,
31    traits: TargetConfigurationTraits,
32}
33
34pub struct TargetConfigurationActor {
35    name: String,
36    configuration: HashMap<&'static str, bool>,
37    supported_options: HashMap<&'static str, bool>,
38}
39
40impl Actor for TargetConfigurationActor {
41    fn name(&self) -> String {
42        self.name.clone()
43    }
44
45    /// The target configuration actor can handle the following messages:
46    ///
47    /// - `updateConfiguration`: Receives new configuration flags from the devtools host.
48    fn handle_message(
49        &self,
50        request: ClientRequest,
51        registry: &ActorRegistry,
52        msg_type: &str,
53        msg: &Map<String, Value>,
54        _id: StreamId,
55    ) -> Result<(), ActorError> {
56        match msg_type {
57            "updateConfiguration" => {
58                let config = msg
59                    .get("configuration")
60                    .ok_or(ActorError::MissingParameter)?
61                    .as_object()
62                    .ok_or(ActorError::BadParameterType)?;
63                if let Some(scheme) = config.get("colorSchemeSimulation").and_then(|v| v.as_str()) {
64                    let theme = match scheme {
65                        "dark" => Theme::Dark,
66                        "light" => Theme::Light,
67                        _ => Theme::Light,
68                    };
69                    let root_actor = registry.find::<RootActor>("root");
70                    if let Some(tab_name) = root_actor.active_tab() {
71                        let tab_actor = registry.find::<TabDescriptorActor>(&tab_name);
72                        let browsing_context_name = tab_actor.browsing_context();
73                        let browsing_context_actor =
74                            registry.find::<BrowsingContextActor>(&browsing_context_name);
75                        browsing_context_actor
76                            .simulate_color_scheme(theme)
77                            .map_err(|_| ActorError::Internal)?;
78                    } else {
79                        warn!("No active tab for updateConfiguration");
80                    }
81                }
82                let msg = EmptyReplyMsg { from: self.name() };
83                request.reply_final(&msg)?
84            },
85            _ => return Err(ActorError::UnrecognizedPacketType),
86        };
87        Ok(())
88    }
89}
90
91impl TargetConfigurationActor {
92    pub fn new(name: String) -> Self {
93        Self {
94            name,
95            configuration: HashMap::new(),
96            supported_options: HashMap::from([
97                ("cacheDisabled", false),
98                ("colorSchemeSimulation", true),
99                ("customFormatters", false),
100                ("customUserAgent", false),
101                ("javascriptEnabled", false),
102                ("overrideDPPX", false),
103                ("printSimulationEnabled", false),
104                ("rdmPaneMaxTouchPoints", false),
105                ("rdmPaneOrientation", false),
106                ("recordAllocations", false),
107                ("reloadOnTouchSimulationToggle", false),
108                ("restoreFocus", false),
109                ("serviceWorkersTestingEnabled", false),
110                ("setTabOffline", false),
111                ("touchEventsOverride", false),
112                ("tracerOptions", false),
113                ("useSimpleHighlightersForReducedMotion", false),
114            ]),
115        }
116    }
117
118    pub fn encodable(&self) -> TargetConfigurationActorMsg {
119        TargetConfigurationActorMsg {
120            actor: self.name(),
121            configuration: self.configuration.clone(),
122            traits: TargetConfigurationTraits {
123                supported_options: self.supported_options.clone(),
124            },
125        }
126    }
127}