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