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