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::realms::enter_auto_realm;
97use crate::runtime::job_queue::{JobQueue, job_queue_clear};
98use crate::scripting::script_module::EnsureModuleHooksInitialized;
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 global = target.global();
518 let reason_string = run_a_script::<DomTypeHolder, _, _>(
519 cx,
520 &global,
521 |cx: &mut JSContext| stringify_handle_value(cx, reason.handle()),
522 );
523 Console::internal_error(
524 cx,
525 &global,
526 format!("Unhandled promise rejection: {reason_string}")
527 );
528 }
529
530 if !promise.get_promise_is_handled() {
533 target.global().add_consumed_rejection(promise.reflector().get_jsobject().into_handle());
534 }
535 }
536 })
537 );
538}
539
540#[derive(Default, JSTraceable, MallocSizeOf)]
543struct RuntimeCallbackData {
544 script_event_loop_sender: Option<ScriptEventLoopSender>,
545 #[no_trace]
546 #[ignore_malloc_size_of = "ScriptThread measures its own memory itself."]
547 script_thread: Option<Weak<ScriptThread>>,
548}
549
550#[derive(JSTraceable, MallocSizeOf)]
551pub(crate) struct Runtime {
552 #[ignore_malloc_size_of = "Type from mozjs"]
553 rt: RustRuntime,
554 job_queue: JobQueue,
555 runtime_callback_data: Box<RuntimeCallbackData>,
557}
558
559impl Runtime {
560 #[expect(unsafe_code)]
570 pub(crate) fn new(main_thread_sender: Option<ScriptEventLoopSender>) -> Runtime {
571 unsafe { Self::new_with_parent(None, main_thread_sender) }
572 }
573
574 #[allow(unsafe_code)]
575 pub(crate) unsafe fn cx(&self) -> JSContext {
579 unsafe { JSContext::from_ptr(RustRuntime::get().unwrap()) }
580 }
581
582 #[expect(unsafe_code)]
595 pub(crate) unsafe fn new_with_parent(
596 parent: Option<ParentRuntime>,
597 script_event_loop_sender: Option<ScriptEventLoopSender>,
598 ) -> Runtime {
599 let mut runtime = if let Some(parent) = parent {
600 unsafe { RustRuntime::create_with_parent(parent) }
601 } else {
602 RustRuntime::new(current_js_engine_handle())
603 };
604 let cx = runtime.cx();
605
606 let have_event_loop_sender = script_event_loop_sender.is_some();
607 let runtime_callback_data = Box::new(RuntimeCallbackData {
608 script_event_loop_sender,
609 script_thread: None,
610 });
611 let runtime_callback_data = Box::into_raw(runtime_callback_data);
612
613 unsafe {
614 JS_AddExtraGCRootsTracer(
615 cx,
616 Some(trace_rust_roots),
617 runtime_callback_data as *mut c_void,
618 );
619
620 JS_SetSecurityCallbacks(cx, &SECURITY_CALLBACKS);
621
622 JS_InitDestroyPrincipalsCallback(cx, Some(principals::destroy_servo_jsprincipal));
623 JS_InitReadPrincipalsCallback(cx, Some(principals::read_jsprincipal));
624
625 if cfg!(debug_assertions) {
627 JS_SetGCCallback(cx, Some(debug_gc_callback), ptr::null_mut());
628 }
629
630 if opts::get()
631 .debug
632 .is_enabled(DiagnosticsLoggingOption::GcProfile)
633 {
634 SetGCSliceCallback(cx, Some(gc_slice_callback));
635 }
636 }
637
638 unsafe extern "C" fn empty_wrapper_callback(_: *mut RawJSContext, _: HandleObject) -> bool {
639 true
640 }
641 unsafe extern "C" fn empty_has_released_callback(_: HandleObject) -> bool {
642 false
644 }
645
646 unsafe {
647 SetDOMCallbacks(cx, &DOM_CALLBACKS);
648 SetPreserveWrapperCallbacks(
649 cx,
650 Some(empty_wrapper_callback),
651 Some(empty_has_released_callback),
652 );
653 }
654
655 unsafe extern "C" fn dispatch_to_event_loop(
656 data: *mut c_void,
657 dispatchable: *mut DispatchablePointer,
658 ) -> bool {
659 let runtime_callback_data: &RuntimeCallbackData =
660 unsafe { &*(data as *mut RuntimeCallbackData) };
661 let Some(script_event_loop_sender) =
662 runtime_callback_data.script_event_loop_sender.as_ref()
663 else {
664 return false;
665 };
666
667 let runnable = Runnable(dispatchable);
668 let task = task!(dispatch_to_event_loop_message: move |cx| {
669 runnable.run(cx, Dispatchable_MaybeShuttingDown::NotShuttingDown);
670 });
671
672 script_event_loop_sender
673 .send(CommonScriptMsg::Task(
674 ScriptThreadEventCategory::NetworkEvent,
675 Box::new(task),
676 None, TaskSourceName::Networking,
678 ))
679 .is_ok()
680 }
681
682 if have_event_loop_sender {
683 unsafe {
684 SetUpEventLoopDispatch(
685 cx,
686 Some(dispatch_to_event_loop),
687 None,
688 None,
689 runtime_callback_data as *mut c_void,
690 );
691 }
692 }
693
694 unsafe {
695 InitConsumeStreamCallback(cx, Some(consume_stream), Some(report_stream_error));
696 }
697
698 let cx_opts;
699 let job_queue;
700 unsafe {
701 let cx = runtime.cx();
702 job_queue = JobQueue::new();
703 job_queue.set_on_context(cx);
704 SetPromiseRejectionTrackerCallback(
705 cx,
706 Some(promise_rejection_tracker),
707 ptr::null_mut(),
708 );
709
710 RegisterScriptEnvironmentPreparer(
711 cx.raw_cx(),
712 Some(invoke_script_environment_preparer),
713 );
714
715 EnsureModuleHooksInitialized(runtime.rt());
716
717 let cx = runtime.cx();
718
719 set_gc_zeal_options(cx.raw_cx());
720
721 cx_opts = &mut *ContextOptionsRef(cx);
723 JS_SetGlobalJitCompilerOption(
724 cx,
725 JSJitCompilerOption::JSJITCOMPILER_BASELINE_INTERPRETER_ENABLE,
726 pref!(js_baseline_interpreter_enabled) as u32,
727 );
728 JS_SetGlobalJitCompilerOption(
729 cx,
730 JSJitCompilerOption::JSJITCOMPILER_BASELINE_ENABLE,
731 pref!(js_baseline_jit_enabled) as u32,
732 );
733 JS_SetGlobalJitCompilerOption(
734 cx,
735 JSJitCompilerOption::JSJITCOMPILER_ION_ENABLE,
736 pref!(js_ion_enabled) as u32,
737 );
738 }
739 cx_opts.compileOptions_.asmJSOption_ = if pref!(js_asmjs_enabled) {
740 AsmJSOption::Enabled
741 } else {
742 AsmJSOption::DisabledByAsmJSPref
743 };
744 let wasm_enabled = pref!(js_wasm_enabled);
745 cx_opts.set_wasm_(wasm_enabled);
746 if wasm_enabled {
747 unsafe { SetProcessBuildIdOp(Some(servo_build_id)) };
751 }
752 cx_opts.set_wasmBaseline_(pref!(js_wasm_baseline_enabled));
753 cx_opts.set_wasmIon_(pref!(js_wasm_ion_enabled));
754
755 unsafe {
756 let cx = runtime.cx();
757 JS_SetGlobalJitCompilerOption(
759 cx,
760 JSJitCompilerOption::JSJITCOMPILER_NATIVE_REGEXP_ENABLE,
761 pref!(js_native_regex_enabled) as u32,
762 );
763 JS_SetOffthreadIonCompilationEnabled(cx, pref!(js_offthread_compilation_enabled));
764 JS_SetGlobalJitCompilerOption(
765 cx,
766 JSJitCompilerOption::JSJITCOMPILER_BASELINE_WARMUP_TRIGGER,
767 if pref!(js_baseline_jit_unsafe_eager_compilation_enabled) {
768 0
769 } else {
770 u32::MAX
771 },
772 );
773 JS_SetGlobalJitCompilerOption(
774 cx,
775 JSJitCompilerOption::JSJITCOMPILER_ION_NORMAL_WARMUP_TRIGGER,
776 if pref!(js_ion_unsafe_eager_compilation_enabled) {
777 0
778 } else {
779 u32::MAX
780 },
781 );
782 JS_SetGCParameter(
788 cx,
789 JSGCParamKey::JSGC_MAX_BYTES,
790 in_range(pref!(js_mem_max), 1, 0x100)
791 .map(|val| (val * 1024 * 1024) as u32)
792 .unwrap_or(u32::MAX),
793 );
794
795 JS_SetGCParameter(
798 cx,
799 JSGCParamKey::JSGC_INCREMENTAL_GC_ENABLED,
800 pref!(js_mem_gc_incremental_enabled) as u32,
801 );
802
803 JS_SetGCParameter(
804 cx,
805 JSGCParamKey::JSGC_PER_ZONE_GC_ENABLED,
806 pref!(js_mem_gc_per_zone_enabled) as u32,
807 );
808 if let Some(val) = in_range(pref!(js_mem_gc_incremental_slice_ms), 0, 100_000) {
809 JS_SetGCParameter(cx, JSGCParamKey::JSGC_SLICE_TIME_BUDGET_MS, val as u32);
810 }
811 JS_SetGCParameter(
812 cx,
813 JSGCParamKey::JSGC_COMPACTING_ENABLED,
814 pref!(js_mem_gc_compacting_enabled) as u32,
815 );
816
817 if let Some(val) = in_range(pref!(js_mem_gc_high_frequency_time_limit_ms), 0, 10_000) {
818 JS_SetGCParameter(cx, JSGCParamKey::JSGC_HIGH_FREQUENCY_TIME_LIMIT, val as u32);
819 }
820 if let Some(val) = in_range(pref!(js_mem_gc_low_frequency_heap_growth), 0, 10_000) {
821 JS_SetGCParameter(cx, JSGCParamKey::JSGC_LOW_FREQUENCY_HEAP_GROWTH, val as u32);
822 }
823 if let Some(val) = in_range(pref!(js_mem_gc_high_frequency_heap_growth_min), 0, 10_000)
824 {
825 JS_SetGCParameter(
826 cx,
827 JSGCParamKey::JSGC_HIGH_FREQUENCY_LARGE_HEAP_GROWTH,
828 val as u32,
829 );
830 }
831 if let Some(val) = in_range(pref!(js_mem_gc_high_frequency_heap_growth_max), 0, 10_000)
832 {
833 JS_SetGCParameter(
834 cx,
835 JSGCParamKey::JSGC_HIGH_FREQUENCY_SMALL_HEAP_GROWTH,
836 val as u32,
837 );
838 }
839 if let Some(val) = in_range(pref!(js_mem_gc_high_frequency_low_limit_mb), 0, 10_000) {
840 JS_SetGCParameter(cx, JSGCParamKey::JSGC_SMALL_HEAP_SIZE_MAX, val as u32);
841 }
842 if let Some(val) = in_range(pref!(js_mem_gc_high_frequency_high_limit_mb), 0, 10_000) {
843 JS_SetGCParameter(cx, JSGCParamKey::JSGC_LARGE_HEAP_SIZE_MIN, val as u32);
844 }
845 if let Some(val) = in_range(pref!(js_mem_gc_empty_chunk_count_min), 0, 10_000) {
846 JS_SetGCParameter(cx, JSGCParamKey::JSGC_MIN_EMPTY_CHUNK_COUNT, val as u32);
847 }
848 if let Some(val) = in_range(pref!(js_mem_gc_malloc_threshold_base_mb), 0, 10_000) {
849 JS_SetGCParameter(cx, JSGCParamKey::JSGC_MALLOC_THRESHOLD_BASE, val as u32);
850 }
851 if let Some(val) = in_range(pref!(js_mem_gc_urgent_threshold_mb), 0, 10_000) {
852 JS_SetGCParameter(cx, JSGCParamKey::JSGC_URGENT_THRESHOLD_MB, val as u32);
853 }
854 }
855 Runtime {
856 rt: runtime,
857 job_queue,
858 runtime_callback_data: unsafe { Box::from_raw(runtime_callback_data) },
859 }
860 }
861
862 pub(crate) fn set_script_thread(&mut self, script_thread: Weak<ScriptThread>) {
863 self.runtime_callback_data
864 .script_thread
865 .replace(script_thread);
866 }
867
868 pub(crate) fn thread_safe_js_context(&self) -> ThreadSafeJSContext {
869 self.rt.thread_safe_js_context()
870 }
871}
872
873impl Drop for Runtime {
874 fn drop(&mut self) {
875 job_queue_clear(self.rt.cx_no_gc());
877
878 LivePromiseReferences::destruct();
879 script_bindings::refcounted::LiveDOMReferences::destruct();
880 mark_runtime_dead();
881 }
882}
883
884impl Deref for Runtime {
885 type Target = RustRuntime;
886 fn deref(&self) -> &RustRuntime {
887 &self.rt
888 }
889}
890
891impl DerefMut for Runtime {
892 fn deref_mut(&mut self) -> &mut RustRuntime {
893 &mut self.rt
894 }
895}
896
897fn in_range<T: PartialOrd + Copy>(val: T, min: T, max: T) -> Option<T> {
898 if val < min || val >= max {
899 None
900 } else {
901 Some(val)
902 }
903}
904
905thread_local!(static MALLOC_SIZE_OF_OPS: Cell<*mut MallocSizeOfOps> = const { Cell::new(ptr::null_mut()) });
906
907#[derive(Default)]
908struct InterfaceSizeData {
909 count: usize,
911 bytes: usize,
913}
914
915struct GlobalSizeData {
916 url: ServoUrl,
918 interface_sizes: HashMap<&'static str, InterfaceSizeData>,
920 is_zombie: bool,
922}
923
924#[derive(Default)]
925pub(crate) struct PerGlobalInterfaceSizes(HashMap<usize, GlobalSizeData>);
928
929thread_local!(
930 static DOM_OBJECT_SIZES: LazyCell<RefCell<PerGlobalInterfaceSizes>> = const {
931 LazyCell::new(Default::default)
932 }
933);
934
935#[expect(unsafe_code)]
936unsafe extern "C" fn get_size(obj: *mut JSObject) -> usize {
937 let ops = MALLOC_SIZE_OF_OPS.get();
938 ALREADY_COMPUTED_OBJECTS.with(|objects| {
939 let ignored = objects.borrow();
940 DOM_OBJECT_SIZES.with(|dom_sizes| {
941 let mut per_global_interface_sizes = dom_sizes.borrow_mut();
942 compute_size(
943 obj,
944 unsafe { &mut *ops },
945 &ignored,
946 Some(&mut per_global_interface_sizes),
947 )
948 })
949 })
950}
951
952thread_local!(static GC_CYCLE_START: Cell<Option<Instant>> = const { Cell::new(None) });
953thread_local!(static GC_SLICE_START: Cell<Option<Instant>> = const { Cell::new(None) });
954
955#[expect(unsafe_code)]
956unsafe extern "C" fn gc_slice_callback(
957 _cx: *mut RawJSContext,
958 progress: GCProgress,
959 desc: *const GCDescription,
960) {
961 match progress {
962 GCProgress::GC_CYCLE_BEGIN => GC_CYCLE_START.with(|start| {
963 start.set(Some(Instant::now()));
964 println!("GC cycle began");
965 }),
966 GCProgress::GC_SLICE_BEGIN => GC_SLICE_START.with(|start| {
967 start.set(Some(Instant::now()));
968 println!("GC slice began");
969 }),
970 GCProgress::GC_SLICE_END => GC_SLICE_START.with(|start| {
971 let duration = start.get().unwrap().elapsed();
972 start.set(None);
973 println!("GC slice ended: duration={:?}", duration);
974 }),
975 GCProgress::GC_CYCLE_END => GC_CYCLE_START.with(|start| {
976 let duration = start.get().unwrap().elapsed();
977 start.set(None);
978 println!("GC cycle ended: duration={:?}", duration);
979 }),
980 };
981 if !desc.is_null() {
982 let desc: &GCDescription = unsafe { &*desc };
983 let options = match desc.options_ {
984 GCOptions::Normal => "Normal",
985 GCOptions::Shrink => "Shrink",
986 GCOptions::Shutdown => "Shutdown",
987 };
988 println!(" isZone={}, options={}", desc.isZone_, options);
989 }
990 let _ = stdout().flush();
991}
992
993#[expect(unsafe_code)]
994unsafe extern "C" fn debug_gc_callback(
995 _cx: *mut RawJSContext,
996 status: JSGCStatus,
997 _reason: GCReason,
998 _data: *mut os::raw::c_void,
999) {
1000 match status {
1001 JSGCStatus::JSGC_BEGIN => thread_state::enter(ThreadState::IN_GC),
1002 JSGCStatus::JSGC_END => thread_state::exit(ThreadState::IN_GC),
1003 }
1004}
1005
1006#[expect(unsafe_code)]
1007unsafe extern "C" fn trace_rust_roots(tr: *mut JSTracer, data: *mut os::raw::c_void) {
1008 if !runtime_is_alive() {
1009 return;
1010 }
1011 trace!("starting custom root handler");
1012
1013 let runtime_callback_data = unsafe { &*(data as *const RuntimeCallbackData) };
1014 if let Some(script_thread) = runtime_callback_data
1015 .script_thread
1016 .as_ref()
1017 .and_then(Weak::upgrade)
1018 {
1019 trace!("tracing fields of ScriptThread");
1020 unsafe { script_thread.trace(tr) };
1021 };
1022
1023 unsafe {
1024 trace_roots(tr);
1025 trace_refcounted_objects(tr);
1026 settings_stack::trace(tr);
1027 }
1028 trace!("done custom root handler");
1029}
1030
1031#[expect(unsafe_code)]
1032unsafe extern "C" fn servo_build_id(build_id: *mut BuildIdCharVector) -> bool {
1033 let servo_id = b"Servo\0";
1034 unsafe { SetBuildId(build_id, servo_id[0] as *const c_char, servo_id.len()) }
1035}
1036
1037#[expect(unsafe_code)]
1038#[cfg(feature = "debugmozjs")]
1039unsafe fn set_gc_zeal_options(cx: *mut RawJSContext) {
1040 use js::jsapi::SetGCZeal;
1041
1042 let level = match pref!(js_mem_gc_zeal_level) {
1043 level @ 0..=14 => level as u8,
1044 _ => return,
1045 };
1046 let frequency = match pref!(js_mem_gc_zeal_frequency) {
1047 frequency if frequency >= 0 => frequency as u32,
1048 _ => 5000,
1050 };
1051 unsafe {
1052 SetGCZeal(cx, level, frequency);
1053 }
1054}
1055
1056#[expect(unsafe_code)]
1057#[cfg(not(feature = "debugmozjs"))]
1058unsafe fn set_gc_zeal_options(_: *mut RawJSContext) {}
1059
1060thread_local!(static ALREADY_COMPUTED_OBJECTS: LazyCell<RefCell<HashSet<*const JSObject>>> = const {
1061 LazyCell::new(Default::default)
1062});
1063
1064#[expect(unsafe_code)]
1065pub(crate) fn compute_size(
1066 obj: *mut JSObject,
1067 ops: &mut MallocSizeOfOps,
1068 ignored: &HashSet<*const JSObject>,
1069 per_global_interface_sizes: Option<&mut PerGlobalInterfaceSizes>,
1070) -> usize {
1071 if ignored.contains(&(obj as *const JSObject)) {
1072 return 0;
1073 }
1074
1075 match unsafe { get_dom_class(obj) } {
1076 Ok(v) => {
1077 let dom_object = unsafe { private_from_object(obj) as *const c_void };
1078
1079 if dom_object.is_null() {
1080 return 0;
1081 }
1082 let size = unsafe { (v.malloc_size_of)(&mut *ops, dom_object) };
1083
1084 let Some(per_global_interface_sizes) = per_global_interface_sizes else {
1085 return size;
1086 };
1087
1088 let global = unsafe { js::jsapi::GetNonCCWObjectGlobal(obj) };
1089 let interface = v.interface_chain[v.depth as usize];
1090 let interface_size = per_global_interface_sizes
1091 .0
1092 .entry(global as usize)
1093 .or_insert_with(|| {
1094 let global = unsafe { GlobalScope::from_object(obj) };
1095 GlobalSizeData {
1096 url: global.get_url(),
1097 interface_sizes: HashMap::new(),
1098 is_zombie: global
1099 .downcast::<Window>()
1100 .is_some_and(|window| !window.is_alive()),
1101 }
1102 })
1103 .interface_sizes
1104 .entry(interface.into())
1105 .or_default();
1106 interface_size.count += 1;
1107 interface_size.bytes += size;
1108 0
1111 },
1112 Err(_e) => 0,
1113 }
1114}
1115
1116#[expect(unsafe_code)]
1117pub(crate) fn get_reports(
1118 cx: &mut JSContext,
1119 path_seg: String,
1120 ops: &mut MallocSizeOfOps,
1121 already_computed_objects: HashSet<*const JSObject>,
1122) -> Vec<Report> {
1123 MALLOC_SIZE_OF_OPS.with(|ops_tls| ops_tls.set(ops));
1124 ALREADY_COMPUTED_OBJECTS.with(|objects| {
1125 *objects.borrow_mut() = already_computed_objects;
1126 });
1127
1128 let stats = unsafe {
1129 let mut stats = ::std::mem::zeroed();
1130 if !CollectServoSizes(cx, &mut stats, Some(get_size)) {
1131 return vec![];
1132 }
1133 stats
1134 };
1135 MALLOC_SIZE_OF_OPS.with(|ops| ops.set(ptr::null_mut()));
1136 ALREADY_COMPUTED_OBJECTS.with(|objects| {
1137 let mut objects = objects.borrow_mut();
1138 objects.clear();
1139 objects.shrink_to_fit();
1140 });
1141
1142 let mut reports = vec![];
1143 let mut report = |mut path_suffix, kind, size| {
1144 let mut path = path![path_seg, "js"];
1145 path.append(&mut path_suffix);
1146 reports.push(Report { path, kind, size })
1147 };
1148
1149 DOM_OBJECT_SIZES.with(|sizes| {
1150 let mut sizes = sizes.borrow_mut();
1151 let mut known_globals = HashMap::new();
1152 for global_size_data in sizes.0.values() {
1153 let url = global_size_data.url.as_str();
1154 let suffix = if global_size_data.is_zombie {
1155 "-zombie"
1156 } else {
1157 ""
1158 };
1159 let index = known_globals.entry(url).or_insert(0);
1160 *index += 1;
1161 for (interface, interface_data) in &global_size_data.interface_sizes {
1162 report(
1163 path![
1164 "dom",
1165 "out-of-tree",
1166 format!("url({url}){suffix}-{}", *index),
1167 format!("{interface} [{}]", interface_data.count)
1168 ],
1169 ReportKind::ExplicitJemallocHeapSize,
1170 interface_data.bytes,
1171 );
1172 }
1173 }
1174 sizes.0.clear();
1175 });
1176
1177 report(
1181 path!["gc-heap", "used"],
1182 ReportKind::ExplicitNonHeapSize,
1183 stats.gcHeapUsed,
1184 );
1185
1186 report(
1187 path!["gc-heap", "unused"],
1188 ReportKind::ExplicitNonHeapSize,
1189 stats.gcHeapUnused,
1190 );
1191
1192 report(
1193 path!["gc-heap", "admin"],
1194 ReportKind::ExplicitNonHeapSize,
1195 stats.gcHeapAdmin,
1196 );
1197
1198 report(
1199 path!["gc-heap", "decommitted"],
1200 ReportKind::ExplicitNonHeapSize,
1201 stats.gcHeapDecommitted,
1202 );
1203
1204 report(
1206 path!["malloc-heap"],
1207 ReportKind::ExplicitSystemHeapSize,
1208 stats.mallocHeap,
1209 );
1210
1211 report(
1212 path!["non-heap"],
1213 ReportKind::ExplicitNonHeapSize,
1214 stats.nonHeap,
1215 );
1216 reports
1217}
1218
1219pub(crate) struct StreamConsumer(*mut JSStreamConsumer);
1220
1221#[expect(unsafe_code)]
1222impl StreamConsumer {
1223 pub(crate) fn consume_chunk(&self, stream: &[u8]) -> bool {
1224 unsafe {
1225 let stream_ptr = stream.as_ptr();
1226 StreamConsumerConsumeChunk(self.0, stream_ptr, stream.len())
1227 }
1228 }
1229
1230 pub(crate) fn stream_end(&self) {
1231 unsafe {
1232 StreamConsumerStreamEnd(self.0);
1233 }
1234 }
1235
1236 pub(crate) fn stream_error(&self, error_code: usize) {
1237 unsafe {
1238 StreamConsumerStreamError(self.0, error_code);
1239 }
1240 }
1241
1242 pub(crate) fn note_response_urls(
1243 &self,
1244 maybe_url: Option<String>,
1245 maybe_source_map_url: Option<String>,
1246 ) {
1247 unsafe {
1248 let maybe_url = maybe_url.map(|url| CString::new(url).unwrap());
1249 let maybe_source_map_url = maybe_source_map_url.map(|url| CString::new(url).unwrap());
1250
1251 let maybe_url_param = match maybe_url.as_ref() {
1252 Some(url) => url.as_ptr(),
1253 None => ptr::null(),
1254 };
1255 let maybe_source_map_url_param = match maybe_source_map_url.as_ref() {
1256 Some(url) => url.as_ptr(),
1257 None => ptr::null(),
1258 };
1259
1260 StreamConsumerNoteResponseURLs(self.0, maybe_url_param, maybe_source_map_url_param);
1261 }
1262 }
1263}
1264
1265#[expect(unsafe_code)]
1268unsafe extern "C" fn consume_stream(
1269 cx: *mut RawJSContext,
1270 obj: HandleObject,
1271 _mime_type: MimeType,
1272 _consumer: *mut JSStreamConsumer,
1273) -> bool {
1274 let mut cx = unsafe {
1275 JSContext::from_ptr(NonNull::new(cx).expect("JSContext should not be null in SM hook"))
1277 };
1278 let cx = &mut cx;
1279 let mut realm = CurrentRealm::assert(cx);
1280 let global = GlobalScope::from_current_realm(&mut realm);
1281
1282 if let Ok(unwrapped_source) =
1284 unsafe { root_from_handleobject::<Response>(cx, RustHandleObject::from_raw(obj)) }
1285 {
1286 let mimetype = unwrapped_source.Headers(cx).extract_mime_type();
1288
1289 if !&mimetype[..].eq_ignore_ascii_case(b"application/wasm") {
1291 throw_dom_exception(
1292 cx,
1293 &global,
1294 Error::Type(c"Response has unsupported MIME type".to_owned()),
1295 );
1296 return false;
1297 }
1298
1299 match unwrapped_source.Type() {
1301 DOMResponseType::Basic | DOMResponseType::Cors | DOMResponseType::Default => {},
1302 _ => {
1303 throw_dom_exception(
1304 cx,
1305 &global,
1306 Error::Type(c"Response.type must be 'basic', 'cors' or 'default'".to_owned()),
1307 );
1308 return false;
1309 },
1310 }
1311
1312 if !unwrapped_source.Ok() {
1314 throw_dom_exception(
1315 cx,
1316 &global,
1317 Error::Type(c"Response does not have ok status".to_owned()),
1318 );
1319 return false;
1320 }
1321
1322 if unwrapped_source.is_locked() {
1324 throw_dom_exception(
1325 cx,
1326 &global,
1327 Error::Type(c"There was an error consuming the Response".to_owned()),
1328 );
1329 return false;
1330 }
1331
1332 if unwrapped_source.is_disturbed() {
1334 throw_dom_exception(
1335 cx,
1336 &global,
1337 Error::Type(c"Response already consumed".to_owned()),
1338 );
1339 return false;
1340 }
1341 unwrapped_source.set_stream_consumer(Some(StreamConsumer(_consumer)));
1342 } else {
1343 throw_dom_exception(
1345 cx,
1346 &global,
1347 Error::Type(c"expected Response or Promise resolving to Response".to_owned()),
1348 );
1349 return false;
1350 }
1351 true
1352}
1353
1354#[expect(unsafe_code)]
1355unsafe extern "C" fn report_stream_error(_cx: *mut RawJSContext, error_code: usize) {
1356 error!("Error initializing StreamConsumer: {:?}", unsafe {
1357 RUST_js_GetErrorMessage(ptr::null_mut(), error_code as u32)
1358 });
1359}
1360
1361#[expect(unsafe_code)]
1362unsafe extern "C" fn invoke_script_environment_preparer(
1363 global: HandleObject,
1364 closure: *mut ScriptEnvironmentPreparer_Closure,
1365) {
1366 let mut cx = unsafe { temp_cx() };
1368 let global = unsafe { GlobalScope::from_object(global.get()) };
1369 let mut realm = enter_auto_realm(&mut cx, &*global);
1370 let cx = &mut realm.current_realm();
1371
1372 run_a_script::<DomTypeHolder, _, _>(cx, &global, |cx| {
1373 if unsafe { !RunScriptEnvironmentPreparerClosure(cx.raw_cx(), closure) } {
1374 report_pending_exception(cx);
1375 };
1376 });
1377}
1378
1379pub(crate) struct Runnable(*mut DispatchablePointer);
1380
1381#[expect(unsafe_code)]
1382unsafe impl Sync for Runnable {}
1383#[expect(unsafe_code)]
1384unsafe impl Send for Runnable {}
1385
1386#[expect(unsafe_code)]
1387impl Runnable {
1388 fn run(&self, cx: &mut JSContext, maybe_shutting_down: Dispatchable_MaybeShuttingDown) {
1389 unsafe {
1390 DispatchableRun(cx, self.0, maybe_shutting_down);
1391 }
1392 }
1393}
1394
1395pub(crate) struct IntroductionType;
1401impl IntroductionType {
1402 pub const EVAL: &CStr = c"eval";
1404 pub const EVAL_STR: &str = "eval";
1405
1406 pub const DEBUGGER_EVAL: &CStr = c"debugger eval";
1409 pub const DEBUGGER_EVAL_STR: &str = "debugger eval";
1410
1411 pub const FUNCTION: &CStr = c"Function";
1413 pub const FUNCTION_STR: &str = "Function";
1414
1415 pub const WORKLET: &CStr = c"Worklet";
1417 pub const WORKLET_STR: &str = "Worklet";
1418
1419 pub const EVENT_HANDLER: &CStr = c"eventHandler";
1421 pub const EVENT_HANDLER_STR: &str = "eventHandler";
1422
1423 pub const SRC_SCRIPT: &CStr = c"srcScript";
1426 pub const SRC_SCRIPT_STR: &str = "srcScript";
1427
1428 pub const INLINE_SCRIPT: &CStr = c"inlineScript";
1431 pub const INLINE_SCRIPT_STR: &str = "inlineScript";
1432
1433 pub const INJECTED_SCRIPT: &CStr = c"injectedScript";
1439 pub const INJECTED_SCRIPT_STR: &str = "injectedScript";
1440
1441 pub const IMPORTED_MODULE: &CStr = c"importedModule";
1444 pub const IMPORTED_MODULE_STR: &str = "importedModule";
1445
1446 pub const JAVASCRIPT_URL: &CStr = c"javascriptURL";
1448 pub const JAVASCRIPT_URL_STR: &str = "javascriptURL";
1449
1450 pub const DOM_TIMER: &CStr = c"domTimer";
1452 pub const DOM_TIMER_STR: &str = "domTimer";
1453
1454 pub const WORKER: &CStr = c"Worker";
1458 pub const WORKER_STR: &str = "Worker";
1459}