Skip to main content

devtools/actors/
environment.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
5use std::collections::HashMap;
6
7use atomic_refcell::AtomicRefCell;
8use devtools_traits::{DebuggerValue, EnvironmentInfo};
9use malloc_size_of_derive::MallocSizeOf;
10use serde::Serialize;
11use serde_json::Value;
12
13use crate::actor::{Actor, ActorEncode, ActorRegistry, new_actor_name};
14use crate::actors::console::ConsoleActor;
15use crate::actors::object::ObjectPropertyDescriptor;
16use crate::debugger_value_to_json;
17
18#[derive(Serialize)]
19struct EnvironmentBindings {
20    arguments: Vec<Value>,
21    variables: HashMap<String, ObjectPropertyDescriptor>,
22}
23
24#[derive(Serialize)]
25#[serde(rename_all = "camelCase")]
26struct EnvironmentFunction {
27    display_name: String,
28}
29
30#[derive(Serialize)]
31#[serde(rename_all = "camelCase")]
32pub(crate) struct EnvironmentActorMsg {
33    actor: String,
34    #[serde(rename = "type")]
35    type_: Option<String>,
36    scope_kind: Option<String>,
37    #[serde(skip_serializing_if = "Option::is_none")]
38    parent: Option<Box<EnvironmentActorMsg>>,
39    #[serde(skip_serializing_if = "Option::is_none")]
40    bindings: Option<EnvironmentBindings>,
41    /// Should be set if `type_` is `EnvironmentType::Function`
42    #[serde(skip_serializing_if = "Option::is_none")]
43    function: Option<EnvironmentFunction>,
44    /// Should be set if `type_` is `EnvironmentType::Object`
45    #[serde(skip_serializing_if = "Option::is_none")]
46    object: Option<Value>,
47}
48
49/// Resposible for listing the bindings in an environment and assigning new values to them.
50/// Referenced by `FrameActor` when replying to `getEnvironment` messages.
51/// <https://searchfox.org/firefox-main/source/devtools/server/actors/environment.js>
52#[derive(MallocSizeOf)]
53pub(crate) struct EnvironmentActor {
54    name: String,
55    environment: AtomicRefCell<EnvironmentInfo>,
56    parent_name: Option<String>,
57}
58
59impl Actor for EnvironmentActor {
60    fn name(&self) -> &str {
61        &self.name
62    }
63}
64
65impl EnvironmentActor {
66    pub fn register_or_update(
67        registry: &ActorRegistry,
68        environment: EnvironmentInfo,
69        parent_name: Option<String>,
70        actor_name: Option<String>,
71    ) -> String {
72        if let Some(actor_name) = actor_name {
73            let environment_actor = registry.find::<Self>(&actor_name);
74            *environment_actor.environment.borrow_mut() = environment;
75            return actor_name;
76        }
77
78        let environment_name = new_actor_name::<Self>();
79        let environment_actor = Self {
80            name: environment_name.clone(),
81            parent_name,
82            environment: environment.into(),
83        };
84        registry.register(environment_actor);
85        environment_name
86    }
87
88    /// Recursively searches for property and variable names in this lineage of environments.
89    /// The resulting vec is sorted and deduplicated. Intended for autocomplete.
90    pub(crate) fn search_identifiers_recursive(
91        &self,
92        registry: &ActorRegistry,
93        prefix: &str,
94    ) -> Vec<String> {
95        let mut names: Vec<String> = vec![];
96        self.recurse_identifiers(registry, prefix, &mut names);
97
98        names.sort_unstable();
99        names.dedup();
100        names
101    }
102
103    fn recurse_identifiers(&self, registry: &ActorRegistry, prefix: &str, names: &mut Vec<String>) {
104        let environment = self.environment.borrow();
105        names.extend(
106            environment
107                .binding_variables
108                .iter()
109                .map(|bind| &bind.name)
110                .filter(|name| ConsoleActor::autocomplete_match(prefix, name))
111                .cloned(),
112        );
113
114        if let Some(DebuggerValue::ObjectValue {
115            preview: Some(preview),
116            ..
117        }) = &environment.object &&
118            let Some(props) = &preview.own_properties
119        {
120            names.extend(
121                props
122                    .iter()
123                    .map(|p| &p.name)
124                    .filter(|name| ConsoleActor::autocomplete_match(prefix, name))
125                    .cloned(),
126            );
127        }
128
129        if let Some(parent_name) = &self.parent_name {
130            registry
131                .find::<EnvironmentActor>(parent_name)
132                .recurse_identifiers(registry, prefix, names);
133        }
134    }
135}
136
137impl ActorEncode<EnvironmentActorMsg> for EnvironmentActor {
138    fn encode(&self, registry: &ActorRegistry) -> EnvironmentActorMsg {
139        let parent = self
140            .parent_name
141            .as_ref()
142            .map(|p| registry.find::<EnvironmentActor>(p))
143            .map(|p| Box::new(p.encode(registry)));
144        let environment = self.environment.borrow();
145        let bindings = match environment.type_.as_deref() {
146            Some("object") | Some("with") => None,
147            _ => Some(EnvironmentBindings {
148                arguments: [].to_vec(),
149                variables: environment
150                    .binding_variables
151                    .clone()
152                    .into_iter()
153                    .map(|ref property_descriptor| {
154                        (
155                            property_descriptor.name.clone(),
156                            ObjectPropertyDescriptor::from_property_descriptor(
157                                registry,
158                                property_descriptor,
159                            ),
160                        )
161                    })
162                    .collect(),
163            }),
164        };
165
166        EnvironmentActorMsg {
167            actor: self.name().to_string(),
168            type_: environment.type_.clone(),
169            scope_kind: environment.scope_kind.clone(),
170            parent,
171            function: environment
172                .function_display_name
173                .clone()
174                .map(|display_name| EnvironmentFunction { display_name }),
175            object: environment
176                .object
177                .clone()
178                .map(|object| debugger_value_to_json(registry, object)),
179            bindings,
180        }
181    }
182}