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