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