1use std::sync::Arc;
13
14use devtools_traits::DevtoolScriptControlMsg;
15use malloc_size_of_derive::MallocSizeOf;
16use serde::Serialize;
17use serde_json::{Map, Value};
18use servo_url::ServoUrl;
19
20use crate::actor::{Actor, ActorEncode, ActorError, ActorRegistry, new_actor_name};
21use crate::actors::browsing_context::{BrowsingContextActor, BrowsingContextActorMsg};
22use crate::actors::root::{DescriptorTraits, RootActor};
23use crate::actors::watcher::{WatcherActor, WatcherActorMsg};
24use crate::protocol::ClientRequest;
25use crate::{EmptyReplyMsg, StreamId};
26
27#[derive(Serialize)]
28#[serde(rename_all = "camelCase")]
29pub(crate) struct TabDescriptorActorMsg {
30 actor: String,
31 #[serde(rename = "browserId")]
33 browser_id: u32,
34 #[serde(rename = "browsingContextID")]
35 browsing_context_id: u32,
36 is_zombie_tab: bool,
37 #[serde(rename = "outerWindowID")]
38 outer_window_id: u32,
39 pub selected: bool,
40 title: String,
41 traits: DescriptorTraits,
42 url: String,
43}
44
45#[derive(Serialize)]
46struct GetTargetReply {
47 from: String,
48 frame: BrowsingContextActorMsg,
49}
50
51#[derive(Serialize)]
52struct GetFaviconReply {
53 from: String,
54 favicon: String,
55}
56
57#[derive(Serialize)]
58struct GetWatcherReply {
59 from: String,
60 #[serde(flatten)]
61 watcher: WatcherActorMsg,
62}
63
64#[derive(MallocSizeOf)]
65pub(crate) struct TabDescriptorActor {
66 name: String,
67 pub browsing_context_name: String,
68}
69
70impl Actor for TabDescriptorActor {
71 fn name(&self) -> &str {
72 &self.name
73 }
74
75 fn handle_message(
86 &self,
87 request: ClientRequest,
88 registry: &ActorRegistry,
89 msg_type: &str,
90 msg: &Map<String, Value>,
91 _id: StreamId,
92 ) -> Result<(), ActorError> {
93 let browsing_context_actor =
94 registry.find::<BrowsingContextActor>(&self.browsing_context_name);
95 let pipeline = browsing_context_actor.pipeline_id();
96
97 match msg_type {
98 "getTarget" => request.reply_final(&GetTargetReply {
99 from: self.name().into(),
100 frame: browsing_context_actor.encode(registry),
101 })?,
102 "getFavicon" => {
103 request.reply_final(&GetFaviconReply {
105 from: self.name().into(),
106 favicon: String::new(),
107 })?
108 },
109 "getWatcher" => request.reply_final(&GetWatcherReply {
110 from: self.name().into(),
111 watcher: registry.encode::<WatcherActor, _>(&browsing_context_actor.watcher_name),
112 })?,
113 "goBack" => {
114 browsing_context_actor
115 .script_chan()
116 .send(DevtoolScriptControlMsg::GoBack(pipeline))
117 .map_err(|_| ActorError::Internal)?;
118 request.reply_final(&EmptyReplyMsg {
119 from: self.name().into(),
120 })?
121 },
122 "goForward" => {
123 browsing_context_actor
124 .script_chan()
125 .send(DevtoolScriptControlMsg::GoForward(pipeline))
126 .map_err(|_| ActorError::Internal)?;
127 request.reply_final(&EmptyReplyMsg {
128 from: self.name().into(),
129 })?
130 },
131 "navigateTo" => {
132 if msg.get("waitForLoad").unwrap_or(&Value::Bool(false)) != &Value::Bool(false) {
133 log::warn!("waitForLoad option for devtools navigation is not supported.");
134 }
135 let url = msg
136 .get("url")
137 .and_then(|value| value.as_str())
138 .map(ServoUrl::parse)
139 .ok_or(ActorError::Internal)?
140 .map_err(|_| ActorError::Internal)?;
141
142 browsing_context_actor
143 .script_chan()
144 .send(DevtoolScriptControlMsg::NavigateTo(pipeline, url))
145 .map_err(|_| ActorError::Internal)?;
146
147 request.reply_final(&EmptyReplyMsg {
148 from: self.name().into(),
149 })?
150 },
151 "reloadDescriptor" => {
152 browsing_context_actor
154 .script_chan()
155 .send(DevtoolScriptControlMsg::Reload(pipeline))
156 .map_err(|_| ActorError::Internal)?;
157
158 request.reply_final(&EmptyReplyMsg {
159 from: self.name().into(),
160 })?
161 },
162 _ => return Err(ActorError::UnrecognizedPacketType),
163 };
164 Ok(())
165 }
166}
167
168impl TabDescriptorActor {
169 pub(crate) fn register(registry: &ActorRegistry, browsing_context_name: String) -> Arc<Self> {
170 let name = new_actor_name::<Self>();
171 let root_actor = registry.find::<RootActor>("root");
172 root_actor.tabs.borrow_mut().push(name.clone());
173 let actor = Self {
174 name,
175 browsing_context_name,
176 };
177 registry.register::<Self>(actor)
178 }
179
180 pub(crate) fn is_top_level_global(&self, registry: &ActorRegistry) -> bool {
181 registry
182 .find::<BrowsingContextActor>(&self.browsing_context_name)
183 .page_info
184 .borrow()
185 .is_top_level_global
186 }
187}
188
189impl ActorEncode<TabDescriptorActorMsg> for TabDescriptorActor {
190 fn encode(&self, registry: &ActorRegistry) -> TabDescriptorActorMsg {
191 let browsing_context_actor =
192 registry.find::<BrowsingContextActor>(&self.browsing_context_name);
193 let root_actor = registry.find::<RootActor>("root");
194 TabDescriptorActorMsg {
195 actor: self.name().into(),
196 browser_id: browsing_context_actor.browser_id.value(),
197 browsing_context_id: browsing_context_actor.browsing_context_id.value(),
198 is_zombie_tab: false,
199 outer_window_id: browsing_context_actor.outer_window_id().value(),
200 selected: root_actor.active_tab().as_deref() == Some(self.name()),
201 title: browsing_context_actor.title(),
202 traits: DescriptorTraits {
203 watcher: true,
204 supports_navigation: true,
205 supports_reload_descriptor: true,
206 },
207 url: browsing_context_actor.url(),
208 }
209 }
210}