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