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, JSEngineHandle,
57 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::realms::enter_auto_realm;
100use crate::script_module::EnsureModuleHooksInitialized;
101use crate::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(JSEngine);
1083
1084impl Default for JSEngineSetup {
1085 fn default() -> Self {
1086 let engine = JSEngine::init().unwrap();
1087 *JS_ENGINE.lock().unwrap() = Some(engine.handle());
1088 Self(engine)
1089 }
1090}
1091
1092impl Drop for JSEngineSetup {
1093 fn drop(&mut self) {
1094 *JS_ENGINE.lock().unwrap() = None;
1095
1096 while !self.0.can_shutdown() {
1097 thread::sleep(Duration::from_millis(50));
1098 }
1099 }
1100}
1101
1102static JS_ENGINE: Mutex<Option<JSEngineHandle>> = Mutex::new(None);
1103
1104fn in_range<T: PartialOrd + Copy>(val: T, min: T, max: T) -> Option<T> {
1105 if val < min || val >= max {
1106 None
1107 } else {
1108 Some(val)
1109 }
1110}
1111
1112thread_local!(static MALLOC_SIZE_OF_OPS: Cell<*mut MallocSizeOfOps> = const { Cell::new(ptr::null_mut()) });
1113
1114#[expect(unsafe_code)]
1115unsafe extern "C" fn get_size(obj: *mut JSObject) -> usize {
1116 match unsafe { get_dom_class(obj) } {
1117 Ok(v) => {
1118 let dom_object = unsafe { private_from_object(obj) as *const c_void };
1119
1120 if dom_object.is_null() {
1121 return 0;
1122 }
1123 let ops = MALLOC_SIZE_OF_OPS.get();
1124 unsafe { (v.malloc_size_of)(&mut *ops, dom_object) }
1125 },
1126 Err(_e) => 0,
1127 }
1128}
1129
1130thread_local!(static GC_CYCLE_START: Cell<Option<Instant>> = const { Cell::new(None) });
1131thread_local!(static GC_SLICE_START: Cell<Option<Instant>> = const { Cell::new(None) });
1132
1133#[expect(unsafe_code)]
1134unsafe extern "C" fn gc_slice_callback(
1135 _cx: *mut RawJSContext,
1136 progress: GCProgress,
1137 desc: *const GCDescription,
1138) {
1139 match progress {
1140 GCProgress::GC_CYCLE_BEGIN => GC_CYCLE_START.with(|start| {
1141 start.set(Some(Instant::now()));
1142 println!("GC cycle began");
1143 }),
1144 GCProgress::GC_SLICE_BEGIN => GC_SLICE_START.with(|start| {
1145 start.set(Some(Instant::now()));
1146 println!("GC slice began");
1147 }),
1148 GCProgress::GC_SLICE_END => GC_SLICE_START.with(|start| {
1149 let duration = start.get().unwrap().elapsed();
1150 start.set(None);
1151 println!("GC slice ended: duration={:?}", duration);
1152 }),
1153 GCProgress::GC_CYCLE_END => GC_CYCLE_START.with(|start| {
1154 let duration = start.get().unwrap().elapsed();
1155 start.set(None);
1156 println!("GC cycle ended: duration={:?}", duration);
1157 }),
1158 };
1159 if !desc.is_null() {
1160 let desc: &GCDescription = unsafe { &*desc };
1161 let options = match desc.options_ {
1162 GCOptions::Normal => "Normal",
1163 GCOptions::Shrink => "Shrink",
1164 GCOptions::Shutdown => "Shutdown",
1165 };
1166 println!(" isZone={}, options={}", desc.isZone_, options);
1167 }
1168 let _ = stdout().flush();
1169}
1170
1171#[expect(unsafe_code)]
1172unsafe extern "C" fn debug_gc_callback(
1173 _cx: *mut RawJSContext,
1174 status: JSGCStatus,
1175 _reason: GCReason,
1176 _data: *mut os::raw::c_void,
1177) {
1178 match status {
1179 JSGCStatus::JSGC_BEGIN => thread_state::enter(ThreadState::IN_GC),
1180 JSGCStatus::JSGC_END => thread_state::exit(ThreadState::IN_GC),
1181 }
1182}
1183
1184#[expect(unsafe_code)]
1185unsafe extern "C" fn trace_rust_roots(tr: *mut JSTracer, data: *mut os::raw::c_void) {
1186 if !runtime_is_alive() {
1187 return;
1188 }
1189 trace!("starting custom root handler");
1190
1191 let runtime_callback_data = unsafe { &*(data as *const RuntimeCallbackData) };
1192 if let Some(script_thread) = runtime_callback_data
1193 .script_thread
1194 .as_ref()
1195 .and_then(Weak::upgrade)
1196 {
1197 trace!("tracing fields of ScriptThread");
1198 unsafe { script_thread.trace(tr) };
1199 };
1200
1201 unsafe {
1202 trace_roots(tr);
1203 trace_refcounted_objects(tr);
1204 settings_stack::trace(tr);
1205 }
1206 trace!("done custom root handler");
1207}
1208
1209#[expect(unsafe_code)]
1210unsafe extern "C" fn servo_build_id(build_id: *mut BuildIdCharVector) -> bool {
1211 let servo_id = b"Servo\0";
1212 unsafe { SetBuildId(build_id, servo_id[0] as *const c_char, servo_id.len()) }
1213}
1214
1215#[expect(unsafe_code)]
1216#[cfg(feature = "debugmozjs")]
1217unsafe fn set_gc_zeal_options(cx: *mut RawJSContext) {
1218 use js::jsapi::SetGCZeal;
1219
1220 let level = match pref!(js_mem_gc_zeal_level) {
1221 level @ 0..=14 => level as u8,
1222 _ => return,
1223 };
1224 let frequency = match pref!(js_mem_gc_zeal_frequency) {
1225 frequency if frequency >= 0 => frequency as u32,
1226 _ => 5000,
1228 };
1229 unsafe {
1230 SetGCZeal(cx, level, frequency);
1231 }
1232}
1233
1234#[expect(unsafe_code)]
1235#[cfg(not(feature = "debugmozjs"))]
1236unsafe fn set_gc_zeal_options(_: *mut RawJSContext) {}
1237
1238#[expect(unsafe_code)]
1239pub(crate) fn get_reports(
1240 cx: &mut JSContext,
1241 path_seg: String,
1242 ops: &mut MallocSizeOfOps,
1243) -> Vec<Report> {
1244 MALLOC_SIZE_OF_OPS.with(|ops_tls| ops_tls.set(ops));
1245 let stats = unsafe {
1246 let mut stats = ::std::mem::zeroed();
1247 if !CollectServoSizes(cx, &mut stats, Some(get_size)) {
1248 return vec![];
1249 }
1250 stats
1251 };
1252 MALLOC_SIZE_OF_OPS.with(|ops| ops.set(ptr::null_mut()));
1253
1254 let mut reports = vec![];
1255 let mut report = |mut path_suffix, kind, size| {
1256 let mut path = path![path_seg, "js"];
1257 path.append(&mut path_suffix);
1258 reports.push(Report { path, kind, size })
1259 };
1260
1261 report(
1265 path!["gc-heap", "used"],
1266 ReportKind::ExplicitNonHeapSize,
1267 stats.gcHeapUsed,
1268 );
1269
1270 report(
1271 path!["gc-heap", "unused"],
1272 ReportKind::ExplicitNonHeapSize,
1273 stats.gcHeapUnused,
1274 );
1275
1276 report(
1277 path!["gc-heap", "admin"],
1278 ReportKind::ExplicitNonHeapSize,
1279 stats.gcHeapAdmin,
1280 );
1281
1282 report(
1283 path!["gc-heap", "decommitted"],
1284 ReportKind::ExplicitNonHeapSize,
1285 stats.gcHeapDecommitted,
1286 );
1287
1288 report(
1290 path!["malloc-heap"],
1291 ReportKind::ExplicitSystemHeapSize,
1292 stats.mallocHeap,
1293 );
1294
1295 report(
1296 path!["non-heap"],
1297 ReportKind::ExplicitNonHeapSize,
1298 stats.nonHeap,
1299 );
1300 reports
1301}
1302
1303pub(crate) struct StreamConsumer(*mut JSStreamConsumer);
1304
1305#[expect(unsafe_code)]
1306impl StreamConsumer {
1307 pub(crate) fn consume_chunk(&self, stream: &[u8]) -> bool {
1308 unsafe {
1309 let stream_ptr = stream.as_ptr();
1310 StreamConsumerConsumeChunk(self.0, stream_ptr, stream.len())
1311 }
1312 }
1313
1314 pub(crate) fn stream_end(&self) {
1315 unsafe {
1316 StreamConsumerStreamEnd(self.0);
1317 }
1318 }
1319
1320 pub(crate) fn stream_error(&self, error_code: usize) {
1321 unsafe {
1322 StreamConsumerStreamError(self.0, error_code);
1323 }
1324 }
1325
1326 pub(crate) fn note_response_urls(
1327 &self,
1328 maybe_url: Option<String>,
1329 maybe_source_map_url: Option<String>,
1330 ) {
1331 unsafe {
1332 let maybe_url = maybe_url.map(|url| CString::new(url).unwrap());
1333 let maybe_source_map_url = maybe_source_map_url.map(|url| CString::new(url).unwrap());
1334
1335 let maybe_url_param = match maybe_url.as_ref() {
1336 Some(url) => url.as_ptr(),
1337 None => ptr::null(),
1338 };
1339 let maybe_source_map_url_param = match maybe_source_map_url.as_ref() {
1340 Some(url) => url.as_ptr(),
1341 None => ptr::null(),
1342 };
1343
1344 StreamConsumerNoteResponseURLs(self.0, maybe_url_param, maybe_source_map_url_param);
1345 }
1346 }
1347}
1348
1349#[expect(unsafe_code)]
1352unsafe extern "C" fn consume_stream(
1353 cx: *mut RawJSContext,
1354 obj: HandleObject,
1355 _mime_type: MimeType,
1356 _consumer: *mut JSStreamConsumer,
1357) -> bool {
1358 let mut cx = unsafe {
1359 JSContext::from_ptr(NonNull::new(cx).expect("JSContext should not be null in SM hook"))
1361 };
1362 let cx = &mut cx;
1363 let mut realm = CurrentRealm::assert(cx);
1364 let global = GlobalScope::from_current_realm(&mut realm);
1365
1366 if let Ok(unwrapped_source) =
1368 unsafe { root_from_handleobject::<Response>(cx, RustHandleObject::from_raw(obj)) }
1369 {
1370 let mimetype = unwrapped_source.Headers(cx).extract_mime_type();
1372
1373 if !&mimetype[..].eq_ignore_ascii_case(b"application/wasm") {
1375 throw_dom_exception(
1376 cx,
1377 &global,
1378 Error::Type(c"Response has unsupported MIME type".to_owned()),
1379 );
1380 return false;
1381 }
1382
1383 match unwrapped_source.Type() {
1385 DOMResponseType::Basic | DOMResponseType::Cors | DOMResponseType::Default => {},
1386 _ => {
1387 throw_dom_exception(
1388 cx,
1389 &global,
1390 Error::Type(c"Response.type must be 'basic', 'cors' or 'default'".to_owned()),
1391 );
1392 return false;
1393 },
1394 }
1395
1396 if !unwrapped_source.Ok() {
1398 throw_dom_exception(
1399 cx,
1400 &global,
1401 Error::Type(c"Response does not have ok status".to_owned()),
1402 );
1403 return false;
1404 }
1405
1406 if unwrapped_source.is_locked() {
1408 throw_dom_exception(
1409 cx,
1410 &global,
1411 Error::Type(c"There was an error consuming the Response".to_owned()),
1412 );
1413 return false;
1414 }
1415
1416 if unwrapped_source.is_disturbed() {
1418 throw_dom_exception(
1419 cx,
1420 &global,
1421 Error::Type(c"Response already consumed".to_owned()),
1422 );
1423 return false;
1424 }
1425 unwrapped_source.set_stream_consumer(Some(StreamConsumer(_consumer)));
1426 } else {
1427 throw_dom_exception(
1429 cx,
1430 &global,
1431 Error::Type(c"expected Response or Promise resolving to Response".to_owned()),
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 mut cx = unsafe { temp_cx() };
1452 let global = unsafe { GlobalScope::from_object(global.get()) };
1453 let mut realm = enter_auto_realm(&mut cx, &*global);
1454 let cx = &mut realm.current_realm();
1455
1456 run_a_script::<DomTypeHolder, _, _>(cx, &global, |cx| {
1457 if unsafe { !RunScriptEnvironmentPreparerClosure(cx.raw_cx(), closure) } {
1458 report_pending_exception(cx);
1459 };
1460 });
1461}
1462
1463pub(crate) struct Runnable(*mut DispatchablePointer);
1464
1465#[expect(unsafe_code)]
1466unsafe impl Sync for Runnable {}
1467#[expect(unsafe_code)]
1468unsafe impl Send for Runnable {}
1469
1470#[expect(unsafe_code)]
1471impl Runnable {
1472 fn run(&self, cx: &mut JSContext, maybe_shutting_down: Dispatchable_MaybeShuttingDown) {
1473 unsafe {
1474 DispatchableRun(cx, self.0, maybe_shutting_down);
1475 }
1476 }
1477}
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}