1use std::cell::RefCell;
6
7use devtools_traits::{
8 BlackboxCoverage, DebuggerValue, DevtoolScriptControlMsg, EvaluateJSReply,
9 ScriptToDevtoolsControlMsg, SourceInfo, WorkerId,
10};
11use dom_struct::dom_struct;
12use embedder_traits::ScriptToEmbedderChan;
13use embedder_traits::resources::{self, Resource};
14use js::context::JSContext;
15use js::rust::wrappers2::JS_DefineDebuggerObject;
16use net_traits::ResourceThreads;
17use profile_traits::{mem, time};
18use script_bindings::interfaces::HasOrigin;
19use script_bindings::reflector::DomObject;
20use servo_base::generic_channel::{GenericCallback, GenericSender, channel};
21use servo_base::id::{Index, PipelineId, PipelineNamespaceId};
22use servo_constellation_traits::ScriptToConstellationSender;
23use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl};
24use storage_traits::StorageThreads;
25
26use crate::dom::bindings::codegen::Bindings::DebuggerGetEnvironmentEventBinding::EnvironmentInfo;
27use crate::dom::bindings::codegen::Bindings::DebuggerGlobalScopeBinding;
28use crate::dom::bindings::codegen::Bindings::DebuggerInterruptEventBinding::{
29 FrameInfo, FrameOffset, PauseReason,
30};
31use crate::dom::bindings::codegen::GenericBindings::DebuggerEvalEventBinding::EvalResult;
32use crate::dom::bindings::codegen::GenericBindings::DebuggerGetPossibleBreakpointsEventBinding::RecommendedBreakpointLocation;
33use crate::dom::bindings::codegen::GenericBindings::DebuggerGlobalScopeBinding::{
34 DebuggerGlobalScopeMethods, NotifyNewSource, PipelineIdInit,
35};
36use crate::dom::bindings::inheritance::Castable;
37use crate::dom::bindings::root::DomRoot;
38use crate::dom::bindings::str::DOMString;
39use crate::dom::bindings::utils::define_all_exposed_interfaces;
40use crate::dom::debugger::debuggerblackboxevent::DebuggerBlackboxEvent;
41use crate::dom::debugger::debuggerclearbreakpointevent::DebuggerClearBreakpointEvent;
42use crate::dom::debugger::debuggerframeevent::DebuggerFrameEvent;
43use crate::dom::debugger::debuggergetenvironmentevent::DebuggerGetEnvironmentEvent;
44use crate::dom::debugger::debuggerinterruptevent::DebuggerInterruptEvent;
45use crate::dom::debugger::debuggerresumeevent::DebuggerResumeEvent;
46use crate::dom::debugger::debuggersetbreakpointevent::DebuggerSetBreakpointEvent;
47use crate::dom::debugger::debuggerunblackboxevent::DebuggerUnblackboxEvent;
48use crate::dom::globalscope::GlobalScope;
49use crate::dom::types::{
50 DebuggerAddDebuggeeEvent, DebuggerEvalEvent, DebuggerGetPossibleBreakpointsEvent, Event,
51};
52#[cfg(feature = "webgpu")]
53use crate::dom::webgpu::identityhub::IdentityHub;
54use crate::realms::enter_auto_realm;
55use crate::script_runtime::{CanGc, IntroductionType};
56use crate::script_thread::with_script_thread;
57
58#[dom_struct]
59pub(crate) struct DebuggerGlobalScope {
63 global_scope: GlobalScope,
64 #[no_trace]
65 devtools_to_script_sender: GenericSender<DevtoolScriptControlMsg>,
66 #[no_trace]
67 get_possible_breakpoints_result_sender:
68 RefCell<Option<GenericSender<Vec<devtools_traits::RecommendedBreakpointLocation>>>>,
69 #[no_trace]
70 eval_result_sender: RefCell<Option<GenericSender<EvaluateJSReply>>>,
71 #[no_trace]
72 get_list_frame_result_sender: RefCell<Option<GenericSender<Vec<String>>>>,
73 #[no_trace]
74 get_environment_result_sender: RefCell<Option<GenericSender<String>>>,
75 #[no_trace]
76 pipeline_id: PipelineId,
77 #[no_trace]
78 origin: MutableOrigin,
79}
80
81impl DebuggerGlobalScope {
82 #[expect(unsafe_code, clippy::too_many_arguments)]
90 pub(crate) fn new(
91 debugger_pipeline_id: PipelineId,
92 script_to_devtools_sender: Option<GenericCallback<ScriptToDevtoolsControlMsg>>,
93 devtools_to_script_sender: GenericSender<DevtoolScriptControlMsg>,
94 mem_profiler_chan: mem::ProfilerChan,
95 time_profiler_chan: time::ProfilerChan,
96 script_to_constellation_sender: ScriptToConstellationSender,
97 script_to_embedder_chan: ScriptToEmbedderChan,
98 resource_threads: ResourceThreads,
99 storage_threads: StorageThreads,
100 #[cfg(feature = "webgpu")] gpu_id_hub: std::sync::Arc<IdentityHub>,
101 cx: &mut JSContext,
102 ) -> DomRoot<Self> {
103 let global = Box::new(Self {
104 global_scope: GlobalScope::new_inherited(
105 script_to_devtools_sender,
106 mem_profiler_chan,
107 time_profiler_chan,
108 script_to_constellation_sender,
109 script_to_embedder_chan,
110 resource_threads,
111 storage_threads,
112 ServoUrl::parse_with_base(None, "about:internal/debugger")
113 .expect("Guaranteed by argument"),
114 None,
115 #[cfg(feature = "webgpu")]
116 gpu_id_hub,
117 None,
118 false,
119 None, ),
121 devtools_to_script_sender,
122 get_possible_breakpoints_result_sender: RefCell::new(None),
123 get_list_frame_result_sender: RefCell::new(None),
124 get_environment_result_sender: RefCell::new(None),
125 eval_result_sender: RefCell::new(None),
126 pipeline_id: debugger_pipeline_id,
127 origin: MutableOrigin::new(ImmutableOrigin::new_opaque()),
128 });
129 let global =
130 DebuggerGlobalScopeBinding::Wrap::<crate::DomTypeHolder>(cx, &global.origin(), global);
131
132 let mut realm = enter_auto_realm(cx, &*global);
133 let mut realm = realm.current_realm();
134 define_all_exposed_interfaces(&mut realm, global.upcast());
135 assert!(unsafe {
136 JS_DefineDebuggerObject(&mut realm, global.global_scope.reflector().get_jsobject())
138 });
139
140 global
141 }
142
143 pub(crate) fn origin(&self) -> MutableOrigin {
144 self.origin.clone()
145 }
146
147 pub(crate) fn as_global_scope(&self) -> &GlobalScope {
148 self.upcast::<GlobalScope>()
149 }
150
151 pub(crate) fn execute(&self, cx: &mut JSContext) {
152 let mut realm = enter_auto_realm(cx, self);
153 let cx = &mut realm.current_realm();
154
155 let _ = self.global_scope.evaluate_js_on_global(
156 cx,
157 resources::read_string(Resource::DebuggerJS).into(),
158 "",
159 None,
160 None,
161 );
162 }
163
164 pub(crate) fn fire_add_debuggee(
165 &self,
166 cx: &mut JSContext,
167 debuggee_global: &GlobalScope,
168 debuggee_pipeline_id: PipelineId,
169 debuggee_worker_id: Option<WorkerId>,
170 ) {
171 let mut realm = enter_auto_realm(cx, self);
172 let cx = &mut realm;
173 let debuggee_pipeline_id = crate::dom::pipelineid::PipelineId::new(
174 self.upcast(),
175 debuggee_pipeline_id,
176 CanGc::from_cx(cx),
177 );
178 let event = DomRoot::upcast::<Event>(DebuggerAddDebuggeeEvent::new(
179 cx,
180 self.upcast(),
181 debuggee_global,
182 &debuggee_pipeline_id,
183 debuggee_worker_id.map(|id| id.to_string().into()),
184 ));
185 assert!(
186 event.fire(cx, self.upcast()),
187 "Guaranteed by DebuggerAddDebuggeeEvent::new"
188 );
189 }
190
191 pub(crate) fn fire_eval(
192 &self,
193 cx: &mut JSContext,
194 code: DOMString,
195 debuggee_pipeline_id: PipelineId,
196 debuggee_worker_id: Option<WorkerId>,
197 frame_actor_id: Option<String>,
198 result_sender: GenericSender<EvaluateJSReply>,
199 ) {
200 assert!(
201 self.eval_result_sender
202 .replace(Some(result_sender))
203 .is_none()
204 );
205 let mut realm = enter_auto_realm(cx, self);
206 let cx = &mut realm;
207 let debuggee_pipeline_id = crate::dom::pipelineid::PipelineId::new(
208 self.upcast(),
209 debuggee_pipeline_id,
210 CanGc::from_cx(cx),
211 );
212 let event = DomRoot::upcast::<Event>(DebuggerEvalEvent::new(
213 self.upcast(),
214 code,
215 &debuggee_pipeline_id,
216 debuggee_worker_id.map(|id| id.to_string().into()),
217 frame_actor_id.map(|id| id.into()),
218 CanGc::from_cx(cx),
219 ));
220 assert!(
221 event.fire(cx, self.upcast()),
222 "Guaranteed by DebuggerEvalEvent::new"
223 );
224 }
225
226 pub(crate) fn fire_get_possible_breakpoints(
227 &self,
228 cx: &mut JSContext,
229 spidermonkey_id: u32,
230 result_sender: GenericSender<Vec<devtools_traits::RecommendedBreakpointLocation>>,
231 ) {
232 assert!(
233 self.get_possible_breakpoints_result_sender
234 .replace(Some(result_sender))
235 .is_none()
236 );
237 let mut realm = enter_auto_realm(cx, self);
238 let cx = &mut realm.current_realm();
239 let event = DomRoot::upcast::<Event>(DebuggerGetPossibleBreakpointsEvent::new(
240 self.upcast(),
241 spidermonkey_id,
242 CanGc::from_cx(cx),
243 ));
244 assert!(
245 event.fire(cx, self.upcast()),
246 "Guaranteed by DebuggerGetPossibleBreakpointsEvent::new"
247 );
248 }
249
250 pub(crate) fn fire_set_breakpoint(
251 &self,
252 cx: &mut JSContext,
253 spidermonkey_id: u32,
254 script_id: u32,
255 offset: u32,
256 ) {
257 let event = DomRoot::upcast::<Event>(DebuggerSetBreakpointEvent::new(
258 self.upcast(),
259 spidermonkey_id,
260 script_id,
261 offset,
262 CanGc::from_cx(cx),
263 ));
264 assert!(
265 event.fire(cx, self.upcast()),
266 "Guaranteed by DebuggerSetBreakpointEvent::new"
267 );
268 }
269
270 pub(crate) fn fire_interrupt(&self, cx: &mut js::context::JSContext) {
271 let event = DomRoot::upcast::<Event>(DebuggerInterruptEvent::new(
272 self.upcast(),
273 CanGc::from_cx(cx),
274 ));
275 assert!(
276 event.fire(cx, self.upcast()),
277 "Guaranteed by DebuggerInterruptEvent::new"
278 );
279 }
280
281 pub(crate) fn fire_list_frames(
282 &self,
283 cx: &mut js::context::JSContext,
284 pipeline_id: PipelineId,
285 start: u32,
286 count: u32,
287 result_sender: GenericSender<Vec<String>>,
288 ) {
289 assert!(
290 self.get_list_frame_result_sender
291 .replace(Some(result_sender))
292 .is_none()
293 );
294 let mut realm = enter_auto_realm(cx, self);
295 let cx = &mut realm.current_realm();
296 let pipeline_id =
297 crate::dom::pipelineid::PipelineId::new(self.upcast(), pipeline_id, CanGc::from_cx(cx));
298 let event = DomRoot::upcast::<Event>(DebuggerFrameEvent::new(
299 self.upcast(),
300 &pipeline_id,
301 start,
302 count,
303 CanGc::from_cx(cx),
304 ));
305 assert!(
306 event.fire(cx, self.upcast()),
307 "Guaranteed by DebuggerFrameEvent::new"
308 );
309 }
310
311 pub(crate) fn fire_get_environment(
312 &self,
313 cx: &mut JSContext,
314 frame_actor_id: String,
315 result_sender: GenericSender<String>,
316 ) {
317 assert!(
318 self.get_environment_result_sender
319 .replace(Some(result_sender))
320 .is_none()
321 );
322 let mut realm = enter_auto_realm(cx, self);
323 let cx = &mut realm.current_realm();
324 let event = DomRoot::upcast::<Event>(DebuggerGetEnvironmentEvent::new(
325 self.upcast(),
326 frame_actor_id.into(),
327 CanGc::from_cx(cx),
328 ));
329 assert!(
330 event.fire(cx, self.upcast()),
331 "Guaranteed by DebuggerGetEnvironmentEvent::new"
332 );
333 }
334
335 pub(crate) fn fire_resume(
336 &self,
337 cx: &mut JSContext,
338 resume_limit_type: Option<String>,
339 frame_actor_id: Option<String>,
340 ) {
341 let event = DomRoot::upcast::<Event>(DebuggerResumeEvent::new(
342 self.upcast(),
343 resume_limit_type.map(DOMString::from),
344 frame_actor_id.map(DOMString::from),
345 CanGc::from_cx(cx),
346 ));
347 assert!(
348 event.fire(cx, self.upcast()),
349 "Guaranteed by DebuggerResumeEvent::new"
350 );
351 }
352
353 pub(crate) fn fire_clear_breakpoint(
354 &self,
355 cx: &mut JSContext,
356 spidermonkey_id: u32,
357 script_id: u32,
358 offset: u32,
359 ) {
360 let event = DomRoot::upcast::<Event>(DebuggerClearBreakpointEvent::new(
361 self.upcast(),
362 spidermonkey_id,
363 script_id,
364 offset,
365 CanGc::from_cx(cx),
366 ));
367 assert!(
368 event.fire(cx, self.upcast()),
369 "Guaranteed by DebuggerClearBreakpointEvent::new"
370 );
371 }
372
373 pub(crate) fn fire_blackbox(
374 &self,
375 cx: &mut JSContext,
376 spidermonkey_id: u32,
377 coverage: BlackboxCoverage,
378 ) {
379 let event = DomRoot::upcast::<Event>(DebuggerBlackboxEvent::new(
380 self.upcast(),
381 spidermonkey_id,
382 coverage,
383 CanGc::from_cx(cx),
384 ));
385 assert!(
386 event.fire(cx, self.upcast()),
387 "Guaranteed by DebuggerBlackboxEvent::new"
388 );
389 }
390
391 pub(crate) fn fire_unblackbox(
392 &self,
393 cx: &mut JSContext,
394 spidermonkey_id: u32,
395 coverage: BlackboxCoverage,
396 ) {
397 let event = DomRoot::upcast::<Event>(DebuggerUnblackboxEvent::new(
398 self.upcast(),
399 spidermonkey_id,
400 coverage,
401 CanGc::from_cx(cx),
402 ));
403 assert!(
404 event.fire(cx, self.upcast()),
405 "Guaranteed by DebuggerUnblackboxEvent::new"
406 );
407 }
408
409 pub(crate) fn pipeline_id(&self) -> PipelineId {
410 self.pipeline_id
411 }
412}
413
414impl DebuggerGlobalScopeMethods<crate::DomTypeHolder> for DebuggerGlobalScope {
415 fn NotifyNewSource(&self, args: &NotifyNewSource) {
417 let Some(devtools_chan) = self.as_global_scope().devtools_chan() else {
418 return;
419 };
420 let pipeline_id = PipelineId {
421 namespace_id: PipelineNamespaceId(args.pipelineId.namespaceId),
422 index: Index::new(args.pipelineId.index).expect("`pipelineId.index` must not be zero"),
423 };
424
425 if let Some(introduction_type) = args.introductionType.as_ref() {
426 let url_original = args.url.str();
439 let url_original = ServoUrl::parse(&url_original).ok();
441
442 let url_override = args
447 .urlOverride
448 .as_ref()
449 .map(|url| url.str())
450 .and_then(|url| ServoUrl::parse_with_base(url_original.as_ref(), &url).ok());
452
453 if [
457 IntroductionType::INJECTED_SCRIPT_STR,
458 IntroductionType::EVAL_STR,
459 IntroductionType::DEBUGGER_EVAL_STR,
460 IntroductionType::FUNCTION_STR,
461 IntroductionType::JAVASCRIPT_URL_STR,
462 IntroductionType::EVENT_HANDLER_STR,
463 IntroductionType::DOM_TIMER_STR,
464 ]
465 .contains(&&*introduction_type.str()) &&
466 url_override.is_none()
467 {
468 debug!(
469 "Not creating debuggee: `introductionType` is `{introduction_type}` but no valid url"
470 );
471 return;
472 }
473
474 let inline = introduction_type.str() == "inlineScript" && url_override.is_none();
482 let Some(url) = url_override.or(url_original) else {
483 debug!("Not creating debuggee: no valid url");
484 return;
485 };
486
487 let worker_id = args.workerId.as_ref().map(|id| id.parse().unwrap());
488
489 let source_info = SourceInfo {
490 url,
491 introduction_type: introduction_type.str().to_owned(),
492 inline,
493 worker_id,
494 content: (!inline).then(|| args.text.to_string()),
495 content_type: None, spidermonkey_id: args.spidermonkeyId,
497 };
498 if let Err(error) = devtools_chan.send(ScriptToDevtoolsControlMsg::CreateSourceActor(
499 self.devtools_to_script_sender.clone(),
500 pipeline_id,
501 source_info,
502 )) {
503 warn!("Failed to send to devtools server: {error:?}");
504 }
505 } else {
506 debug!("Not creating debuggee for script with no `introductionType`");
507 }
508 }
509
510 fn GetPossibleBreakpointsResult(
511 &self,
512 event: &DebuggerGetPossibleBreakpointsEvent,
513 result: Vec<RecommendedBreakpointLocation>,
514 ) {
515 info!("GetPossibleBreakpointsResult: {event:?} {result:?}");
516 let sender = self
517 .get_possible_breakpoints_result_sender
518 .take()
519 .expect("Guaranteed by Self::fire_get_possible_breakpoints()");
520 let _ = sender.send(
521 result
522 .into_iter()
523 .map(|entry| devtools_traits::RecommendedBreakpointLocation {
524 script_id: entry.scriptId,
525 offset: entry.offset,
526 line_number: entry.lineNumber,
527 column_number: entry.columnNumber,
528 is_step_start: entry.isStepStart,
529 })
530 .collect(),
531 );
532 }
533
534 fn EvalResult(&self, _event: &DebuggerEvalEvent, result: &EvalResult) {
539 let sender = self
540 .eval_result_sender
541 .take()
542 .expect("Guaranteed by Self::fire_eval()");
543
544 let has_exception = result.hasException.unwrap_or(false);
545 let value = match serde_json::from_str::<devtools_traits::DebuggerValue>(
546 &result.serializedValue.str(),
547 ) {
548 Ok(value) => value,
549 Err(error) => {
550 warn!("Failed to parse serialized debugger eval value: {error}");
551 devtools_traits::DebuggerValue::StringValue(
552 "failed to parse eval result".to_string(),
553 )
554 },
555 };
556
557 let exception_message = result
558 .exceptionMessage
559 .as_ref()
560 .map(|message| message.str().to_string());
561
562 let reply = EvaluateJSReply {
563 value,
564 exception_message,
565 has_exception,
566 };
567
568 let _ = sender.send(reply);
569 }
570
571 fn RegisterObjectActor(&self, serialized_value: DOMString) -> Option<DOMString> {
572 let chan = self.upcast::<GlobalScope>().devtools_chan()?;
573 let (tx, rx) = channel::<String>().unwrap();
574
575 let value =
576 match serde_json::from_str::<devtools_traits::DebuggerValue>(&serialized_value.str()) {
577 Ok(value) => value,
578 Err(error) => {
579 warn!("Failed to parse serialized debugger object value: {error}");
580 return None;
581 },
582 };
583
584 let msg = ScriptToDevtoolsControlMsg::CreateObjectActor(tx, value);
585 let _ = chan.send(msg);
586
587 rx.recv().ok().map(DOMString::from)
588 }
589
590 fn PauseAndRespond(
591 &self,
592 pipeline_id: &PipelineIdInit,
593 frame_offset: &FrameOffset,
594 pause_reason: &PauseReason,
595 ) {
596 let pipeline_id = PipelineId {
597 namespace_id: PipelineNamespaceId(pipeline_id.namespaceId),
598 index: Index::new(pipeline_id.index).expect("`pipelineId.index` must not be zero"),
599 };
600
601 let frame_offset = devtools_traits::FrameOffset {
602 actor: frame_offset.frameActorId.clone().into(),
603 column: frame_offset.column,
604 line: frame_offset.line,
605 };
606
607 let pause_reason = devtools_traits::PauseReason {
608 type_: pause_reason.type_.clone().into(),
609 on_next: pause_reason.onNext,
610 };
611
612 if let Some(chan) = self.upcast::<GlobalScope>().devtools_chan() {
613 let msg =
614 ScriptToDevtoolsControlMsg::DebuggerPause(pipeline_id, frame_offset, pause_reason);
615 let _ = chan.send(msg);
616 }
617
618 with_script_thread(|script_thread| {
619 script_thread.enter_debugger_pause_loop();
620 });
621 }
622
623 fn RegisterFrameActor(
624 &self,
625 pipeline_id: &PipelineIdInit,
626 result: &FrameInfo,
627 ) -> Option<DOMString> {
628 let pipeline_id = PipelineId {
629 namespace_id: PipelineNamespaceId(pipeline_id.namespaceId),
630 index: Index::new(pipeline_id.index).expect("`pipelineId.index` must not be zero"),
631 };
632
633 let chan = self.upcast::<GlobalScope>().devtools_chan()?;
634 let (tx, rx) = channel::<String>().unwrap();
635
636 let this_value = match serde_json::from_str::<devtools_traits::DebuggerValue>(
637 &result.serializedThis.str(),
638 ) {
639 Ok(this_value) => this_value,
640 Err(error) => {
641 warn!("Failed to parse serialized debugger frame this value: {error}");
642 return None;
643 },
644 };
645
646 let frame = devtools_traits::FrameInfo {
647 display_name: result.displayName.clone().map(String::from),
648 on_stack: result.onStack,
649 oldest: result.oldest,
650 this_value,
651 terminated: result.terminated,
652 type_: result.type_.clone().into(),
653 url: result.url.clone().into(),
654 };
655 let msg = ScriptToDevtoolsControlMsg::CreateFrameActor(tx, pipeline_id, frame);
656 let _ = chan.send(msg);
657
658 rx.recv().ok().map(DOMString::from)
659 }
660
661 fn ListFramesResult(&self, frame_actor_ids: Vec<DOMString>) {
662 info!("ListFramesResult: {frame_actor_ids:?}");
663 let sender = self
664 .get_list_frame_result_sender
665 .take()
666 .expect("Guaranteed by Self::fire_list_frames()");
667
668 let _ = sender.send(frame_actor_ids.into_iter().map(|i| i.into()).collect());
669 }
670
671 fn RegisterEnvironmentActor(
672 &self,
673 environment: &EnvironmentInfo,
674 parent: Option<DOMString>,
675 actor: Option<DOMString>,
676 ) -> Option<DOMString> {
677 let chan = self.upcast::<GlobalScope>().devtools_chan()?;
678 let (tx, rx) = channel::<String>().unwrap();
679
680 let binding_variables = match serde_json::from_str::<Vec<devtools_traits::PropertyDescriptor>>(
681 &environment.serializedBindings.str(),
682 ) {
683 Ok(binding_variables) => binding_variables,
684 Err(error) => {
685 warn!("Failed to parse serialized debugger environment bindings: {error}");
686 return None;
687 },
688 };
689 let object = match environment.serializedObject.as_ref() {
690 Some(serialized_object) => {
691 match serde_json::from_str::<DebuggerValue>(&serialized_object.str()) {
692 Ok(object) => Some(object),
693 Err(error) => {
694 warn!("Failed to parse serialized debugger environment object: {error}");
695 return None;
696 },
697 }
698 },
699 None => None,
700 };
701 let environment = devtools_traits::EnvironmentInfo {
702 type_: environment.type_.clone().map(String::from),
703 scope_kind: environment.scopeKind.clone().map(String::from),
704 function_display_name: environment.functionDisplayName.clone().map(String::from),
705 object,
706 binding_variables,
707 };
708
709 let msg = ScriptToDevtoolsControlMsg::CreateEnvironmentActor(
710 tx,
711 environment,
712 parent.map(String::from),
713 actor.map(String::from),
714 );
715 let _ = chan.send(msg);
716
717 rx.recv().ok().map(DOMString::from)
718 }
719
720 fn GetEnvironmentResult(&self, environment_actor_id: DOMString) {
721 let sender = self
722 .get_environment_result_sender
723 .take()
724 .expect("Guaranteed by Self::fire_get_environment()");
725
726 let _ = sender.send(environment_actor_id.into());
727 }
728}
729
730impl HasOrigin for DebuggerGlobalScope {
731 fn origin(&self) -> MutableOrigin {
732 DebuggerGlobalScope::origin(self)
733 }
734}