Skip to main content

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;
9use std::sync::Arc;
10
11use embedder_traits::Theme;
12use log::warn;
13use malloc_size_of_derive::MallocSizeOf;
14use serde::Serialize;
15use serde_json::{Map, Value};
16
17use crate::actor::{Actor, ActorEncode, ActorError, ActorRegistry, new_actor_name};
18use crate::actors::browsing_context::BrowsingContextActor;
19use crate::actors::tab::TabDescriptorActor;
20use crate::protocol::ClientRequest;
21use crate::{EmptyReplyMsg, RootActor, StreamId};
22
23#[derive(Serialize)]
24#[serde(rename_all = "camelCase")]
25pub(crate) struct TargetConfigurationTraits {
26    supported_options: HashMap<&'static str, bool>,
27}
28
29#[derive(Serialize)]
30pub(crate) struct TargetConfigurationActorMsg {
31    actor: String,
32    configuration: HashMap<&'static str, bool>,
33    traits: TargetConfigurationTraits,
34}
35
36#[derive(MallocSizeOf)]
37pub(crate) struct TargetConfigurationActor {
38    name: String,
39    configuration: HashMap<&'static str, bool>,
40    supported_options: HashMap<&'static str, bool>,
41}
42
43#[derive(Serialize)]
44#[serde(rename_all = "camelCase")]
45struct JavascriptEnabledReply {
46    from: String,
47    javascript_enabled: bool,
48}
49
50impl Actor for TargetConfigurationActor {
51    fn name(&self) -> &str {
52        &self.name
53    }
54
55    /// The target configuration actor can handle the following messages:
56    ///
57    /// - `updateConfiguration`: Receives new configuration flags from the devtools host.
58    fn handle_message(
59        &self,
60        request: ClientRequest,
61        registry: &ActorRegistry,
62        msg_type: &str,
63        msg: &Map<String, Value>,
64        _id: StreamId,
65    ) -> Result<(), ActorError> {
66        match msg_type {
67            "updateConfiguration" => {
68                let config = msg
69                    .get("configuration")
70                    .ok_or(ActorError::MissingParameter)?
71                    .as_object()
72                    .ok_or(ActorError::BadParameterType)?;
73                if let Some(scheme_value) = config.get("colorSchemeSimulation") {
74                    let theme = match scheme_value.as_str() {
75                        Some("dark") => Some(Theme::Dark),
76                        Some("light") => Some(Theme::Light),
77                        _ => None,
78                    };
79                    let root_actor = registry.find::<RootActor>("root");
80                    if let Some(tab_name) = root_actor.active_tab() {
81                        let tab_descriptor_actor = registry.find::<TabDescriptorActor>(&tab_name);
82                        let browsing_context_actor = registry.find::<BrowsingContextActor>(
83                            &tab_descriptor_actor.browsing_context_name,
84                        );
85                        browsing_context_actor
86                            .simulate_color_scheme(theme)
87                            .map_err(|_| ActorError::Internal)?;
88                    } else {
89                        warn!("No active tab for updateConfiguration");
90                    }
91                }
92                let msg = EmptyReplyMsg {
93                    from: self.name().into(),
94                };
95                request.reply_final(&msg)?
96            },
97            "isJavascriptEnabled" => {
98                let msg = JavascriptEnabledReply {
99                    from: self.name().into(),
100                    javascript_enabled: true,
101                };
102                request.reply_final(&msg)?
103            },
104            _ => return Err(ActorError::UnrecognizedPacketType),
105        };
106        Ok(())
107    }
108}
109
110impl TargetConfigurationActor {
111    pub fn register(registry: &ActorRegistry) -> Arc<Self> {
112        let name = new_actor_name::<Self>();
113        let actor = Self {
114            name,
115            configuration: HashMap::new(),
116            supported_options: HashMap::from([
117                ("cacheDisabled", false),
118                ("colorSchemeSimulation", true),
119                ("customFormatters", false),
120                ("customUserAgent", false),
121                ("javascriptEnabled", false),
122                ("overrideDPPX", false),
123                ("printSimulationEnabled", false),
124                ("rdmPaneMaxTouchPoints", false),
125                ("rdmPaneOrientation", false),
126                ("recordAllocations", false),
127                ("reloadOnTouchSimulationToggle", false),
128                ("restoreFocus", false),
129                ("serviceWorkersTestingEnabled", false),
130                ("setTabOffline", false),
131                ("touchEventsOverride", false),
132                ("tracerOptions", false),
133                ("useSimpleHighlightersForReducedMotion", false),
134            ]),
135        };
136        registry.register::<Self>(actor)
137    }
138}
139
140impl ActorEncode<TargetConfigurationActorMsg> for TargetConfigurationActor {
141    fn encode(&self, _: &ActorRegistry) -> TargetConfigurationActorMsg {
142        TargetConfigurationActorMsg {
143            actor: self.name().into(),
144            configuration: self.configuration.clone(),
145            traits: TargetConfigurationTraits {
146                supported_options: self.supported_options.clone(),
147            },
148        }
149    }
150}