1#![expect(dead_code)]
9
10use core::ffi::c_char;
11use std::cell::Cell;
12use std::ffi::{CStr, CString};
13use std::io::{Write, stdout};
14use std::ops::{Deref, DerefMut};
15use std::os::raw::c_void;
16use std::ptr::NonNull;
17use std::rc::{Rc, Weak};
18use std::sync::Mutex;
19use std::time::{Duration, Instant};
20use std::{os, ptr, thread};
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 CreateJobQueue, DeleteJobQueue, DispatchablePointer, JS_GetReservedSlot, JobQueueTraps,
28 RUST_js_GetErrorMessage, RegisterScriptEnvironmentPreparer,
29 RunScriptEnvironmentPreparerClosure, SetBuildId, StreamConsumerConsumeChunk,
30 StreamConsumerNoteResponseURLs, StreamConsumerStreamEnd, StreamConsumerStreamError,
31};
32use js::jsapi::{
33 AsmJSOption, BuildIdCharVector, CompilationType, Dispatchable_MaybeShuttingDown, GCDescription,
34 GCOptions, GCProgress, GCReason, GetPromiseUserInputEventHandlingState, Handle as RawHandle,
35 HandleObject, HandleString, HandleValue as RawHandleValue, Heap, JS_SetReservedSlot,
36 JSCLASS_RESERVED_SLOTS_MASK, JSCLASS_RESERVED_SLOTS_SHIFT, JSClass, JSClassOps,
37 JSContext as RawJSContext, JSGCParamKey, JSGCStatus, JSJitCompilerOption, JSObject,
38 JSSecurityCallbacks, JSString, JSTracer, JobQueue, MimeType, MutableHandleObject,
39 MutableHandleString, PromiseRejectionHandlingState, PromiseUserInputEventHandlingState,
40 RuntimeCode, ScriptEnvironmentPreparer_Closure, SetProcessBuildIdOp,
41 StreamConsumer as JSStreamConsumer,
42};
43use js::jsval::{JSVal, ObjectValue, UndefinedValue};
44use js::panic::wrap_panic;
45use js::realm::CurrentRealm;
46pub(crate) use js::rust::ThreadSafeJSContext;
47use js::rust::wrappers2::{
48 CollectServoSizes, ContextOptionsRef, DispatchableRun, InitConsumeStreamCallback,
49 JS_AddExtraGCRootsTracer, JS_GetPromiseResult, JS_InitDestroyPrincipalsCallback,
50 JS_InitReadPrincipalsCallback, JS_NewObject, JS_NewStringCopyUTF8N, JS_SetGCCallback,
51 JS_SetGCParameter, JS_SetGlobalJitCompilerOption, JS_SetOffthreadIonCompilationEnabled,
52 JS_SetSecurityCallbacks, SetDOMCallbacks, SetGCSliceCallback, SetJobQueue,
53 SetPreserveWrapperCallbacks, SetPromiseRejectionTrackerCallback, SetUpEventLoopDispatch,
54};
55use js::rust::{
56 Handle, HandleObject as RustHandleObject, HandleValue, IntoHandle, JSEngine, JSEngineError,
57 JSEngineHandle, ParentRuntime, Runtime as RustRuntime, Trace,
58};
59use malloc_size_of::MallocSizeOfOps;
60use malloc_size_of_derive::MallocSizeOf;
61use profile_traits::mem::{Report, ReportKind};
62use profile_traits::path;
63use profile_traits::time::ProfilerCategory;
64use script_bindings::reflector::DomObject;
65use script_bindings::script_runtime::{mark_runtime_dead, runtime_is_alive, temp_cx};
66use script_bindings::settings_stack::run_a_script;
67use servo_config::opts::{self, DiagnosticsLoggingOption};
68use servo_config::pref;
69use style::thread_state::{self, ThreadState};
70
71use crate::dom::bindings::codegen::Bindings::PromiseBinding::PromiseJobCallback;
72use crate::dom::bindings::codegen::Bindings::ResponseBinding::Response_Binding::ResponseMethods;
73use crate::dom::bindings::codegen::Bindings::ResponseBinding::ResponseType as DOMResponseType;
74use crate::dom::bindings::codegen::UnionTypes::TrustedScriptOrString;
75use crate::dom::bindings::conversions::{
76 get_dom_class, private_from_object, root_from_handleobject, root_from_object,
77};
78use crate::dom::bindings::error::{Error, report_pending_exception, throw_dom_exception};
79use crate::dom::bindings::inheritance::Castable;
80use crate::dom::bindings::refcounted::{
81 LiveDOMReferences, Trusted, TrustedPromise, trace_refcounted_objects,
82};
83use crate::dom::bindings::reflector::DomGlobal;
84use crate::dom::bindings::root::trace_roots;
85use crate::dom::bindings::str::DOMString;
86use crate::dom::bindings::utils::DOM_CALLBACKS;
87use crate::dom::bindings::{principals, settings_stack};
88use crate::dom::console::stringify_handle_value;
89use crate::dom::csp::CspReporting;
90use crate::dom::event::{Event, EventBubbles, EventCancelable};
91use crate::dom::eventtarget::EventTarget;
92use crate::dom::globalscope::GlobalScope;
93use crate::dom::promise::Promise;
94use crate::dom::promiserejectionevent::PromiseRejectionEvent;
95use crate::dom::response::Response;
96use crate::dom::trustedtypes::trustedscript::TrustedScript;
97use crate::messaging::{CommonScriptMsg, ScriptEventLoopSender};
98use crate::microtask::{EnqueuedPromiseCallback, MicrotaskQueue};
99use crate::modules::script_module::EnsureModuleHooksInitialized;
100use crate::realms::enter_auto_realm;
101use crate::tasks::task_source::TaskSourceName;
102use crate::{DomTypeHolder, ScriptThread};
103
104static JOB_QUEUE_TRAPS: JobQueueTraps = JobQueueTraps {
105 getHostDefinedData: Some(get_host_defined_data),
106 enqueuePromiseJob: Some(enqueue_promise_job),
107 runJobs: Some(run_jobs),
108 empty: Some(empty),
109 pushNewInterruptQueue: Some(push_new_interrupt_queue),
110 popInterruptQueue: Some(pop_interrupt_queue),
111 dropInterruptQueues: Some(drop_interrupt_queues),
112};
113
114static SECURITY_CALLBACKS: JSSecurityCallbacks = JSSecurityCallbacks {
115 contentSecurityPolicyAllows: Some(content_security_policy_allows),
116 codeForEvalGets: Some(code_for_eval_gets),
117 subsumes: Some(principals::subsumes),
118};
119
120#[derive(Clone, Copy, Debug, Eq, Hash, JSTraceable, MallocSizeOf, PartialEq)]
121pub(crate) enum ScriptThreadEventCategory {
122 SpawnPipeline,
123 ConstellationMsg,
124 DatabaseAccessEvent,
125 DevtoolsMsg,
126 DocumentEvent,
127 FileRead,
128 FontLoading,
129 FormPlannedNavigation,
130 GeolocationEvent,
131 ImageCacheMsg,
132 InputEvent,
133 NavigationAndTraversalEvent,
134 NetworkEvent,
135 PortMessage,
136 Rendering,
137 Resize,
138 ScriptEvent,
139 SetScrollState,
140 SetViewport,
141 StylesheetLoad,
142 TimerEvent,
143 UpdateReplacedElement,
144 WebSocketEvent,
145 WorkerEvent,
146 WorkletEvent,
147 ServiceWorkerEvent,
148 EnterFullscreen,
149 ExitFullscreen,
150 PerformanceTimelineTask,
151 #[cfg(feature = "webgpu")]
152 WebGPUMsg,
153}
154
155impl From<ScriptThreadEventCategory> for ProfilerCategory {
156 fn from(category: ScriptThreadEventCategory) -> Self {
157 match category {
158 ScriptThreadEventCategory::SpawnPipeline => ProfilerCategory::ScriptSpawnPipeline,
159 ScriptThreadEventCategory::ConstellationMsg => ProfilerCategory::ScriptConstellationMsg,
160 ScriptThreadEventCategory::DatabaseAccessEvent => {
161 ProfilerCategory::ScriptDatabaseAccessEvent
162 },
163 ScriptThreadEventCategory::DevtoolsMsg => ProfilerCategory::ScriptDevtoolsMsg,
164 ScriptThreadEventCategory::DocumentEvent => ProfilerCategory::ScriptDocumentEvent,
165 ScriptThreadEventCategory::EnterFullscreen => ProfilerCategory::ScriptEnterFullscreen,
166 ScriptThreadEventCategory::ExitFullscreen => ProfilerCategory::ScriptExitFullscreen,
167 ScriptThreadEventCategory::FileRead => ProfilerCategory::ScriptFileRead,
168 ScriptThreadEventCategory::FontLoading => ProfilerCategory::ScriptFontLoading,
169 ScriptThreadEventCategory::FormPlannedNavigation => {
170 ProfilerCategory::ScriptPlannedNavigation
171 },
172 ScriptThreadEventCategory::GeolocationEvent => ProfilerCategory::ScriptGeolocationEvent,
173 ScriptThreadEventCategory::NavigationAndTraversalEvent => {
174 ProfilerCategory::ScriptNavigationAndTraversalEvent
175 },
176 ScriptThreadEventCategory::ImageCacheMsg => ProfilerCategory::ScriptImageCacheMsg,
177 ScriptThreadEventCategory::InputEvent => ProfilerCategory::ScriptInputEvent,
178 ScriptThreadEventCategory::NetworkEvent => ProfilerCategory::ScriptNetworkEvent,
179 ScriptThreadEventCategory::PerformanceTimelineTask => {
180 ProfilerCategory::ScriptPerformanceEvent
181 },
182 ScriptThreadEventCategory::PortMessage => ProfilerCategory::ScriptPortMessage,
183 ScriptThreadEventCategory::Resize => ProfilerCategory::ScriptResize,
184 ScriptThreadEventCategory::Rendering => ProfilerCategory::ScriptRendering,
185 ScriptThreadEventCategory::ScriptEvent => ProfilerCategory::ScriptEvent,
186 ScriptThreadEventCategory::ServiceWorkerEvent => {
187 ProfilerCategory::ScriptServiceWorkerEvent
188 },
189 ScriptThreadEventCategory::SetScrollState => ProfilerCategory::ScriptSetScrollState,
190 ScriptThreadEventCategory::SetViewport => ProfilerCategory::ScriptSetViewport,
191 ScriptThreadEventCategory::StylesheetLoad => ProfilerCategory::ScriptStylesheetLoad,
192 ScriptThreadEventCategory::TimerEvent => ProfilerCategory::ScriptTimerEvent,
193 ScriptThreadEventCategory::UpdateReplacedElement => {
194 ProfilerCategory::ScriptUpdateReplacedElement
195 },
196 ScriptThreadEventCategory::WebSocketEvent => ProfilerCategory::ScriptWebSocketEvent,
197 ScriptThreadEventCategory::WorkerEvent => ProfilerCategory::ScriptWorkerEvent,
198 ScriptThreadEventCategory::WorkletEvent => ProfilerCategory::ScriptWorkletEvent,
199 #[cfg(feature = "webgpu")]
200 ScriptThreadEventCategory::WebGPUMsg => ProfilerCategory::ScriptWebGPUMsg,
201 }
202 }
203}
204
205impl From<ScriptThreadEventCategory> for ScriptHangAnnotation {
206 fn from(category: ScriptThreadEventCategory) -> Self {
207 match category {
208 ScriptThreadEventCategory::SpawnPipeline => ScriptHangAnnotation::SpawnPipeline,
209 ScriptThreadEventCategory::ConstellationMsg => ScriptHangAnnotation::ConstellationMsg,
210 ScriptThreadEventCategory::DatabaseAccessEvent => {
211 ScriptHangAnnotation::DatabaseAccessEvent
212 },
213 ScriptThreadEventCategory::DevtoolsMsg => ScriptHangAnnotation::DevtoolsMsg,
214 ScriptThreadEventCategory::DocumentEvent => ScriptHangAnnotation::DocumentEvent,
215 ScriptThreadEventCategory::InputEvent => ScriptHangAnnotation::InputEvent,
216 ScriptThreadEventCategory::FileRead => ScriptHangAnnotation::FileRead,
217 ScriptThreadEventCategory::FontLoading => ScriptHangAnnotation::FontLoading,
218 ScriptThreadEventCategory::FormPlannedNavigation => {
219 ScriptHangAnnotation::FormPlannedNavigation
220 },
221 ScriptThreadEventCategory::GeolocationEvent => ScriptHangAnnotation::GeolocationEvent,
222 ScriptThreadEventCategory::NavigationAndTraversalEvent => {
223 ScriptHangAnnotation::NavigationAndTraversalEvent
224 },
225 ScriptThreadEventCategory::ImageCacheMsg => ScriptHangAnnotation::ImageCacheMsg,
226 ScriptThreadEventCategory::NetworkEvent => ScriptHangAnnotation::NetworkEvent,
227 ScriptThreadEventCategory::Rendering => ScriptHangAnnotation::Rendering,
228 ScriptThreadEventCategory::Resize => ScriptHangAnnotation::Resize,
229 ScriptThreadEventCategory::ScriptEvent => ScriptHangAnnotation::ScriptEvent,
230 ScriptThreadEventCategory::SetScrollState => ScriptHangAnnotation::SetScrollState,
231 ScriptThreadEventCategory::SetViewport => ScriptHangAnnotation::SetViewport,
232 ScriptThreadEventCategory::StylesheetLoad => ScriptHangAnnotation::StylesheetLoad,
233 ScriptThreadEventCategory::TimerEvent => ScriptHangAnnotation::TimerEvent,
234 ScriptThreadEventCategory::UpdateReplacedElement => {
235 ScriptHangAnnotation::UpdateReplacedElement
236 },
237 ScriptThreadEventCategory::WebSocketEvent => ScriptHangAnnotation::WebSocketEvent,
238 ScriptThreadEventCategory::WorkerEvent => ScriptHangAnnotation::WorkerEvent,
239 ScriptThreadEventCategory::WorkletEvent => ScriptHangAnnotation::WorkletEvent,
240 ScriptThreadEventCategory::ServiceWorkerEvent => {
241 ScriptHangAnnotation::ServiceWorkerEvent
242 },
243 ScriptThreadEventCategory::EnterFullscreen => ScriptHangAnnotation::EnterFullscreen,
244 ScriptThreadEventCategory::ExitFullscreen => ScriptHangAnnotation::ExitFullscreen,
245 ScriptThreadEventCategory::PerformanceTimelineTask => {
246 ScriptHangAnnotation::PerformanceTimelineTask
247 },
248 ScriptThreadEventCategory::PortMessage => ScriptHangAnnotation::PortMessage,
249 #[cfg(feature = "webgpu")]
250 ScriptThreadEventCategory::WebGPUMsg => ScriptHangAnnotation::WebGPUMsg,
251 }
252 }
253}
254
255static HOST_DEFINED_DATA: JSClassOps = JSClassOps {
256 addProperty: None,
257 delProperty: None,
258 enumerate: None,
259 newEnumerate: None,
260 resolve: None,
261 mayResolve: None,
262 finalize: None,
263 call: None,
264 construct: None,
265 trace: None,
266};
267
268static HOST_DEFINED_DATA_CLASS: JSClass = JSClass {
269 name: c"HostDefinedData".as_ptr(),
270 flags: (HOST_DEFINED_DATA_SLOTS & JSCLASS_RESERVED_SLOTS_MASK) << JSCLASS_RESERVED_SLOTS_SHIFT,
271 cOps: &HOST_DEFINED_DATA,
272 spec: ptr::null(),
273 ext: ptr::null(),
274 oOps: ptr::null(),
275};
276
277const INCUMBENT_SETTING_SLOT: u32 = 0;
278const HOST_DEFINED_DATA_SLOTS: u32 = 1;
279
280#[expect(unsafe_code)]
282unsafe extern "C" fn get_host_defined_data(
283 _: *const c_void,
284 cx: *mut RawJSContext,
285 data: MutableHandleObject,
286) -> bool {
287 let mut cx = unsafe {
288 JSContext::from_ptr(NonNull::new(cx).expect("JSContext should not be null in SM hook"))
290 };
291 wrap_panic(&mut || {
292 let Some(incumbent_global) = GlobalScope::incumbent() else {
293 data.set(ptr::null_mut());
294 return;
295 };
296
297 let mut realm = enter_auto_realm(&mut cx, &*incumbent_global);
298 let cx = &mut realm.current_realm();
299
300 rooted!(&in(cx) let result = unsafe { JS_NewObject(cx, &HOST_DEFINED_DATA_CLASS)});
301 assert!(!result.is_null());
302
303 unsafe {
304 JS_SetReservedSlot(
305 *result,
306 INCUMBENT_SETTING_SLOT,
307 &ObjectValue(*incumbent_global.reflector().get_jsobject()),
308 )
309 };
310
311 data.set(result.get());
312 });
313 true
314}
315
316#[expect(unsafe_code)]
317unsafe extern "C" fn run_jobs(microtask_queue: *const c_void, cx: *mut RawJSContext) {
318 let mut cx = unsafe {
319 JSContext::from_ptr(NonNull::new(cx).expect("JSContext should not be null in SM hook"))
321 };
322 wrap_panic(&mut || {
323 let microtask_queue = unsafe { &*(microtask_queue as *const MicrotaskQueue) };
324 microtask_queue.checkpoint(&mut cx, vec![]);
327 });
328}
329
330#[expect(unsafe_code)]
331unsafe extern "C" fn empty(extra: *const c_void) -> bool {
332 let mut result = false;
333 wrap_panic(&mut || {
334 let microtask_queue = unsafe { &*(extra as *const MicrotaskQueue) };
335 result = microtask_queue.empty()
336 });
337 result
338}
339
340#[expect(unsafe_code)]
341unsafe extern "C" fn push_new_interrupt_queue(interrupt_queues: *mut c_void) -> *const c_void {
342 let mut result = std::ptr::null();
343 wrap_panic(&mut || {
344 let mut interrupt_queues =
345 unsafe { Box::from_raw(interrupt_queues as *mut Vec<Rc<MicrotaskQueue>>) };
346 let new_queue = Rc::new(MicrotaskQueue::default());
347 result = Rc::as_ptr(&new_queue) as *const c_void;
348 interrupt_queues.push(new_queue);
349 std::mem::forget(interrupt_queues);
350 });
351 result
352}
353
354#[expect(unsafe_code)]
355unsafe extern "C" fn pop_interrupt_queue(interrupt_queues: *mut c_void) -> *const c_void {
356 let mut result = std::ptr::null();
357 wrap_panic(&mut || {
358 let mut interrupt_queues =
359 unsafe { Box::from_raw(interrupt_queues as *mut Vec<Rc<MicrotaskQueue>>) };
360 let popped_queue: Rc<MicrotaskQueue> =
361 interrupt_queues.pop().expect("Guaranteed by SpiderMonkey?");
362 result = Rc::as_ptr(&popped_queue) as *const c_void;
364 std::mem::forget(interrupt_queues);
365 });
366 result
367}
368
369#[expect(unsafe_code)]
370unsafe extern "C" fn drop_interrupt_queues(interrupt_queues: *mut c_void) {
371 wrap_panic(&mut || {
372 let interrupt_queues =
373 unsafe { Box::from_raw(interrupt_queues as *mut Vec<Rc<MicrotaskQueue>>) };
374 drop(interrupt_queues);
375 });
376}
377
378#[expect(unsafe_code)]
382unsafe extern "C" fn enqueue_promise_job(
383 extra: *const c_void,
384 cx: *mut RawJSContext,
385 promise: HandleObject,
386 job: HandleObject,
387 _allocation_site: HandleObject,
388 host_defined_data: HandleObject,
389) -> bool {
390 let mut cx = unsafe { JSContext::from_ptr(NonNull::new(cx).unwrap()) };
392 let cx = &mut cx;
393
394 let mut result = false;
395 wrap_panic(&mut || {
396 let microtask_queue = unsafe { &*(extra as *const MicrotaskQueue) };
397 let global = if !host_defined_data.is_null() {
398 let mut incumbent_global = UndefinedValue();
399 unsafe {
400 JS_GetReservedSlot(
401 host_defined_data.get(),
402 INCUMBENT_SETTING_SLOT,
403 &mut incumbent_global,
404 );
405 GlobalScope::from_object(incumbent_global.to_object())
406 }
407 } else {
408 let mut realm = CurrentRealm::assert(cx);
409 GlobalScope::from_current_realm(&mut realm)
410 };
411 let interaction = if promise.get().is_null() {
412 PromiseUserInputEventHandlingState::DontCare
413 } else {
414 unsafe { GetPromiseUserInputEventHandlingState(promise) }
415 };
416 let is_user_interacting =
417 interaction == PromiseUserInputEventHandlingState::HadUserInteractionAtCreation;
418 microtask_queue.enqueue(
419 cx,
420 Box::new(EnqueuedPromiseCallback {
421 callback: unsafe { PromiseJobCallback::new(cx, job.get()) },
422 global: global.as_traced(),
423 is_user_interacting,
424 }),
425 );
426 result = true
427 });
428 result
429}
430
431#[expect(unsafe_code)]
432unsafe extern "C" fn promise_rejection_tracker(
434 cx: *mut RawJSContext,
435 muted_errors: bool,
436 promise: HandleObject,
437 state: PromiseRejectionHandlingState,
438 _data: *mut c_void,
439) {
440 if muted_errors {
443 return;
444 }
445
446 let mut cx = unsafe { JSContext::from_ptr(NonNull::new(cx).unwrap()) };
449 let mut realm = CurrentRealm::assert(&mut cx);
450
451 let global = GlobalScope::from_current_realm(&mut realm);
452 let cx = &mut realm;
453
454 wrap_panic(&mut || {
455 match state {
456 PromiseRejectionHandlingState::Unhandled => {
458 global.add_uncaught_rejection(promise);
459 },
460 PromiseRejectionHandlingState::Handled => {
462 if global
464 .get_uncaught_rejections()
465 .borrow()
466 .contains(&Heap::boxed(promise.get()))
467 {
468 global.remove_uncaught_rejection(promise);
469 return;
470 }
471
472 if !global
474 .get_consumed_rejections()
475 .borrow()
476 .contains(&Heap::boxed(promise.get()))
477 {
478 return;
479 }
480
481 global.remove_consumed_rejection(promise);
483
484 let target = Trusted::new(global.upcast::<EventTarget>());
485 let promise =
486 Promise::new_with_js_promise(cx, unsafe { Handle::from_raw(promise) });
487 let trusted_promise = TrustedPromise::new(promise);
488
489 global.task_manager().dom_manipulation_task_source().queue(
491 task!(rejection_handled_event: move |cx| {
492 let target = target.root();
493 let root_promise = trusted_promise.root();
494
495 rooted!(&in(cx) let mut reason = UndefinedValue());
496 unsafe {
497 JS_GetPromiseResult(root_promise.reflector().get_jsobject(), reason.handle_mut());
498 }
499
500 let event = PromiseRejectionEvent::new(
501 cx,
502 &target.global(),
503 atom!("rejectionhandled"),
504 EventBubbles::DoesNotBubble,
505 EventCancelable::Cancelable,
506 root_promise,
507 reason.handle(),
508 );
509
510 event.upcast::<Event>().fire(cx, &target);
511 })
512 );
513 },
514 };
515 })
516}
517
518#[expect(unsafe_code)]
519fn safely_convert_null_to_string(cx: &JSContext, str_: HandleString) -> DOMString {
520 DOMString::from(match std::ptr::NonNull::new(*str_) {
521 None => "".to_owned(),
522 Some(str_) => unsafe { jsstr_to_string(cx, str_) },
523 })
524}
525
526#[expect(unsafe_code)]
527unsafe extern "C" fn code_for_eval_gets(
528 cx: *mut RawJSContext,
529 code: HandleObject,
530 code_for_eval: MutableHandleString,
531) -> bool {
532 let mut cx = unsafe { JSContext::from_ptr(NonNull::new(cx).unwrap()) };
534 let cx = &mut cx;
535 if let Ok(trusted_script) = unsafe { root_from_object::<TrustedScript>(cx, code.get()) } {
536 let script_str = trusted_script.data().str();
537 let s = js::conversions::Utf8Chars::from(&*script_str);
538 let new_string = unsafe { JS_NewStringCopyUTF8N(cx, &*s as *const _) };
539 code_for_eval.set(new_string);
540 }
541 true
542}
543
544#[expect(unsafe_code)]
545unsafe extern "C" fn content_security_policy_allows(
546 cx: *mut RawJSContext,
547 runtime_code: RuntimeCode,
548 code_string: HandleString,
549 compilation_type: CompilationType,
550 parameter_strings: RawHandle<StackGCVector<*mut JSString>>,
551 body_string: HandleString,
552 parameter_args: RawHandle<StackGCVector<JSVal>>,
553 body_arg: RawHandleValue,
554 can_compile_strings: *mut bool,
555) -> bool {
556 let mut allowed = false;
557 let mut cx = unsafe { JSContext::from_ptr(NonNull::new(cx).unwrap()) };
559 let cx = &mut cx;
560 wrap_panic(&mut || {
561 let mut realm = CurrentRealm::assert(cx);
563 let global = GlobalScope::from_current_realm(&mut realm);
564 let csp_list = global.get_csp_list();
565
566 allowed = csp_list.is_none() ||
568 match runtime_code {
569 RuntimeCode::JS => {
570 let parameter_strings = unsafe { Handle::from_raw(parameter_strings) };
571 let parameter_strings_length = parameter_strings.len();
572 let mut parameter_strings_vec =
573 Vec::with_capacity(parameter_strings_length as usize);
574
575 for i in 0..parameter_strings_length {
576 let Some(str_) = parameter_strings.at(i) else {
577 unreachable!();
578 };
579 parameter_strings_vec.push(safely_convert_null_to_string(cx, str_.into()));
580 }
581
582 let parameter_args = unsafe { Handle::from_raw(parameter_args) };
583 let parameter_args_length = parameter_args.len();
584 let mut parameter_args_vec = Vec::with_capacity(parameter_args_length as usize);
585
586 for i in 0..parameter_args_length {
587 let Some(arg) = parameter_args.at(i) else {
588 unreachable!();
589 };
590 let value = arg.into_handle().get();
591 if value.is_object() {
592 if let Ok(trusted_script) =
593 unsafe { root_from_object::<TrustedScript>(cx, value.to_object()) }
594 {
595 parameter_args_vec
596 .push(TrustedScriptOrString::TrustedScript(trusted_script));
597 } else {
598 parameter_args_vec
602 .push(TrustedScriptOrString::String(DOMString::new()));
603 }
604 } else if value.is_string() {
605 parameter_args_vec
607 .push(TrustedScriptOrString::String(DOMString::new()));
608 } else {
609 unreachable!();
610 }
611 }
612
613 let code_string = safely_convert_null_to_string(cx, code_string);
614 let body_string = safely_convert_null_to_string(cx, body_string);
615
616 TrustedScript::can_compile_string_with_trusted_type(
617 cx,
618 &global,
619 code_string,
620 compilation_type,
621 parameter_strings_vec,
622 body_string,
623 parameter_args_vec,
624 unsafe { HandleValue::from_raw(body_arg) },
625 )
626 },
627 RuntimeCode::WASM => global
628 .get_csp_list()
629 .is_wasm_evaluation_allowed(cx, &global),
630 };
631 });
632 unsafe { *can_compile_strings = allowed };
633 true
634}
635
636#[expect(unsafe_code)]
637pub(crate) fn notify_about_rejected_promises(cx: &mut JSContext, global: &GlobalScope) {
639 let uncaught_rejections: Vec<TrustedPromise> = global
641 .get_uncaught_rejections()
642 .borrow_mut()
643 .drain(..)
644 .map(|promise| {
645 let promise =
646 Promise::new_with_js_promise(cx, unsafe { Handle::from_raw(promise.handle()) });
647
648 TrustedPromise::new(promise)
649 })
650 .collect();
651
652 if uncaught_rejections.is_empty() {
654 return;
655 }
656
657 let target = Trusted::new(global.upcast::<EventTarget>());
662 global.task_manager().dom_manipulation_task_source().queue(
663 task!(unhandled_rejection_event: move |cx| {
664 let target = target.root();
665
666 for promise in uncaught_rejections {
668 let promise = promise.root();
669
670 if promise.get_promise_is_handled() {
672 continue;
673 }
674
675 rooted!(&in(cx) let mut reason = UndefinedValue());
679 unsafe {
680 JS_GetPromiseResult(promise.reflector().get_jsobject(), reason.handle_mut());
681 }
682
683 log::error!(
684 "Unhandled promise rejection: {}",
685 stringify_handle_value( cx, reason.handle())
686 );
687
688 let event = PromiseRejectionEvent::new(
689 cx,
690 &target.global(),
691 atom!("unhandledrejection"),
692 EventBubbles::DoesNotBubble,
693 EventCancelable::Cancelable,
694 promise.clone(),
695 reason.handle(),
696 );
697 event.upcast::<Event>().fire(cx, &target);
698
699 if !promise.get_promise_is_handled() {
705 target.global().add_consumed_rejection(promise.reflector().get_jsobject().into_handle());
706 }
707 }
708 })
709 );
710}
711
712#[derive(Default, JSTraceable, MallocSizeOf)]
715struct RuntimeCallbackData {
716 script_event_loop_sender: Option<ScriptEventLoopSender>,
717 #[no_trace]
718 #[ignore_malloc_size_of = "ScriptThread measures its own memory itself."]
719 script_thread: Option<Weak<ScriptThread>>,
720}
721
722#[derive(JSTraceable, MallocSizeOf)]
723pub(crate) struct Runtime {
724 #[ignore_malloc_size_of = "Type from mozjs"]
725 rt: RustRuntime,
726 #[conditional_malloc_size_of]
728 pub(crate) microtask_queue: Rc<MicrotaskQueue>,
729 #[ignore_malloc_size_of = "Type from mozjs"]
730 job_queue: *mut JobQueue,
731 runtime_callback_data: Box<RuntimeCallbackData>,
733}
734
735impl Runtime {
736 #[expect(unsafe_code)]
746 pub(crate) fn new(main_thread_sender: Option<ScriptEventLoopSender>) -> Runtime {
747 unsafe { Self::new_with_parent(None, main_thread_sender) }
748 }
749
750 #[allow(unsafe_code)]
751 pub(crate) unsafe fn cx(&self) -> JSContext {
755 unsafe { JSContext::from_ptr(RustRuntime::get().unwrap()) }
756 }
757
758 #[expect(unsafe_code)]
771 pub(crate) unsafe fn new_with_parent(
772 parent: Option<ParentRuntime>,
773 script_event_loop_sender: Option<ScriptEventLoopSender>,
774 ) -> Runtime {
775 let mut runtime = if let Some(parent) = parent {
776 unsafe { RustRuntime::create_with_parent(parent) }
777 } else {
778 RustRuntime::new(JS_ENGINE.lock().unwrap().as_ref().unwrap().clone())
779 };
780 let cx = runtime.cx();
781
782 let have_event_loop_sender = script_event_loop_sender.is_some();
783 let runtime_callback_data = Box::new(RuntimeCallbackData {
784 script_event_loop_sender,
785 script_thread: None,
786 });
787 let runtime_callback_data = Box::into_raw(runtime_callback_data);
788
789 unsafe {
790 JS_AddExtraGCRootsTracer(
791 cx,
792 Some(trace_rust_roots),
793 runtime_callback_data as *mut c_void,
794 );
795
796 JS_SetSecurityCallbacks(cx, &SECURITY_CALLBACKS);
797
798 JS_InitDestroyPrincipalsCallback(cx, Some(principals::destroy_servo_jsprincipal));
799 JS_InitReadPrincipalsCallback(cx, Some(principals::read_jsprincipal));
800
801 if cfg!(debug_assertions) {
803 JS_SetGCCallback(cx, Some(debug_gc_callback), ptr::null_mut());
804 }
805
806 if opts::get()
807 .debug
808 .is_enabled(DiagnosticsLoggingOption::GcProfile)
809 {
810 SetGCSliceCallback(cx, Some(gc_slice_callback));
811 }
812 }
813
814 unsafe extern "C" fn empty_wrapper_callback(_: *mut RawJSContext, _: HandleObject) -> bool {
815 true
816 }
817 unsafe extern "C" fn empty_has_released_callback(_: HandleObject) -> bool {
818 false
820 }
821
822 unsafe {
823 SetDOMCallbacks(cx, &DOM_CALLBACKS);
824 SetPreserveWrapperCallbacks(
825 cx,
826 Some(empty_wrapper_callback),
827 Some(empty_has_released_callback),
828 );
829 }
830
831 unsafe extern "C" fn dispatch_to_event_loop(
832 data: *mut c_void,
833 dispatchable: *mut DispatchablePointer,
834 ) -> bool {
835 let runtime_callback_data: &RuntimeCallbackData =
836 unsafe { &*(data as *mut RuntimeCallbackData) };
837 let Some(script_event_loop_sender) =
838 runtime_callback_data.script_event_loop_sender.as_ref()
839 else {
840 return false;
841 };
842
843 let runnable = Runnable(dispatchable);
844 let task = task!(dispatch_to_event_loop_message: move |cx| {
845 runnable.run(cx, Dispatchable_MaybeShuttingDown::NotShuttingDown);
846 });
847
848 script_event_loop_sender
849 .send(CommonScriptMsg::Task(
850 ScriptThreadEventCategory::NetworkEvent,
851 Box::new(task),
852 None, TaskSourceName::Networking,
854 ))
855 .is_ok()
856 }
857
858 if have_event_loop_sender {
859 unsafe {
860 SetUpEventLoopDispatch(
861 cx,
862 Some(dispatch_to_event_loop),
863 runtime_callback_data as *mut c_void,
864 );
865 }
866 }
867
868 unsafe {
869 InitConsumeStreamCallback(cx, Some(consume_stream), Some(report_stream_error));
870 }
871
872 let microtask_queue = Rc::new(MicrotaskQueue::default());
873
874 let interrupt_queues: Box<Vec<Rc<MicrotaskQueue>>> = Box::default();
878
879 let cx_opts;
880 let job_queue;
881 unsafe {
882 let cx = runtime.cx();
883 job_queue = CreateJobQueue(
884 &JOB_QUEUE_TRAPS,
885 &*microtask_queue as *const _ as *const c_void,
886 Box::into_raw(interrupt_queues) as *mut c_void,
887 );
888 SetJobQueue(cx, job_queue);
889 SetPromiseRejectionTrackerCallback(
890 cx,
891 Some(promise_rejection_tracker),
892 ptr::null_mut(),
893 );
894
895 RegisterScriptEnvironmentPreparer(
896 cx.raw_cx(),
897 Some(invoke_script_environment_preparer),
898 );
899
900 EnsureModuleHooksInitialized(runtime.rt());
901
902 let cx = runtime.cx();
903
904 set_gc_zeal_options(cx.raw_cx());
905
906 cx_opts = &mut *ContextOptionsRef(cx);
908 JS_SetGlobalJitCompilerOption(
909 cx,
910 JSJitCompilerOption::JSJITCOMPILER_BASELINE_INTERPRETER_ENABLE,
911 pref!(js_baseline_interpreter_enabled) as u32,
912 );
913 JS_SetGlobalJitCompilerOption(
914 cx,
915 JSJitCompilerOption::JSJITCOMPILER_BASELINE_ENABLE,
916 pref!(js_baseline_jit_enabled) as u32,
917 );
918 JS_SetGlobalJitCompilerOption(
919 cx,
920 JSJitCompilerOption::JSJITCOMPILER_ION_ENABLE,
921 pref!(js_ion_enabled) as u32,
922 );
923 }
924 cx_opts.compileOptions_.asmJSOption_ = if pref!(js_asmjs_enabled) {
925 AsmJSOption::Enabled
926 } else {
927 AsmJSOption::DisabledByAsmJSPref
928 };
929 cx_opts.compileOptions_.set_importAttributes_(true);
930 let wasm_enabled = pref!(js_wasm_enabled);
931 cx_opts.set_wasm_(wasm_enabled);
932 if wasm_enabled {
933 unsafe { SetProcessBuildIdOp(Some(servo_build_id)) };
937 }
938 cx_opts.set_wasmBaseline_(pref!(js_wasm_baseline_enabled));
939 cx_opts.set_wasmIon_(pref!(js_wasm_ion_enabled));
940
941 unsafe {
942 let cx = runtime.cx();
943 JS_SetGlobalJitCompilerOption(
945 cx,
946 JSJitCompilerOption::JSJITCOMPILER_NATIVE_REGEXP_ENABLE,
947 pref!(js_native_regex_enabled) as u32,
948 );
949 JS_SetOffthreadIonCompilationEnabled(cx, pref!(js_offthread_compilation_enabled));
950 JS_SetGlobalJitCompilerOption(
951 cx,
952 JSJitCompilerOption::JSJITCOMPILER_BASELINE_WARMUP_TRIGGER,
953 if pref!(js_baseline_jit_unsafe_eager_compilation_enabled) {
954 0
955 } else {
956 u32::MAX
957 },
958 );
959 JS_SetGlobalJitCompilerOption(
960 cx,
961 JSJitCompilerOption::JSJITCOMPILER_ION_NORMAL_WARMUP_TRIGGER,
962 if pref!(js_ion_unsafe_eager_compilation_enabled) {
963 0
964 } else {
965 u32::MAX
966 },
967 );
968 JS_SetGCParameter(
974 cx,
975 JSGCParamKey::JSGC_MAX_BYTES,
976 in_range(pref!(js_mem_max), 1, 0x100)
977 .map(|val| (val * 1024 * 1024) as u32)
978 .unwrap_or(u32::MAX),
979 );
980
981 JS_SetGCParameter(
984 cx,
985 JSGCParamKey::JSGC_INCREMENTAL_GC_ENABLED,
986 pref!(js_mem_gc_incremental_enabled) as u32,
987 );
988
989 JS_SetGCParameter(
990 cx,
991 JSGCParamKey::JSGC_PER_ZONE_GC_ENABLED,
992 pref!(js_mem_gc_per_zone_enabled) as u32,
993 );
994 if let Some(val) = in_range(pref!(js_mem_gc_incremental_slice_ms), 0, 100_000) {
995 JS_SetGCParameter(cx, JSGCParamKey::JSGC_SLICE_TIME_BUDGET_MS, val as u32);
996 }
997 JS_SetGCParameter(
998 cx,
999 JSGCParamKey::JSGC_COMPACTING_ENABLED,
1000 pref!(js_mem_gc_compacting_enabled) as u32,
1001 );
1002
1003 if let Some(val) = in_range(pref!(js_mem_gc_high_frequency_time_limit_ms), 0, 10_000) {
1004 JS_SetGCParameter(cx, JSGCParamKey::JSGC_HIGH_FREQUENCY_TIME_LIMIT, val as u32);
1005 }
1006 if let Some(val) = in_range(pref!(js_mem_gc_low_frequency_heap_growth), 0, 10_000) {
1007 JS_SetGCParameter(cx, JSGCParamKey::JSGC_LOW_FREQUENCY_HEAP_GROWTH, val as u32);
1008 }
1009 if let Some(val) = in_range(pref!(js_mem_gc_high_frequency_heap_growth_min), 0, 10_000)
1010 {
1011 JS_SetGCParameter(
1012 cx,
1013 JSGCParamKey::JSGC_HIGH_FREQUENCY_LARGE_HEAP_GROWTH,
1014 val as u32,
1015 );
1016 }
1017 if let Some(val) = in_range(pref!(js_mem_gc_high_frequency_heap_growth_max), 0, 10_000)
1018 {
1019 JS_SetGCParameter(
1020 cx,
1021 JSGCParamKey::JSGC_HIGH_FREQUENCY_SMALL_HEAP_GROWTH,
1022 val as u32,
1023 );
1024 }
1025 if let Some(val) = in_range(pref!(js_mem_gc_high_frequency_low_limit_mb), 0, 10_000) {
1026 JS_SetGCParameter(cx, JSGCParamKey::JSGC_SMALL_HEAP_SIZE_MAX, val as u32);
1027 }
1028 if let Some(val) = in_range(pref!(js_mem_gc_high_frequency_high_limit_mb), 0, 10_000) {
1029 JS_SetGCParameter(cx, JSGCParamKey::JSGC_LARGE_HEAP_SIZE_MIN, val as u32);
1030 }
1031 if let Some(val) = in_range(pref!(js_mem_gc_empty_chunk_count_min), 0, 10_000) {
1032 JS_SetGCParameter(cx, JSGCParamKey::JSGC_MIN_EMPTY_CHUNK_COUNT, val as u32);
1033 }
1034 }
1035 Runtime {
1036 rt: runtime,
1037 microtask_queue,
1038 job_queue,
1039 runtime_callback_data: unsafe { Box::from_raw(runtime_callback_data) },
1040 }
1041 }
1042
1043 pub(crate) fn set_script_thread(&mut self, script_thread: Weak<ScriptThread>) {
1044 self.runtime_callback_data
1045 .script_thread
1046 .replace(script_thread);
1047 }
1048
1049 pub(crate) fn thread_safe_js_context(&self) -> ThreadSafeJSContext {
1050 self.rt.thread_safe_js_context()
1051 }
1052}
1053
1054impl Drop for Runtime {
1055 #[expect(unsafe_code)]
1056 fn drop(&mut self) {
1057 self.microtask_queue.clear();
1059
1060 unsafe {
1062 DeleteJobQueue(self.job_queue);
1063 }
1064 LiveDOMReferences::destruct();
1065 mark_runtime_dead();
1066 }
1067}
1068
1069impl Deref for Runtime {
1070 type Target = RustRuntime;
1071 fn deref(&self) -> &RustRuntime {
1072 &self.rt
1073 }
1074}
1075
1076impl DerefMut for Runtime {
1077 fn deref_mut(&mut self) -> &mut RustRuntime {
1078 &mut self.rt
1079 }
1080}
1081
1082pub struct JSEngineSetup(Option<JSEngine>);
1083
1084impl Default for JSEngineSetup {
1085 fn default() -> Self {
1086 let engine = match JSEngine::init() {
1102 Ok(engine) => {
1103 *JS_ENGINE.lock().unwrap() = Some(engine.handle());
1104 Some(engine)
1105 }
1106 Err(JSEngineError::AlreadyInitialized) => {
1107 let mut attempts = 0;
1112 loop {
1113 if let Some(h) = JSEngine::process_handle() {
1114 let mut slot = JS_ENGINE.lock().unwrap();
1115 if slot.is_none() {
1116 *slot = Some(h);
1117 }
1118 break;
1119 }
1120 if JS_ENGINE.lock().unwrap().is_some() {
1121 break;
1122 }
1123 attempts += 1;
1124 if attempts > 50 {
1125 break;
1126 }
1127 thread::sleep(Duration::from_millis(1));
1128 }
1129 None
1131 }
1132 Err(JSEngineError::AlreadyShutDown) => {
1133 None
1139 }
1140 Err(e) => panic!("JSEngine::init() failed: {:?}", e),
1141 };
1142 Self(engine)
1143 }
1144}
1145
1146impl Drop for JSEngineSetup {
1147 fn drop(&mut self) {
1148 let Some(engine) = self.0.take() else {
1163 return;
1164 };
1165 std::mem::forget(engine);
1166 }
1167}
1168
1169static JS_ENGINE: Mutex<Option<JSEngineHandle>> = Mutex::new(None);
1170
1171fn in_range<T: PartialOrd + Copy>(val: T, min: T, max: T) -> Option<T> {
1172 if val < min || val >= max {
1173 None
1174 } else {
1175 Some(val)
1176 }
1177}
1178
1179thread_local!(static MALLOC_SIZE_OF_OPS: Cell<*mut MallocSizeOfOps> = const { Cell::new(ptr::null_mut()) });
1180
1181#[expect(unsafe_code)]
1182unsafe extern "C" fn get_size(obj: *mut JSObject) -> usize {
1183 match unsafe { get_dom_class(obj) } {
1184 Ok(v) => {
1185 let dom_object = unsafe { private_from_object(obj) as *const c_void };
1186
1187 if dom_object.is_null() {
1188 return 0;
1189 }
1190 let ops = MALLOC_SIZE_OF_OPS.get();
1191 unsafe { (v.malloc_size_of)(&mut *ops, dom_object) }
1192 },
1193 Err(_e) => 0,
1194 }
1195}
1196
1197thread_local!(static GC_CYCLE_START: Cell<Option<Instant>> = const { Cell::new(None) });
1198thread_local!(static GC_SLICE_START: Cell<Option<Instant>> = const { Cell::new(None) });
1199
1200#[expect(unsafe_code)]
1201unsafe extern "C" fn gc_slice_callback(
1202 _cx: *mut RawJSContext,
1203 progress: GCProgress,
1204 desc: *const GCDescription,
1205) {
1206 match progress {
1207 GCProgress::GC_CYCLE_BEGIN => GC_CYCLE_START.with(|start| {
1208 start.set(Some(Instant::now()));
1209 println!("GC cycle began");
1210 }),
1211 GCProgress::GC_SLICE_BEGIN => GC_SLICE_START.with(|start| {
1212 start.set(Some(Instant::now()));
1213 println!("GC slice began");
1214 }),
1215 GCProgress::GC_SLICE_END => GC_SLICE_START.with(|start| {
1216 let duration = start.get().unwrap().elapsed();
1217 start.set(None);
1218 println!("GC slice ended: duration={:?}", duration);
1219 }),
1220 GCProgress::GC_CYCLE_END => GC_CYCLE_START.with(|start| {
1221 let duration = start.get().unwrap().elapsed();
1222 start.set(None);
1223 println!("GC cycle ended: duration={:?}", duration);
1224 }),
1225 };
1226 if !desc.is_null() {
1227 let desc: &GCDescription = unsafe { &*desc };
1228 let options = match desc.options_ {
1229 GCOptions::Normal => "Normal",
1230 GCOptions::Shrink => "Shrink",
1231 GCOptions::Shutdown => "Shutdown",
1232 };
1233 println!(" isZone={}, options={}", desc.isZone_, options);
1234 }
1235 let _ = stdout().flush();
1236}
1237
1238#[expect(unsafe_code)]
1239unsafe extern "C" fn debug_gc_callback(
1240 _cx: *mut RawJSContext,
1241 status: JSGCStatus,
1242 _reason: GCReason,
1243 _data: *mut os::raw::c_void,
1244) {
1245 match status {
1246 JSGCStatus::JSGC_BEGIN => thread_state::enter(ThreadState::IN_GC),
1247 JSGCStatus::JSGC_END => thread_state::exit(ThreadState::IN_GC),
1248 }
1249}
1250
1251#[expect(unsafe_code)]
1252unsafe extern "C" fn trace_rust_roots(tr: *mut JSTracer, data: *mut os::raw::c_void) {
1253 if !runtime_is_alive() {
1254 return;
1255 }
1256 trace!("starting custom root handler");
1257
1258 let runtime_callback_data = unsafe { &*(data as *const RuntimeCallbackData) };
1259 if let Some(script_thread) = runtime_callback_data
1260 .script_thread
1261 .as_ref()
1262 .and_then(Weak::upgrade)
1263 {
1264 trace!("tracing fields of ScriptThread");
1265 unsafe { script_thread.trace(tr) };
1266 };
1267
1268 unsafe {
1269 trace_roots(tr);
1270 trace_refcounted_objects(tr);
1271 settings_stack::trace(tr);
1272 }
1273 trace!("done custom root handler");
1274}
1275
1276#[expect(unsafe_code)]
1277unsafe extern "C" fn servo_build_id(build_id: *mut BuildIdCharVector) -> bool {
1278 let servo_id = b"Servo\0";
1279 unsafe { SetBuildId(build_id, servo_id[0] as *const c_char, servo_id.len()) }
1280}
1281
1282#[expect(unsafe_code)]
1283#[cfg(feature = "debugmozjs")]
1284unsafe fn set_gc_zeal_options(cx: *mut RawJSContext) {
1285 use js::jsapi::SetGCZeal;
1286
1287 let level = match pref!(js_mem_gc_zeal_level) {
1288 level @ 0..=14 => level as u8,
1289 _ => return,
1290 };
1291 let frequency = match pref!(js_mem_gc_zeal_frequency) {
1292 frequency if frequency >= 0 => frequency as u32,
1293 _ => 5000,
1295 };
1296 unsafe {
1297 SetGCZeal(cx, level, frequency);
1298 }
1299}
1300
1301#[expect(unsafe_code)]
1302#[cfg(not(feature = "debugmozjs"))]
1303unsafe fn set_gc_zeal_options(_: *mut RawJSContext) {}
1304
1305#[expect(unsafe_code)]
1306pub(crate) fn get_reports(
1307 cx: &mut JSContext,
1308 path_seg: String,
1309 ops: &mut MallocSizeOfOps,
1310) -> Vec<Report> {
1311 MALLOC_SIZE_OF_OPS.with(|ops_tls| ops_tls.set(ops));
1312 let stats = unsafe {
1313 let mut stats = ::std::mem::zeroed();
1314 if !CollectServoSizes(cx, &mut stats, Some(get_size)) {
1315 return vec![];
1316 }
1317 stats
1318 };
1319 MALLOC_SIZE_OF_OPS.with(|ops| ops.set(ptr::null_mut()));
1320
1321 let mut reports = vec![];
1322 let mut report = |mut path_suffix, kind, size| {
1323 let mut path = path![path_seg, "js"];
1324 path.append(&mut path_suffix);
1325 reports.push(Report { path, kind, size })
1326 };
1327
1328 report(
1332 path!["gc-heap", "used"],
1333 ReportKind::ExplicitNonHeapSize,
1334 stats.gcHeapUsed,
1335 );
1336
1337 report(
1338 path!["gc-heap", "unused"],
1339 ReportKind::ExplicitNonHeapSize,
1340 stats.gcHeapUnused,
1341 );
1342
1343 report(
1344 path!["gc-heap", "admin"],
1345 ReportKind::ExplicitNonHeapSize,
1346 stats.gcHeapAdmin,
1347 );
1348
1349 report(
1350 path!["gc-heap", "decommitted"],
1351 ReportKind::ExplicitNonHeapSize,
1352 stats.gcHeapDecommitted,
1353 );
1354
1355 report(
1357 path!["malloc-heap"],
1358 ReportKind::ExplicitSystemHeapSize,
1359 stats.mallocHeap,
1360 );
1361
1362 report(
1363 path!["non-heap"],
1364 ReportKind::ExplicitNonHeapSize,
1365 stats.nonHeap,
1366 );
1367 reports
1368}
1369
1370pub(crate) struct StreamConsumer(*mut JSStreamConsumer);
1371
1372#[expect(unsafe_code)]
1373impl StreamConsumer {
1374 pub(crate) fn consume_chunk(&self, stream: &[u8]) -> bool {
1375 unsafe {
1376 let stream_ptr = stream.as_ptr();
1377 StreamConsumerConsumeChunk(self.0, stream_ptr, stream.len())
1378 }
1379 }
1380
1381 pub(crate) fn stream_end(&self) {
1382 unsafe {
1383 StreamConsumerStreamEnd(self.0);
1384 }
1385 }
1386
1387 pub(crate) fn stream_error(&self, error_code: usize) {
1388 unsafe {
1389 StreamConsumerStreamError(self.0, error_code);
1390 }
1391 }
1392
1393 pub(crate) fn note_response_urls(
1394 &self,
1395 maybe_url: Option<String>,
1396 maybe_source_map_url: Option<String>,
1397 ) {
1398 unsafe {
1399 let maybe_url = maybe_url.map(|url| CString::new(url).unwrap());
1400 let maybe_source_map_url = maybe_source_map_url.map(|url| CString::new(url).unwrap());
1401
1402 let maybe_url_param = match maybe_url.as_ref() {
1403 Some(url) => url.as_ptr(),
1404 None => ptr::null(),
1405 };
1406 let maybe_source_map_url_param = match maybe_source_map_url.as_ref() {
1407 Some(url) => url.as_ptr(),
1408 None => ptr::null(),
1409 };
1410
1411 StreamConsumerNoteResponseURLs(self.0, maybe_url_param, maybe_source_map_url_param);
1412 }
1413 }
1414}
1415
1416#[expect(unsafe_code)]
1419unsafe extern "C" fn consume_stream(
1420 cx: *mut RawJSContext,
1421 obj: HandleObject,
1422 _mime_type: MimeType,
1423 _consumer: *mut JSStreamConsumer,
1424) -> bool {
1425 let mut cx = unsafe {
1426 JSContext::from_ptr(NonNull::new(cx).expect("JSContext should not be null in SM hook"))
1428 };
1429 let cx = &mut cx;
1430 let mut realm = CurrentRealm::assert(cx);
1431 let global = GlobalScope::from_current_realm(&mut realm);
1432
1433 if let Ok(unwrapped_source) =
1435 unsafe { root_from_handleobject::<Response>(cx, RustHandleObject::from_raw(obj)) }
1436 {
1437 let mimetype = unwrapped_source.Headers(cx).extract_mime_type();
1439
1440 if !&mimetype[..].eq_ignore_ascii_case(b"application/wasm") {
1442 throw_dom_exception(
1443 cx,
1444 &global,
1445 Error::Type(c"Response has unsupported MIME type".to_owned()),
1446 );
1447 return false;
1448 }
1449
1450 match unwrapped_source.Type() {
1452 DOMResponseType::Basic | DOMResponseType::Cors | DOMResponseType::Default => {},
1453 _ => {
1454 throw_dom_exception(
1455 cx,
1456 &global,
1457 Error::Type(c"Response.type must be 'basic', 'cors' or 'default'".to_owned()),
1458 );
1459 return false;
1460 },
1461 }
1462
1463 if !unwrapped_source.Ok() {
1465 throw_dom_exception(
1466 cx,
1467 &global,
1468 Error::Type(c"Response does not have ok status".to_owned()),
1469 );
1470 return false;
1471 }
1472
1473 if unwrapped_source.is_locked() {
1475 throw_dom_exception(
1476 cx,
1477 &global,
1478 Error::Type(c"There was an error consuming the Response".to_owned()),
1479 );
1480 return false;
1481 }
1482
1483 if unwrapped_source.is_disturbed() {
1485 throw_dom_exception(
1486 cx,
1487 &global,
1488 Error::Type(c"Response already consumed".to_owned()),
1489 );
1490 return false;
1491 }
1492 unwrapped_source.set_stream_consumer(Some(StreamConsumer(_consumer)));
1493 } else {
1494 throw_dom_exception(
1496 cx,
1497 &global,
1498 Error::Type(c"expected Response or Promise resolving to Response".to_owned()),
1499 );
1500 return false;
1501 }
1502 true
1503}
1504
1505#[expect(unsafe_code)]
1506unsafe extern "C" fn report_stream_error(_cx: *mut RawJSContext, error_code: usize) {
1507 error!("Error initializing StreamConsumer: {:?}", unsafe {
1508 RUST_js_GetErrorMessage(ptr::null_mut(), error_code as u32)
1509 });
1510}
1511
1512#[expect(unsafe_code)]
1513unsafe extern "C" fn invoke_script_environment_preparer(
1514 global: HandleObject,
1515 closure: *mut ScriptEnvironmentPreparer_Closure,
1516) {
1517 let mut cx = unsafe { temp_cx() };
1519 let global = unsafe { GlobalScope::from_object(global.get()) };
1520 let mut realm = enter_auto_realm(&mut cx, &*global);
1521 let cx = &mut realm.current_realm();
1522
1523 run_a_script::<DomTypeHolder, _, _>(cx, &global, |cx| {
1524 if unsafe { !RunScriptEnvironmentPreparerClosure(cx.raw_cx(), closure) } {
1525 report_pending_exception(cx);
1526 };
1527 });
1528}
1529
1530pub(crate) struct Runnable(*mut DispatchablePointer);
1531
1532#[expect(unsafe_code)]
1533unsafe impl Sync for Runnable {}
1534#[expect(unsafe_code)]
1535unsafe impl Send for Runnable {}
1536
1537#[expect(unsafe_code)]
1538impl Runnable {
1539 fn run(&self, cx: &mut JSContext, maybe_shutting_down: Dispatchable_MaybeShuttingDown) {
1540 unsafe {
1541 DispatchableRun(cx, self.0, maybe_shutting_down);
1542 }
1543 }
1544}
1545
1546pub(crate) struct IntroductionType;
1552impl IntroductionType {
1553 pub const EVAL: &CStr = c"eval";
1555 pub const EVAL_STR: &str = "eval";
1556
1557 pub const DEBUGGER_EVAL: &CStr = c"debugger eval";
1560 pub const DEBUGGER_EVAL_STR: &str = "debugger eval";
1561
1562 pub const FUNCTION: &CStr = c"Function";
1564 pub const FUNCTION_STR: &str = "Function";
1565
1566 pub const WORKLET: &CStr = c"Worklet";
1568 pub const WORKLET_STR: &str = "Worklet";
1569
1570 pub const EVENT_HANDLER: &CStr = c"eventHandler";
1572 pub const EVENT_HANDLER_STR: &str = "eventHandler";
1573
1574 pub const SRC_SCRIPT: &CStr = c"srcScript";
1577 pub const SRC_SCRIPT_STR: &str = "srcScript";
1578
1579 pub const INLINE_SCRIPT: &CStr = c"inlineScript";
1582 pub const INLINE_SCRIPT_STR: &str = "inlineScript";
1583
1584 pub const INJECTED_SCRIPT: &CStr = c"injectedScript";
1590 pub const INJECTED_SCRIPT_STR: &str = "injectedScript";
1591
1592 pub const IMPORTED_MODULE: &CStr = c"importedModule";
1595 pub const IMPORTED_MODULE_STR: &str = "importedModule";
1596
1597 pub const JAVASCRIPT_URL: &CStr = c"javascriptURL";
1599 pub const JAVASCRIPT_URL_STR: &str = "javascriptURL";
1600
1601 pub const DOM_TIMER: &CStr = c"domTimer";
1603 pub const DOM_TIMER_STR: &str = "domTimer";
1604
1605 pub const WORKER: &CStr = c"Worker";
1609 pub const WORKER_STR: &str = "Worker";
1610}