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