1use std::cell::RefCell;
6
7use devtools_traits::{
8 BlackboxCoverage, DebuggerValue, DevtoolScriptControlMsg, EvaluateJSReply,
9 GetEnvironmentRequest, 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::event_loop::script_thread::with_script_thread;
55use crate::realms::enter_auto_realm;
56use crate::runtime::script_runtime::IntroductionType;
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 ),
120 devtools_to_script_sender,
121 get_possible_breakpoints_result_sender: RefCell::new(None),
122 get_list_frame_result_sender: RefCell::new(None),
123 get_environment_result_sender: RefCell::new(None),
124 eval_result_sender: RefCell::new(None),
125 pipeline_id: debugger_pipeline_id,
126 origin: MutableOrigin::new(ImmutableOrigin::new_opaque()),
127 });
128 let global =
129 DebuggerGlobalScopeBinding::Wrap::<crate::DomTypeHolder>(cx, &global.origin(), global);
130
131 let mut realm = enter_auto_realm(cx, &*global);
132 let mut realm = realm.current_realm();
133 define_all_exposed_interfaces(&mut realm, global.upcast());
134 assert!(unsafe {
135 JS_DefineDebuggerObject(&mut realm, global.global_scope.reflector().get_jsobject())
137 });
138
139 global
140 }
141
142 pub(crate) fn origin(&self) -> MutableOrigin {
143 self.origin.clone()
144 }
145
146 pub(crate) fn as_global_scope(&self) -> &GlobalScope {
147 self.upcast::<GlobalScope>()
148 }
149
150 pub(crate) fn execute(&self, cx: &mut JSContext) {
151 let mut realm = enter_auto_realm(cx, self);
152 let cx = &mut realm.current_realm();
153
154 let _ = self.global_scope.evaluate_js_on_global(
155 cx,
156 resources::read_string(Resource::DebuggerJS).into(),
157 "",
158 None,
159 None,
160 );
161 }
162
163 pub(crate) fn fire_add_debuggee(
164 &self,
165 cx: &mut JSContext,
166 debuggee_global: &GlobalScope,
167 debuggee_pipeline_id: PipelineId,
168 debuggee_worker_id: Option<WorkerId>,
169 ) {
170 let mut realm = enter_auto_realm(cx, self);
171 let cx = &mut realm;
172 let debuggee_pipeline_id =
173 crate::dom::pipelineid::PipelineId::new(cx, self.upcast(), debuggee_pipeline_id);
174 let event = DomRoot::upcast::<Event>(DebuggerAddDebuggeeEvent::new(
175 cx,
176 self.upcast(),
177 debuggee_global,
178 &debuggee_pipeline_id,
179 debuggee_worker_id.map(|id| id.to_string().into()),
180 ));
181 assert!(
182 event.fire(cx, self.upcast()),
183 "Guaranteed by DebuggerAddDebuggeeEvent::new"
184 );
185 }
186
187 #[expect(clippy::too_many_arguments)]
188 pub(crate) fn fire_eval(
189 &self,
190 cx: &mut JSContext,
191 code: DOMString,
192 debuggee_pipeline_id: PipelineId,
193 debuggee_worker_id: Option<WorkerId>,
194 frame_actor_id: Option<String>,
195 eager: bool,
196 result_sender: GenericSender<EvaluateJSReply>,
197 ) {
198 assert!(
199 self.eval_result_sender
200 .replace(Some(result_sender))
201 .is_none()
202 );
203 let mut realm = enter_auto_realm(cx, self);
204 let cx = &mut realm;
205 let debuggee_pipeline_id =
206 crate::dom::pipelineid::PipelineId::new(cx, self.upcast(), debuggee_pipeline_id);
207 let event = DomRoot::upcast::<Event>(DebuggerEvalEvent::new(
208 cx,
209 self.upcast(),
210 code,
211 &debuggee_pipeline_id,
212 debuggee_worker_id.map(|id| id.to_string().into()),
213 frame_actor_id.map(|id| id.into()),
214 eager,
215 ));
216 assert!(
217 event.fire(cx, self.upcast()),
218 "Guaranteed by DebuggerEvalEvent::new"
219 );
220 }
221
222 pub(crate) fn fire_get_possible_breakpoints(
223 &self,
224 cx: &mut JSContext,
225 spidermonkey_id: u32,
226 result_sender: GenericSender<Vec<devtools_traits::RecommendedBreakpointLocation>>,
227 ) {
228 assert!(
229 self.get_possible_breakpoints_result_sender
230 .replace(Some(result_sender))
231 .is_none()
232 );
233 let mut realm = enter_auto_realm(cx, self);
234 let cx = &mut realm.current_realm();
235 let event = DomRoot::upcast::<Event>(DebuggerGetPossibleBreakpointsEvent::new(
236 cx,
237 self.upcast(),
238 spidermonkey_id,
239 ));
240 assert!(
241 event.fire(cx, self.upcast()),
242 "Guaranteed by DebuggerGetPossibleBreakpointsEvent::new"
243 );
244 }
245
246 pub(crate) fn fire_set_breakpoint(
247 &self,
248 cx: &mut JSContext,
249 spidermonkey_id: u32,
250 script_id: u32,
251 offset: u32,
252 ) {
253 let event = DomRoot::upcast::<Event>(DebuggerSetBreakpointEvent::new(
254 cx,
255 self.upcast(),
256 spidermonkey_id,
257 script_id,
258 offset,
259 ));
260 assert!(
261 event.fire(cx, self.upcast()),
262 "Guaranteed by DebuggerSetBreakpointEvent::new"
263 );
264 }
265
266 pub(crate) fn fire_interrupt(&self, cx: &mut js::context::JSContext) {
267 let event = DomRoot::upcast::<Event>(DebuggerInterruptEvent::new(cx, self.upcast()));
268 assert!(
269 event.fire(cx, self.upcast()),
270 "Guaranteed by DebuggerInterruptEvent::new"
271 );
272 }
273
274 pub(crate) fn fire_list_frames(
275 &self,
276 cx: &mut js::context::JSContext,
277 pipeline_id: PipelineId,
278 start: u32,
279 count: u32,
280 result_sender: GenericSender<Vec<String>>,
281 ) {
282 assert!(
283 self.get_list_frame_result_sender
284 .replace(Some(result_sender))
285 .is_none()
286 );
287 let mut realm = enter_auto_realm(cx, self);
288 let cx = &mut realm.current_realm();
289 let pipeline_id = crate::dom::pipelineid::PipelineId::new(cx, self.upcast(), pipeline_id);
290 let event = DomRoot::upcast::<Event>(DebuggerFrameEvent::new(
291 cx,
292 self.upcast(),
293 &pipeline_id,
294 start,
295 count,
296 ));
297 assert!(
298 event.fire(cx, self.upcast()),
299 "Guaranteed by DebuggerFrameEvent::new"
300 );
301 }
302
303 pub(crate) fn fire_get_environment(
304 &self,
305 cx: &mut JSContext,
306 request: GetEnvironmentRequest,
307 result_sender: GenericSender<String>,
308 ) {
309 assert!(
310 self.get_environment_result_sender
311 .replace(Some(result_sender))
312 .is_none()
313 );
314 let mut realm = enter_auto_realm(cx, self);
315 let cx = &mut realm.current_realm();
316
317 let event = DomRoot::upcast::<Event>(DebuggerGetEnvironmentEvent::new(cx, self, request));
318 assert!(
319 event.fire(cx, self.upcast()),
320 "Guaranteed by DebuggerGetEnvironmentEvent::new"
321 );
322 }
323
324 pub(crate) fn fire_resume(
325 &self,
326 cx: &mut JSContext,
327 resume_limit_type: Option<String>,
328 frame_actor_id: Option<String>,
329 ) {
330 let event = DomRoot::upcast::<Event>(DebuggerResumeEvent::new(
331 cx,
332 self.upcast(),
333 resume_limit_type.map(DOMString::from),
334 frame_actor_id.map(DOMString::from),
335 ));
336 assert!(
337 event.fire(cx, self.upcast()),
338 "Guaranteed by DebuggerResumeEvent::new"
339 );
340 }
341
342 pub(crate) fn fire_clear_breakpoint(
343 &self,
344 cx: &mut JSContext,
345 spidermonkey_id: u32,
346 script_id: u32,
347 offset: u32,
348 ) {
349 let event = DomRoot::upcast::<Event>(DebuggerClearBreakpointEvent::new(
350 cx,
351 self.upcast(),
352 spidermonkey_id,
353 script_id,
354 offset,
355 ));
356 assert!(
357 event.fire(cx, self.upcast()),
358 "Guaranteed by DebuggerClearBreakpointEvent::new"
359 );
360 }
361
362 pub(crate) fn fire_blackbox(
363 &self,
364 cx: &mut JSContext,
365 spidermonkey_id: u32,
366 coverage: BlackboxCoverage,
367 ) {
368 let event = DomRoot::upcast::<Event>(DebuggerBlackboxEvent::new(
369 cx,
370 self.upcast(),
371 spidermonkey_id,
372 coverage,
373 ));
374 assert!(
375 event.fire(cx, self.upcast()),
376 "Guaranteed by DebuggerBlackboxEvent::new"
377 );
378 }
379
380 pub(crate) fn fire_unblackbox(
381 &self,
382 cx: &mut JSContext,
383 spidermonkey_id: u32,
384 coverage: BlackboxCoverage,
385 ) {
386 let event = DomRoot::upcast::<Event>(DebuggerUnblackboxEvent::new(
387 cx,
388 self.upcast(),
389 spidermonkey_id,
390 coverage,
391 ));
392 assert!(
393 event.fire(cx, self.upcast()),
394 "Guaranteed by DebuggerUnblackboxEvent::new"
395 );
396 }
397
398 pub(crate) fn pipeline_id(&self) -> PipelineId {
399 self.pipeline_id
400 }
401}
402
403impl DebuggerGlobalScopeMethods<crate::DomTypeHolder> for DebuggerGlobalScope {
404 fn NotifyNewSource(&self, args: &NotifyNewSource) {
406 let Some(devtools_chan) = self.as_global_scope().devtools_chan() else {
407 return;
408 };
409 let pipeline_id = PipelineId {
410 namespace_id: PipelineNamespaceId(args.pipelineId.namespaceId),
411 index: Index::new(args.pipelineId.index).expect("`pipelineId.index` must not be zero"),
412 };
413
414 if let Some(introduction_type) = args.introductionType.as_ref() {
415 let url_original = args.url.str();
428 let url_original = ServoUrl::parse(&url_original).ok();
430
431 let url_override = args
436 .urlOverride
437 .as_ref()
438 .map(|url| url.str())
439 .and_then(|url| ServoUrl::parse_with_base(url_original.as_ref(), &url).ok());
441
442 if [
446 IntroductionType::INJECTED_SCRIPT_STR,
447 IntroductionType::EVAL_STR,
448 IntroductionType::DEBUGGER_EVAL_STR,
449 IntroductionType::FUNCTION_STR,
450 IntroductionType::JAVASCRIPT_URL_STR,
451 IntroductionType::EVENT_HANDLER_STR,
452 IntroductionType::DOM_TIMER_STR,
453 ]
454 .contains(&&*introduction_type.str()) &&
455 url_override.is_none()
456 {
457 debug!(
458 "Not creating debuggee: `introductionType` is `{introduction_type}` but no valid url"
459 );
460 return;
461 }
462
463 let inline = introduction_type.str() == "inlineScript" && url_override.is_none();
471 let Some(url) = url_override.or(url_original) else {
472 debug!("Not creating debuggee: no valid url");
473 return;
474 };
475
476 let worker_id = args.workerId.as_ref().map(|id| id.parse().unwrap());
477
478 let source_info = SourceInfo {
479 url,
480 introduction_type: introduction_type.str().to_owned(),
481 inline,
482 worker_id,
483 content: (!inline).then(|| args.text.to_string()),
484 content_type: None, spidermonkey_id: args.spidermonkeyId,
486 };
487 if let Err(error) = devtools_chan.send(ScriptToDevtoolsControlMsg::CreateSourceActor(
488 self.devtools_to_script_sender.clone(),
489 pipeline_id,
490 source_info,
491 )) {
492 warn!("Failed to send to devtools server: {error:?}");
493 }
494 } else {
495 debug!("Not creating debuggee for script with no `introductionType`");
496 }
497 }
498
499 fn GetPossibleBreakpointsResult(
500 &self,
501 event: &DebuggerGetPossibleBreakpointsEvent,
502 result: Vec<RecommendedBreakpointLocation>,
503 ) {
504 info!("GetPossibleBreakpointsResult: {event:?} {result:?}");
505 let sender = self
506 .get_possible_breakpoints_result_sender
507 .take()
508 .expect("Guaranteed by Self::fire_get_possible_breakpoints()");
509 let _ = sender.send(
510 result
511 .into_iter()
512 .map(|entry| devtools_traits::RecommendedBreakpointLocation {
513 script_id: entry.scriptId,
514 offset: entry.offset,
515 line_number: entry.lineNumber,
516 column_number: entry.columnNumber,
517 is_step_start: entry.isStepStart,
518 })
519 .collect(),
520 );
521 }
522
523 fn EvalResult(&self, _event: &DebuggerEvalEvent, result: &EvalResult) {
528 let sender = self
529 .eval_result_sender
530 .take()
531 .expect("Guaranteed by Self::fire_eval()");
532
533 let has_exception = result.hasException.unwrap_or(false);
534 let value = match serde_json::from_str::<devtools_traits::DebuggerValue>(
535 &result.serializedValue.str(),
536 ) {
537 Ok(value) => value,
538 Err(error) => {
539 warn!("Failed to parse serialized debugger eval value: {error}");
540 devtools_traits::DebuggerValue::StringValue(
541 "failed to parse eval result".to_string(),
542 )
543 },
544 };
545
546 let exception_message = result
547 .exceptionMessage
548 .as_ref()
549 .map(|message| message.str().to_string());
550
551 let reply = EvaluateJSReply {
552 value,
553 exception_message,
554 has_exception,
555 };
556
557 let _ = sender.send(reply);
558 }
559
560 fn RegisterObjectActor(&self, serialized_value: DOMString) -> Option<DOMString> {
561 let chan = self.upcast::<GlobalScope>().devtools_chan()?;
562 let (tx, rx) = channel::<String>().unwrap();
563
564 let value =
565 match serde_json::from_str::<devtools_traits::DebuggerValue>(&serialized_value.str()) {
566 Ok(value) => value,
567 Err(error) => {
568 warn!("Failed to parse serialized debugger object value: {error}");
569 return None;
570 },
571 };
572
573 let msg = ScriptToDevtoolsControlMsg::CreateObjectActor(tx, value);
574 let _ = chan.send(msg);
575
576 rx.recv().ok().map(DOMString::from)
577 }
578
579 fn PauseAndRespond(
580 &self,
581 pipeline_id: &PipelineIdInit,
582 frame_offset: &FrameOffset,
583 pause_reason: &PauseReason,
584 ) {
585 let pipeline_id = PipelineId {
586 namespace_id: PipelineNamespaceId(pipeline_id.namespaceId),
587 index: Index::new(pipeline_id.index).expect("`pipelineId.index` must not be zero"),
588 };
589
590 let frame_offset = devtools_traits::FrameOffset {
591 actor: frame_offset.frameActorId.clone().into(),
592 column: frame_offset.column,
593 line: frame_offset.line,
594 };
595
596 let pause_reason = devtools_traits::PauseReason {
597 type_: pause_reason.type_.clone().into(),
598 on_next: pause_reason.onNext,
599 };
600
601 if let Some(chan) = self.upcast::<GlobalScope>().devtools_chan() {
602 let msg =
603 ScriptToDevtoolsControlMsg::DebuggerPause(pipeline_id, frame_offset, pause_reason);
604 let _ = chan.send(msg);
605 }
606
607 with_script_thread(|script_thread| {
608 script_thread.enter_debugger_pause_loop();
609 });
610 }
611
612 fn RegisterFrameActor(
613 &self,
614 pipeline_id: &PipelineIdInit,
615 result: &FrameInfo,
616 ) -> Option<DOMString> {
617 let pipeline_id = PipelineId {
618 namespace_id: PipelineNamespaceId(pipeline_id.namespaceId),
619 index: Index::new(pipeline_id.index).expect("`pipelineId.index` must not be zero"),
620 };
621
622 let chan = self.upcast::<GlobalScope>().devtools_chan()?;
623 let (tx, rx) = channel::<String>().unwrap();
624
625 let this_value = match serde_json::from_str::<devtools_traits::DebuggerValue>(
626 &result.serializedThis.str(),
627 ) {
628 Ok(this_value) => this_value,
629 Err(error) => {
630 warn!("Failed to parse serialized debugger frame this value: {error}");
631 return None;
632 },
633 };
634
635 let frame = devtools_traits::FrameInfo {
636 display_name: result.displayName.clone().map(String::from),
637 on_stack: result.onStack,
638 oldest: result.oldest,
639 this_value,
640 terminated: result.terminated,
641 type_: result.type_.clone().into(),
642 url: result.url.clone().into(),
643 };
644 let msg = ScriptToDevtoolsControlMsg::CreateFrameActor(tx, pipeline_id, frame);
645 let _ = chan.send(msg);
646
647 rx.recv().ok().map(DOMString::from)
648 }
649
650 fn ListFramesResult(&self, frame_actor_ids: Vec<DOMString>) {
651 info!("ListFramesResult: {frame_actor_ids:?}");
652 let sender = self
653 .get_list_frame_result_sender
654 .take()
655 .expect("Guaranteed by Self::fire_list_frames()");
656
657 let _ = sender.send(frame_actor_ids.into_iter().map(|i| i.into()).collect());
658 }
659
660 fn RegisterEnvironmentActor(
661 &self,
662 environment: &EnvironmentInfo,
663 parent: Option<DOMString>,
664 actor: Option<DOMString>,
665 ) -> Option<DOMString> {
666 let chan = self.upcast::<GlobalScope>().devtools_chan()?;
667 let (tx, rx) = channel::<String>().unwrap();
668
669 let binding_variables = match serde_json::from_str::<Vec<devtools_traits::PropertyDescriptor>>(
670 &environment.serializedBindings.str(),
671 ) {
672 Ok(binding_variables) => binding_variables,
673 Err(error) => {
674 warn!("Failed to parse serialized debugger environment bindings: {error}");
675 return None;
676 },
677 };
678 let object = match environment.serializedObject.as_ref() {
679 Some(serialized_object) => {
680 match serde_json::from_str::<DebuggerValue>(&serialized_object.str()) {
681 Ok(object) => Some(object),
682 Err(error) => {
683 warn!("Failed to parse serialized debugger environment object: {error}");
684 return None;
685 },
686 }
687 },
688 None => None,
689 };
690 let environment = devtools_traits::EnvironmentInfo {
691 type_: environment.type_.clone().map(String::from),
692 scope_kind: environment.scopeKind.clone().map(String::from),
693 function_display_name: environment.functionDisplayName.clone().map(String::from),
694 object,
695 binding_variables,
696 };
697
698 let msg = ScriptToDevtoolsControlMsg::CreateEnvironmentActor(
699 tx,
700 environment,
701 parent.map(String::from),
702 actor.map(String::from),
703 );
704 let _ = chan.send(msg);
705
706 rx.recv().ok().map(DOMString::from)
707 }
708
709 fn GetEnvironmentResult(&self, environment_actor_id: DOMString) {
710 let sender = self
711 .get_environment_result_sender
712 .take()
713 .expect("Guaranteed by Self::fire_get_environment()");
714
715 let _ = sender.send(environment_actor_id.into());
716 }
717}
718
719impl HasOrigin for DebuggerGlobalScope {
720 fn origin(&self) -> MutableOrigin {
721 DebuggerGlobalScope::origin(self)
722 }
723}