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::{Ipv4Addr, Shutdown, SocketAddr, TcpListener, TcpStream};
18use std::str::FromStr;
19use std::sync::{Arc, Mutex};
20use std::thread;
21
22use crossbeam_channel::{Receiver, Sender, unbounded};
23use devtools_traits::{
24 ChromeToDevtoolsControlMsg, ConsoleLogLevel, ConsoleMessage, ConsoleMessageFields,
25 DebuggerValue, DevtoolScriptControlMsg, DevtoolsControlMsg, DevtoolsPageInfo, DomMutation,
26 EnvironmentInfo, FrameInfo, FrameOffset, NavigationState, NetworkEvent, PauseReason,
27 ScriptToDevtoolsControlMsg, SourceInfo, WorkerId, get_time_stamp,
28};
29use embedder_traits::{AllowOrDeny, EmbedderMsg, EmbedderProxy};
30use log::{trace, warn};
31use malloc_size_of::MallocSizeOf;
32use malloc_size_of_derive::MallocSizeOf;
33use profile_traits::path;
34use rand::{Rng, rng};
35use resource::{ResourceArrayType, ResourceAvailable};
36use rustc_hash::FxHashMap;
37use serde::Serialize;
38use serde_json::{Map, Number, Value};
39use servo_base::generic_channel::{self, GenericSender};
40use servo_base::id::{BrowsingContextId, PipelineId, WebViewId};
41use servo_config::pref;
42use uuid::Uuid;
43
44use crate::actor::{Actor, ActorEncode, ActorError, ActorRegistry, new_actor_name};
45use crate::actors::browsing_context::BrowsingContextActor;
46use crate::actors::console::{ConsoleActor, ConsoleResource, DevtoolsConsoleMessage, Root};
47use crate::actors::environment::EnvironmentActor;
48use crate::actors::frame::FrameActor;
49use crate::actors::framerate::FramerateActor;
50use crate::actors::inspector::InspectorActor;
51use crate::actors::inspector::walker::WalkerActor;
52use crate::actors::network_event::NetworkEventActor;
53use crate::actors::object::ObjectActor;
54use crate::actors::pause::PauseActor;
55use crate::actors::root::RootActor;
56use crate::actors::source::SourceActor;
57use crate::actors::thread::{ThreadActor, ThreadInterruptedReply};
58use crate::actors::watcher::WatcherActor;
59use crate::actors::worker::{WorkerTargetActor, WorkerType};
60use crate::id::IdMap;
61use crate::network_handler::handle_network_event;
62use crate::protocol::{DevtoolsConnection, JsonPacketStream};
63
64mod actor;
65mod actors {
67 pub mod blackboxing;
68 pub mod breakpoint;
69 pub mod browsing_context;
70 pub mod console;
71 pub mod device;
72 pub mod environment;
73 pub mod frame;
74 pub mod framerate;
75 pub mod inspector;
76 pub mod long_string;
77 pub mod memory;
78 pub mod network_event;
79 pub mod object;
80 pub mod pause;
81 pub mod performance;
82 pub mod preference;
83 pub mod process;
84 pub mod property_iterator;
85 pub mod reflow;
86 pub mod root;
87 pub mod source;
88 pub mod stylesheets;
89 pub mod symbol_iterator;
90 pub mod tab;
91 pub mod thread;
92 pub mod timeline;
93 pub mod watcher;
94 pub mod worker;
95}
96mod id;
97mod network_handler;
98mod protocol;
99mod resource;
100use profile_traits::mem::{
101 ProcessReports, ProfilerChan, Report, ReportKind, perform_memory_report,
102};
103
104#[derive(Clone, Debug, Eq, Hash, PartialEq, MallocSizeOf)]
105enum UniqueId {
106 Pipeline(PipelineId),
107 Worker(WorkerId),
108}
109
110#[derive(Serialize)]
111pub(crate) struct EmptyReplyMsg {
112 pub from: String,
113}
114
115#[derive(Serialize)]
116pub(crate) struct ActorMsg {
117 pub actor: String,
118}
119
120pub fn start_server(
122 embedder: EmbedderProxy,
123 mem_profiler_chan: ProfilerChan,
124) -> Sender<DevtoolsControlMsg> {
125 let (sender, receiver) = unbounded();
126 {
127 let sender = sender.clone();
128 let sender2 = sender.clone();
129 thread::Builder::new()
130 .name("Devtools".to_owned())
131 .spawn(move || {
132 mem_profiler_chan.run_with_memory_reporting(
133 || {
134 if let Some(instance) = DevtoolsInstance::create(sender, receiver, embedder)
135 {
136 instance.run()
137 }
138 },
139 String::from("devtools-reporter"),
140 sender2,
141 |chan| {
142 DevtoolsControlMsg::FromChrome(
143 ChromeToDevtoolsControlMsg::CollectMemoryReport(chan),
144 )
145 },
146 )
147 })
148 .expect("Thread spawning failed");
149 }
150 sender
151}
152
153#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, MallocSizeOf)]
154pub(crate) struct StreamId(u32);
155
156#[derive(MallocSizeOf)]
157struct DevtoolsInstance {
158 #[conditional_malloc_size_of]
159 registry: Arc<ActorRegistry>,
160 #[conditional_malloc_size_of]
161 id_map: Arc<Mutex<IdMap>>,
162 browsing_contexts: FxHashMap<BrowsingContextId, String>,
163 sender: Sender<DevtoolsControlMsg>,
166 receiver: Receiver<DevtoolsControlMsg>,
167 pipelines: FxHashMap<PipelineId, BrowsingContextId>,
168 actor_workers: FxHashMap<WorkerId, String>,
169 actor_requests: HashMap<Uuid, String>,
170 #[conditional_malloc_size_of]
174 connections: Arc<Mutex<FxHashMap<StreamId, DevtoolsConnection>>>,
175 next_resource_id: u64,
176}
177
178impl DevtoolsInstance {
179 fn create(
180 sender: Sender<DevtoolsControlMsg>,
181 receiver: Receiver<DevtoolsControlMsg>,
182 embedder: EmbedderProxy,
183 ) -> Option<Self> {
184 let address = if pref!(devtools_server_listen_address).is_empty() {
185 SocketAddr::new(Ipv4Addr::new(127, 0, 0, 1).into(), 7000)
186 } else if let Ok(addr) = SocketAddr::from_str(&pref!(devtools_server_listen_address)) {
187 addr
188 } else if let Ok(port) = pref!(devtools_server_listen_address).parse() {
189 SocketAddr::new(Ipv4Addr::new(127, 0, 0, 1).into(), port)
190 } else {
191 SocketAddr::new(Ipv4Addr::new(127, 0, 0, 1).into(), 7000)
192 };
193 println!("Binding devtools to {address}");
194
195 let bound = TcpListener::bind(address).ok().and_then(|l| {
196 l.local_addr()
197 .map(|addr| addr.port())
198 .ok()
199 .map(|port| (l, port))
200 });
201
202 let port = if bound.is_some() {
204 Ok(address.port())
205 } else {
206 Err(())
207 };
208 let token = format!("{:X}", rng().next_u32());
209 embedder.send(EmbedderMsg::OnDevtoolsStarted(port, token.clone()));
210
211 let listener = match bound {
212 Some((l, _)) => l,
213 None => {
214 return None;
215 },
216 };
217
218 let mut registry = ActorRegistry::default();
220 RootActor::register(&mut registry);
221
222 let instance = Self {
223 registry: Arc::new(registry),
224 id_map: Arc::new(Mutex::new(IdMap::default())),
225 browsing_contexts: FxHashMap::default(),
226 pipelines: FxHashMap::default(),
227 sender: sender.clone(),
228 receiver,
229 actor_requests: HashMap::new(),
230 actor_workers: FxHashMap::default(),
231 connections: Default::default(),
232 next_resource_id: 1,
233 };
234
235 thread::Builder::new()
236 .name("DevtoolsCliAcceptor".to_owned())
237 .spawn(move || {
238 for stream in listener.incoming() {
240 let mut stream = stream.expect("Can't retrieve stream");
241 if !allow_devtools_client(&mut stream, &embedder, &token) {
242 continue;
243 };
244 sender
246 .send(DevtoolsControlMsg::FromChrome(
247 ChromeToDevtoolsControlMsg::AddClient(stream),
248 ))
249 .unwrap();
250 }
251 })
252 .expect("Thread spawning failed");
253
254 Some(instance)
255 }
256
257 fn run(mut self) {
258 let mut next_id = StreamId(0);
259 while let Ok(msg) = self.receiver.recv() {
260 trace!("{:?}", msg);
261 match msg {
262 DevtoolsControlMsg::FromChrome(ChromeToDevtoolsControlMsg::AddClient(stream)) => {
263 let id = next_id;
264 next_id = StreamId(id.0 + 1);
265
266 {
267 let connections = self.connections.lock().unwrap();
268 if connections.is_empty() {
269 for browsing_context_name in self.browsing_contexts.values() {
272 let browsing_context_actor = self
273 .registry
274 .find::<BrowsingContextActor>(browsing_context_name);
275 browsing_context_actor.instruct_script_to_send_live_updates(true);
276 }
277 }
278 }
279
280 let connection: DevtoolsConnection = stream.into();
281 let registry = self.registry.clone();
282 let connections = self.connections.clone();
283 let sender_clone = self.sender.clone();
284 thread::Builder::new()
285 .name("DevtoolsClientHandler".to_owned())
286 .spawn(move || {
287 handle_client(registry, connection, id, connections, sender_clone)
288 })
289 .expect("Thread spawning failed");
290 },
291 DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::FramerateTick(
292 actor_name,
293 tick,
294 )) => self.handle_framerate_tick(actor_name, tick),
295 DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::TitleChanged(
296 pipeline,
297 title,
298 )) => self.handle_title_changed(pipeline, title),
299 DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::NewGlobal(
300 ids,
301 script_sender,
302 pageinfo,
303 )) => self.handle_new_global(ids, script_sender, pageinfo),
304 DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::Navigate(
305 browsing_context,
306 state,
307 )) => self.handle_navigate(browsing_context, state),
308 DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::ConsoleAPI(
309 pipeline_id,
310 console_message,
311 worker_id,
312 )) => {
313 let console_message =
314 DevtoolsConsoleMessage::new(console_message, &self.registry);
315 self.handle_console_resource(
316 pipeline_id,
317 worker_id,
318 ConsoleResource::ConsoleMessage(console_message),
319 );
320 },
321 DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::ClearConsole(
322 pipeline_id,
323 worker_id,
324 )) => self.handle_clear_console(pipeline_id, worker_id),
325 DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::CreateSourceActor(
326 script_sender,
327 pipeline_id,
328 source_info,
329 )) => self.handle_create_source_actor(script_sender, pipeline_id, source_info),
330 DevtoolsControlMsg::FromScript(
331 ScriptToDevtoolsControlMsg::UpdateSourceContent(pipeline_id, source_content),
332 ) => self.handle_update_source_content(pipeline_id, source_content),
333 DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::ReportPageError(
334 pipeline_id,
335 page_error,
336 )) => self.handle_console_resource(
337 pipeline_id,
338 None,
339 ConsoleResource::PageError(page_error.into()),
340 ),
341 DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::ReportCSSError(
342 pipeline_id,
343 css_error,
344 )) => {
345 let console_message = ConsoleMessage {
346 fields: ConsoleMessageFields {
347 level: ConsoleLogLevel::Warn,
348 filename: css_error.filename,
349 line_number: css_error.line,
350 column_number: css_error.column,
351 time_stamp: get_time_stamp(),
352 },
353 arguments: vec![DebuggerValue::StringValue(css_error.msg)],
354 stacktrace: None,
355 };
356 let console_message =
357 DevtoolsConsoleMessage::new(console_message, &self.registry);
358
359 self.handle_console_resource(
360 pipeline_id,
361 None,
362 ConsoleResource::ConsoleMessage(console_message),
363 )
364 },
365 DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::DomMutation(
366 pipeline_id,
367 dom_mutation,
368 )) => {
369 self.handle_dom_mutation(pipeline_id, dom_mutation).unwrap();
370 },
371 DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::DebuggerPause(
372 pipeline_id,
373 frame_offset,
374 pause_reason,
375 )) => self.handle_debugger_pause(pipeline_id, frame_offset, pause_reason),
376 DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::CreateFrameActor(
377 result_sender,
378 pipeline_id,
379 frame_info,
380 )) => self.handle_create_frame_actor(result_sender, pipeline_id, frame_info),
381 DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::CreateObjectActor(
382 result_sender,
383 value,
384 )) => self.handle_create_object_actor(result_sender, value),
385 DevtoolsControlMsg::FromScript(
386 ScriptToDevtoolsControlMsg::CreateEnvironmentActor(
387 result_sender,
388 environment,
389 parent,
390 actor,
391 ),
392 ) => {
393 self.handle_create_environment_actor(result_sender, environment, parent, actor)
394 },
395 DevtoolsControlMsg::FromChrome(ChromeToDevtoolsControlMsg::NetworkEvent(
396 request_id,
397 network_event,
398 )) => {
399 let mut connections = Vec::<DevtoolsConnection>::new();
404 for connection in self.connections.lock().unwrap().values() {
405 connections.push(connection.clone());
406 }
407 self.handle_network_event(connections, request_id, network_event);
408 },
409 DevtoolsControlMsg::FromChrome(ChromeToDevtoolsControlMsg::ServerExitMsg) => break,
410 DevtoolsControlMsg::FromChrome(
411 ChromeToDevtoolsControlMsg::CollectMemoryReport(chan),
412 ) => {
413 perform_memory_report(|ops| {
414 let reports = vec![Report {
415 path: path!["devtools"],
416 kind: ReportKind::ExplicitSystemHeapSize,
417 size: self.size_of(ops),
418 }];
419 chan.send(ProcessReports::new(reports));
420 });
421 },
422 DevtoolsControlMsg::ClientExited => {
423 if self.connections.lock().unwrap().is_empty() {
424 for browsing_context_name in self.browsing_contexts.values() {
427 let browsing_context_actor = self
428 .registry
429 .find::<BrowsingContextActor>(browsing_context_name);
430 browsing_context_actor.instruct_script_to_send_live_updates(false);
431 }
432 }
433 },
434 }
435 }
436
437 let mut connections = self.connections.lock().unwrap();
439 for connection in connections.values_mut() {
440 let _ = connection.shutdown(Shutdown::Both);
441 }
442 connections.clear();
443 }
444
445 fn handle_framerate_tick(&self, actor_name: String, tick: f64) {
446 let framerate_actor = self.registry.find::<FramerateActor>(&actor_name);
447 framerate_actor.add_tick(tick);
448 }
449
450 fn handle_navigate(&mut self, browsing_context_id: BrowsingContextId, state: NavigationState) {
451 let Some(browsing_context_name) = self.browsing_contexts.get(&browsing_context_id) else {
452 return;
453 };
454 let browsing_context_actor = self
455 .registry
456 .find::<BrowsingContextActor>(browsing_context_name);
457 let watcher_actor = self
458 .registry
459 .find::<WatcherActor>(&browsing_context_actor.watcher_name);
460 let mut id_map = self.id_map.lock().unwrap();
461 let mut connections = self.connections.lock().unwrap();
462
463 match &state {
464 NavigationState::Start(url) => {
465 watcher_actor.emit_will_navigate(
466 browsing_context_id,
467 url.clone(),
468 &mut connections.values_mut(),
469 &mut id_map,
470 );
471 },
472 NavigationState::Stop(pipeline_id, page_info) => {
473 watcher_actor.emit_target_available_or_destroyed(
474 &browsing_context_actor,
475 &self.registry,
476 connections.values_mut(),
477 false,
478 );
479
480 let outer_window_id = id_map.outer_window_id(*pipeline_id);
481 browsing_context_actor.update_pipeline(
482 *pipeline_id,
483 outer_window_id,
484 page_info.clone(),
485 );
486
487 watcher_actor.emit_target_available_or_destroyed(
488 &browsing_context_actor,
489 &self.registry,
490 connections.values_mut(),
491 true,
492 );
493
494 },
496 }
497 }
498
499 fn handle_new_global(
503 &mut self,
504 ids: (BrowsingContextId, PipelineId, Option<WorkerId>, WebViewId),
505 script_sender: GenericSender<DevtoolScriptControlMsg>,
506 page_info: DevtoolsPageInfo,
507 ) {
508 let (browsing_context_id, pipeline_id, worker_id, webview_id) = ids;
509 let id_map = &mut self.id_map.lock().unwrap();
510 let devtools_browser_id = id_map.browser_id(webview_id);
511 let devtools_browsing_context_id = id_map.browsing_context_id(browsing_context_id);
512 let devtools_outer_window_id = id_map.outer_window_id(pipeline_id);
513
514 let console_name = new_actor_name::<ConsoleActor>();
515
516 let parent_actor = if let Some(id) = worker_id {
517 let thread_actor = ThreadActor::register(&self.registry, script_sender.clone(), None);
518
519 let worker_type = if page_info.is_service_worker {
520 WorkerType::Service
521 } else {
522 WorkerType::Dedicated
523 };
524 let worker_actor = WorkerTargetActor::register(
525 &self.registry,
526 console_name.clone(),
527 thread_actor.name().into(),
528 id,
529 page_info.url,
530 worker_type,
531 script_sender,
532 );
533 let root_actor = self.registry.find::<RootActor>("root");
534 if page_info.is_service_worker {
535 root_actor
536 .service_workers
537 .borrow_mut()
538 .push(worker_actor.name().into());
539 } else {
540 root_actor
541 .workers
542 .borrow_mut()
543 .push(worker_actor.name().into());
544 }
545
546 self.actor_workers.insert(id, worker_actor.name().into());
547
548 Root::DedicatedWorker(worker_actor.name().into())
549 } else {
550 self.pipelines.insert(pipeline_id, browsing_context_id);
551 let browsing_context_name = self
552 .browsing_contexts
553 .entry(browsing_context_id)
554 .or_insert_with(|| {
555 BrowsingContextActor::register(
556 &self.registry,
557 console_name.clone(),
558 devtools_browser_id,
559 devtools_browsing_context_id,
560 page_info,
561 pipeline_id,
562 devtools_outer_window_id,
563 script_sender.clone(),
564 )
565 .name()
566 .into()
567 });
568 let browsing_context_actor = self
569 .registry
570 .find::<BrowsingContextActor>(browsing_context_name);
571 browsing_context_actor.handle_new_global(pipeline_id, script_sender);
572 Root::BrowsingContext(browsing_context_name.clone())
573 };
574
575 ConsoleActor::register(&self.registry, console_name, parent_actor);
576 }
577
578 fn handle_title_changed(&self, pipeline_id: PipelineId, title: String) {
579 let browsing_context_id = match self.pipelines.get(&pipeline_id) {
580 Some(bc) => bc,
581 None => return,
582 };
583 let browsing_context_name = match self.browsing_contexts.get(browsing_context_id) {
584 Some(name) => name,
585 None => return,
586 };
587 let browsing_context_actor = self
588 .registry
589 .find::<BrowsingContextActor>(browsing_context_name);
590 browsing_context_actor.title_changed(pipeline_id, title);
591 }
592
593 fn handle_console_resource(
594 &mut self,
595 pipeline_id: PipelineId,
596 worker_id: Option<WorkerId>,
597 resource: ConsoleResource,
598 ) {
599 let console_actor_name = match self.find_console_actor(pipeline_id, worker_id) {
600 Some(name) => name,
601 None => return,
602 };
603 let console_actor = self.registry.find::<ConsoleActor>(&console_actor_name);
604 let id = worker_id.map_or(UniqueId::Pipeline(pipeline_id), UniqueId::Worker);
605
606 for connection in self.connections.lock().unwrap().values_mut() {
607 console_actor.handle_console_resource(
608 resource.clone(),
609 id.clone(),
610 &self.registry,
611 connection,
612 );
613 }
614 }
615
616 fn handle_dom_mutation(
617 &mut self,
618 pipeline_id: PipelineId,
619 dom_mutation: DomMutation,
620 ) -> Result<(), ActorError> {
621 let Some(browsing_context_id) = self.pipelines.get(&pipeline_id) else {
622 log::warn!("Devtools received notification for unknown pipeline {pipeline_id}");
623 return Err(ActorError::Internal);
624 };
625 let Some(browsing_context_name) = self.browsing_contexts.get(browsing_context_id) else {
626 return Err(ActorError::Internal);
627 };
628 let browsing_context_actor = self
629 .registry
630 .find::<BrowsingContextActor>(browsing_context_name);
631 let inspector_actor = self
632 .registry
633 .find::<InspectorActor>(&browsing_context_actor.inspector_name);
634 let walker_actor = self
635 .registry
636 .find::<WalkerActor>(&inspector_actor.walker_name);
637
638 for connection in self.connections.lock().unwrap().values_mut() {
639 walker_actor.handle_dom_mutation(dom_mutation.clone(), connection)?;
640 }
641
642 Ok(())
643 }
644
645 fn handle_clear_console(&mut self, pipeline_id: PipelineId, worker_id: Option<WorkerId>) {
646 let console_actor_name = match self.find_console_actor(pipeline_id, worker_id) {
647 Some(name) => name,
648 None => return,
649 };
650 let console_actor = self.registry.find::<ConsoleActor>(&console_actor_name);
651 let id = worker_id.map_or(UniqueId::Pipeline(pipeline_id), UniqueId::Worker);
652
653 for stream in self.connections.lock().unwrap().values_mut() {
654 console_actor.send_clear_message(id.clone(), &self.registry, stream);
655 }
656 }
657
658 fn find_console_actor(
659 &self,
660 pipeline_id: PipelineId,
661 worker_id: Option<WorkerId>,
662 ) -> Option<String> {
663 if let Some(worker_id) = worker_id {
664 let worker_name = self.actor_workers.get(&worker_id)?;
665 Some(
666 self.registry
667 .find::<WorkerTargetActor>(worker_name)
668 .console_name
669 .clone(),
670 )
671 } else {
672 let browsing_context_id = self.pipelines.get(&pipeline_id)?;
673 let browsing_context_name = self.browsing_contexts.get(browsing_context_id)?;
674 Some(
675 self.registry
676 .find::<BrowsingContextActor>(browsing_context_name)
677 .console_name
678 .clone(),
679 )
680 }
681 }
682
683 fn handle_network_event(
684 &mut self,
685 connections: Vec<DevtoolsConnection>,
686 request_id: Uuid,
687 network_event: NetworkEvent,
688 ) {
689 let browsing_context_id = match &network_event {
690 NetworkEvent::HttpRequest(req) => req.browsing_context_id,
691 NetworkEvent::HttpRequestUpdate(req) => req.browsing_context_id,
692 NetworkEvent::HttpResponse(resp) => resp.browsing_context_id,
693 NetworkEvent::SecurityInfo(update) => update.browsing_context_id,
694 };
695
696 let Some(browsing_context_name) = self.browsing_contexts.get(&browsing_context_id) else {
697 return;
698 };
699
700 let network_event_name = match self.actor_requests.get(&request_id) {
701 Some(name) => name.clone(),
702 None => self.create_network_event_actor(request_id, browsing_context_name.clone()),
703 };
704
705 handle_network_event(
706 Arc::clone(&self.registry),
707 network_event_name,
708 connections,
709 network_event,
710 )
711 }
712
713 fn create_network_event_actor(
715 &mut self,
716 request_id: Uuid,
717 browsing_context_name: String,
718 ) -> String {
719 let resource_id = self.next_resource_id;
720 self.next_resource_id += 1;
721
722 let network_event_actor =
723 NetworkEventActor::register(&self.registry, resource_id, browsing_context_name);
724
725 self.actor_requests
726 .insert(request_id, network_event_actor.name().into());
727
728 network_event_actor.name().into()
729 }
730
731 fn handle_create_source_actor(
732 &mut self,
733 script_sender: GenericSender<DevtoolScriptControlMsg>,
734 pipeline_id: PipelineId,
735 source_info: SourceInfo,
736 ) {
737 let source_content = source_info
738 .content
739 .or_else(|| self.registry.inline_source_content(pipeline_id));
740 let source_actor = SourceActor::register(
741 &self.registry,
742 pipeline_id,
743 source_info.url,
744 source_content,
745 source_info.content_type,
746 source_info.spidermonkey_id,
747 source_info.introduction_type,
748 script_sender,
749 );
750 let source_form = self
751 .registry
752 .find::<SourceActor>(source_actor.name())
753 .source_form();
754
755 if let Some(worker_id) = source_info.worker_id {
756 let Some(worker_name) = self.actor_workers.get(&worker_id) else {
757 return;
758 };
759
760 let thread_actor_name = self
761 .registry
762 .find::<WorkerTargetActor>(worker_name)
763 .thread_name
764 .clone();
765 let thread_actor = self.registry.find::<ThreadActor>(&thread_actor_name);
766
767 thread_actor.source_manager.add_source(source_actor.name());
768
769 let worker_actor = self.registry.find::<WorkerTargetActor>(worker_name);
770
771 for stream in self.connections.lock().unwrap().values_mut() {
772 worker_actor.resource_array(
773 &source_form,
774 "source".into(),
775 ResourceArrayType::Available,
776 stream,
777 );
778 }
779 } else {
780 let Some(browsing_context_id) = self.pipelines.get(&pipeline_id) else {
781 return;
782 };
783 let Some(browsing_context_name) = self.browsing_contexts.get(browsing_context_id)
784 else {
785 return;
786 };
787
788 let browsing_context_actor = self
790 .registry
791 .find::<BrowsingContextActor>(browsing_context_name);
792
793 let thread_actor = self
794 .registry
795 .find::<ThreadActor>(&browsing_context_actor.thread_name);
796 thread_actor.source_manager.add_source(source_actor.name());
797
798 for stream in self.connections.lock().unwrap().values_mut() {
799 browsing_context_actor.resource_array(
800 &source_form,
801 "source".into(),
802 ResourceArrayType::Available,
803 stream,
804 );
805 }
806 }
807 }
808
809 fn handle_update_source_content(&mut self, pipeline_id: PipelineId, source_content: String) {
810 for source_name in self.registry.source_actor_names_for_pipeline(pipeline_id) {
811 let source_actor = self.registry.find::<SourceActor>(&source_name);
812 let mut content = source_actor.content.borrow_mut();
813 if content.is_none() {
814 *content = Some(source_content.clone());
815 }
816 }
817
818 self.registry
821 .set_inline_source_content(pipeline_id, source_content);
822 }
823
824 fn handle_debugger_pause(
825 &mut self,
826 pipeline_id: PipelineId,
827 frame_offset: FrameOffset,
828 pause_reason: PauseReason,
829 ) {
830 let Some(browsing_context_name) = self
831 .pipelines
832 .get(&pipeline_id)
833 .and_then(|id| self.browsing_contexts.get(id))
834 else {
835 return;
836 };
837
838 let browsing_context_actor = self
839 .registry
840 .find::<BrowsingContextActor>(browsing_context_name);
841 let thread_actor = self
842 .registry
843 .find::<ThreadActor>(&browsing_context_actor.thread_name);
844
845 let pause_name = PauseActor::register(&self.registry).name().into();
846
847 let frame_actor = self.registry.find::<FrameActor>(&frame_offset.actor);
848 frame_actor.set_offset(frame_offset.column, frame_offset.line);
849
850 let msg = ThreadInterruptedReply {
851 from: thread_actor.name().into(),
852 type_: "paused".to_owned(),
853 actor: pause_name,
854 frame: frame_actor.encode(&self.registry),
855 why: pause_reason,
856 };
857
858 for stream in self.connections.lock().unwrap().values_mut() {
859 let _ = stream.write_json_packet(&msg);
860 }
861 }
862
863 fn handle_create_frame_actor(
864 &mut self,
865 result_sender: GenericSender<String>,
866 pipeline_id: PipelineId,
867 frame: FrameInfo,
868 ) {
869 let Some(browsing_context_id) = self.pipelines.get(&pipeline_id) else {
870 return;
871 };
872 let Some(browsing_context_name) = self.browsing_contexts.get(browsing_context_id) else {
873 return;
874 };
875 let browsing_context_actor = self
876 .registry
877 .find::<BrowsingContextActor>(browsing_context_name);
878 let thread_actor = self
879 .registry
880 .find::<ThreadActor>(&browsing_context_actor.thread_name);
881
882 let source_name = match thread_actor
883 .source_manager
884 .find_source(&self.registry, &frame.url)
885 {
886 Some(source_actor) => source_actor.name().into(),
887 None => {
888 warn!("No source actor found for URL: {}", frame.url);
889 return;
890 },
891 };
892
893 let frame_actor = FrameActor::register(&self.registry, source_name, frame);
894
895 let _ = result_sender.send(frame_actor.name().into());
896 }
897
898 fn handle_create_object_actor(
899 &mut self,
900 result_sender: GenericSender<String>,
901 value: DebuggerValue,
902 ) {
903 let DebuggerValue::ObjectValue {
904 actor,
905 class,
906 own_property_length,
907 preview,
908 prototype,
909 } = value
910 else {
911 return;
912 };
913
914 let object_actor = ObjectActor::register(
915 &self.registry,
916 actor,
917 class,
918 own_property_length,
919 preview.map(|preview| *preview),
920 prototype.map(|prototype| *prototype),
921 );
922 let _ = result_sender.send(object_actor);
923 }
924
925 fn handle_create_environment_actor(
926 &mut self,
927 result_sender: GenericSender<String>,
928 environment_info: EnvironmentInfo,
929 parent: Option<String>,
930 actor: Option<String>,
931 ) {
932 let environment_name =
933 EnvironmentActor::register_or_update(&self.registry, environment_info, parent, actor);
934 let _ = result_sender.send(environment_name);
935 }
936}
937
938fn allow_devtools_client(stream: &mut TcpStream, embedder: &EmbedderProxy, token: &str) -> bool {
939 let token = format!("25:{{\"auth_token\":\"{}\"}}", token);
941 let mut buf = [0; 28];
942 let timeout = std::time::Duration::from_millis(500);
943 stream.set_read_timeout(Some(timeout)).unwrap();
945 let peek = stream.peek(&mut buf);
946 stream.set_read_timeout(None).unwrap();
947 if let Ok(len) = peek &&
948 len == buf.len() &&
949 let Ok(s) = std::str::from_utf8(&buf) &&
950 s == token
951 {
952 let _ = stream.read_exact(&mut buf);
954 return true;
955 };
956
957 let (request_sender, request_receiver) =
959 generic_channel::channel().expect("Failed to create IPC channel!");
960 embedder.send(EmbedderMsg::RequestDevtoolsConnection(request_sender));
961 request_receiver.recv().unwrap() == AllowOrDeny::Allow
962}
963
964fn handle_client(
966 registry: Arc<ActorRegistry>,
967 mut stream: DevtoolsConnection,
968 stream_id: StreamId,
969 connections: Arc<Mutex<FxHashMap<StreamId, DevtoolsConnection>>>,
970 sender: Sender<DevtoolsControlMsg>,
971) {
972 connections
973 .lock()
974 .unwrap()
975 .insert(stream_id, stream.clone());
976
977 log::info!("Connection established to {}", stream.peer_addr().unwrap());
978 let msg = registry.encode::<RootActor, _>("root");
979 if let Err(error) = stream.write_json_packet(&msg) {
980 warn!("Failed to send initial packet from root actor: {error:?}");
981 return;
982 }
983
984 loop {
985 match stream.read_json_packet() {
986 Ok(Some(json_packet)) => {
987 if let Err(()) = registry.handle_message(
988 json_packet.as_object().unwrap(),
989 &mut stream,
990 stream_id,
991 ) {
992 log::error!("Devtools actor stopped responding");
993 let _ = stream.shutdown(Shutdown::Both);
994 break;
995 }
996 },
997 Ok(None) => {
998 log::info!("Devtools connection closed");
999 break;
1000 },
1001 Err(err_msg) => {
1002 log::error!("Failed to read message from devtools client: {}", err_msg);
1003 break;
1004 },
1005 }
1006 }
1007
1008 connections.lock().unwrap().remove(&stream_id);
1009 let _ = sender.send(DevtoolsControlMsg::ClientExited);
1010
1011 registry.cleanup(stream_id);
1012}
1013
1014pub(crate) fn debugger_value_to_json(registry: &ActorRegistry, value: DebuggerValue) -> Value {
1016 let mut v = Map::new();
1017 match value {
1018 DebuggerValue::VoidValue => {
1019 v.insert("type".to_owned(), Value::String("undefined".to_owned()));
1020 Value::Object(v)
1021 },
1022 DebuggerValue::NullValue(uninitialized) => {
1023 v.insert("type".to_owned(), Value::String("null".to_owned()));
1024 if uninitialized {
1025 v.insert("uninitialized".to_owned(), Value::Bool(true));
1026 }
1027 Value::Object(v)
1028 },
1029 DebuggerValue::BooleanValue(boolean) => Value::Bool(boolean),
1030 DebuggerValue::NumberValue(val) => {
1031 if val.is_nan() {
1032 v.insert("type".to_owned(), Value::String("NaN".to_owned()));
1033 Value::Object(v)
1034 } else if val.is_infinite() {
1035 if val < 0. {
1036 v.insert("type".to_owned(), Value::String("-Infinity".to_owned()));
1037 } else {
1038 v.insert("type".to_owned(), Value::String("Infinity".to_owned()));
1039 }
1040 Value::Object(v)
1041 } else if val == 0. && val.is_sign_negative() {
1042 v.insert("type".to_owned(), Value::String("-0".to_owned()));
1043 Value::Object(v)
1044 } else {
1045 Value::Number(Number::from_f64(val).unwrap())
1046 }
1047 },
1048 DebuggerValue::StringValue(str) => Value::String(str),
1049 DebuggerValue::ObjectValue {
1050 actor,
1051 class,
1052 own_property_length,
1053 preview,
1054 prototype,
1055 } => {
1056 let object_name = ObjectActor::register(
1057 registry,
1058 actor,
1059 class,
1060 own_property_length,
1061 preview.map(|preview| *preview),
1062 prototype.map(|prototype| *prototype),
1063 );
1064 let object_msg = registry.encode::<ObjectActor, _>(&object_name);
1065 let value = serde_json::to_value(object_msg).unwrap_or_default();
1066 Value::Object(value.as_object().cloned().unwrap_or_default())
1067 },
1068 }
1069}