devtools/actors/
inspector.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 the [Firefox JS implementation](http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/inspector.js).
6
7use std::cell::RefCell;
8use std::collections::HashMap;
9
10use devtools_traits::DevtoolScriptControlMsg;
11use devtools_traits::DevtoolScriptControlMsg::GetRootNode;
12use ipc_channel::ipc::{self, IpcSender};
13use serde::Serialize;
14use serde_json::{self, Map, Value};
15
16use crate::StreamId;
17use crate::actor::{Actor, ActorError, ActorRegistry};
18use crate::actors::browsing_context::BrowsingContextActor;
19use crate::actors::inspector::highlighter::{HighlighterActor, HighlighterMsg};
20use crate::actors::inspector::node::NodeInfoToProtocol;
21use crate::actors::inspector::page_style::{PageStyleActor, PageStyleMsg};
22use crate::actors::inspector::walker::{WalkerActor, WalkerMsg};
23use crate::protocol::ClientRequest;
24
25pub mod accessibility;
26pub mod css_properties;
27pub mod highlighter;
28pub mod layout;
29pub mod node;
30pub mod page_style;
31pub mod style_rule;
32pub mod walker;
33
34#[derive(Serialize)]
35#[serde(rename_all = "camelCase")]
36struct GetPageStyleReply {
37    from: String,
38    page_style: PageStyleMsg,
39}
40
41#[derive(Serialize)]
42struct GetWalkerReply {
43    from: String,
44    walker: WalkerMsg,
45}
46
47#[derive(Serialize)]
48struct SupportsHighlightersReply {
49    from: String,
50    value: bool,
51}
52
53#[derive(Serialize)]
54struct GetHighlighterReply {
55    from: String,
56    highlighter: HighlighterMsg,
57}
58
59pub struct InspectorActor {
60    pub name: String,
61    pub walker: RefCell<Option<String>>,
62    pub page_style: RefCell<Option<String>>,
63    pub highlighter: RefCell<Option<String>>,
64    pub script_chan: IpcSender<DevtoolScriptControlMsg>,
65    pub browsing_context: String,
66}
67
68impl Actor for InspectorActor {
69    fn name(&self) -> String {
70        self.name.clone()
71    }
72
73    fn handle_message(
74        &self,
75        request: ClientRequest,
76        registry: &ActorRegistry,
77        msg_type: &str,
78        _msg: &Map<String, Value>,
79        _id: StreamId,
80    ) -> Result<(), ActorError> {
81        let browsing_context = registry.find::<BrowsingContextActor>(&self.browsing_context);
82        let pipeline = browsing_context.active_pipeline_id.get();
83        match msg_type {
84            "getWalker" => {
85                let (tx, rx) = ipc::channel().unwrap();
86                self.script_chan.send(GetRootNode(pipeline, tx)).unwrap();
87                let root_info = rx.recv().unwrap().ok_or(ActorError::Internal)?;
88
89                let name = self
90                    .walker
91                    .borrow()
92                    .clone()
93                    .unwrap_or_else(|| registry.new_name("walker"));
94
95                let root =
96                    root_info.encode(registry, self.script_chan.clone(), pipeline, name.clone());
97
98                if self.walker.borrow().is_none() {
99                    let walker = WalkerActor {
100                        name,
101                        script_chan: self.script_chan.clone(),
102                        pipeline,
103                        root_node: root.clone(),
104                        mutations: RefCell::new(vec![]),
105                    };
106                    let mut walker_name = self.walker.borrow_mut();
107                    *walker_name = Some(walker.name());
108                    registry.register_later(Box::new(walker));
109                }
110
111                let msg = GetWalkerReply {
112                    from: self.name(),
113                    walker: WalkerMsg {
114                        actor: self.walker.borrow().clone().unwrap(),
115                        root,
116                    },
117                };
118                request.reply_final(&msg)?
119            },
120
121            "getPageStyle" => {
122                if self.page_style.borrow().is_none() {
123                    let style = PageStyleActor {
124                        name: registry.new_name("page-style"),
125                        script_chan: self.script_chan.clone(),
126                        pipeline,
127                    };
128                    let mut page_style = self.page_style.borrow_mut();
129                    *page_style = Some(style.name());
130                    registry.register_later(Box::new(style));
131                }
132
133                let msg = GetPageStyleReply {
134                    from: self.name(),
135                    page_style: PageStyleMsg {
136                        actor: self.page_style.borrow().clone().unwrap(),
137                        traits: HashMap::from([
138                            ("fontStretchLevel4".into(), true),
139                            ("fontStyleLevel4".into(), true),
140                            ("fontVariations".into(), true),
141                            ("fontWeightLevel4".into(), true),
142                        ]),
143                    },
144                };
145                request.reply_final(&msg)?
146            },
147
148            "supportsHighlighters" => {
149                let msg = SupportsHighlightersReply {
150                    from: self.name(),
151                    value: true,
152                };
153                request.reply_final(&msg)?
154            },
155
156            "getHighlighterByType" => {
157                if self.highlighter.borrow().is_none() {
158                    let highlighter_actor = HighlighterActor {
159                        name: registry.new_name("highlighter"),
160                        pipeline,
161                        script_sender: self.script_chan.clone(),
162                    };
163                    let mut highlighter = self.highlighter.borrow_mut();
164                    *highlighter = Some(highlighter_actor.name());
165                    registry.register_later(Box::new(highlighter_actor));
166                }
167
168                let msg = GetHighlighterReply {
169                    from: self.name(),
170                    highlighter: HighlighterMsg {
171                        actor: self.highlighter.borrow().clone().unwrap(),
172                    },
173                };
174                request.reply_final(&msg)?
175            },
176            _ => return Err(ActorError::UnrecognizedPacketType),
177        };
178        Ok(())
179    }
180}