1#![crate_name = "devtools"]
11#![crate_type = "rlib"]
12#![deny(unsafe_code)]
13
14use std::borrow::ToOwned;
15use std::collections::HashMap;
16use std::io::Read;
17use std::net::{Shutdown, TcpListener, TcpStream};
18use std::sync::{Arc, Mutex};
19use std::thread;
20
21use base::generic_channel;
22use base::id::{BrowsingContextId, PipelineId, WebViewId};
23use crossbeam_channel::{Receiver, Sender, unbounded};
24use devtools_traits::{
25 ChromeToDevtoolsControlMsg, ConsoleMessage, ConsoleMessageBuilder, DevtoolScriptControlMsg,
26 DevtoolsControlMsg, DevtoolsPageInfo, LogLevel, NavigationState, NetworkEvent, PageError,
27 ScriptToDevtoolsControlMsg, SourceInfo, WorkerId,
28};
29use embedder_traits::{AllowOrDeny, EmbedderMsg, EmbedderProxy};
30use ipc_channel::ipc::IpcSender;
31use log::{trace, warn};
32use resource::{ResourceArrayType, ResourceAvailable};
33use rustc_hash::FxHashMap;
34use serde::Serialize;
35use servo_rand::RngCore;
36
37use crate::actor::{Actor, ActorRegistry};
38use crate::actors::browsing_context::BrowsingContextActor;
39use crate::actors::console::{ConsoleActor, Root};
40use crate::actors::device::DeviceActor;
41use crate::actors::framerate::FramerateActor;
42use crate::actors::network_event::NetworkEventActor;
43use crate::actors::performance::PerformanceActor;
44use crate::actors::preference::PreferenceActor;
45use crate::actors::process::ProcessActor;
46use crate::actors::root::RootActor;
47use crate::actors::source::SourceActor;
48use crate::actors::thread::ThreadActor;
49use crate::actors::watcher::WatcherActor;
50use crate::actors::worker::{WorkerActor, WorkerType};
51use crate::id::IdMap;
52use crate::network_handler::handle_network_event;
53use crate::protocol::JsonPacketStream;
54
55mod actor;
56mod actors {
58 pub mod breakpoint;
59 pub mod browsing_context;
60 pub mod console;
61 pub mod device;
62 pub mod framerate;
63 pub mod inspector;
64 pub mod long_string;
65 pub mod memory;
66 pub mod network_event;
67 pub mod object;
68 pub mod performance;
69 pub mod preference;
70 pub mod process;
71 pub mod reflow;
72 pub mod root;
73 pub mod source;
74 pub mod stylesheets;
75 pub mod tab;
76 pub mod thread;
77 pub mod timeline;
78 pub mod watcher;
79 pub mod worker;
80}
81mod id;
82mod network_handler;
83mod protocol;
84mod resource;
85
86#[derive(Clone, Debug, Eq, Hash, PartialEq)]
87enum UniqueId {
88 Pipeline(PipelineId),
89 Worker(WorkerId),
90}
91
92#[derive(Serialize)]
93pub struct EmptyReplyMsg {
94 pub from: String,
95}
96
97pub fn start_server(port: u16, embedder: EmbedderProxy) -> Sender<DevtoolsControlMsg> {
99 let (sender, receiver) = unbounded();
100 {
101 let sender = sender.clone();
102 thread::Builder::new()
103 .name("Devtools".to_owned())
104 .spawn(move || {
105 if let Some(instance) = DevtoolsInstance::create(sender, receiver, port, embedder) {
106 instance.run()
107 }
108 })
109 .expect("Thread spawning failed");
110 }
111 sender
112}
113
114#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
115pub(crate) struct StreamId(u32);
116
117struct DevtoolsInstance {
118 actors: Arc<Mutex<ActorRegistry>>,
119 id_map: Arc<Mutex<IdMap>>,
120 browsing_contexts: FxHashMap<BrowsingContextId, String>,
121 receiver: Receiver<DevtoolsControlMsg>,
122 pipelines: FxHashMap<PipelineId, BrowsingContextId>,
123 actor_workers: FxHashMap<WorkerId, String>,
124 actor_requests: HashMap<String, String>,
125 connections: FxHashMap<StreamId, TcpStream>,
126 next_resource_id: u64,
127}
128
129impl DevtoolsInstance {
130 fn create(
131 sender: Sender<DevtoolsControlMsg>,
132 receiver: Receiver<DevtoolsControlMsg>,
133 port: u16,
134 embedder: EmbedderProxy,
135 ) -> Option<Self> {
136 let bound = TcpListener::bind(("0.0.0.0", port)).ok().and_then(|l| {
137 l.local_addr()
138 .map(|addr| addr.port())
139 .ok()
140 .map(|port| (l, port))
141 });
142
143 let port = if bound.is_some() { Ok(port) } else { Err(()) };
145 let token = format!("{:X}", servo_rand::ServoRng::default().next_u32());
146 embedder.send(EmbedderMsg::OnDevtoolsStarted(port, token.clone()));
147
148 let listener = match bound {
149 Some((l, _)) => l,
150 None => {
151 return None;
152 },
153 };
154
155 let mut registry = ActorRegistry::new();
157 let performance = PerformanceActor::new(registry.new_name("performance"));
158 let device = DeviceActor::new(registry.new_name("device"));
159 let preference = PreferenceActor::new(registry.new_name("preference"));
160 let process = ProcessActor::new(registry.new_name("process"));
161 let root = Box::new(RootActor {
162 tabs: vec![],
163 workers: vec![],
164 device: device.name(),
165 performance: performance.name(),
166 preference: preference.name(),
167 process: process.name(),
168 active_tab: None.into(),
169 });
170
171 registry.register(root);
172 registry.register(Box::new(performance));
173 registry.register(Box::new(device));
174 registry.register(Box::new(preference));
175 registry.register(Box::new(process));
176 registry.find::<RootActor>("root");
177
178 let actors = registry.create_shareable();
179
180 let instance = Self {
181 actors,
182 id_map: Arc::new(Mutex::new(IdMap::default())),
183 browsing_contexts: FxHashMap::default(),
184 pipelines: FxHashMap::default(),
185 receiver,
186 actor_requests: HashMap::new(),
187 actor_workers: FxHashMap::default(),
188 connections: FxHashMap::default(),
189 next_resource_id: 1,
190 };
191
192 thread::Builder::new()
193 .name("DevtoolsCliAcceptor".to_owned())
194 .spawn(move || {
195 for stream in listener.incoming() {
197 let mut stream = stream.expect("Can't retrieve stream");
198 if !allow_devtools_client(&mut stream, &embedder, &token) {
199 continue;
200 };
201 sender
203 .send(DevtoolsControlMsg::FromChrome(
204 ChromeToDevtoolsControlMsg::AddClient(stream),
205 ))
206 .unwrap();
207 }
208 })
209 .expect("Thread spawning failed");
210
211 Some(instance)
212 }
213
214 fn run(mut self) {
215 let mut next_id = StreamId(0);
216 while let Ok(msg) = self.receiver.recv() {
217 trace!("{:?}", msg);
218 match msg {
219 DevtoolsControlMsg::FromChrome(ChromeToDevtoolsControlMsg::AddClient(stream)) => {
220 let actors = self.actors.clone();
221 let id = next_id;
222 next_id = StreamId(id.0 + 1);
223 self.connections.insert(id, stream.try_clone().unwrap());
224
225 for name in self.browsing_contexts.values() {
227 let actors = actors.lock().unwrap();
228 let browsing_context = actors.find::<BrowsingContextActor>(name);
229 let mut streams = browsing_context.streams.borrow_mut();
230 streams.insert(id, stream.try_clone().unwrap());
231 }
232
233 thread::Builder::new()
234 .name("DevtoolsClientHandler".to_owned())
235 .spawn(move || handle_client(actors, stream.try_clone().unwrap(), id))
236 .expect("Thread spawning failed");
237 },
238 DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::FramerateTick(
239 actor_name,
240 tick,
241 )) => self.handle_framerate_tick(actor_name, tick),
242 DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::TitleChanged(
243 pipeline,
244 title,
245 )) => self.handle_title_changed(pipeline, title),
246 DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::NewGlobal(
247 ids,
248 script_sender,
249 pageinfo,
250 )) => self.handle_new_global(ids, script_sender, pageinfo),
251 DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::Navigate(
252 browsing_context,
253 state,
254 )) => self.handle_navigate(browsing_context, state),
255 DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::ConsoleAPI(
256 pipeline_id,
257 console_message,
258 worker_id,
259 )) => self.handle_console_message(pipeline_id, worker_id, console_message),
260 DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::CreateSourceActor(
261 script_sender,
262 pipeline_id,
263 source_info,
264 )) => self.handle_create_source_actor(script_sender, pipeline_id, source_info),
265 DevtoolsControlMsg::FromScript(
266 ScriptToDevtoolsControlMsg::UpdateSourceContent(pipeline_id, source_content),
267 ) => self.handle_update_source_content(pipeline_id, source_content),
268 DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::ReportPageError(
269 pipeline_id,
270 page_error,
271 )) => self.handle_page_error(pipeline_id, None, page_error),
272 DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::ReportCSSError(
273 pipeline_id,
274 css_error,
275 )) => {
276 let mut console_message = ConsoleMessageBuilder::new(
277 LogLevel::Warn,
278 css_error.filename,
279 css_error.line,
280 css_error.column,
281 );
282 console_message.add_argument(css_error.msg.into());
283
284 self.handle_console_message(pipeline_id, None, console_message.finish())
285 },
286 DevtoolsControlMsg::FromChrome(ChromeToDevtoolsControlMsg::NetworkEvent(
287 request_id,
288 network_event,
289 )) => {
290 let mut connections = Vec::<TcpStream>::new();
292 for stream in self.connections.values() {
293 connections.push(stream.try_clone().unwrap());
294 }
295
296 self.handle_network_event(connections, request_id, network_event);
297 },
298 DevtoolsControlMsg::FromChrome(ChromeToDevtoolsControlMsg::ServerExitMsg) => break,
299 }
300 }
301
302 for connection in self.connections.values_mut() {
304 let _ = connection.shutdown(Shutdown::Both);
305 }
306 }
307
308 fn handle_framerate_tick(&self, actor_name: String, tick: f64) {
309 let mut actors = self.actors.lock().unwrap();
310 let framerate_actor = actors.find_mut::<FramerateActor>(&actor_name);
311 framerate_actor.add_tick(tick);
312 }
313
314 fn handle_navigate(&self, browsing_context_id: BrowsingContextId, state: NavigationState) {
315 let actor_name = self.browsing_contexts.get(&browsing_context_id).unwrap();
316 let actors = self.actors.lock().unwrap();
317 let actor = actors.find::<BrowsingContextActor>(actor_name);
318 let mut id_map = self.id_map.lock().expect("Mutex poisoned");
319 if let NavigationState::Start(url) = &state {
320 let mut connections = Vec::<TcpStream>::new();
321 for stream in self.connections.values() {
322 connections.push(stream.try_clone().unwrap());
323 }
324 let watcher_actor = actors.find::<WatcherActor>(&actor.watcher);
325 watcher_actor.emit_will_navigate(
326 browsing_context_id,
327 url.clone(),
328 &mut connections,
329 &mut id_map,
330 );
331 };
332 actor.navigate(state, &mut id_map);
333 }
334
335 fn handle_new_global(
339 &mut self,
340 ids: (BrowsingContextId, PipelineId, Option<WorkerId>, WebViewId),
341 script_sender: IpcSender<DevtoolScriptControlMsg>,
342 page_info: DevtoolsPageInfo,
343 ) {
344 let mut actors = self.actors.lock().unwrap();
345
346 let (browsing_context_id, pipeline_id, worker_id, webview_id) = ids;
347 let id_map = &mut self.id_map.lock().expect("Mutex poisoned");
348 let devtools_browser_id = id_map.browser_id(webview_id);
349 let devtools_browsing_context_id = id_map.browsing_context_id(browsing_context_id);
350 let devtools_outer_window_id = id_map.outer_window_id(pipeline_id);
351
352 let console_name = actors.new_name("console");
353
354 let parent_actor = if let Some(id) = worker_id {
355 assert!(self.pipelines.contains_key(&pipeline_id));
356 assert!(self.browsing_contexts.contains_key(&browsing_context_id));
357
358 let thread = ThreadActor::new(actors.new_name("thread"));
359 let thread_name = thread.name();
360 actors.register(Box::new(thread));
361
362 let worker_name = actors.new_name("worker");
363 let worker = WorkerActor {
364 name: worker_name.clone(),
365 console: console_name.clone(),
366 thread: thread_name,
367 worker_id: id,
368 url: page_info.url.clone(),
369 type_: WorkerType::Dedicated,
370 script_chan: script_sender,
371 streams: Default::default(),
372 };
373 let root = actors.find_mut::<RootActor>("root");
374 root.workers.push(worker.name.clone());
375
376 self.actor_workers.insert(id, worker_name.clone());
377 actors.register(Box::new(worker));
378
379 Root::DedicatedWorker(worker_name)
380 } else {
381 self.pipelines.insert(pipeline_id, browsing_context_id);
382 let name = self
383 .browsing_contexts
384 .entry(browsing_context_id)
385 .or_insert_with(|| {
386 let browsing_context_actor = BrowsingContextActor::new(
387 console_name.clone(),
388 devtools_browser_id,
389 devtools_browsing_context_id,
390 page_info,
391 pipeline_id,
392 devtools_outer_window_id,
393 script_sender,
394 &mut actors,
395 );
396 let name = browsing_context_actor.name();
397 actors.register(Box::new(browsing_context_actor));
398 name
399 });
400
401 let browsing_context = actors.find::<BrowsingContextActor>(name);
403 let mut streams = browsing_context.streams.borrow_mut();
404 for (id, stream) in &self.connections {
405 streams.insert(*id, stream.try_clone().unwrap());
406 }
407
408 Root::BrowsingContext(name.clone())
409 };
410
411 let console = ConsoleActor {
412 name: console_name,
413 cached_events: Default::default(),
414 root: parent_actor,
415 };
416
417 actors.register(Box::new(console));
418 }
419
420 fn handle_title_changed(&self, pipeline_id: PipelineId, title: String) {
421 let bc = match self.pipelines.get(&pipeline_id) {
422 Some(bc) => bc,
423 None => return,
424 };
425 let name = match self.browsing_contexts.get(bc) {
426 Some(name) => name,
427 None => return,
428 };
429 let actors = self.actors.lock().unwrap();
430 let browsing_context = actors.find::<BrowsingContextActor>(name);
431 browsing_context.title_changed(pipeline_id, title);
432 }
433
434 fn handle_page_error(
435 &mut self,
436 pipeline_id: PipelineId,
437 worker_id: Option<WorkerId>,
438 page_error: PageError,
439 ) {
440 let console_actor_name = match self.find_console_actor(pipeline_id, worker_id) {
441 Some(name) => name,
442 None => return,
443 };
444 let actors = self.actors.lock().unwrap();
445 let console_actor = actors.find::<ConsoleActor>(&console_actor_name);
446 let id = worker_id.map_or(UniqueId::Pipeline(pipeline_id), UniqueId::Worker);
447 for stream in self.connections.values_mut() {
448 console_actor.handle_page_error(page_error.clone(), id.clone(), &actors, stream);
449 }
450 }
451
452 fn handle_console_message(
453 &mut self,
454 pipeline_id: PipelineId,
455 worker_id: Option<WorkerId>,
456 console_message: ConsoleMessage,
457 ) {
458 let console_actor_name = match self.find_console_actor(pipeline_id, worker_id) {
459 Some(name) => name,
460 None => return,
461 };
462 let actors = self.actors.lock().unwrap();
463 let console_actor = actors.find::<ConsoleActor>(&console_actor_name);
464 let id = worker_id.map_or(UniqueId::Pipeline(pipeline_id), UniqueId::Worker);
465 for stream in self.connections.values_mut() {
466 console_actor.handle_console_api(console_message.clone(), id.clone(), &actors, stream);
467 }
468 }
469
470 fn find_console_actor(
471 &self,
472 pipeline_id: PipelineId,
473 worker_id: Option<WorkerId>,
474 ) -> Option<String> {
475 let actors = self.actors.lock().unwrap();
476 if let Some(worker_id) = worker_id {
477 let actor_name = self.actor_workers.get(&worker_id)?;
478 Some(actors.find::<WorkerActor>(actor_name).console.clone())
479 } else {
480 let id = self.pipelines.get(&pipeline_id)?;
481 let actor_name = self.browsing_contexts.get(id)?;
482 Some(
483 actors
484 .find::<BrowsingContextActor>(actor_name)
485 .console
486 .clone(),
487 )
488 }
489 }
490
491 fn handle_network_event(
492 &mut self,
493 connections: Vec<TcpStream>,
494 request_id: String,
495 network_event: NetworkEvent,
496 ) {
497 let browsing_context_id = match &network_event {
498 NetworkEvent::HttpRequest(req) => req.browsing_context_id,
499 NetworkEvent::HttpRequestUpdate(req) => req.browsing_context_id,
500 NetworkEvent::HttpResponse(resp) => resp.browsing_context_id,
501 };
502
503 let Some(browsing_context_actor_name) = self.browsing_contexts.get(&browsing_context_id)
504 else {
505 return;
506 };
507 let watcher_name = self
508 .actors
509 .lock()
510 .unwrap()
511 .find::<BrowsingContextActor>(browsing_context_actor_name)
512 .watcher
513 .clone();
514
515 let netevent_actor_name = match self.actor_requests.get(&request_id) {
516 Some(name) => name.clone(),
517 None => self.create_network_event_actor(request_id, watcher_name),
518 };
519
520 handle_network_event(
521 Arc::clone(&self.actors),
522 netevent_actor_name,
523 connections,
524 network_event,
525 )
526 }
527
528 fn create_network_event_actor(&mut self, request_id: String, watcher_name: String) -> String {
530 let mut actors = self.actors.lock().unwrap();
531 let resource_id = self.next_resource_id;
532 self.next_resource_id += 1;
533
534 let actor_name = actors.new_name("netevent");
535 let actor = NetworkEventActor::new(actor_name.clone(), resource_id, watcher_name);
536
537 self.actor_requests.insert(request_id, actor_name.clone());
538 actors.register(Box::new(actor));
539
540 actor_name
541 }
542
543 fn handle_create_source_actor(
544 &mut self,
545 script_sender: IpcSender<DevtoolScriptControlMsg>,
546 pipeline_id: PipelineId,
547 source_info: SourceInfo,
548 ) {
549 let mut actors = self.actors.lock().unwrap();
550
551 let source_content = source_info
552 .content
553 .or_else(|| actors.inline_source_content(pipeline_id));
554 let source_actor = SourceActor::new_registered(
555 &mut actors,
556 pipeline_id,
557 source_info.url,
558 source_content,
559 source_info.content_type,
560 source_info.spidermonkey_id,
561 source_info.introduction_type,
562 script_sender,
563 );
564 let source_actor_name = source_actor.name.clone();
565 let source_form = source_actor.source_form();
566
567 if let Some(worker_id) = source_info.worker_id {
568 let Some(worker_actor_name) = self.actor_workers.get(&worker_id) else {
569 return;
570 };
571
572 let thread_actor_name = actors.find::<WorkerActor>(worker_actor_name).thread.clone();
573 let thread_actor = actors.find_mut::<ThreadActor>(&thread_actor_name);
574
575 thread_actor.source_manager.add_source(&source_actor_name);
576
577 let worker_actor = actors.find::<WorkerActor>(worker_actor_name);
578
579 for stream in self.connections.values_mut() {
580 worker_actor.resource_array(
581 &source_form,
582 "source".into(),
583 ResourceArrayType::Available,
584 stream,
585 );
586 }
587 } else {
588 let Some(browsing_context_id) = self.pipelines.get(&pipeline_id) else {
589 return;
590 };
591 let Some(actor_name) = self.browsing_contexts.get(browsing_context_id) else {
592 return;
593 };
594
595 let thread_actor_name = {
596 let browsing_context = actors.find::<BrowsingContextActor>(actor_name);
597 browsing_context.thread.clone()
598 };
599
600 let thread_actor = actors.find_mut::<ThreadActor>(&thread_actor_name);
601
602 thread_actor.source_manager.add_source(&source_actor_name);
603
604 let browsing_context = actors.find::<BrowsingContextActor>(actor_name);
606
607 for stream in self.connections.values_mut() {
608 browsing_context.resource_array(
609 &source_form,
610 "source".into(),
611 ResourceArrayType::Available,
612 stream,
613 );
614 }
615 }
616 }
617
618 fn handle_update_source_content(&mut self, pipeline_id: PipelineId, source_content: String) {
619 let mut actors = self.actors.lock().unwrap();
620
621 for actor_name in actors.source_actor_names_for_pipeline(pipeline_id) {
622 let source_actor: &mut SourceActor = actors.find_mut(&actor_name);
623 if source_actor.content.is_none() {
624 source_actor.content = Some(source_content.clone());
625 }
626 }
627
628 actors.set_inline_source_content(pipeline_id, source_content);
631 }
632}
633
634fn allow_devtools_client(stream: &mut TcpStream, embedder: &EmbedderProxy, token: &str) -> bool {
635 let token = format!("25:{{\"auth_token\":\"{}\"}}", token);
637 let mut buf = [0; 28];
638 let timeout = std::time::Duration::from_millis(500);
639 stream.set_read_timeout(Some(timeout)).unwrap();
641 let peek = stream.peek(&mut buf);
642 stream.set_read_timeout(None).unwrap();
643 if let Ok(len) = peek {
644 if len == buf.len() {
645 if let Ok(s) = std::str::from_utf8(&buf) {
646 if s == token {
647 let _ = stream.read_exact(&mut buf);
649 return true;
650 }
651 }
652 }
653 };
654
655 let (request_sender, request_receiver) =
657 generic_channel::channel().expect("Failed to create IPC channel!");
658 embedder.send(EmbedderMsg::RequestDevtoolsConnection(request_sender));
659 request_receiver.recv().unwrap() == AllowOrDeny::Allow
660}
661
662fn handle_client(actors: Arc<Mutex<ActorRegistry>>, mut stream: TcpStream, stream_id: StreamId) {
664 log::info!("Connection established to {}", stream.peer_addr().unwrap());
665 let msg = actors.lock().unwrap().find::<RootActor>("root").encodable();
666 if let Err(error) = stream.write_json_packet(&msg) {
667 warn!("Failed to send initial packet from root actor: {error:?}");
668 return;
669 }
670
671 loop {
672 match stream.read_json_packet() {
673 Ok(Some(json_packet)) => {
674 if let Err(()) = actors.lock().unwrap().handle_message(
675 json_packet.as_object().unwrap(),
676 &mut stream,
677 stream_id,
678 ) {
679 log::error!("Devtools actor stopped responding");
680 let _ = stream.shutdown(Shutdown::Both);
681 break;
682 }
683 },
684 Ok(None) => {
685 log::info!("Devtools connection closed");
686 break;
687 },
688 Err(err_msg) => {
689 log::error!("Failed to read message from devtools client: {}", err_msg);
690 break;
691 },
692 }
693 }
694
695 actors.lock().unwrap().cleanup(stream_id);
696}