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