devtools/actors/
tab.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//! Descriptor actor that represents a web view. It can link a tab to the corresponding watcher
6//! actor to enable inspection.
7//!
8//! Liberally derived from the [Firefox JS implementation].
9//!
10//! [Firefox JS implementation]: https://searchfox.org/mozilla-central/source/devtools/server/actors/descriptors/tab.js
11
12use devtools_traits::DevtoolScriptControlMsg;
13use serde::Serialize;
14use serde_json::{Map, Value};
15
16use crate::actor::{Actor, ActorError, ActorRegistry};
17use crate::actors::browsing_context::{BrowsingContextActor, BrowsingContextActorMsg};
18use crate::actors::root::{DescriptorTraits, RootActor};
19use crate::actors::watcher::{WatcherActor, WatcherActorMsg};
20use crate::protocol::ClientRequest;
21use crate::{EmptyReplyMsg, StreamId};
22
23#[derive(Serialize)]
24#[serde(rename_all = "camelCase")]
25pub struct TabDescriptorActorMsg {
26    actor: String,
27    /// This correspond to webview_id
28    #[serde(rename = "browserId")]
29    browser_id: u32,
30    #[serde(rename = "browsingContextID")]
31    browsing_context_id: u32,
32    is_zombie_tab: bool,
33    #[serde(rename = "outerWindowID")]
34    outer_window_id: u32,
35    selected: bool,
36    title: String,
37    traits: DescriptorTraits,
38    url: String,
39}
40
41impl TabDescriptorActorMsg {
42    pub fn browser_id(&self) -> u32 {
43        self.browser_id
44    }
45
46    pub fn actor(&self) -> String {
47        self.actor.clone()
48    }
49}
50
51#[derive(Serialize)]
52struct GetTargetReply {
53    from: String,
54    frame: BrowsingContextActorMsg,
55}
56
57#[derive(Serialize)]
58struct GetFaviconReply {
59    from: String,
60    favicon: String,
61}
62
63#[derive(Serialize)]
64struct GetWatcherReply {
65    from: String,
66    #[serde(flatten)]
67    watcher: WatcherActorMsg,
68}
69
70pub struct TabDescriptorActor {
71    name: String,
72    browsing_context_actor: String,
73    is_top_level_global: bool,
74}
75
76impl Actor for TabDescriptorActor {
77    fn name(&self) -> String {
78        self.name.clone()
79    }
80
81    /// The tab actor can handle the following messages:
82    ///
83    /// - `getTarget`: Returns the surrounding `BrowsingContextActor`.
84    ///
85    /// - `getFavicon`: Should return the tab favicon, but it is not yet supported.
86    ///
87    /// - `getWatcher`: Returns a `WatcherActor` linked to the tab's `BrowsingContext`. It is used
88    ///   to describe the debugging capabilities of this tab.
89    ///
90    /// - `reloadDescriptor`: Causes the page to reload.
91    fn handle_message(
92        &self,
93        request: ClientRequest,
94        registry: &ActorRegistry,
95        msg_type: &str,
96        _msg: &Map<String, Value>,
97        _id: StreamId,
98    ) -> Result<(), ActorError> {
99        match msg_type {
100            "getTarget" => {
101                let frame = registry
102                    .find::<BrowsingContextActor>(&self.browsing_context_actor)
103                    .encodable();
104                request.reply_final(&GetTargetReply {
105                    from: self.name(),
106                    frame,
107                })?
108            },
109            "getFavicon" => {
110                // TODO: Return a favicon when available
111                request.reply_final(&GetFaviconReply {
112                    from: self.name(),
113                    favicon: String::new(),
114                })?
115            },
116            "getWatcher" => {
117                let ctx_actor = registry.find::<BrowsingContextActor>(&self.browsing_context_actor);
118                let watcher = registry.find::<WatcherActor>(&ctx_actor.watcher);
119                request.reply_final(&GetWatcherReply {
120                    from: self.name(),
121                    watcher: watcher.encodable(),
122                })?
123            },
124            "reloadDescriptor" => {
125                // There is an extra bypassCache parameter that we don't currently use.
126                let ctx_actor = registry.find::<BrowsingContextActor>(&self.browsing_context_actor);
127                let pipeline = ctx_actor.active_pipeline_id.get();
128                ctx_actor
129                    .script_chan
130                    .send(DevtoolScriptControlMsg::Reload(pipeline))
131                    .map_err(|_| ActorError::Internal)?;
132
133                request.reply_final(&EmptyReplyMsg { from: self.name() })?
134            },
135            _ => return Err(ActorError::UnrecognizedPacketType),
136        };
137        Ok(())
138    }
139}
140
141impl TabDescriptorActor {
142    pub(crate) fn new(
143        actors: &mut ActorRegistry,
144        browsing_context_actor: String,
145        is_top_level_global: bool,
146    ) -> TabDescriptorActor {
147        let name = actors.new_name("tab-description");
148        let root = actors.find_mut::<RootActor>("root");
149        root.tabs.push(name.clone());
150        TabDescriptorActor {
151            name,
152            browsing_context_actor,
153            is_top_level_global,
154        }
155    }
156
157    pub fn encodable(&self, registry: &ActorRegistry, selected: bool) -> TabDescriptorActorMsg {
158        let ctx_actor = registry.find::<BrowsingContextActor>(&self.browsing_context_actor);
159        let title = ctx_actor.title.borrow().clone();
160        let url = ctx_actor.url.borrow().clone();
161
162        TabDescriptorActorMsg {
163            actor: self.name(),
164            browser_id: ctx_actor.browser_id.value(),
165            browsing_context_id: ctx_actor.browsing_context_id.value(),
166            is_zombie_tab: false,
167            outer_window_id: ctx_actor.active_outer_window_id.get().value(),
168            selected,
169            title,
170            traits: DescriptorTraits {
171                watcher: true,
172                supports_reload_descriptor: true,
173            },
174            url,
175        }
176    }
177
178    pub(crate) fn is_top_level_global(&self) -> bool {
179        self.is_top_level_global
180    }
181
182    pub fn browsing_context(&self) -> String {
183        self.browsing_context_actor.clone()
184    }
185}