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