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