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