1#![expect(dead_code)]
9
10use core::ffi::c_char;
11use std::cell::{Cell, LazyCell, RefCell};
12use std::collections::{HashMap, HashSet};
13use std::ffi::{CStr, CString};
14use std::io::{Write, stdout};
15use std::ops::{Deref, DerefMut};
16use std::os::raw::c_void;
17use std::ptr::NonNull;
18use std::rc::Weak;
19use std::time::Instant;
20use std::{os, ptr};
21
22use background_hang_monitor_api::ScriptHangAnnotation;
23use js::context::JSContext;
24use js::conversions::jsstr_to_string;
25use js::gc::StackGCVector;
26use js::glue::{
27 DispatchablePointer, RUST_js_GetErrorMessage, RegisterScriptEnvironmentPreparer,
28 RunScriptEnvironmentPreparerClosure, SetBuildId, StreamConsumerConsumeChunk,
29 StreamConsumerNoteResponseURLs, StreamConsumerStreamEnd, StreamConsumerStreamError,
30};
31use js::jsapi::{
32 AsmJSOption, BuildIdCharVector, CompilationType, Dispatchable_MaybeShuttingDown, GCDescription,
33 GCOptions, GCProgress, GCReason, Handle as RawHandle, HandleObject, HandleString,
34 HandleValue as RawHandleValue, Heap, JSContext as RawJSContext, JSGCParamKey, JSGCStatus,
35 JSJitCompilerOption, JSObject, JSSecurityCallbacks, JSString, JSTracer, MimeType,
36 MutableHandleString, PromiseRejectionHandlingState, RuntimeCode,
37 ScriptEnvironmentPreparer_Closure, SetProcessBuildIdOp, StreamConsumer as JSStreamConsumer,
38};
39use js::jsval::{JSVal, UndefinedValue};
40use js::panic::wrap_panic;
41use js::realm::CurrentRealm;
42pub(crate) use js::rust::ThreadSafeJSContext;
43use js::rust::wrappers2::{
44 CollectServoSizes, ContextOptionsRef, DispatchableRun, InitConsumeStreamCallback,
45 JS_AddExtraGCRootsTracer, JS_GetPromiseResult, JS_InitDestroyPrincipalsCallback,
46 JS_InitReadPrincipalsCallback, JS_NewStringCopyUTF8N, JS_SetGCCallback, JS_SetGCParameter,
47 JS_SetGlobalJitCompilerOption, JS_SetOffthreadIonCompilationEnabled, JS_SetSecurityCallbacks,
48 SetDOMCallbacks, SetGCSliceCallback, SetPreserveWrapperCallbacks,
49 SetPromiseRejectionTrackerCallback, SetUpEventLoopDispatch,
50};
51use js::rust::{
52 Handle, HandleObject as RustHandleObject, HandleValue, IntoHandle, ParentRuntime,
53 Runtime as RustRuntime, Trace,
54};
55use malloc_size_of::MallocSizeOfOps;
56use malloc_size_of_derive::MallocSizeOf;
57use profile_traits::mem::{Report, ReportKind};
58use profile_traits::path;
59use profile_traits::time::ProfilerCategory;
60use script_bindings::reflector::DomObject;
61use script_bindings::script_runtime::{mark_runtime_dead, runtime_is_alive, temp_cx};
62use script_bindings::settings_stack::run_a_script;
63use servo_config::opts::{self, DiagnosticsLoggingOption};
64use servo_config::pref;
65use servo_url::ServoUrl;
66use style::thread_state::{self, ThreadState};
67
68use crate::dom::bindings::codegen::Bindings::ResponseBinding::Response_Binding::ResponseMethods;
69use crate::dom::bindings::codegen::Bindings::ResponseBinding::ResponseType as DOMResponseType;
70use crate::dom::bindings::codegen::UnionTypes::TrustedScriptOrString;
71use crate::dom::bindings::conversions::{
72 get_dom_class, private_from_object, root_from_handleobject, root_from_object,
73};
74use crate::dom::bindings::error::{Error, report_pending_exception, throw_dom_exception};
75use crate::dom::bindings::inheritance::Castable;
76use crate::dom::bindings::refcounted::{
77 LivePromiseReferences, Trusted, TrustedPromise, trace_refcounted_objects,
78};
79use crate::dom::bindings::reflector::DomGlobal;
80use crate::dom::bindings::root::trace_roots;
81use crate::dom::bindings::str::DOMString;
82use crate::dom::bindings::utils::DOM_CALLBACKS;
83use crate::dom::bindings::{principals, settings_stack};
84use crate::dom::console::stringify_handle_value;
85use crate::dom::csp::CspReporting;
86use crate::dom::event::{Event, EventBubbles, EventCancelable};
87use crate::dom::eventtarget::EventTarget;
88use crate::dom::globalscope::GlobalScope;
89use crate::dom::promise::Promise;
90use crate::dom::promiserejectionevent::PromiseRejectionEvent;
91use crate::dom::response::Response;
92use crate::dom::trustedtypes::trustedscript::TrustedScript;
93use crate::engine::handle::current_js_engine_handle;
94use crate::messaging::{CommonScriptMsg, ScriptEventLoopSender};
95use crate::modules::script_module::EnsureModuleHooksInitialized;
96use crate::realms::enter_auto_realm;
97use crate::runtime::job_queue::{JobQueue, job_queue_clear};
98use crate::tasks::task_source::TaskSourceName;
99use crate::{DomTypeHolder, ScriptThread};
100
101static SECURITY_CALLBACKS: JSSecurityCallbacks = JSSecurityCallbacks {
102 contentSecurityPolicyAllows: Some(content_security_policy_allows),
103 codeForEvalGets: Some(code_for_eval_gets),
104 subsumes: Some(principals::subsumes),
105};
106
107#[derive(Clone, Copy, Debug, Eq, Hash, JSTraceable, MallocSizeOf, PartialEq)]
108pub(crate) enum ScriptThreadEventCategory {
109 SpawnPipeline,
110 ConstellationMsg,
111 DatabaseAccessEvent,
112 DevtoolsMsg,
113 DocumentEvent,
114 FileRead,
115 FontLoading,
116 FormPlannedNavigation,
117 GeolocationEvent,
118 ImageCacheMsg,
119 InputEvent,
120 NavigationAndTraversalEvent,
121 NetworkEvent,
122 PortMessage,
123 Rendering,
124 Resize,
125 ScriptEvent,
126 SetScrollState,
127 SetViewport,
128 StylesheetLoad,
129 TimerEvent,
130 UpdateReplacedElement,
131 WebSocketEvent,
132 WorkerEvent,
133 WorkletEvent,
134 ServiceWorkerEvent,
135 EnterFullscreen,
136 ExitFullscreen,
137 PerformanceTimelineTask,
138 #[cfg(feature = "webgpu")]
139 WebGPUMsg,
140}
141
142impl From<ScriptThreadEventCategory> for ProfilerCategory {
143 fn from(category: ScriptThreadEventCategory) -> Self {
144 match category {
145 ScriptThreadEventCategory::SpawnPipeline => ProfilerCategory::ScriptSpawnPipeline,
146 ScriptThreadEventCategory::ConstellationMsg => ProfilerCategory::ScriptConstellationMsg,
147 ScriptThreadEventCategory::DatabaseAccessEvent => {
148 ProfilerCategory::ScriptDatabaseAccessEvent
149 },
150 ScriptThreadEventCategory::DevtoolsMsg => ProfilerCategory::ScriptDevtoolsMsg,
151 ScriptThreadEventCategory::DocumentEvent => ProfilerCategory::ScriptDocumentEvent,
152 ScriptThreadEventCategory::EnterFullscreen => ProfilerCategory::ScriptEnterFullscreen,
153 ScriptThreadEventCategory::ExitFullscreen => ProfilerCategory::ScriptExitFullscreen,
154 ScriptThreadEventCategory::FileRead => ProfilerCategory::ScriptFileRead,
155 ScriptThreadEventCategory::FontLoading => ProfilerCategory::ScriptFontLoading,
156 ScriptThreadEventCategory::FormPlannedNavigation => {
157 ProfilerCategory::ScriptPlannedNavigation
158 },
159 ScriptThreadEventCategory::GeolocationEvent => ProfilerCategory::ScriptGeolocationEvent,
160 ScriptThreadEventCategory::NavigationAndTraversalEvent => {
161 ProfilerCategory::ScriptNavigationAndTraversalEvent
162 },
163 ScriptThreadEventCategory::ImageCacheMsg => ProfilerCategory::ScriptImageCacheMsg,
164 ScriptThreadEventCategory::InputEvent => ProfilerCategory::ScriptInputEvent,
165 ScriptThreadEventCategory::NetworkEvent => ProfilerCategory::ScriptNetworkEvent,
166 ScriptThreadEventCategory::PerformanceTimelineTask => {
167 ProfilerCategory::ScriptPerformanceEvent
168 },
169 ScriptThreadEventCategory::PortMessage => ProfilerCategory::ScriptPortMessage,
170 ScriptThreadEventCategory::Resize => ProfilerCategory::ScriptResize,
171 ScriptThreadEventCategory::Rendering => ProfilerCategory::ScriptRendering,
172 ScriptThreadEventCategory::ScriptEvent => ProfilerCategory::ScriptEvent,
173 ScriptThreadEventCategory::ServiceWorkerEvent => {
174 ProfilerCategory::ScriptServiceWorkerEvent
175 },
176 ScriptThreadEventCategory::SetScrollState => ProfilerCategory::ScriptSetScrollState,
177 ScriptThreadEventCategory::SetViewport => ProfilerCategory::ScriptSetViewport,
178 ScriptThreadEventCategory::StylesheetLoad => ProfilerCategory::ScriptStylesheetLoad,
179 ScriptThreadEventCategory::TimerEvent => ProfilerCategory::ScriptTimerEvent,
180 ScriptThreadEventCategory::UpdateReplacedElement => {
181 ProfilerCategory::ScriptUpdateReplacedElement
182 },
183 ScriptThreadEventCategory::WebSocketEvent => ProfilerCategory::ScriptWebSocketEvent,
184 ScriptThreadEventCategory::WorkerEvent => ProfilerCategory::ScriptWorkerEvent,
185 ScriptThreadEventCategory::WorkletEvent => ProfilerCategory::ScriptWorkletEvent,
186 #[cfg(feature = "webgpu")]
187 ScriptThreadEventCategory::WebGPUMsg => ProfilerCategory::ScriptWebGPUMsg,
188 }
189 }
190}
191
192impl From<ScriptThreadEventCategory> for ScriptHangAnnotation {
193 fn from(category: ScriptThreadEventCategory) -> Self {
194 match category {
195 ScriptThreadEventCategory::SpawnPipeline => ScriptHangAnnotation::SpawnPipeline,
196 ScriptThreadEventCategory::ConstellationMsg => ScriptHangAnnotation::ConstellationMsg,
197 ScriptThreadEventCategory::DatabaseAccessEvent => {
198 ScriptHangAnnotation::DatabaseAccessEvent
199 },
200 ScriptThreadEventCategory::DevtoolsMsg => ScriptHangAnnotation::DevtoolsMsg,
201 ScriptThreadEventCategory::DocumentEvent => ScriptHangAnnotation::DocumentEvent,
202 ScriptThreadEventCategory::InputEvent => ScriptHangAnnotation::InputEvent,
203 ScriptThreadEventCategory::FileRead => ScriptHangAnnotation::FileRead,
204 ScriptThreadEventCategory::FontLoading => ScriptHangAnnotation::FontLoading,
205 ScriptThreadEventCategory::FormPlannedNavigation => {
206 ScriptHangAnnotation::FormPlannedNavigation
207 },
208 ScriptThreadEventCategory::GeolocationEvent => ScriptHangAnnotation::GeolocationEvent,
209 ScriptThreadEventCategory::NavigationAndTraversalEvent => {
210 ScriptHangAnnotation::NavigationAndTraversalEvent
211 },
212 ScriptThreadEventCategory::ImageCacheMsg => ScriptHangAnnotation::ImageCacheMsg,
213 ScriptThreadEventCategory::NetworkEvent => ScriptHangAnnotation::NetworkEvent,
214 ScriptThreadEventCategory::Rendering => ScriptHangAnnotation::Rendering,
215 ScriptThreadEventCategory::Resize => ScriptHangAnnotation::Resize,
216 ScriptThreadEventCategory::ScriptEvent => ScriptHangAnnotation::ScriptEvent,
217 ScriptThreadEventCategory::SetScrollState => ScriptHangAnnotation::SetScrollState,
218 ScriptThreadEventCategory::SetViewport => ScriptHangAnnotation::SetViewport,
219 ScriptThreadEventCategory::StylesheetLoad => ScriptHangAnnotation::StylesheetLoad,
220 ScriptThreadEventCategory::TimerEvent => ScriptHangAnnotation::TimerEvent,
221 ScriptThreadEventCategory::UpdateReplacedElement => {
222 ScriptHangAnnotation::UpdateReplacedElement
223 },
224 ScriptThreadEventCategory::WebSocketEvent => ScriptHangAnnotation::WebSocketEvent,
225 ScriptThreadEventCategory::WorkerEvent => ScriptHangAnnotation::WorkerEvent,
226 ScriptThreadEventCategory::WorkletEvent => ScriptHangAnnotation::WorkletEvent,
227 ScriptThreadEventCategory::ServiceWorkerEvent => {
228 ScriptHangAnnotation::ServiceWorkerEvent
229 },
230 ScriptThreadEventCategory::EnterFullscreen => ScriptHangAnnotation::EnterFullscreen,
231 ScriptThreadEventCategory::ExitFullscreen => ScriptHangAnnotation::ExitFullscreen,
232 ScriptThreadEventCategory::PerformanceTimelineTask => {
233 ScriptHangAnnotation::PerformanceTimelineTask
234 },
235 ScriptThreadEventCategory::PortMessage => ScriptHangAnnotation::PortMessage,
236 #[cfg(feature = "webgpu")]
237 ScriptThreadEventCategory::WebGPUMsg => ScriptHangAnnotation::WebGPUMsg,
238 }
239 }
240}
241
242#[expect(unsafe_code)]
243unsafe extern "C" fn promise_rejection_tracker(
245 cx: *mut RawJSContext,
246 muted_errors: bool,
247 promise: HandleObject,
248 state: PromiseRejectionHandlingState,
249 _data: *mut c_void,
250) {
251 if muted_errors {
254 return;
255 }
256
257 let mut cx = unsafe { JSContext::from_ptr(NonNull::new(cx).unwrap()) };
260 let mut realm = CurrentRealm::assert(&mut cx);
261
262 let global = GlobalScope::from_current_realm(&mut realm);
263 let cx = &mut realm;
264
265 wrap_panic(&mut || {
266 match state {
267 PromiseRejectionHandlingState::Unhandled => {
269 global.add_uncaught_rejection(promise);
270 },
271 PromiseRejectionHandlingState::Handled => {
273 if global
275 .get_uncaught_rejections()
276 .borrow()
277 .contains(&Heap::boxed(promise.get()))
278 {
279 global.remove_uncaught_rejection(promise);
280 return;
281 }
282
283 if !global
285 .get_consumed_rejections()
286 .borrow()
287 .contains(&Heap::boxed(promise.get()))
288 {
289 return;
290 }
291
292 global.remove_consumed_rejection(promise);
294
295 let target = Trusted::new(global.upcast::<EventTarget>());
296 let promise =
297 Promise::new_with_js_promise(cx, unsafe { Handle::from_raw(promise) });
298 let trusted_promise = TrustedPromise::new(promise);
299
300 global.task_manager().dom_manipulation_task_source().queue(
302 task!(rejection_handled_event: move |cx| {
303 let target = target.root();
304 let root_promise = trusted_promise.root(cx);
305
306 rooted!(&in(cx) let mut reason = UndefinedValue());
307 unsafe {
308 JS_GetPromiseResult(root_promise.reflector().get_jsobject(), reason.handle_mut());
309 }
310
311 let event = PromiseRejectionEvent::new(
312 cx,
313 &target.global(),
314 atom!("rejectionhandled"),
315 EventBubbles::DoesNotBubble,
316 EventCancelable::Cancelable,
317 &root_promise,
318 reason.handle(),
319 );
320
321 event.upcast::<Event>().fire(cx, &target);
322 })
323 );
324 },
325 };
326 })
327}
328
329#[expect(unsafe_code)]
330fn safely_convert_null_to_string(cx: &JSContext, str_: HandleString) -> DOMString {
331 DOMString::from(match std::ptr::NonNull::new(*str_) {
332 None => String::new(),
333 Some(str_) => unsafe { jsstr_to_string(cx, str_) },
334 })
335}
336
337#[expect(unsafe_code)]
338unsafe extern "C" fn code_for_eval_gets(
339 cx: *mut RawJSContext,
340 code: HandleObject,
341 code_for_eval: MutableHandleString,
342) -> bool {
343 let mut cx = unsafe { JSContext::from_ptr(NonNull::new(cx).unwrap()) };
345 let cx = &mut cx;
346 if let Ok(trusted_script) = unsafe { root_from_object::<TrustedScript>(cx, code.get()) } {
347 let script_str = trusted_script.data().str();
348 let s = js::conversions::Utf8Chars::from(&*script_str);
349 let new_string = unsafe { JS_NewStringCopyUTF8N(cx, &*s as *const _) };
350 code_for_eval.set(new_string);
351 }
352 true
353}
354
355#[expect(unsafe_code)]
356unsafe extern "C" fn content_security_policy_allows(
357 cx: *mut RawJSContext,
358 runtime_code: RuntimeCode,
359 code_string: HandleString,
360 compilation_type: CompilationType,
361 parameter_strings: RawHandle<StackGCVector<*mut JSString>>,
362 body_string: HandleString,
363 parameter_args: RawHandle<StackGCVector<JSVal>>,
364 body_arg: RawHandleValue,
365 can_compile_strings: *mut bool,
366) -> bool {
367 let mut allowed = false;
368 let mut cx = unsafe { JSContext::from_ptr(NonNull::new(cx).unwrap()) };
370 let cx = &mut cx;
371 wrap_panic(&mut || {
372 let mut realm = CurrentRealm::assert(cx);
374 let global = GlobalScope::from_current_realm(&mut realm);
375 let csp_list = global.get_csp_list();
376
377 allowed = csp_list.is_none() ||
379 match runtime_code {
380 RuntimeCode::JS => {
381 let parameter_strings = unsafe { Handle::from_raw(parameter_strings) };
382 let parameter_strings_length = parameter_strings.len();
383 let mut parameter_strings_vec =
384 Vec::with_capacity(parameter_strings_length as usize);
385
386 for i in 0..parameter_strings_length {
387 let Some(str_) = parameter_strings.at(i) else {
388 unreachable!();
389 };
390 parameter_strings_vec.push(safely_convert_null_to_string(cx, str_.into()));
391 }
392
393 let parameter_args = unsafe { Handle::from_raw(parameter_args) };
394 let parameter_args_length = parameter_args.len();
395 let mut parameter_args_vec = Vec::with_capacity(parameter_args_length as usize);
396
397 for i in 0..parameter_args_length {
398 let Some(arg) = parameter_args.at(i) else {
399 unreachable!();
400 };
401 let value = arg.into_handle().get();
402 if value.is_object() {
403 if let Ok(trusted_script) =
404 unsafe { root_from_object::<TrustedScript>(cx, value.to_object()) }
405 {
406 parameter_args_vec
407 .push(TrustedScriptOrString::TrustedScript(trusted_script));
408 } else {
409 parameter_args_vec
413 .push(TrustedScriptOrString::String(DOMString::new()));
414 }
415 } else if value.is_string() {
416 parameter_args_vec
418 .push(TrustedScriptOrString::String(DOMString::new()));
419 } else {
420 unreachable!();
421 }
422 }
423
424 let code_string = safely_convert_null_to_string(cx, code_string);
425 let body_string = safely_convert_null_to_string(cx, body_string);
426
427 TrustedScript::can_compile_string_with_trusted_type(
428 cx,
429 &global,
430 code_string,
431 compilation_type,
432 parameter_strings_vec,
433 body_string,
434 parameter_args_vec,
435 unsafe { HandleValue::from_raw(body_arg) },
436 )
437 },
438 RuntimeCode::WASM => global
439 .get_csp_list()
440 .is_wasm_evaluation_allowed(cx, &global),
441 };
442 });
443 unsafe { *can_compile_strings = allowed };
444 true
445}
446
447#[expect(unsafe_code)]
448pub(crate) fn notify_about_rejected_promises(cx: &mut JSContext, global: &GlobalScope) {
450 let uncaught_rejections: Vec<TrustedPromise> = {
452 global
453 .get_uncaught_rejections()
454 .borrow()
455 .iter()
456 .map(|promise| {
457 let promise =
458 Promise::new_with_js_promise(cx, unsafe { Handle::from_raw(promise.handle()) });
459
460 TrustedPromise::new(promise)
461 })
462 .collect()
463 };
464 global.get_uncaught_rejections().safe_borrow_mut(cx).clear();
465
466 if uncaught_rejections.is_empty() {
468 return;
469 }
470
471 let target = Trusted::new(global.upcast::<EventTarget>());
476 global.task_manager().dom_manipulation_task_source().queue(
477 task!(unhandled_rejection_event: move |cx| {
478 let target = target.root();
479
480 for promise in uncaught_rejections {
482 let promise = promise.root(cx);
483
484 if promise.get_promise_is_handled() {
486 continue;
487 }
488
489 rooted!(&in(cx) let mut reason = UndefinedValue());
493 unsafe {
494 JS_GetPromiseResult(promise.reflector().get_jsobject(), reason.handle_mut());
495 }
496
497 log::error!(
498 "Unhandled promise rejection: {}",
499 stringify_handle_value( cx, reason.handle())
500 );
501
502 let event = PromiseRejectionEvent::new(
503 cx,
504 &target.global(),
505 atom!("unhandledrejection"),
506 EventBubbles::DoesNotBubble,
507 EventCancelable::Cancelable,
508 &promise,
509 reason.handle(),
510 );
511 event.upcast::<Event>().fire(cx, &target);
512
513 if !promise.get_promise_is_handled() {
519 target.global().add_consumed_rejection(promise.reflector().get_jsobject().into_handle());
520 }
521 }
522 })
523 );
524}
525
526#[derive(Default, JSTraceable, MallocSizeOf)]
529struct RuntimeCallbackData {
530 script_event_loop_sender: Option<ScriptEventLoopSender>,
531 #[no_trace]
532 #[ignore_malloc_size_of = "ScriptThread measures its own memory itself."]
533 script_thread: Option<Weak<ScriptThread>>,
534}
535
536#[derive(JSTraceable, MallocSizeOf)]
537pub(crate) struct Runtime {
538 #[ignore_malloc_size_of = "Type from mozjs"]
539 rt: RustRuntime,
540 job_queue: JobQueue,
541 runtime_callback_data: Box<RuntimeCallbackData>,
543}
544
545impl Runtime {
546 #[expect(unsafe_code)]
556 pub(crate) fn new(main_thread_sender: Option<ScriptEventLoopSender>) -> Runtime {
557 unsafe { Self::new_with_parent(None, main_thread_sender) }
558 }
559
560 #[allow(unsafe_code)]
561 pub(crate) unsafe fn cx(&self) -> JSContext {
565 unsafe { JSContext::from_ptr(RustRuntime::get().unwrap()) }
566 }
567
568 #[expect(unsafe_code)]
581 pub(crate) unsafe fn new_with_parent(
582 parent: Option<ParentRuntime>,
583 script_event_loop_sender: Option<ScriptEventLoopSender>,
584 ) -> Runtime {
585 let mut runtime = if let Some(parent) = parent {
586 unsafe { RustRuntime::create_with_parent(parent) }
587 } else {
588 RustRuntime::new(current_js_engine_handle())
589 };
590 let cx = runtime.cx();
591
592 let have_event_loop_sender = script_event_loop_sender.is_some();
593 let runtime_callback_data = Box::new(RuntimeCallbackData {
594 script_event_loop_sender,
595 script_thread: None,
596 });
597 let runtime_callback_data = Box::into_raw(runtime_callback_data);
598
599 unsafe {
600 JS_AddExtraGCRootsTracer(
601 cx,
602 Some(trace_rust_roots),
603 runtime_callback_data as *mut c_void,
604 );
605
606 JS_SetSecurityCallbacks(cx, &SECURITY_CALLBACKS);
607
608 JS_InitDestroyPrincipalsCallback(cx, Some(principals::destroy_servo_jsprincipal));
609 JS_InitReadPrincipalsCallback(cx, Some(principals::read_jsprincipal));
610
611 if cfg!(debug_assertions) {
613 JS_SetGCCallback(cx, Some(debug_gc_callback), ptr::null_mut());
614 }
615
616 if opts::get()
617 .debug
618 .is_enabled(DiagnosticsLoggingOption::GcProfile)
619 {
620 SetGCSliceCallback(cx, Some(gc_slice_callback));
621 }
622 }
623
624 unsafe extern "C" fn empty_wrapper_callback(_: *mut RawJSContext, _: HandleObject) -> bool {
625 true
626 }
627 unsafe extern "C" fn empty_has_released_callback(_: HandleObject) -> bool {
628 false
630 }
631
632 unsafe {
633 SetDOMCallbacks(cx, &DOM_CALLBACKS);
634 SetPreserveWrapperCallbacks(
635 cx,
636 Some(empty_wrapper_callback),
637 Some(empty_has_released_callback),
638 );
639 }
640
641 unsafe extern "C" fn dispatch_to_event_loop(
642 data: *mut c_void,
643 dispatchable: *mut DispatchablePointer,
644 ) -> bool {
645 let runtime_callback_data: &RuntimeCallbackData =
646 unsafe { &*(data as *mut RuntimeCallbackData) };
647 let Some(script_event_loop_sender) =
648 runtime_callback_data.script_event_loop_sender.as_ref()
649 else {
650 return false;
651 };
652
653 let runnable = Runnable(dispatchable);
654 let task = task!(dispatch_to_event_loop_message: move |cx| {
655 runnable.run(cx, Dispatchable_MaybeShuttingDown::NotShuttingDown);
656 });
657
658 script_event_loop_sender
659 .send(CommonScriptMsg::Task(
660 ScriptThreadEventCategory::NetworkEvent,
661 Box::new(task),
662 None, TaskSourceName::Networking,
664 ))
665 .is_ok()
666 }
667
668 if have_event_loop_sender {
669 unsafe {
670 SetUpEventLoopDispatch(
671 cx,
672 Some(dispatch_to_event_loop),
673 None,
674 None,
675 runtime_callback_data as *mut c_void,
676 );
677 }
678 }
679
680 unsafe {
681 InitConsumeStreamCallback(cx, Some(consume_stream), Some(report_stream_error));
682 }
683
684 let cx_opts;
685 let job_queue;
686 unsafe {
687 let cx = runtime.cx();
688 job_queue = JobQueue::new();
689 job_queue.set_on_context(cx);
690 SetPromiseRejectionTrackerCallback(
691 cx,
692 Some(promise_rejection_tracker),
693 ptr::null_mut(),
694 );
695
696 RegisterScriptEnvironmentPreparer(
697 cx.raw_cx(),
698 Some(invoke_script_environment_preparer),
699 );
700
701 EnsureModuleHooksInitialized(runtime.rt());
702
703 let cx = runtime.cx();
704
705 set_gc_zeal_options(cx.raw_cx());
706
707 cx_opts = &mut *ContextOptionsRef(cx);
709 JS_SetGlobalJitCompilerOption(
710 cx,
711 JSJitCompilerOption::JSJITCOMPILER_BASELINE_INTERPRETER_ENABLE,
712 pref!(js_baseline_interpreter_enabled) as u32,
713 );
714 JS_SetGlobalJitCompilerOption(
715 cx,
716 JSJitCompilerOption::JSJITCOMPILER_BASELINE_ENABLE,
717 pref!(js_baseline_jit_enabled) as u32,
718 );
719 JS_SetGlobalJitCompilerOption(
720 cx,
721 JSJitCompilerOption::JSJITCOMPILER_ION_ENABLE,
722 pref!(js_ion_enabled) as u32,
723 );
724 }
725 cx_opts.compileOptions_.asmJSOption_ = if pref!(js_asmjs_enabled) {
726 AsmJSOption::Enabled
727 } else {
728 AsmJSOption::DisabledByAsmJSPref
729 };
730 let wasm_enabled = pref!(js_wasm_enabled);
731 cx_opts.set_wasm_(wasm_enabled);
732 if wasm_enabled {
733 unsafe { SetProcessBuildIdOp(Some(servo_build_id)) };
737 }
738 cx_opts.set_wasmBaseline_(pref!(js_wasm_baseline_enabled));
739 cx_opts.set_wasmIon_(pref!(js_wasm_ion_enabled));
740
741 unsafe {
742 let cx = runtime.cx();
743 JS_SetGlobalJitCompilerOption(
745 cx,
746 JSJitCompilerOption::JSJITCOMPILER_NATIVE_REGEXP_ENABLE,
747 pref!(js_native_regex_enabled) as u32,
748 );
749 JS_SetOffthreadIonCompilationEnabled(cx, pref!(js_offthread_compilation_enabled));
750 JS_SetGlobalJitCompilerOption(
751 cx,
752 JSJitCompilerOption::JSJITCOMPILER_BASELINE_WARMUP_TRIGGER,
753 if pref!(js_baseline_jit_unsafe_eager_compilation_enabled) {
754 0
755 } else {
756 u32::MAX
757 },
758 );
759 JS_SetGlobalJitCompilerOption(
760 cx,
761 JSJitCompilerOption::JSJITCOMPILER_ION_NORMAL_WARMUP_TRIGGER,
762 if pref!(js_ion_unsafe_eager_compilation_enabled) {
763 0
764 } else {
765 u32::MAX
766 },
767 );
768 JS_SetGCParameter(
774 cx,
775 JSGCParamKey::JSGC_MAX_BYTES,
776 in_range(pref!(js_mem_max), 1, 0x100)
777 .map(|val| (val * 1024 * 1024) as u32)
778 .unwrap_or(u32::MAX),
779 );
780
781 JS_SetGCParameter(
784 cx,
785 JSGCParamKey::JSGC_INCREMENTAL_GC_ENABLED,
786 pref!(js_mem_gc_incremental_enabled) as u32,
787 );
788
789 JS_SetGCParameter(
790 cx,
791 JSGCParamKey::JSGC_PER_ZONE_GC_ENABLED,
792 pref!(js_mem_gc_per_zone_enabled) as u32,
793 );
794 if let Some(val) = in_range(pref!(js_mem_gc_incremental_slice_ms), 0, 100_000) {
795 JS_SetGCParameter(cx, JSGCParamKey::JSGC_SLICE_TIME_BUDGET_MS, val as u32);
796 }
797 JS_SetGCParameter(
798 cx,
799 JSGCParamKey::JSGC_COMPACTING_ENABLED,
800 pref!(js_mem_gc_compacting_enabled) as u32,
801 );
802
803 if let Some(val) = in_range(pref!(js_mem_gc_high_frequency_time_limit_ms), 0, 10_000) {
804 JS_SetGCParameter(cx, JSGCParamKey::JSGC_HIGH_FREQUENCY_TIME_LIMIT, val as u32);
805 }
806 if let Some(val) = in_range(pref!(js_mem_gc_low_frequency_heap_growth), 0, 10_000) {
807 JS_SetGCParameter(cx, JSGCParamKey::JSGC_LOW_FREQUENCY_HEAP_GROWTH, val as u32);
808 }
809 if let Some(val) = in_range(pref!(js_mem_gc_high_frequency_heap_growth_min), 0, 10_000)
810 {
811 JS_SetGCParameter(
812 cx,
813 JSGCParamKey::JSGC_HIGH_FREQUENCY_LARGE_HEAP_GROWTH,
814 val as u32,
815 );
816 }
817 if let Some(val) = in_range(pref!(js_mem_gc_high_frequency_heap_growth_max), 0, 10_000)
818 {
819 JS_SetGCParameter(
820 cx,
821 JSGCParamKey::JSGC_HIGH_FREQUENCY_SMALL_HEAP_GROWTH,
822 val as u32,
823 );
824 }
825 if let Some(val) = in_range(pref!(js_mem_gc_high_frequency_low_limit_mb), 0, 10_000) {
826 JS_SetGCParameter(cx, JSGCParamKey::JSGC_SMALL_HEAP_SIZE_MAX, val as u32);
827 }
828 if let Some(val) = in_range(pref!(js_mem_gc_high_frequency_high_limit_mb), 0, 10_000) {
829 JS_SetGCParameter(cx, JSGCParamKey::JSGC_LARGE_HEAP_SIZE_MIN, val as u32);
830 }
831 if let Some(val) = in_range(pref!(js_mem_gc_empty_chunk_count_min), 0, 10_000) {
832 JS_SetGCParameter(cx, JSGCParamKey::JSGC_MIN_EMPTY_CHUNK_COUNT, val as u32);
833 }
834 if let Some(val) = in_range(pref!(js_mem_gc_malloc_threshold_base_mb), 0, 10_000) {
835 JS_SetGCParameter(cx, JSGCParamKey::JSGC_MALLOC_THRESHOLD_BASE, val as u32);
836 }
837 if let Some(val) = in_range(pref!(js_mem_gc_urgent_threshold_mb), 0, 10_000) {
838 JS_SetGCParameter(cx, JSGCParamKey::JSGC_URGENT_THRESHOLD_MB, val as u32);
839 }
840 }
841 Runtime {
842 rt: runtime,
843 job_queue,
844 runtime_callback_data: unsafe { Box::from_raw(runtime_callback_data) },
845 }
846 }
847
848 pub(crate) fn set_script_thread(&mut self, script_thread: Weak<ScriptThread>) {
849 self.runtime_callback_data
850 .script_thread
851 .replace(script_thread);
852 }
853
854 pub(crate) fn thread_safe_js_context(&self) -> ThreadSafeJSContext {
855 self.rt.thread_safe_js_context()
856 }
857}
858
859impl Drop for Runtime {
860 fn drop(&mut self) {
861 job_queue_clear(self.rt.cx_no_gc());
863
864 LivePromiseReferences::destruct();
865 script_bindings::refcounted::LiveDOMReferences::destruct();
866 mark_runtime_dead();
867 }
868}
869
870impl Deref for Runtime {
871 type Target = RustRuntime;
872 fn deref(&self) -> &RustRuntime {
873 &self.rt
874 }
875}
876
877impl DerefMut for Runtime {
878 fn deref_mut(&mut self) -> &mut RustRuntime {
879 &mut self.rt
880 }
881}
882
883fn in_range<T: PartialOrd + Copy>(val: T, min: T, max: T) -> Option<T> {
884 if val < min || val >= max {
885 None
886 } else {
887 Some(val)
888 }
889}
890
891thread_local!(static MALLOC_SIZE_OF_OPS: Cell<*mut MallocSizeOfOps> = const { Cell::new(ptr::null_mut()) });
892
893#[derive(Default)]
894struct InterfaceSizeData {
895 count: usize,
897 bytes: usize,
899}
900
901struct GlobalSizeData {
902 url: ServoUrl,
904 interface_sizes: HashMap<&'static str, InterfaceSizeData>,
906}
907
908#[derive(Default)]
909pub(crate) struct PerGlobalInterfaceSizes(HashMap<usize, GlobalSizeData>);
912
913thread_local!(
914 static DOM_OBJECT_SIZES: LazyCell<RefCell<PerGlobalInterfaceSizes>> = const {
915 LazyCell::new(Default::default)
916 }
917);
918
919#[expect(unsafe_code)]
920unsafe extern "C" fn get_size(obj: *mut JSObject) -> usize {
921 let ops = MALLOC_SIZE_OF_OPS.get();
922 ALREADY_COMPUTED_OBJECTS.with(|objects| {
923 let ignored = objects.borrow();
924 DOM_OBJECT_SIZES.with(|dom_sizes| {
925 let mut per_global_interface_sizes = dom_sizes.borrow_mut();
926 compute_size(
927 obj,
928 unsafe { &mut *ops },
929 &ignored,
930 Some(&mut per_global_interface_sizes),
931 )
932 })
933 })
934}
935
936thread_local!(static GC_CYCLE_START: Cell<Option<Instant>> = const { Cell::new(None) });
937thread_local!(static GC_SLICE_START: Cell<Option<Instant>> = const { Cell::new(None) });
938
939#[expect(unsafe_code)]
940unsafe extern "C" fn gc_slice_callback(
941 _cx: *mut RawJSContext,
942 progress: GCProgress,
943 desc: *const GCDescription,
944) {
945 match progress {
946 GCProgress::GC_CYCLE_BEGIN => GC_CYCLE_START.with(|start| {
947 start.set(Some(Instant::now()));
948 println!("GC cycle began");
949 }),
950 GCProgress::GC_SLICE_BEGIN => GC_SLICE_START.with(|start| {
951 start.set(Some(Instant::now()));
952 println!("GC slice began");
953 }),
954 GCProgress::GC_SLICE_END => GC_SLICE_START.with(|start| {
955 let duration = start.get().unwrap().elapsed();
956 start.set(None);
957 println!("GC slice ended: duration={:?}", duration);
958 }),
959 GCProgress::GC_CYCLE_END => GC_CYCLE_START.with(|start| {
960 let duration = start.get().unwrap().elapsed();
961 start.set(None);
962 println!("GC cycle ended: duration={:?}", duration);
963 }),
964 };
965 if !desc.is_null() {
966 let desc: &GCDescription = unsafe { &*desc };
967 let options = match desc.options_ {
968 GCOptions::Normal => "Normal",
969 GCOptions::Shrink => "Shrink",
970 GCOptions::Shutdown => "Shutdown",
971 };
972 println!(" isZone={}, options={}", desc.isZone_, options);
973 }
974 let _ = stdout().flush();
975}
976
977#[expect(unsafe_code)]
978unsafe extern "C" fn debug_gc_callback(
979 _cx: *mut RawJSContext,
980 status: JSGCStatus,
981 _reason: GCReason,
982 _data: *mut os::raw::c_void,
983) {
984 match status {
985 JSGCStatus::JSGC_BEGIN => thread_state::enter(ThreadState::IN_GC),
986 JSGCStatus::JSGC_END => thread_state::exit(ThreadState::IN_GC),
987 }
988}
989
990#[expect(unsafe_code)]
991unsafe extern "C" fn trace_rust_roots(tr: *mut JSTracer, data: *mut os::raw::c_void) {
992 if !runtime_is_alive() {
993 return;
994 }
995 trace!("starting custom root handler");
996
997 let runtime_callback_data = unsafe { &*(data as *const RuntimeCallbackData) };
998 if let Some(script_thread) = runtime_callback_data
999 .script_thread
1000 .as_ref()
1001 .and_then(Weak::upgrade)
1002 {
1003 trace!("tracing fields of ScriptThread");
1004 unsafe { script_thread.trace(tr) };
1005 };
1006
1007 unsafe {
1008 trace_roots(tr);
1009 trace_refcounted_objects(tr);
1010 settings_stack::trace(tr);
1011 }
1012 trace!("done custom root handler");
1013}
1014
1015#[expect(unsafe_code)]
1016unsafe extern "C" fn servo_build_id(build_id: *mut BuildIdCharVector) -> bool {
1017 let servo_id = b"Servo\0";
1018 unsafe { SetBuildId(build_id, servo_id[0] as *const c_char, servo_id.len()) }
1019}
1020
1021#[expect(unsafe_code)]
1022#[cfg(feature = "debugmozjs")]
1023unsafe fn set_gc_zeal_options(cx: *mut RawJSContext) {
1024 use js::jsapi::SetGCZeal;
1025
1026 let level = match pref!(js_mem_gc_zeal_level) {
1027 level @ 0..=14 => level as u8,
1028 _ => return,
1029 };
1030 let frequency = match pref!(js_mem_gc_zeal_frequency) {
1031 frequency if frequency >= 0 => frequency as u32,
1032 _ => 5000,
1034 };
1035 unsafe {
1036 SetGCZeal(cx, level, frequency);
1037 }
1038}
1039
1040#[expect(unsafe_code)]
1041#[cfg(not(feature = "debugmozjs"))]
1042unsafe fn set_gc_zeal_options(_: *mut RawJSContext) {}
1043
1044thread_local!(static ALREADY_COMPUTED_OBJECTS: LazyCell<RefCell<HashSet<*const JSObject>>> = const {
1045 LazyCell::new(Default::default)
1046});
1047
1048#[expect(unsafe_code)]
1049pub(crate) fn compute_size(
1050 obj: *mut JSObject,
1051 ops: &mut MallocSizeOfOps,
1052 ignored: &HashSet<*const JSObject>,
1053 per_global_interface_sizes: Option<&mut PerGlobalInterfaceSizes>,
1054) -> usize {
1055 if ignored.contains(&(obj as *const JSObject)) {
1056 return 0;
1057 }
1058
1059 match unsafe { get_dom_class(obj) } {
1060 Ok(v) => {
1061 let dom_object = unsafe { private_from_object(obj) as *const c_void };
1062
1063 if dom_object.is_null() {
1064 return 0;
1065 }
1066 let size = unsafe { (v.malloc_size_of)(&mut *ops, dom_object) };
1067
1068 let Some(per_global_interface_sizes) = per_global_interface_sizes else {
1069 return size;
1070 };
1071
1072 let global = unsafe { js::jsapi::GetNonCCWObjectGlobal(obj) };
1073 let interface = v.interface_chain[v.depth as usize];
1074 let interface_size = per_global_interface_sizes
1075 .0
1076 .entry(global as usize)
1077 .or_insert_with(|| {
1078 let global = unsafe { GlobalScope::from_object(obj) };
1079 GlobalSizeData {
1080 url: global.get_url(),
1081 interface_sizes: HashMap::new(),
1082 }
1083 })
1084 .interface_sizes
1085 .entry(interface.into())
1086 .or_default();
1087 interface_size.count += 1;
1088 interface_size.bytes += size;
1089 0
1092 },
1093 Err(_e) => 0,
1094 }
1095}
1096
1097#[expect(unsafe_code)]
1098pub(crate) fn get_reports(
1099 cx: &mut JSContext,
1100 path_seg: String,
1101 ops: &mut MallocSizeOfOps,
1102 already_computed_objects: HashSet<*const JSObject>,
1103) -> Vec<Report> {
1104 MALLOC_SIZE_OF_OPS.with(|ops_tls| ops_tls.set(ops));
1105 ALREADY_COMPUTED_OBJECTS.with(|objects| {
1106 *objects.borrow_mut() = already_computed_objects;
1107 });
1108
1109 let stats = unsafe {
1110 let mut stats = ::std::mem::zeroed();
1111 if !CollectServoSizes(cx, &mut stats, Some(get_size)) {
1112 return vec![];
1113 }
1114 stats
1115 };
1116 MALLOC_SIZE_OF_OPS.with(|ops| ops.set(ptr::null_mut()));
1117 ALREADY_COMPUTED_OBJECTS.with(|objects| {
1118 let mut objects = objects.borrow_mut();
1119 objects.clear();
1120 objects.shrink_to_fit();
1121 });
1122
1123 let mut reports = vec![];
1124 let mut report = |mut path_suffix, kind, size| {
1125 let mut path = path![path_seg, "js"];
1126 path.append(&mut path_suffix);
1127 reports.push(Report { path, kind, size })
1128 };
1129
1130 DOM_OBJECT_SIZES.with(|sizes| {
1131 let mut sizes = sizes.borrow_mut();
1132 for global_size_data in sizes.0.values() {
1133 let url = global_size_data.url.as_str();
1134 for (interface, interface_data) in &global_size_data.interface_sizes {
1135 report(
1136 path![
1137 "dom",
1138 "out-of-tree",
1139 format!("url({url})"),
1140 format!("{interface} [{}]", interface_data.count)
1141 ],
1142 ReportKind::ExplicitJemallocHeapSize,
1143 interface_data.bytes,
1144 );
1145 }
1146 }
1147 sizes.0.clear();
1148 });
1149
1150 report(
1154 path!["gc-heap", "used"],
1155 ReportKind::ExplicitNonHeapSize,
1156 stats.gcHeapUsed,
1157 );
1158
1159 report(
1160 path!["gc-heap", "unused"],
1161 ReportKind::ExplicitNonHeapSize,
1162 stats.gcHeapUnused,
1163 );
1164
1165 report(
1166 path!["gc-heap", "admin"],
1167 ReportKind::ExplicitNonHeapSize,
1168 stats.gcHeapAdmin,
1169 );
1170
1171 report(
1172 path!["gc-heap", "decommitted"],
1173 ReportKind::ExplicitNonHeapSize,
1174 stats.gcHeapDecommitted,
1175 );
1176
1177 report(
1179 path!["malloc-heap"],
1180 ReportKind::ExplicitSystemHeapSize,
1181 stats.mallocHeap,
1182 );
1183
1184 report(
1185 path!["non-heap"],
1186 ReportKind::ExplicitNonHeapSize,
1187 stats.nonHeap,
1188 );
1189 reports
1190}
1191
1192pub(crate) struct StreamConsumer(*mut JSStreamConsumer);
1193
1194#[expect(unsafe_code)]
1195impl StreamConsumer {
1196 pub(crate) fn consume_chunk(&self, stream: &[u8]) -> bool {
1197 unsafe {
1198 let stream_ptr = stream.as_ptr();
1199 StreamConsumerConsumeChunk(self.0, stream_ptr, stream.len())
1200 }
1201 }
1202
1203 pub(crate) fn stream_end(&self) {
1204 unsafe {
1205 StreamConsumerStreamEnd(self.0);
1206 }
1207 }
1208
1209 pub(crate) fn stream_error(&self, error_code: usize) {
1210 unsafe {
1211 StreamConsumerStreamError(self.0, error_code);
1212 }
1213 }
1214
1215 pub(crate) fn note_response_urls(
1216 &self,
1217 maybe_url: Option<String>,
1218 maybe_source_map_url: Option<String>,
1219 ) {
1220 unsafe {
1221 let maybe_url = maybe_url.map(|url| CString::new(url).unwrap());
1222 let maybe_source_map_url = maybe_source_map_url.map(|url| CString::new(url).unwrap());
1223
1224 let maybe_url_param = match maybe_url.as_ref() {
1225 Some(url) => url.as_ptr(),
1226 None => ptr::null(),
1227 };
1228 let maybe_source_map_url_param = match maybe_source_map_url.as_ref() {
1229 Some(url) => url.as_ptr(),
1230 None => ptr::null(),
1231 };
1232
1233 StreamConsumerNoteResponseURLs(self.0, maybe_url_param, maybe_source_map_url_param);
1234 }
1235 }
1236}
1237
1238#[expect(unsafe_code)]
1241unsafe extern "C" fn consume_stream(
1242 cx: *mut RawJSContext,
1243 obj: HandleObject,
1244 _mime_type: MimeType,
1245 _consumer: *mut JSStreamConsumer,
1246) -> bool {
1247 let mut cx = unsafe {
1248 JSContext::from_ptr(NonNull::new(cx).expect("JSContext should not be null in SM hook"))
1250 };
1251 let cx = &mut cx;
1252 let mut realm = CurrentRealm::assert(cx);
1253 let global = GlobalScope::from_current_realm(&mut realm);
1254
1255 if let Ok(unwrapped_source) =
1257 unsafe { root_from_handleobject::<Response>(cx, RustHandleObject::from_raw(obj)) }
1258 {
1259 let mimetype = unwrapped_source.Headers(cx).extract_mime_type();
1261
1262 if !&mimetype[..].eq_ignore_ascii_case(b"application/wasm") {
1264 throw_dom_exception(
1265 cx,
1266 &global,
1267 Error::Type(c"Response has unsupported MIME type".to_owned()),
1268 );
1269 return false;
1270 }
1271
1272 match unwrapped_source.Type() {
1274 DOMResponseType::Basic | DOMResponseType::Cors | DOMResponseType::Default => {},
1275 _ => {
1276 throw_dom_exception(
1277 cx,
1278 &global,
1279 Error::Type(c"Response.type must be 'basic', 'cors' or 'default'".to_owned()),
1280 );
1281 return false;
1282 },
1283 }
1284
1285 if !unwrapped_source.Ok() {
1287 throw_dom_exception(
1288 cx,
1289 &global,
1290 Error::Type(c"Response does not have ok status".to_owned()),
1291 );
1292 return false;
1293 }
1294
1295 if unwrapped_source.is_locked() {
1297 throw_dom_exception(
1298 cx,
1299 &global,
1300 Error::Type(c"There was an error consuming the Response".to_owned()),
1301 );
1302 return false;
1303 }
1304
1305 if unwrapped_source.is_disturbed() {
1307 throw_dom_exception(
1308 cx,
1309 &global,
1310 Error::Type(c"Response already consumed".to_owned()),
1311 );
1312 return false;
1313 }
1314 unwrapped_source.set_stream_consumer(Some(StreamConsumer(_consumer)));
1315 } else {
1316 throw_dom_exception(
1318 cx,
1319 &global,
1320 Error::Type(c"expected Response or Promise resolving to Response".to_owned()),
1321 );
1322 return false;
1323 }
1324 true
1325}
1326
1327#[expect(unsafe_code)]
1328unsafe extern "C" fn report_stream_error(_cx: *mut RawJSContext, error_code: usize) {
1329 error!("Error initializing StreamConsumer: {:?}", unsafe {
1330 RUST_js_GetErrorMessage(ptr::null_mut(), error_code as u32)
1331 });
1332}
1333
1334#[expect(unsafe_code)]
1335unsafe extern "C" fn invoke_script_environment_preparer(
1336 global: HandleObject,
1337 closure: *mut ScriptEnvironmentPreparer_Closure,
1338) {
1339 let mut cx = unsafe { temp_cx() };
1341 let global = unsafe { GlobalScope::from_object(global.get()) };
1342 let mut realm = enter_auto_realm(&mut cx, &*global);
1343 let cx = &mut realm.current_realm();
1344
1345 run_a_script::<DomTypeHolder, _, _>(cx, &global, |cx| {
1346 if unsafe { !RunScriptEnvironmentPreparerClosure(cx.raw_cx(), closure) } {
1347 report_pending_exception(cx);
1348 };
1349 });
1350}
1351
1352pub(crate) struct Runnable(*mut DispatchablePointer);
1353
1354#[expect(unsafe_code)]
1355unsafe impl Sync for Runnable {}
1356#[expect(unsafe_code)]
1357unsafe impl Send for Runnable {}
1358
1359#[expect(unsafe_code)]
1360impl Runnable {
1361 fn run(&self, cx: &mut JSContext, maybe_shutting_down: Dispatchable_MaybeShuttingDown) {
1362 unsafe {
1363 DispatchableRun(cx, self.0, maybe_shutting_down);
1364 }
1365 }
1366}
1367
1368pub(crate) struct IntroductionType;
1374impl IntroductionType {
1375 pub const EVAL: &CStr = c"eval";
1377 pub const EVAL_STR: &str = "eval";
1378
1379 pub const DEBUGGER_EVAL: &CStr = c"debugger eval";
1382 pub const DEBUGGER_EVAL_STR: &str = "debugger eval";
1383
1384 pub const FUNCTION: &CStr = c"Function";
1386 pub const FUNCTION_STR: &str = "Function";
1387
1388 pub const WORKLET: &CStr = c"Worklet";
1390 pub const WORKLET_STR: &str = "Worklet";
1391
1392 pub const EVENT_HANDLER: &CStr = c"eventHandler";
1394 pub const EVENT_HANDLER_STR: &str = "eventHandler";
1395
1396 pub const SRC_SCRIPT: &CStr = c"srcScript";
1399 pub const SRC_SCRIPT_STR: &str = "srcScript";
1400
1401 pub const INLINE_SCRIPT: &CStr = c"inlineScript";
1404 pub const INLINE_SCRIPT_STR: &str = "inlineScript";
1405
1406 pub const INJECTED_SCRIPT: &CStr = c"injectedScript";
1412 pub const INJECTED_SCRIPT_STR: &str = "injectedScript";
1413
1414 pub const IMPORTED_MODULE: &CStr = c"importedModule";
1417 pub const IMPORTED_MODULE_STR: &str = "importedModule";
1418
1419 pub const JAVASCRIPT_URL: &CStr = c"javascriptURL";
1421 pub const JAVASCRIPT_URL_STR: &str = "javascriptURL";
1422
1423 pub const DOM_TIMER: &CStr = c"domTimer";
1425 pub const DOM_TIMER_STR: &str = "domTimer";
1426
1427 pub const WORKER: &CStr = c"Worker";
1431 pub const WORKER_STR: &str = "Worker";
1432}