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, Weak};
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, Trace,
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::ScriptThread;
66use crate::dom::bindings::codegen::Bindings::PromiseBinding::PromiseJobCallback;
67use crate::dom::bindings::codegen::Bindings::ResponseBinding::Response_Binding::ResponseMethods;
68use crate::dom::bindings::codegen::Bindings::ResponseBinding::ResponseType as DOMResponseType;
69use crate::dom::bindings::codegen::UnionTypes::TrustedScriptOrString;
70use crate::dom::bindings::conversions::{
71 get_dom_class, private_from_object, root_from_handleobject, root_from_object,
72};
73use crate::dom::bindings::error::{Error, throw_dom_exception};
74use crate::dom::bindings::inheritance::Castable;
75use crate::dom::bindings::refcounted::{
76 LiveDOMReferences, Trusted, TrustedPromise, trace_refcounted_objects,
77};
78use crate::dom::bindings::reflector::{DomGlobal, DomObject};
79use crate::dom::bindings::root::trace_roots;
80use crate::dom::bindings::str::DOMString;
81use crate::dom::bindings::utils::DOM_CALLBACKS;
82use crate::dom::bindings::{principals, settings_stack};
83use crate::dom::csp::CspReporting;
84use crate::dom::event::{Event, EventBubbles, EventCancelable};
85use crate::dom::eventtarget::EventTarget;
86use crate::dom::globalscope::GlobalScope;
87use crate::dom::promise::Promise;
88use crate::dom::promiserejectionevent::PromiseRejectionEvent;
89use crate::dom::response::Response;
90use crate::dom::trustedscript::TrustedScript;
91use crate::messaging::{CommonScriptMsg, ScriptEventLoopSender};
92use crate::microtask::{EnqueuedPromiseCallback, Microtask, MicrotaskQueue};
93use crate::realms::{AlreadyInRealm, InRealm, enter_realm};
94use crate::script_module::EnsureModuleHooksInitialized;
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(Default, JSTraceable, MallocSizeOf)]
675struct RuntimeCallbackData {
676 script_event_loop_sender: Option<ScriptEventLoopSender>,
677 #[no_trace]
678 #[ignore_malloc_size_of = "ScriptThread measures its own memory itself."]
679 script_thread: Option<Weak<ScriptThread>>,
680}
681
682#[derive(JSTraceable, MallocSizeOf)]
683pub(crate) struct Runtime {
684 #[ignore_malloc_size_of = "Type from mozjs"]
685 rt: RustRuntime,
686 #[conditional_malloc_size_of]
688 pub(crate) microtask_queue: Rc<MicrotaskQueue>,
689 #[ignore_malloc_size_of = "Type from mozjs"]
690 job_queue: *mut JobQueue,
691 runtime_callback_data: Box<RuntimeCallbackData>,
693}
694
695impl Runtime {
696 #[expect(unsafe_code)]
706 pub(crate) fn new(main_thread_sender: Option<ScriptEventLoopSender>) -> Runtime {
707 unsafe { Self::new_with_parent(None, main_thread_sender) }
708 }
709
710 #[allow(unsafe_code)]
711 pub(crate) unsafe fn cx(&self) -> js::context::JSContext {
715 unsafe { js::context::JSContext::from_ptr(RustRuntime::get().unwrap()) }
716 }
717
718 #[expect(unsafe_code)]
731 pub(crate) unsafe fn new_with_parent(
732 parent: Option<ParentRuntime>,
733 script_event_loop_sender: Option<ScriptEventLoopSender>,
734 ) -> Runtime {
735 let mut runtime = if let Some(parent) = parent {
736 unsafe { RustRuntime::create_with_parent(parent) }
737 } else {
738 RustRuntime::new(JS_ENGINE.lock().unwrap().as_ref().unwrap().clone())
739 };
740 let cx = runtime.cx();
741
742 let have_event_loop_sender = script_event_loop_sender.is_some();
743 let runtime_callback_data = Box::new(RuntimeCallbackData {
744 script_event_loop_sender,
745 script_thread: None,
746 });
747 let runtime_callback_data = Box::into_raw(runtime_callback_data);
748
749 unsafe {
750 JS_AddExtraGCRootsTracer(
751 cx,
752 Some(trace_rust_roots),
753 runtime_callback_data as *mut c_void,
754 );
755
756 JS_SetSecurityCallbacks(cx, &SECURITY_CALLBACKS);
757
758 JS_InitDestroyPrincipalsCallback(cx, Some(principals::destroy_servo_jsprincipal));
759 JS_InitReadPrincipalsCallback(cx, Some(principals::read_jsprincipal));
760
761 if cfg!(debug_assertions) {
763 JS_SetGCCallback(cx, Some(debug_gc_callback), ptr::null_mut());
764 }
765
766 if opts::get().debug.gc_profile {
767 SetGCSliceCallback(cx, Some(gc_slice_callback));
768 }
769 }
770
771 unsafe extern "C" fn empty_wrapper_callback(_: *mut RawJSContext, _: HandleObject) -> bool {
772 true
773 }
774 unsafe extern "C" fn empty_has_released_callback(_: HandleObject) -> bool {
775 false
777 }
778
779 unsafe {
780 SetDOMCallbacks(cx, &DOM_CALLBACKS);
781 SetPreserveWrapperCallbacks(
782 cx,
783 Some(empty_wrapper_callback),
784 Some(empty_has_released_callback),
785 );
786 JS_SetGCParameter(cx, JSGCParamKey::JSGC_INCREMENTAL_GC_ENABLED, 0);
788 }
789
790 unsafe extern "C" fn dispatch_to_event_loop(
791 data: *mut c_void,
792 dispatchable: *mut DispatchablePointer,
793 ) -> bool {
794 let runtime_callback_data: &RuntimeCallbackData =
795 unsafe { &*(data as *mut RuntimeCallbackData) };
796 let Some(script_event_loop_sender) =
797 runtime_callback_data.script_event_loop_sender.as_ref()
798 else {
799 return false;
800 };
801
802 let runnable = Runnable(dispatchable);
803 let task = task!(dispatch_to_event_loop_message: move || {
804 if let Some(cx) = RustRuntime::get() {
805 runnable.run(cx.as_ptr(), Dispatchable_MaybeShuttingDown::NotShuttingDown);
806 }
807 });
808
809 script_event_loop_sender
810 .send(CommonScriptMsg::Task(
811 ScriptThreadEventCategory::NetworkEvent,
812 Box::new(task),
813 None, TaskSourceName::Networking,
815 ))
816 .is_ok()
817 }
818
819 if have_event_loop_sender {
820 unsafe {
821 SetUpEventLoopDispatch(
822 cx,
823 Some(dispatch_to_event_loop),
824 runtime_callback_data as *mut c_void,
825 );
826 }
827 }
828
829 unsafe {
830 InitConsumeStreamCallback(cx, Some(consume_stream), Some(report_stream_error));
831 }
832
833 let microtask_queue = Rc::new(MicrotaskQueue::default());
834
835 let interrupt_queues: Box<Vec<Rc<MicrotaskQueue>>> = Box::default();
839
840 let cx_opts;
841 let job_queue;
842 unsafe {
843 let cx = runtime.cx();
844 job_queue = CreateJobQueue(
845 &JOB_QUEUE_TRAPS,
846 &*microtask_queue as *const _ as *const c_void,
847 Box::into_raw(interrupt_queues) as *mut c_void,
848 );
849 SetJobQueue(cx, job_queue);
850 SetPromiseRejectionTrackerCallback(
851 cx,
852 Some(promise_rejection_tracker),
853 ptr::null_mut(),
854 );
855
856 EnsureModuleHooksInitialized(runtime.rt());
857
858 let cx = runtime.cx();
859
860 set_gc_zeal_options(cx.raw_cx());
861
862 cx_opts = &mut *ContextOptionsRef(cx);
864 JS_SetGlobalJitCompilerOption(
865 cx,
866 JSJitCompilerOption::JSJITCOMPILER_BASELINE_INTERPRETER_ENABLE,
867 pref!(js_baseline_interpreter_enabled) as u32,
868 );
869 JS_SetGlobalJitCompilerOption(
870 cx,
871 JSJitCompilerOption::JSJITCOMPILER_BASELINE_ENABLE,
872 pref!(js_baseline_jit_enabled) as u32,
873 );
874 JS_SetGlobalJitCompilerOption(
875 cx,
876 JSJitCompilerOption::JSJITCOMPILER_ION_ENABLE,
877 pref!(js_ion_enabled) as u32,
878 );
879 }
880 cx_opts.compileOptions_.asmJSOption_ = if pref!(js_asmjs_enabled) {
881 AsmJSOption::Enabled
882 } else {
883 AsmJSOption::DisabledByAsmJSPref
884 };
885 cx_opts.compileOptions_.set_importAttributes_(true);
886 let wasm_enabled = pref!(js_wasm_enabled);
887 cx_opts.set_wasm_(wasm_enabled);
888 if wasm_enabled {
889 unsafe { SetProcessBuildIdOp(Some(servo_build_id)) };
893 }
894 cx_opts.set_wasmBaseline_(pref!(js_wasm_baseline_enabled));
895 cx_opts.set_wasmIon_(pref!(js_wasm_ion_enabled));
896
897 unsafe {
898 let cx = runtime.cx();
899 JS_SetGlobalJitCompilerOption(
901 cx,
902 JSJitCompilerOption::JSJITCOMPILER_NATIVE_REGEXP_ENABLE,
903 pref!(js_native_regex_enabled) as u32,
904 );
905 JS_SetOffthreadIonCompilationEnabled(cx, pref!(js_offthread_compilation_enabled));
906 JS_SetGlobalJitCompilerOption(
907 cx,
908 JSJitCompilerOption::JSJITCOMPILER_BASELINE_WARMUP_TRIGGER,
909 if pref!(js_baseline_jit_unsafe_eager_compilation_enabled) {
910 0
911 } else {
912 u32::MAX
913 },
914 );
915 JS_SetGlobalJitCompilerOption(
916 cx,
917 JSJitCompilerOption::JSJITCOMPILER_ION_NORMAL_WARMUP_TRIGGER,
918 if pref!(js_ion_unsafe_eager_compilation_enabled) {
919 0
920 } else {
921 u32::MAX
922 },
923 );
924 JS_SetGCParameter(
930 cx,
931 JSGCParamKey::JSGC_MAX_BYTES,
932 in_range(pref!(js_mem_max), 1, 0x100)
933 .map(|val| (val * 1024 * 1024) as u32)
934 .unwrap_or(u32::MAX),
935 );
936 JS_SetGCParameter(
938 cx,
939 JSGCParamKey::JSGC_INCREMENTAL_GC_ENABLED,
940 pref!(js_mem_gc_incremental_enabled) as u32,
941 );
942 JS_SetGCParameter(
943 cx,
944 JSGCParamKey::JSGC_PER_ZONE_GC_ENABLED,
945 pref!(js_mem_gc_per_zone_enabled) as u32,
946 );
947 if let Some(val) = in_range(pref!(js_mem_gc_incremental_slice_ms), 0, 100_000) {
948 JS_SetGCParameter(cx, JSGCParamKey::JSGC_SLICE_TIME_BUDGET_MS, val as u32);
949 }
950 JS_SetGCParameter(
951 cx,
952 JSGCParamKey::JSGC_COMPACTING_ENABLED,
953 pref!(js_mem_gc_compacting_enabled) as u32,
954 );
955
956 if let Some(val) = in_range(pref!(js_mem_gc_high_frequency_time_limit_ms), 0, 10_000) {
957 JS_SetGCParameter(cx, JSGCParamKey::JSGC_HIGH_FREQUENCY_TIME_LIMIT, val as u32);
958 }
959 if let Some(val) = in_range(pref!(js_mem_gc_low_frequency_heap_growth), 0, 10_000) {
960 JS_SetGCParameter(cx, JSGCParamKey::JSGC_LOW_FREQUENCY_HEAP_GROWTH, val as u32);
961 }
962 if let Some(val) = in_range(pref!(js_mem_gc_high_frequency_heap_growth_min), 0, 10_000)
963 {
964 JS_SetGCParameter(
965 cx,
966 JSGCParamKey::JSGC_HIGH_FREQUENCY_LARGE_HEAP_GROWTH,
967 val as u32,
968 );
969 }
970 if let Some(val) = in_range(pref!(js_mem_gc_high_frequency_heap_growth_max), 0, 10_000)
971 {
972 JS_SetGCParameter(
973 cx,
974 JSGCParamKey::JSGC_HIGH_FREQUENCY_SMALL_HEAP_GROWTH,
975 val as u32,
976 );
977 }
978 if let Some(val) = in_range(pref!(js_mem_gc_high_frequency_low_limit_mb), 0, 10_000) {
979 JS_SetGCParameter(cx, JSGCParamKey::JSGC_SMALL_HEAP_SIZE_MAX, val as u32);
980 }
981 if let Some(val) = in_range(pref!(js_mem_gc_high_frequency_high_limit_mb), 0, 10_000) {
982 JS_SetGCParameter(cx, JSGCParamKey::JSGC_LARGE_HEAP_SIZE_MIN, val as u32);
983 }
984 if let Some(val) = in_range(pref!(js_mem_gc_empty_chunk_count_min), 0, 10_000) {
995 JS_SetGCParameter(cx, JSGCParamKey::JSGC_MIN_EMPTY_CHUNK_COUNT, val as u32);
996 }
997 }
998 Runtime {
999 rt: runtime,
1000 microtask_queue,
1001 job_queue,
1002 runtime_callback_data: unsafe { Box::from_raw(runtime_callback_data) },
1003 }
1004 }
1005
1006 pub(crate) fn set_script_thread(&mut self, script_thread: Weak<ScriptThread>) {
1007 self.runtime_callback_data
1008 .script_thread
1009 .replace(script_thread);
1010 }
1011
1012 pub(crate) fn thread_safe_js_context(&self) -> ThreadSafeJSContext {
1013 self.rt.thread_safe_js_context()
1014 }
1015}
1016
1017impl Drop for Runtime {
1018 #[expect(unsafe_code)]
1019 fn drop(&mut self) {
1020 self.microtask_queue.clear();
1022
1023 unsafe {
1025 DeleteJobQueue(self.job_queue);
1026 }
1027 LiveDOMReferences::destruct();
1028 mark_runtime_dead();
1029 }
1030}
1031
1032impl Deref for Runtime {
1033 type Target = RustRuntime;
1034 fn deref(&self) -> &RustRuntime {
1035 &self.rt
1036 }
1037}
1038
1039impl DerefMut for Runtime {
1040 fn deref_mut(&mut self) -> &mut RustRuntime {
1041 &mut self.rt
1042 }
1043}
1044
1045pub struct JSEngineSetup(JSEngine);
1046
1047impl Default for JSEngineSetup {
1048 fn default() -> Self {
1049 let engine = JSEngine::init().unwrap();
1050 *JS_ENGINE.lock().unwrap() = Some(engine.handle());
1051 Self(engine)
1052 }
1053}
1054
1055impl Drop for JSEngineSetup {
1056 fn drop(&mut self) {
1057 *JS_ENGINE.lock().unwrap() = None;
1058
1059 while !self.0.can_shutdown() {
1060 thread::sleep(Duration::from_millis(50));
1061 }
1062 }
1063}
1064
1065static JS_ENGINE: Mutex<Option<JSEngineHandle>> = Mutex::new(None);
1066
1067fn in_range<T: PartialOrd + Copy>(val: T, min: T, max: T) -> Option<T> {
1068 if val < min || val >= max {
1069 None
1070 } else {
1071 Some(val)
1072 }
1073}
1074
1075thread_local!(static MALLOC_SIZE_OF_OPS: Cell<*mut MallocSizeOfOps> = const { Cell::new(ptr::null_mut()) });
1076
1077#[expect(unsafe_code)]
1078unsafe extern "C" fn get_size(obj: *mut JSObject) -> usize {
1079 match unsafe { get_dom_class(obj) } {
1080 Ok(v) => {
1081 let dom_object = unsafe { private_from_object(obj) as *const c_void };
1082
1083 if dom_object.is_null() {
1084 return 0;
1085 }
1086 let ops = MALLOC_SIZE_OF_OPS.get();
1087 unsafe { (v.malloc_size_of)(&mut *ops, dom_object) }
1088 },
1089 Err(_e) => 0,
1090 }
1091}
1092
1093thread_local!(static GC_CYCLE_START: Cell<Option<Instant>> = const { Cell::new(None) });
1094thread_local!(static GC_SLICE_START: Cell<Option<Instant>> = const { Cell::new(None) });
1095
1096#[expect(unsafe_code)]
1097unsafe extern "C" fn gc_slice_callback(
1098 _cx: *mut RawJSContext,
1099 progress: GCProgress,
1100 desc: *const GCDescription,
1101) {
1102 match progress {
1103 GCProgress::GC_CYCLE_BEGIN => GC_CYCLE_START.with(|start| {
1104 start.set(Some(Instant::now()));
1105 println!("GC cycle began");
1106 }),
1107 GCProgress::GC_SLICE_BEGIN => GC_SLICE_START.with(|start| {
1108 start.set(Some(Instant::now()));
1109 println!("GC slice began");
1110 }),
1111 GCProgress::GC_SLICE_END => GC_SLICE_START.with(|start| {
1112 let duration = start.get().unwrap().elapsed();
1113 start.set(None);
1114 println!("GC slice ended: duration={:?}", duration);
1115 }),
1116 GCProgress::GC_CYCLE_END => GC_CYCLE_START.with(|start| {
1117 let duration = start.get().unwrap().elapsed();
1118 start.set(None);
1119 println!("GC cycle ended: duration={:?}", duration);
1120 }),
1121 };
1122 if !desc.is_null() {
1123 let desc: &GCDescription = unsafe { &*desc };
1124 let options = match desc.options_ {
1125 GCOptions::Normal => "Normal",
1126 GCOptions::Shrink => "Shrink",
1127 GCOptions::Shutdown => "Shutdown",
1128 };
1129 println!(" isZone={}, options={}", desc.isZone_, options);
1130 }
1131 let _ = stdout().flush();
1132}
1133
1134#[expect(unsafe_code)]
1135unsafe extern "C" fn debug_gc_callback(
1136 _cx: *mut RawJSContext,
1137 status: JSGCStatus,
1138 _reason: GCReason,
1139 _data: *mut os::raw::c_void,
1140) {
1141 match status {
1142 JSGCStatus::JSGC_BEGIN => thread_state::enter(ThreadState::IN_GC),
1143 JSGCStatus::JSGC_END => thread_state::exit(ThreadState::IN_GC),
1144 }
1145}
1146
1147#[expect(unsafe_code)]
1148unsafe extern "C" fn trace_rust_roots(tr: *mut JSTracer, data: *mut os::raw::c_void) {
1149 if !runtime_is_alive() {
1150 return;
1151 }
1152 trace!("starting custom root handler");
1153
1154 let runtime_callback_data = unsafe { &*(data as *const RuntimeCallbackData) };
1155 if let Some(script_thread) = runtime_callback_data
1156 .script_thread
1157 .as_ref()
1158 .and_then(Weak::upgrade)
1159 {
1160 trace!("tracing fields of ScriptThread");
1161 unsafe { script_thread.trace(tr) };
1162 };
1163
1164 unsafe {
1165 trace_roots(tr);
1166 trace_refcounted_objects(tr);
1167 settings_stack::trace(tr);
1168 }
1169 trace!("done custom root handler");
1170}
1171
1172#[expect(unsafe_code)]
1173unsafe extern "C" fn servo_build_id(build_id: *mut BuildIdCharVector) -> bool {
1174 let servo_id = b"Servo\0";
1175 unsafe { SetBuildId(build_id, servo_id[0] as *const c_char, servo_id.len()) }
1176}
1177
1178#[expect(unsafe_code)]
1179#[cfg(feature = "debugmozjs")]
1180unsafe fn set_gc_zeal_options(cx: *mut RawJSContext) {
1181 use js::jsapi::SetGCZeal;
1182
1183 let level = match pref!(js_mem_gc_zeal_level) {
1184 level @ 0..=14 => level as u8,
1185 _ => return,
1186 };
1187 let frequency = match pref!(js_mem_gc_zeal_frequency) {
1188 frequency if frequency >= 0 => frequency as u32,
1189 _ => 5000,
1191 };
1192 unsafe {
1193 SetGCZeal(cx, level, frequency);
1194 }
1195}
1196
1197#[expect(unsafe_code)]
1198#[cfg(not(feature = "debugmozjs"))]
1199unsafe fn set_gc_zeal_options(_: *mut RawJSContext) {}
1200
1201pub(crate) use script_bindings::script_runtime::JSContext;
1202
1203pub(crate) trait JSContextHelper {
1206 fn get_reports(&self, path_seg: String, ops: &mut MallocSizeOfOps) -> Vec<Report>;
1207}
1208
1209impl JSContextHelper for JSContext {
1210 #[expect(unsafe_code)]
1211 fn get_reports(&self, path_seg: String, ops: &mut MallocSizeOfOps) -> Vec<Report> {
1212 MALLOC_SIZE_OF_OPS.with(|ops_tls| ops_tls.set(ops));
1213 let stats = unsafe {
1214 let mut stats = ::std::mem::zeroed();
1215 if !CollectServoSizes(**self, &mut stats, Some(get_size)) {
1216 return vec![];
1217 }
1218 stats
1219 };
1220 MALLOC_SIZE_OF_OPS.with(|ops| ops.set(ptr::null_mut()));
1221
1222 let mut reports = vec![];
1223 let mut report = |mut path_suffix, kind, size| {
1224 let mut path = path![path_seg, "js"];
1225 path.append(&mut path_suffix);
1226 reports.push(Report { path, kind, size })
1227 };
1228
1229 report(
1233 path!["gc-heap", "used"],
1234 ReportKind::ExplicitNonHeapSize,
1235 stats.gcHeapUsed,
1236 );
1237
1238 report(
1239 path!["gc-heap", "unused"],
1240 ReportKind::ExplicitNonHeapSize,
1241 stats.gcHeapUnused,
1242 );
1243
1244 report(
1245 path!["gc-heap", "admin"],
1246 ReportKind::ExplicitNonHeapSize,
1247 stats.gcHeapAdmin,
1248 );
1249
1250 report(
1251 path!["gc-heap", "decommitted"],
1252 ReportKind::ExplicitNonHeapSize,
1253 stats.gcHeapDecommitted,
1254 );
1255
1256 report(
1258 path!["malloc-heap"],
1259 ReportKind::ExplicitSystemHeapSize,
1260 stats.mallocHeap,
1261 );
1262
1263 report(
1264 path!["non-heap"],
1265 ReportKind::ExplicitNonHeapSize,
1266 stats.nonHeap,
1267 );
1268 reports
1269 }
1270}
1271
1272pub(crate) struct StreamConsumer(*mut JSStreamConsumer);
1273
1274#[expect(unsafe_code)]
1275impl StreamConsumer {
1276 pub(crate) fn consume_chunk(&self, stream: &[u8]) -> bool {
1277 unsafe {
1278 let stream_ptr = stream.as_ptr();
1279 StreamConsumerConsumeChunk(self.0, stream_ptr, stream.len())
1280 }
1281 }
1282
1283 pub(crate) fn stream_end(&self) {
1284 unsafe {
1285 StreamConsumerStreamEnd(self.0);
1286 }
1287 }
1288
1289 pub(crate) fn stream_error(&self, error_code: usize) {
1290 unsafe {
1291 StreamConsumerStreamError(self.0, error_code);
1292 }
1293 }
1294
1295 pub(crate) fn note_response_urls(
1296 &self,
1297 maybe_url: Option<String>,
1298 maybe_source_map_url: Option<String>,
1299 ) {
1300 unsafe {
1301 let maybe_url = maybe_url.map(|url| CString::new(url).unwrap());
1302 let maybe_source_map_url = maybe_source_map_url.map(|url| CString::new(url).unwrap());
1303
1304 let maybe_url_param = match maybe_url.as_ref() {
1305 Some(url) => url.as_ptr(),
1306 None => ptr::null(),
1307 };
1308 let maybe_source_map_url_param = match maybe_source_map_url.as_ref() {
1309 Some(url) => url.as_ptr(),
1310 None => ptr::null(),
1311 };
1312
1313 StreamConsumerNoteResponseURLs(self.0, maybe_url_param, maybe_source_map_url_param);
1314 }
1315 }
1316}
1317
1318#[expect(unsafe_code)]
1321unsafe extern "C" fn consume_stream(
1322 cx: *mut RawJSContext,
1323 obj: HandleObject,
1324 _mime_type: MimeType,
1325 _consumer: *mut JSStreamConsumer,
1326) -> bool {
1327 let cx = unsafe { JSContext::from_ptr(cx) };
1328 let in_realm_proof = AlreadyInRealm::assert_for_cx(cx);
1329 let global = unsafe { GlobalScope::from_context(*cx, InRealm::Already(&in_realm_proof)) };
1330
1331 if let Ok(unwrapped_source) =
1333 root_from_handleobject::<Response>(unsafe { RustHandleObject::from_raw(obj) }, *cx)
1334 {
1335 let mimetype = unwrapped_source.Headers(CanGc::note()).extract_mime_type();
1337
1338 if !&mimetype[..].eq_ignore_ascii_case(b"application/wasm") {
1340 throw_dom_exception(
1341 cx,
1342 &global,
1343 Error::Type(c"Response has unsupported MIME type".to_owned()),
1344 CanGc::note(),
1345 );
1346 return false;
1347 }
1348
1349 match unwrapped_source.Type() {
1351 DOMResponseType::Basic | DOMResponseType::Cors | DOMResponseType::Default => {},
1352 _ => {
1353 throw_dom_exception(
1354 cx,
1355 &global,
1356 Error::Type(c"Response.type must be 'basic', 'cors' or 'default'".to_owned()),
1357 CanGc::note(),
1358 );
1359 return false;
1360 },
1361 }
1362
1363 if !unwrapped_source.Ok() {
1365 throw_dom_exception(
1366 cx,
1367 &global,
1368 Error::Type(c"Response does not have ok status".to_owned()),
1369 CanGc::note(),
1370 );
1371 return false;
1372 }
1373
1374 if unwrapped_source.is_locked() {
1376 throw_dom_exception(
1377 cx,
1378 &global,
1379 Error::Type(c"There was an error consuming the Response".to_owned()),
1380 CanGc::note(),
1381 );
1382 return false;
1383 }
1384
1385 if unwrapped_source.is_disturbed() {
1387 throw_dom_exception(
1388 cx,
1389 &global,
1390 Error::Type(c"Response already consumed".to_owned()),
1391 CanGc::note(),
1392 );
1393 return false;
1394 }
1395 unwrapped_source.set_stream_consumer(Some(StreamConsumer(_consumer)));
1396 } else {
1397 throw_dom_exception(
1399 cx,
1400 &global,
1401 Error::Type(c"expected Response or Promise resolving to Response".to_owned()),
1402 CanGc::note(),
1403 );
1404 return false;
1405 }
1406 true
1407}
1408
1409#[expect(unsafe_code)]
1410unsafe extern "C" fn report_stream_error(_cx: *mut RawJSContext, error_code: usize) {
1411 error!("Error initializing StreamConsumer: {:?}", unsafe {
1412 RUST_js_GetErrorMessage(ptr::null_mut(), error_code as u32)
1413 });
1414}
1415
1416pub(crate) struct Runnable(*mut DispatchablePointer);
1417
1418#[expect(unsafe_code)]
1419unsafe impl Sync for Runnable {}
1420#[expect(unsafe_code)]
1421unsafe impl Send for Runnable {}
1422
1423#[expect(unsafe_code)]
1424impl Runnable {
1425 fn run(&self, cx: *mut RawJSContext, maybe_shutting_down: Dispatchable_MaybeShuttingDown) {
1426 unsafe {
1427 DispatchableRun(cx, self.0, maybe_shutting_down);
1428 }
1429 }
1430}
1431
1432pub(crate) use script_bindings::script_runtime::CanGc;
1433
1434pub(crate) struct IntroductionType;
1440impl IntroductionType {
1441 pub const EVAL: &CStr = c"eval";
1443 pub const EVAL_STR: &str = "eval";
1444
1445 pub const DEBUGGER_EVAL: &CStr = c"debugger eval";
1448 pub const DEBUGGER_EVAL_STR: &str = "debugger eval";
1449
1450 pub const FUNCTION: &CStr = c"Function";
1452 pub const FUNCTION_STR: &str = "Function";
1453
1454 pub const WORKLET: &CStr = c"Worklet";
1456 pub const WORKLET_STR: &str = "Worklet";
1457
1458 pub const EVENT_HANDLER: &CStr = c"eventHandler";
1460 pub const EVENT_HANDLER_STR: &str = "eventHandler";
1461
1462 pub const SRC_SCRIPT: &CStr = c"srcScript";
1465 pub const SRC_SCRIPT_STR: &str = "srcScript";
1466
1467 pub const INLINE_SCRIPT: &CStr = c"inlineScript";
1470 pub const INLINE_SCRIPT_STR: &str = "inlineScript";
1471
1472 pub const INJECTED_SCRIPT: &CStr = c"injectedScript";
1478 pub const INJECTED_SCRIPT_STR: &str = "injectedScript";
1479
1480 pub const IMPORTED_MODULE: &CStr = c"importedModule";
1483 pub const IMPORTED_MODULE_STR: &str = "importedModule";
1484
1485 pub const JAVASCRIPT_URL: &CStr = c"javascriptURL";
1487 pub const JAVASCRIPT_URL_STR: &str = "javascriptURL";
1488
1489 pub const DOM_TIMER: &CStr = c"domTimer";
1491 pub const DOM_TIMER_STR: &str = "domTimer";
1492
1493 pub const WORKER: &CStr = c"Worker";
1497 pub const WORKER_STR: &str = "Worker";
1498}