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 || {
481 let target = target.root();
482 let cx = GlobalScope::get_cx();
483 let root_promise = trusted_promise.root();
484
485 rooted!(in(*cx) let mut reason = UndefinedValue());
486 unsafe{JS_GetPromiseResult(root_promise.reflector().get_jsobject(), reason.handle_mut())};
487
488 let event = PromiseRejectionEvent::new(
489 &target.global(),
490 atom!("rejectionhandled"),
491 EventBubbles::DoesNotBubble,
492 EventCancelable::Cancelable,
493 root_promise,
494 reason.handle(),
495 CanGc::note()
496 );
497
498 event.upcast::<Event>().fire(&target, CanGc::note());
499 })
500 );
501 },
502 };
503 })
504}
505
506#[expect(unsafe_code)]
507fn safely_convert_null_to_string(cx: &mut js::context::JSContext, str_: HandleString) -> DOMString {
508 DOMString::from(match std::ptr::NonNull::new(*str_) {
509 None => "".to_owned(),
510 Some(str_) => unsafe { jsstr_to_string(cx.raw_cx(), str_) },
511 })
512}
513
514#[expect(unsafe_code)]
515unsafe extern "C" fn code_for_eval_gets(
516 cx: *mut RawJSContext,
517 code: HandleObject,
518 code_for_eval: MutableHandleString,
519) -> bool {
520 let cx = unsafe { JSContext::from_ptr(cx) };
521 if let Ok(trusted_script) = unsafe { root_from_object::<TrustedScript>(code.get(), *cx) } {
522 let script_str = trusted_script.data().str();
523 let s = js::conversions::Utf8Chars::from(&*script_str);
524 let new_string = unsafe { JS_NewStringCopyUTF8N(*cx, &*s as *const _) };
525 code_for_eval.set(new_string);
526 }
527 true
528}
529
530#[expect(unsafe_code)]
531unsafe extern "C" fn content_security_policy_allows(
532 cx: *mut RawJSContext,
533 runtime_code: RuntimeCode,
534 code_string: HandleString,
535 compilation_type: CompilationType,
536 parameter_strings: RawHandle<StackGCVector<*mut JSString>>,
537 body_string: HandleString,
538 parameter_args: RawHandle<StackGCVector<JSVal>>,
539 body_arg: RawHandleValue,
540 can_compile_strings: *mut bool,
541) -> bool {
542 let mut allowed = false;
543 let mut cx = unsafe { js::context::JSContext::from_ptr(NonNull::new(cx).unwrap()) };
545 let cx = &mut cx;
546 wrap_panic(&mut || {
547 let realm = CurrentRealm::assert(cx);
549 let global = GlobalScope::from_current_realm(&realm);
550 let csp_list = global.get_csp_list();
551
552 allowed = csp_list.is_none() ||
554 match runtime_code {
555 RuntimeCode::JS => {
556 let parameter_strings = unsafe { Handle::from_raw(parameter_strings) };
557 let parameter_strings_length = parameter_strings.len();
558 let mut parameter_strings_vec =
559 Vec::with_capacity(parameter_strings_length as usize);
560
561 for i in 0..parameter_strings_length {
562 let Some(str_) = parameter_strings.at(i) else {
563 unreachable!();
564 };
565 parameter_strings_vec.push(safely_convert_null_to_string(cx, str_.into()));
566 }
567
568 let parameter_args = unsafe { Handle::from_raw(parameter_args) };
569 let parameter_args_length = parameter_args.len();
570 let mut parameter_args_vec = Vec::with_capacity(parameter_args_length as usize);
571
572 for i in 0..parameter_args_length {
573 let Some(arg) = parameter_args.at(i) else {
574 unreachable!();
575 };
576 let value = arg.into_handle().get();
577 if value.is_object() {
578 if let Ok(trusted_script) = unsafe {
579 root_from_object::<TrustedScript>(value.to_object(), cx.raw_cx())
580 } {
581 parameter_args_vec
582 .push(TrustedScriptOrString::TrustedScript(trusted_script));
583 } else {
584 parameter_args_vec
588 .push(TrustedScriptOrString::String(DOMString::new()));
589 }
590 } else if value.is_string() {
591 parameter_args_vec
593 .push(TrustedScriptOrString::String(DOMString::new()));
594 } else {
595 unreachable!();
596 }
597 }
598
599 let code_string = safely_convert_null_to_string(cx, code_string);
600 let body_string = safely_convert_null_to_string(cx, body_string);
601
602 TrustedScript::can_compile_string_with_trusted_type(
603 cx,
604 &global,
605 code_string,
606 compilation_type,
607 parameter_strings_vec,
608 body_string,
609 parameter_args_vec,
610 unsafe { HandleValue::from_raw(body_arg) },
611 )
612 },
613 RuntimeCode::WASM => global.get_csp_list().is_wasm_evaluation_allowed(&global),
614 };
615 });
616 unsafe { *can_compile_strings = allowed };
617 true
618}
619
620#[expect(unsafe_code)]
621pub(crate) fn notify_about_rejected_promises(global: &GlobalScope) {
623 let cx = GlobalScope::get_cx();
624
625 let uncaught_rejections: Vec<TrustedPromise> = global
627 .get_uncaught_rejections()
628 .borrow_mut()
629 .drain(..)
630 .map(|promise| {
631 let promise =
632 Promise::new_with_js_promise(unsafe { Handle::from_raw(promise.handle()) }, cx);
633
634 TrustedPromise::new(promise)
635 })
636 .collect();
637
638 if uncaught_rejections.is_empty() {
640 return;
641 }
642
643 let target = Trusted::new(global.upcast::<EventTarget>());
648 global.task_manager().dom_manipulation_task_source().queue(
649 task!(unhandled_rejection_event: move |cx| {
650 let target = target.root();
651
652 for promise in uncaught_rejections {
654 let promise = promise.root();
655
656 let promise_is_handled = unsafe { GetPromiseIsHandled(promise.reflector().get_jsobject()) };
658 if promise_is_handled {
659 continue;
660 }
661
662 rooted!(&in(cx) let mut reason = UndefinedValue());
666 unsafe {
667 JS_GetPromiseResult(promise.reflector().get_jsobject(), reason.handle_mut());
668 }
669
670 log::error!(
671 "Unhandled promise rejection: {}",
672 stringify_handle_value(reason.handle())
673 );
674
675 let event = PromiseRejectionEvent::new(
676 &target.global(),
677 atom!("unhandledrejection"),
678 EventBubbles::DoesNotBubble,
679 EventCancelable::Cancelable,
680 promise.clone(),
681 reason.handle(),
682 CanGc::from_cx(cx)
683 );
684 event.upcast::<Event>().fire(&target, CanGc::from_cx(cx));
685
686 if !promise_is_handled {
692 target.global().add_consumed_rejection(promise.reflector().get_jsobject().into_handle());
693 }
694 }
695 })
696 );
697}
698
699#[derive(Default, JSTraceable, MallocSizeOf)]
702struct RuntimeCallbackData {
703 script_event_loop_sender: Option<ScriptEventLoopSender>,
704 #[no_trace]
705 #[ignore_malloc_size_of = "ScriptThread measures its own memory itself."]
706 script_thread: Option<Weak<ScriptThread>>,
707}
708
709#[derive(JSTraceable, MallocSizeOf)]
710pub(crate) struct Runtime {
711 #[ignore_malloc_size_of = "Type from mozjs"]
712 rt: RustRuntime,
713 #[conditional_malloc_size_of]
715 pub(crate) microtask_queue: Rc<MicrotaskQueue>,
716 #[ignore_malloc_size_of = "Type from mozjs"]
717 job_queue: *mut JobQueue,
718 runtime_callback_data: Box<RuntimeCallbackData>,
720}
721
722impl Runtime {
723 #[expect(unsafe_code)]
733 pub(crate) fn new(main_thread_sender: Option<ScriptEventLoopSender>) -> Runtime {
734 unsafe { Self::new_with_parent(None, main_thread_sender) }
735 }
736
737 #[allow(unsafe_code)]
738 pub(crate) unsafe fn cx(&self) -> js::context::JSContext {
742 unsafe { js::context::JSContext::from_ptr(RustRuntime::get().unwrap()) }
743 }
744
745 #[expect(unsafe_code)]
758 pub(crate) unsafe fn new_with_parent(
759 parent: Option<ParentRuntime>,
760 script_event_loop_sender: Option<ScriptEventLoopSender>,
761 ) -> Runtime {
762 let mut runtime = if let Some(parent) = parent {
763 unsafe { RustRuntime::create_with_parent(parent) }
764 } else {
765 RustRuntime::new(JS_ENGINE.lock().unwrap().as_ref().unwrap().clone())
766 };
767 let cx = runtime.cx();
768
769 let have_event_loop_sender = script_event_loop_sender.is_some();
770 let runtime_callback_data = Box::new(RuntimeCallbackData {
771 script_event_loop_sender,
772 script_thread: None,
773 });
774 let runtime_callback_data = Box::into_raw(runtime_callback_data);
775
776 unsafe {
777 JS_AddExtraGCRootsTracer(
778 cx,
779 Some(trace_rust_roots),
780 runtime_callback_data as *mut c_void,
781 );
782
783 JS_SetSecurityCallbacks(cx, &SECURITY_CALLBACKS);
784
785 JS_InitDestroyPrincipalsCallback(cx, Some(principals::destroy_servo_jsprincipal));
786 JS_InitReadPrincipalsCallback(cx, Some(principals::read_jsprincipal));
787
788 if cfg!(debug_assertions) {
790 JS_SetGCCallback(cx, Some(debug_gc_callback), ptr::null_mut());
791 }
792
793 if opts::get().debug.gc_profile {
794 SetGCSliceCallback(cx, Some(gc_slice_callback));
795 }
796 }
797
798 unsafe extern "C" fn empty_wrapper_callback(_: *mut RawJSContext, _: HandleObject) -> bool {
799 true
800 }
801 unsafe extern "C" fn empty_has_released_callback(_: HandleObject) -> bool {
802 false
804 }
805
806 unsafe {
807 SetDOMCallbacks(cx, &DOM_CALLBACKS);
808 SetPreserveWrapperCallbacks(
809 cx,
810 Some(empty_wrapper_callback),
811 Some(empty_has_released_callback),
812 );
813 JS_SetGCParameter(cx, JSGCParamKey::JSGC_INCREMENTAL_GC_ENABLED, 0);
815 }
816
817 unsafe extern "C" fn dispatch_to_event_loop(
818 data: *mut c_void,
819 dispatchable: *mut DispatchablePointer,
820 ) -> bool {
821 let runtime_callback_data: &RuntimeCallbackData =
822 unsafe { &*(data as *mut RuntimeCallbackData) };
823 let Some(script_event_loop_sender) =
824 runtime_callback_data.script_event_loop_sender.as_ref()
825 else {
826 return false;
827 };
828
829 let runnable = Runnable(dispatchable);
830 let task = task!(dispatch_to_event_loop_message: move || {
831 if let Some(cx) = RustRuntime::get() {
832 runnable.run(cx.as_ptr(), Dispatchable_MaybeShuttingDown::NotShuttingDown);
833 }
834 });
835
836 script_event_loop_sender
837 .send(CommonScriptMsg::Task(
838 ScriptThreadEventCategory::NetworkEvent,
839 Box::new(task),
840 None, TaskSourceName::Networking,
842 ))
843 .is_ok()
844 }
845
846 if have_event_loop_sender {
847 unsafe {
848 SetUpEventLoopDispatch(
849 cx,
850 Some(dispatch_to_event_loop),
851 runtime_callback_data as *mut c_void,
852 );
853 }
854 }
855
856 unsafe {
857 InitConsumeStreamCallback(cx, Some(consume_stream), Some(report_stream_error));
858 }
859
860 let microtask_queue = Rc::new(MicrotaskQueue::default());
861
862 let interrupt_queues: Box<Vec<Rc<MicrotaskQueue>>> = Box::default();
866
867 let cx_opts;
868 let job_queue;
869 unsafe {
870 let cx = runtime.cx();
871 job_queue = CreateJobQueue(
872 &JOB_QUEUE_TRAPS,
873 &*microtask_queue as *const _ as *const c_void,
874 Box::into_raw(interrupt_queues) as *mut c_void,
875 );
876 SetJobQueue(cx, job_queue);
877 SetPromiseRejectionTrackerCallback(
878 cx,
879 Some(promise_rejection_tracker),
880 ptr::null_mut(),
881 );
882
883 RegisterScriptEnvironmentPreparer(
884 cx.raw_cx(),
885 Some(invoke_script_environment_preparer),
886 );
887
888 EnsureModuleHooksInitialized(runtime.rt());
889
890 let cx = runtime.cx();
891
892 set_gc_zeal_options(cx.raw_cx());
893
894 cx_opts = &mut *ContextOptionsRef(cx);
896 JS_SetGlobalJitCompilerOption(
897 cx,
898 JSJitCompilerOption::JSJITCOMPILER_BASELINE_INTERPRETER_ENABLE,
899 pref!(js_baseline_interpreter_enabled) as u32,
900 );
901 JS_SetGlobalJitCompilerOption(
902 cx,
903 JSJitCompilerOption::JSJITCOMPILER_BASELINE_ENABLE,
904 pref!(js_baseline_jit_enabled) as u32,
905 );
906 JS_SetGlobalJitCompilerOption(
907 cx,
908 JSJitCompilerOption::JSJITCOMPILER_ION_ENABLE,
909 pref!(js_ion_enabled) as u32,
910 );
911 }
912 cx_opts.compileOptions_.asmJSOption_ = if pref!(js_asmjs_enabled) {
913 AsmJSOption::Enabled
914 } else {
915 AsmJSOption::DisabledByAsmJSPref
916 };
917 cx_opts.compileOptions_.set_importAttributes_(true);
918 let wasm_enabled = pref!(js_wasm_enabled);
919 cx_opts.set_wasm_(wasm_enabled);
920 if wasm_enabled {
921 unsafe { SetProcessBuildIdOp(Some(servo_build_id)) };
925 }
926 cx_opts.set_wasmBaseline_(pref!(js_wasm_baseline_enabled));
927 cx_opts.set_wasmIon_(pref!(js_wasm_ion_enabled));
928
929 unsafe {
930 let cx = runtime.cx();
931 JS_SetGlobalJitCompilerOption(
933 cx,
934 JSJitCompilerOption::JSJITCOMPILER_NATIVE_REGEXP_ENABLE,
935 pref!(js_native_regex_enabled) as u32,
936 );
937 JS_SetOffthreadIonCompilationEnabled(cx, pref!(js_offthread_compilation_enabled));
938 JS_SetGlobalJitCompilerOption(
939 cx,
940 JSJitCompilerOption::JSJITCOMPILER_BASELINE_WARMUP_TRIGGER,
941 if pref!(js_baseline_jit_unsafe_eager_compilation_enabled) {
942 0
943 } else {
944 u32::MAX
945 },
946 );
947 JS_SetGlobalJitCompilerOption(
948 cx,
949 JSJitCompilerOption::JSJITCOMPILER_ION_NORMAL_WARMUP_TRIGGER,
950 if pref!(js_ion_unsafe_eager_compilation_enabled) {
951 0
952 } else {
953 u32::MAX
954 },
955 );
956 JS_SetGCParameter(
962 cx,
963 JSGCParamKey::JSGC_MAX_BYTES,
964 in_range(pref!(js_mem_max), 1, 0x100)
965 .map(|val| (val * 1024 * 1024) as u32)
966 .unwrap_or(u32::MAX),
967 );
968 JS_SetGCParameter(
970 cx,
971 JSGCParamKey::JSGC_INCREMENTAL_GC_ENABLED,
972 pref!(js_mem_gc_incremental_enabled) as u32,
973 );
974 JS_SetGCParameter(
975 cx,
976 JSGCParamKey::JSGC_PER_ZONE_GC_ENABLED,
977 pref!(js_mem_gc_per_zone_enabled) as u32,
978 );
979 if let Some(val) = in_range(pref!(js_mem_gc_incremental_slice_ms), 0, 100_000) {
980 JS_SetGCParameter(cx, JSGCParamKey::JSGC_SLICE_TIME_BUDGET_MS, val as u32);
981 }
982 JS_SetGCParameter(
983 cx,
984 JSGCParamKey::JSGC_COMPACTING_ENABLED,
985 pref!(js_mem_gc_compacting_enabled) as u32,
986 );
987
988 if let Some(val) = in_range(pref!(js_mem_gc_high_frequency_time_limit_ms), 0, 10_000) {
989 JS_SetGCParameter(cx, JSGCParamKey::JSGC_HIGH_FREQUENCY_TIME_LIMIT, val as u32);
990 }
991 if let Some(val) = in_range(pref!(js_mem_gc_low_frequency_heap_growth), 0, 10_000) {
992 JS_SetGCParameter(cx, JSGCParamKey::JSGC_LOW_FREQUENCY_HEAP_GROWTH, val as u32);
993 }
994 if let Some(val) = in_range(pref!(js_mem_gc_high_frequency_heap_growth_min), 0, 10_000)
995 {
996 JS_SetGCParameter(
997 cx,
998 JSGCParamKey::JSGC_HIGH_FREQUENCY_LARGE_HEAP_GROWTH,
999 val as u32,
1000 );
1001 }
1002 if let Some(val) = in_range(pref!(js_mem_gc_high_frequency_heap_growth_max), 0, 10_000)
1003 {
1004 JS_SetGCParameter(
1005 cx,
1006 JSGCParamKey::JSGC_HIGH_FREQUENCY_SMALL_HEAP_GROWTH,
1007 val as u32,
1008 );
1009 }
1010 if let Some(val) = in_range(pref!(js_mem_gc_high_frequency_low_limit_mb), 0, 10_000) {
1011 JS_SetGCParameter(cx, JSGCParamKey::JSGC_SMALL_HEAP_SIZE_MAX, val as u32);
1012 }
1013 if let Some(val) = in_range(pref!(js_mem_gc_high_frequency_high_limit_mb), 0, 10_000) {
1014 JS_SetGCParameter(cx, JSGCParamKey::JSGC_LARGE_HEAP_SIZE_MIN, val as u32);
1015 }
1016 if let Some(val) = in_range(pref!(js_mem_gc_empty_chunk_count_min), 0, 10_000) {
1017 JS_SetGCParameter(cx, JSGCParamKey::JSGC_MIN_EMPTY_CHUNK_COUNT, val as u32);
1018 }
1019 }
1020 Runtime {
1021 rt: runtime,
1022 microtask_queue,
1023 job_queue,
1024 runtime_callback_data: unsafe { Box::from_raw(runtime_callback_data) },
1025 }
1026 }
1027
1028 pub(crate) fn set_script_thread(&mut self, script_thread: Weak<ScriptThread>) {
1029 self.runtime_callback_data
1030 .script_thread
1031 .replace(script_thread);
1032 }
1033
1034 pub(crate) fn thread_safe_js_context(&self) -> ThreadSafeJSContext {
1035 self.rt.thread_safe_js_context()
1036 }
1037}
1038
1039impl Drop for Runtime {
1040 #[expect(unsafe_code)]
1041 fn drop(&mut self) {
1042 self.microtask_queue.clear();
1044
1045 unsafe {
1047 DeleteJobQueue(self.job_queue);
1048 }
1049 LiveDOMReferences::destruct();
1050 mark_runtime_dead();
1051 }
1052}
1053
1054impl Deref for Runtime {
1055 type Target = RustRuntime;
1056 fn deref(&self) -> &RustRuntime {
1057 &self.rt
1058 }
1059}
1060
1061impl DerefMut for Runtime {
1062 fn deref_mut(&mut self) -> &mut RustRuntime {
1063 &mut self.rt
1064 }
1065}
1066
1067pub struct JSEngineSetup(JSEngine);
1068
1069impl Default for JSEngineSetup {
1070 fn default() -> Self {
1071 let engine = JSEngine::init().unwrap();
1072 *JS_ENGINE.lock().unwrap() = Some(engine.handle());
1073 Self(engine)
1074 }
1075}
1076
1077impl Drop for JSEngineSetup {
1078 fn drop(&mut self) {
1079 *JS_ENGINE.lock().unwrap() = None;
1080
1081 while !self.0.can_shutdown() {
1082 thread::sleep(Duration::from_millis(50));
1083 }
1084 }
1085}
1086
1087static JS_ENGINE: Mutex<Option<JSEngineHandle>> = Mutex::new(None);
1088
1089fn in_range<T: PartialOrd + Copy>(val: T, min: T, max: T) -> Option<T> {
1090 if val < min || val >= max {
1091 None
1092 } else {
1093 Some(val)
1094 }
1095}
1096
1097thread_local!(static MALLOC_SIZE_OF_OPS: Cell<*mut MallocSizeOfOps> = const { Cell::new(ptr::null_mut()) });
1098
1099#[expect(unsafe_code)]
1100unsafe extern "C" fn get_size(obj: *mut JSObject) -> usize {
1101 match unsafe { get_dom_class(obj) } {
1102 Ok(v) => {
1103 let dom_object = unsafe { private_from_object(obj) as *const c_void };
1104
1105 if dom_object.is_null() {
1106 return 0;
1107 }
1108 let ops = MALLOC_SIZE_OF_OPS.get();
1109 unsafe { (v.malloc_size_of)(&mut *ops, dom_object) }
1110 },
1111 Err(_e) => 0,
1112 }
1113}
1114
1115thread_local!(static GC_CYCLE_START: Cell<Option<Instant>> = const { Cell::new(None) });
1116thread_local!(static GC_SLICE_START: Cell<Option<Instant>> = const { Cell::new(None) });
1117
1118#[expect(unsafe_code)]
1119unsafe extern "C" fn gc_slice_callback(
1120 _cx: *mut RawJSContext,
1121 progress: GCProgress,
1122 desc: *const GCDescription,
1123) {
1124 match progress {
1125 GCProgress::GC_CYCLE_BEGIN => GC_CYCLE_START.with(|start| {
1126 start.set(Some(Instant::now()));
1127 println!("GC cycle began");
1128 }),
1129 GCProgress::GC_SLICE_BEGIN => GC_SLICE_START.with(|start| {
1130 start.set(Some(Instant::now()));
1131 println!("GC slice began");
1132 }),
1133 GCProgress::GC_SLICE_END => GC_SLICE_START.with(|start| {
1134 let duration = start.get().unwrap().elapsed();
1135 start.set(None);
1136 println!("GC slice ended: duration={:?}", duration);
1137 }),
1138 GCProgress::GC_CYCLE_END => GC_CYCLE_START.with(|start| {
1139 let duration = start.get().unwrap().elapsed();
1140 start.set(None);
1141 println!("GC cycle ended: duration={:?}", duration);
1142 }),
1143 };
1144 if !desc.is_null() {
1145 let desc: &GCDescription = unsafe { &*desc };
1146 let options = match desc.options_ {
1147 GCOptions::Normal => "Normal",
1148 GCOptions::Shrink => "Shrink",
1149 GCOptions::Shutdown => "Shutdown",
1150 };
1151 println!(" isZone={}, options={}", desc.isZone_, options);
1152 }
1153 let _ = stdout().flush();
1154}
1155
1156#[expect(unsafe_code)]
1157unsafe extern "C" fn debug_gc_callback(
1158 _cx: *mut RawJSContext,
1159 status: JSGCStatus,
1160 _reason: GCReason,
1161 _data: *mut os::raw::c_void,
1162) {
1163 match status {
1164 JSGCStatus::JSGC_BEGIN => thread_state::enter(ThreadState::IN_GC),
1165 JSGCStatus::JSGC_END => thread_state::exit(ThreadState::IN_GC),
1166 }
1167}
1168
1169#[expect(unsafe_code)]
1170unsafe extern "C" fn trace_rust_roots(tr: *mut JSTracer, data: *mut os::raw::c_void) {
1171 if !runtime_is_alive() {
1172 return;
1173 }
1174 trace!("starting custom root handler");
1175
1176 let runtime_callback_data = unsafe { &*(data as *const RuntimeCallbackData) };
1177 if let Some(script_thread) = runtime_callback_data
1178 .script_thread
1179 .as_ref()
1180 .and_then(Weak::upgrade)
1181 {
1182 trace!("tracing fields of ScriptThread");
1183 unsafe { script_thread.trace(tr) };
1184 };
1185
1186 unsafe {
1187 trace_roots(tr);
1188 trace_refcounted_objects(tr);
1189 settings_stack::trace(tr);
1190 }
1191 trace!("done custom root handler");
1192}
1193
1194#[expect(unsafe_code)]
1195unsafe extern "C" fn servo_build_id(build_id: *mut BuildIdCharVector) -> bool {
1196 let servo_id = b"Servo\0";
1197 unsafe { SetBuildId(build_id, servo_id[0] as *const c_char, servo_id.len()) }
1198}
1199
1200#[expect(unsafe_code)]
1201#[cfg(feature = "debugmozjs")]
1202unsafe fn set_gc_zeal_options(cx: *mut RawJSContext) {
1203 use js::jsapi::SetGCZeal;
1204
1205 let level = match pref!(js_mem_gc_zeal_level) {
1206 level @ 0..=14 => level as u8,
1207 _ => return,
1208 };
1209 let frequency = match pref!(js_mem_gc_zeal_frequency) {
1210 frequency if frequency >= 0 => frequency as u32,
1211 _ => 5000,
1213 };
1214 unsafe {
1215 SetGCZeal(cx, level, frequency);
1216 }
1217}
1218
1219#[expect(unsafe_code)]
1220#[cfg(not(feature = "debugmozjs"))]
1221unsafe fn set_gc_zeal_options(_: *mut RawJSContext) {}
1222
1223pub(crate) use script_bindings::script_runtime::JSContext;
1224
1225pub(crate) trait JSContextHelper {
1228 fn get_reports(&self, path_seg: String, ops: &mut MallocSizeOfOps) -> Vec<Report>;
1229}
1230
1231impl JSContextHelper for JSContext {
1232 #[expect(unsafe_code)]
1233 fn get_reports(&self, path_seg: String, ops: &mut MallocSizeOfOps) -> Vec<Report> {
1234 MALLOC_SIZE_OF_OPS.with(|ops_tls| ops_tls.set(ops));
1235 let stats = unsafe {
1236 let mut stats = ::std::mem::zeroed();
1237 if !CollectServoSizes(**self, &mut stats, Some(get_size)) {
1238 return vec![];
1239 }
1240 stats
1241 };
1242 MALLOC_SIZE_OF_OPS.with(|ops| ops.set(ptr::null_mut()));
1243
1244 let mut reports = vec![];
1245 let mut report = |mut path_suffix, kind, size| {
1246 let mut path = path![path_seg, "js"];
1247 path.append(&mut path_suffix);
1248 reports.push(Report { path, kind, size })
1249 };
1250
1251 report(
1255 path!["gc-heap", "used"],
1256 ReportKind::ExplicitNonHeapSize,
1257 stats.gcHeapUsed,
1258 );
1259
1260 report(
1261 path!["gc-heap", "unused"],
1262 ReportKind::ExplicitNonHeapSize,
1263 stats.gcHeapUnused,
1264 );
1265
1266 report(
1267 path!["gc-heap", "admin"],
1268 ReportKind::ExplicitNonHeapSize,
1269 stats.gcHeapAdmin,
1270 );
1271
1272 report(
1273 path!["gc-heap", "decommitted"],
1274 ReportKind::ExplicitNonHeapSize,
1275 stats.gcHeapDecommitted,
1276 );
1277
1278 report(
1280 path!["malloc-heap"],
1281 ReportKind::ExplicitSystemHeapSize,
1282 stats.mallocHeap,
1283 );
1284
1285 report(
1286 path!["non-heap"],
1287 ReportKind::ExplicitNonHeapSize,
1288 stats.nonHeap,
1289 );
1290 reports
1291 }
1292}
1293
1294pub(crate) struct StreamConsumer(*mut JSStreamConsumer);
1295
1296#[expect(unsafe_code)]
1297impl StreamConsumer {
1298 pub(crate) fn consume_chunk(&self, stream: &[u8]) -> bool {
1299 unsafe {
1300 let stream_ptr = stream.as_ptr();
1301 StreamConsumerConsumeChunk(self.0, stream_ptr, stream.len())
1302 }
1303 }
1304
1305 pub(crate) fn stream_end(&self) {
1306 unsafe {
1307 StreamConsumerStreamEnd(self.0);
1308 }
1309 }
1310
1311 pub(crate) fn stream_error(&self, error_code: usize) {
1312 unsafe {
1313 StreamConsumerStreamError(self.0, error_code);
1314 }
1315 }
1316
1317 pub(crate) fn note_response_urls(
1318 &self,
1319 maybe_url: Option<String>,
1320 maybe_source_map_url: Option<String>,
1321 ) {
1322 unsafe {
1323 let maybe_url = maybe_url.map(|url| CString::new(url).unwrap());
1324 let maybe_source_map_url = maybe_source_map_url.map(|url| CString::new(url).unwrap());
1325
1326 let maybe_url_param = match maybe_url.as_ref() {
1327 Some(url) => url.as_ptr(),
1328 None => ptr::null(),
1329 };
1330 let maybe_source_map_url_param = match maybe_source_map_url.as_ref() {
1331 Some(url) => url.as_ptr(),
1332 None => ptr::null(),
1333 };
1334
1335 StreamConsumerNoteResponseURLs(self.0, maybe_url_param, maybe_source_map_url_param);
1336 }
1337 }
1338}
1339
1340#[expect(unsafe_code)]
1343unsafe extern "C" fn consume_stream(
1344 cx: *mut RawJSContext,
1345 obj: HandleObject,
1346 _mime_type: MimeType,
1347 _consumer: *mut JSStreamConsumer,
1348) -> bool {
1349 let cx = unsafe { JSContext::from_ptr(cx) };
1350 let in_realm_proof = AlreadyInRealm::assert_for_cx(cx);
1351 let global = unsafe { GlobalScope::from_context(*cx, InRealm::Already(&in_realm_proof)) };
1352
1353 if let Ok(unwrapped_source) =
1355 root_from_handleobject::<Response>(unsafe { RustHandleObject::from_raw(obj) }, *cx)
1356 {
1357 let mimetype = unwrapped_source.Headers(CanGc::note()).extract_mime_type();
1359
1360 if !&mimetype[..].eq_ignore_ascii_case(b"application/wasm") {
1362 throw_dom_exception(
1363 cx,
1364 &global,
1365 Error::Type(c"Response has unsupported MIME type".to_owned()),
1366 CanGc::note(),
1367 );
1368 return false;
1369 }
1370
1371 match unwrapped_source.Type() {
1373 DOMResponseType::Basic | DOMResponseType::Cors | DOMResponseType::Default => {},
1374 _ => {
1375 throw_dom_exception(
1376 cx,
1377 &global,
1378 Error::Type(c"Response.type must be 'basic', 'cors' or 'default'".to_owned()),
1379 CanGc::note(),
1380 );
1381 return false;
1382 },
1383 }
1384
1385 if !unwrapped_source.Ok() {
1387 throw_dom_exception(
1388 cx,
1389 &global,
1390 Error::Type(c"Response does not have ok status".to_owned()),
1391 CanGc::note(),
1392 );
1393 return false;
1394 }
1395
1396 if unwrapped_source.is_locked() {
1398 throw_dom_exception(
1399 cx,
1400 &global,
1401 Error::Type(c"There was an error consuming the Response".to_owned()),
1402 CanGc::note(),
1403 );
1404 return false;
1405 }
1406
1407 if unwrapped_source.is_disturbed() {
1409 throw_dom_exception(
1410 cx,
1411 &global,
1412 Error::Type(c"Response already consumed".to_owned()),
1413 CanGc::note(),
1414 );
1415 return false;
1416 }
1417 unwrapped_source.set_stream_consumer(Some(StreamConsumer(_consumer)));
1418 } else {
1419 throw_dom_exception(
1421 cx,
1422 &global,
1423 Error::Type(c"expected Response or Promise resolving to Response".to_owned()),
1424 CanGc::note(),
1425 );
1426 return false;
1427 }
1428 true
1429}
1430
1431#[expect(unsafe_code)]
1432unsafe extern "C" fn report_stream_error(_cx: *mut RawJSContext, error_code: usize) {
1433 error!("Error initializing StreamConsumer: {:?}", unsafe {
1434 RUST_js_GetErrorMessage(ptr::null_mut(), error_code as u32)
1435 });
1436}
1437
1438#[expect(unsafe_code)]
1439unsafe extern "C" fn invoke_script_environment_preparer(
1440 global: HandleObject,
1441 closure: *mut ScriptEnvironmentPreparer_Closure,
1442) {
1443 let cx = GlobalScope::get_cx();
1444 let global = unsafe { GlobalScope::from_object(global.get()) };
1445 let ar = enter_realm(&*global);
1446
1447 run_a_script::<DomTypeHolder, _>(&global, || {
1448 if unsafe { !RunScriptEnvironmentPreparerClosure(*cx, closure) } {
1449 report_pending_exception(cx, InRealm::Entered(&ar), CanGc::note());
1450 };
1451 });
1452}
1453
1454pub(crate) struct Runnable(*mut DispatchablePointer);
1455
1456#[expect(unsafe_code)]
1457unsafe impl Sync for Runnable {}
1458#[expect(unsafe_code)]
1459unsafe impl Send for Runnable {}
1460
1461#[expect(unsafe_code)]
1462impl Runnable {
1463 fn run(&self, cx: *mut RawJSContext, maybe_shutting_down: Dispatchable_MaybeShuttingDown) {
1464 unsafe {
1465 DispatchableRun(cx, self.0, maybe_shutting_down);
1466 }
1467 }
1468}
1469
1470pub(crate) use script_bindings::script_runtime::CanGc;
1471
1472pub(crate) struct IntroductionType;
1478impl IntroductionType {
1479 pub const EVAL: &CStr = c"eval";
1481 pub const EVAL_STR: &str = "eval";
1482
1483 pub const DEBUGGER_EVAL: &CStr = c"debugger eval";
1486 pub const DEBUGGER_EVAL_STR: &str = "debugger eval";
1487
1488 pub const FUNCTION: &CStr = c"Function";
1490 pub const FUNCTION_STR: &str = "Function";
1491
1492 pub const WORKLET: &CStr = c"Worklet";
1494 pub const WORKLET_STR: &str = "Worklet";
1495
1496 pub const EVENT_HANDLER: &CStr = c"eventHandler";
1498 pub const EVENT_HANDLER_STR: &str = "eventHandler";
1499
1500 pub const SRC_SCRIPT: &CStr = c"srcScript";
1503 pub const SRC_SCRIPT_STR: &str = "srcScript";
1504
1505 pub const INLINE_SCRIPT: &CStr = c"inlineScript";
1508 pub const INLINE_SCRIPT_STR: &str = "inlineScript";
1509
1510 pub const INJECTED_SCRIPT: &CStr = c"injectedScript";
1516 pub const INJECTED_SCRIPT_STR: &str = "injectedScript";
1517
1518 pub const IMPORTED_MODULE: &CStr = c"importedModule";
1521 pub const IMPORTED_MODULE_STR: &str = "importedModule";
1522
1523 pub const JAVASCRIPT_URL: &CStr = c"javascriptURL";
1525 pub const JAVASCRIPT_URL_STR: &str = "javascriptURL";
1526
1527 pub const DOM_TIMER: &CStr = c"domTimer";
1529 pub const DOM_TIMER_STR: &str = "domTimer";
1530
1531 pub const WORKER: &CStr = c"Worker";
1535 pub const WORKER_STR: &str = "Worker";
1536}