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