1#![allow(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)]
411#[cfg_attr(crown, allow(crown::unrooted_must_root))]
412unsafe extern "C" fn promise_rejection_tracker(
414 cx: *mut RawJSContext,
415 _muted_errors: bool,
416 promise: HandleObject,
417 state: PromiseRejectionHandlingState,
418 _data: *mut c_void,
419) {
420 let cx = unsafe { JSContext::from_ptr(cx) };
424 let in_realm_proof = AlreadyInRealm::assert_for_cx(cx);
425 let global = unsafe { GlobalScope::from_context(*cx, InRealm::Already(&in_realm_proof)) };
426
427 wrap_panic(&mut || {
428 match state {
429 PromiseRejectionHandlingState::Unhandled => {
431 global.add_uncaught_rejection(promise);
432 },
433 PromiseRejectionHandlingState::Handled => {
435 if global
437 .get_uncaught_rejections()
438 .borrow()
439 .contains(&Heap::boxed(promise.get()))
440 {
441 global.remove_uncaught_rejection(promise);
442 return;
443 }
444
445 if !global
447 .get_consumed_rejections()
448 .borrow()
449 .contains(&Heap::boxed(promise.get()))
450 {
451 return;
452 }
453
454 global.remove_consumed_rejection(promise);
456
457 let target = Trusted::new(global.upcast::<EventTarget>());
458 let promise =
459 Promise::new_with_js_promise(unsafe { Handle::from_raw(promise) }, cx);
460 let trusted_promise = TrustedPromise::new(promise.clone());
461
462 global.task_manager().dom_manipulation_task_source().queue(
464 task!(rejection_handled_event: move || {
465 let target = target.root();
466 let cx = GlobalScope::get_cx();
467 let root_promise = trusted_promise.root();
468
469 rooted!(in(*cx) let mut reason = UndefinedValue());
470 unsafe{JS_GetPromiseResult(root_promise.reflector().get_jsobject(), reason.handle_mut())};
471
472 let event = PromiseRejectionEvent::new(
473 &target.global(),
474 atom!("rejectionhandled"),
475 EventBubbles::DoesNotBubble,
476 EventCancelable::Cancelable,
477 root_promise,
478 reason.handle(),
479 CanGc::note()
480 );
481
482 event.upcast::<Event>().fire(&target, CanGc::note());
483 })
484 );
485 },
486 };
487 })
488}
489
490#[expect(unsafe_code)]
491fn safely_convert_null_to_string(cx: JSContext, str_: HandleString) -> DOMString {
492 DOMString::from(match std::ptr::NonNull::new(*str_) {
493 None => "".to_owned(),
494 Some(str_) => unsafe { jsstr_to_string(*cx, str_) },
495 })
496}
497
498#[expect(unsafe_code)]
499unsafe extern "C" fn code_for_eval_gets(
500 cx: *mut RawJSContext,
501 code: HandleObject,
502 code_for_eval: MutableHandleString,
503) -> bool {
504 let cx = unsafe { JSContext::from_ptr(cx) };
505 if let Ok(trusted_script) = unsafe { root_from_object::<TrustedScript>(code.get(), *cx) } {
506 let script_str = trusted_script.data().str();
507 let s = js::conversions::Utf8Chars::from(&*script_str);
508 let new_string = unsafe { JS_NewStringCopyUTF8N(*cx, &*s as *const _) };
509 code_for_eval.set(new_string);
510 }
511 true
512}
513
514#[expect(unsafe_code)]
515unsafe extern "C" fn content_security_policy_allows(
516 cx: *mut RawJSContext,
517 runtime_code: RuntimeCode,
518 code_string: HandleString,
519 compilation_type: CompilationType,
520 parameter_strings: RawHandle<StackGCVector<*mut JSString>>,
521 body_string: HandleString,
522 parameter_args: RawHandle<StackGCVector<JSVal>>,
523 body_arg: RawHandleValue,
524 can_compile_strings: *mut bool,
525) -> bool {
526 let mut allowed = false;
527 let cx = unsafe { JSContext::from_ptr(cx) };
528 wrap_panic(&mut || {
529 let in_realm_proof = AlreadyInRealm::assert_for_cx(cx);
531 let global = unsafe { &GlobalScope::from_context(*cx, InRealm::Already(&in_realm_proof)) };
532 let csp_list = global.get_csp_list();
533
534 allowed = csp_list.is_none() ||
536 match runtime_code {
537 RuntimeCode::JS => {
538 let parameter_strings = unsafe { Handle::from_raw(parameter_strings) };
539 let parameter_strings_length = parameter_strings.len();
540 let mut parameter_strings_vec =
541 Vec::with_capacity(parameter_strings_length as usize);
542
543 for i in 0..parameter_strings_length {
544 let Some(str_) = parameter_strings.at(i) else {
545 unreachable!();
546 };
547 parameter_strings_vec.push(safely_convert_null_to_string(cx, str_.into()));
548 }
549
550 let parameter_args = unsafe { Handle::from_raw(parameter_args) };
551 let parameter_args_length = parameter_args.len();
552 let mut parameter_args_vec = Vec::with_capacity(parameter_args_length as usize);
553
554 for i in 0..parameter_args_length {
555 let Some(arg) = parameter_args.at(i) else {
556 unreachable!();
557 };
558 let value = arg.into_handle().get();
559 if value.is_object() {
560 if let Ok(trusted_script) =
561 unsafe { root_from_object::<TrustedScript>(value.to_object(), *cx) }
562 {
563 parameter_args_vec
564 .push(TrustedScriptOrString::TrustedScript(trusted_script));
565 } else {
566 parameter_args_vec
570 .push(TrustedScriptOrString::String(DOMString::new()));
571 }
572 } else if value.is_string() {
573 parameter_args_vec
575 .push(TrustedScriptOrString::String(DOMString::new()));
576 } else {
577 unreachable!();
578 }
579 }
580
581 TrustedScript::can_compile_string_with_trusted_type(
582 cx,
583 global,
584 safely_convert_null_to_string(cx, code_string),
585 compilation_type,
586 parameter_strings_vec,
587 safely_convert_null_to_string(cx, body_string),
588 parameter_args_vec,
589 unsafe { HandleValue::from_raw(body_arg) },
590 CanGc::note(),
591 )
592 },
593 RuntimeCode::WASM => global.get_csp_list().is_wasm_evaluation_allowed(global),
594 };
595 });
596 unsafe { *can_compile_strings = allowed };
597 true
598}
599
600#[expect(unsafe_code)]
601#[cfg_attr(crown, allow(crown::unrooted_must_root))]
602pub(crate) fn notify_about_rejected_promises(global: &GlobalScope) {
604 let cx = GlobalScope::get_cx();
605 unsafe {
606 if !global.get_uncaught_rejections().borrow().is_empty() {
608 let uncaught_rejections: Vec<TrustedPromise> = global
610 .get_uncaught_rejections()
611 .borrow()
612 .iter()
613 .map(|promise| {
614 let promise =
615 Promise::new_with_js_promise(Handle::from_raw(promise.handle()), cx);
616
617 TrustedPromise::new(promise)
618 })
619 .collect();
620
621 global.get_uncaught_rejections().borrow_mut().clear();
623
624 let target = Trusted::new(global.upcast::<EventTarget>());
625
626 global.task_manager().dom_manipulation_task_source().queue(
628 task!(unhandled_rejection_event: move || {
629 let target = target.root();
630 let cx = GlobalScope::get_cx();
631
632 for promise in uncaught_rejections {
633 let promise = promise.root();
634
635 let promise_is_handled = GetPromiseIsHandled(promise.reflector().get_jsobject());
637 if promise_is_handled {
638 continue;
639 }
640
641 rooted!(in(*cx) let mut reason = UndefinedValue());
643 JS_GetPromiseResult(promise.reflector().get_jsobject(), reason.handle_mut());
644
645 let event = PromiseRejectionEvent::new(
646 &target.global(),
647 atom!("unhandledrejection"),
648 EventBubbles::DoesNotBubble,
649 EventCancelable::Cancelable,
650 promise.clone(),
651 reason.handle(),
652 CanGc::note()
653 );
654
655 let not_canceled = event.upcast::<Event>().fire(&target, CanGc::note());
656
657 if not_canceled {
660 }
662
663 if !promise_is_handled {
665 target.global().add_consumed_rejection(promise.reflector().get_jsobject().into_handle());
666 }
667 }
668 })
669 );
670 }
671 }
672}
673
674#[derive(JSTraceable, MallocSizeOf)]
675pub(crate) struct Runtime {
676 #[ignore_malloc_size_of = "Type from mozjs"]
677 rt: RustRuntime,
678 #[conditional_malloc_size_of]
680 pub(crate) microtask_queue: Rc<MicrotaskQueue>,
681 #[ignore_malloc_size_of = "Type from mozjs"]
682 job_queue: *mut JobQueue,
683 script_event_loop_sender: Option<Box<ScriptEventLoopSender>>,
684}
685
686impl Runtime {
687 #[expect(unsafe_code)]
697 pub(crate) fn new(main_thread_sender: Option<ScriptEventLoopSender>) -> Runtime {
698 unsafe { Self::new_with_parent(None, main_thread_sender) }
699 }
700
701 #[expect(unsafe_code)]
714 pub(crate) unsafe fn new_with_parent(
715 parent: Option<ParentRuntime>,
716 script_event_looper_sender: Option<ScriptEventLoopSender>,
717 ) -> Runtime {
718 let mut runtime = if let Some(parent) = parent {
719 unsafe { RustRuntime::create_with_parent(parent) }
720 } else {
721 RustRuntime::new(JS_ENGINE.lock().unwrap().as_ref().unwrap().clone())
722 };
723 let cx = runtime.cx();
724
725 unsafe {
726 JS_AddExtraGCRootsTracer(cx, Some(trace_rust_roots), ptr::null_mut());
727
728 JS_SetSecurityCallbacks(cx, &SECURITY_CALLBACKS);
729
730 JS_InitDestroyPrincipalsCallback(cx, Some(principals::destroy_servo_jsprincipal));
731 JS_InitReadPrincipalsCallback(cx, Some(principals::read_jsprincipal));
732
733 if cfg!(debug_assertions) {
735 JS_SetGCCallback(cx, Some(debug_gc_callback), ptr::null_mut());
736 }
737
738 if opts::get().debug.gc_profile {
739 SetGCSliceCallback(cx, Some(gc_slice_callback));
740 }
741 }
742
743 unsafe extern "C" fn empty_wrapper_callback(_: *mut RawJSContext, _: HandleObject) -> bool {
744 true
745 }
746 unsafe extern "C" fn empty_has_released_callback(_: HandleObject) -> bool {
747 false
749 }
750
751 unsafe {
752 SetDOMCallbacks(cx, &DOM_CALLBACKS);
753 SetPreserveWrapperCallbacks(
754 cx,
755 Some(empty_wrapper_callback),
756 Some(empty_has_released_callback),
757 );
758 JS_SetGCParameter(cx, JSGCParamKey::JSGC_INCREMENTAL_GC_ENABLED, 0);
760 }
761
762 unsafe extern "C" fn dispatch_to_event_loop(
763 data: *mut c_void,
764 dispatchable: *mut DispatchablePointer,
765 ) -> bool {
766 let script_event_loop_sender: &ScriptEventLoopSender =
767 unsafe { &*(data as *mut ScriptEventLoopSender) };
768 let runnable = Runnable(dispatchable);
769 let task = task!(dispatch_to_event_loop_message: move || {
770 if let Some(cx) = RustRuntime::get() {
771 runnable.run(cx.as_ptr(), Dispatchable_MaybeShuttingDown::NotShuttingDown);
772 }
773 });
774
775 script_event_loop_sender
776 .send(CommonScriptMsg::Task(
777 ScriptThreadEventCategory::NetworkEvent,
778 Box::new(task),
779 None, TaskSourceName::Networking,
781 ))
782 .is_ok()
783 }
784
785 let mut script_event_loop_sender_pointer = std::ptr::null_mut();
786 if let Some(script_event_loop_sender) = script_event_looper_sender {
787 script_event_loop_sender_pointer = Box::into_raw(Box::new(script_event_loop_sender));
788 unsafe {
789 SetUpEventLoopDispatch(
790 cx,
791 Some(dispatch_to_event_loop),
792 script_event_loop_sender_pointer as *mut c_void,
793 );
794 }
795 }
796
797 unsafe {
798 InitConsumeStreamCallback(cx, Some(consume_stream), Some(report_stream_error));
799 }
800
801 let microtask_queue = Rc::new(MicrotaskQueue::default());
802
803 let interrupt_queues: Box<Vec<Rc<MicrotaskQueue>>> = Box::default();
807
808 let cx_opts;
809 let job_queue;
810 unsafe {
811 let cx = runtime.cx();
812 job_queue = CreateJobQueue(
813 &JOB_QUEUE_TRAPS,
814 &*microtask_queue as *const _ as *const c_void,
815 Box::into_raw(interrupt_queues) as *mut c_void,
816 );
817 SetJobQueue(cx, job_queue);
818 SetPromiseRejectionTrackerCallback(
819 cx,
820 Some(promise_rejection_tracker),
821 ptr::null_mut(),
822 );
823
824 EnsureModuleHooksInitialized(runtime.rt());
825
826 let cx = runtime.cx();
827
828 set_gc_zeal_options(cx.raw_cx());
829
830 cx_opts = &mut *ContextOptionsRef(cx);
832 JS_SetGlobalJitCompilerOption(
833 cx,
834 JSJitCompilerOption::JSJITCOMPILER_BASELINE_INTERPRETER_ENABLE,
835 pref!(js_baseline_interpreter_enabled) as u32,
836 );
837 JS_SetGlobalJitCompilerOption(
838 cx,
839 JSJitCompilerOption::JSJITCOMPILER_BASELINE_ENABLE,
840 pref!(js_baseline_jit_enabled) as u32,
841 );
842 JS_SetGlobalJitCompilerOption(
843 cx,
844 JSJitCompilerOption::JSJITCOMPILER_ION_ENABLE,
845 pref!(js_ion_enabled) as u32,
846 );
847 }
848 cx_opts.compileOptions_.asmJSOption_ = if pref!(js_asmjs_enabled) {
849 AsmJSOption::Enabled
850 } else {
851 AsmJSOption::DisabledByAsmJSPref
852 };
853 let wasm_enabled = pref!(js_wasm_enabled);
854 cx_opts.set_wasm_(wasm_enabled);
855 if wasm_enabled {
856 unsafe { SetProcessBuildIdOp(Some(servo_build_id)) };
860 }
861 cx_opts.set_wasmBaseline_(pref!(js_wasm_baseline_enabled));
862 cx_opts.set_wasmIon_(pref!(js_wasm_ion_enabled));
863
864 unsafe {
865 let cx = runtime.cx();
866 JS_SetGlobalJitCompilerOption(
868 cx,
869 JSJitCompilerOption::JSJITCOMPILER_NATIVE_REGEXP_ENABLE,
870 pref!(js_native_regex_enabled) as u32,
871 );
872 JS_SetOffthreadIonCompilationEnabled(cx, pref!(js_offthread_compilation_enabled));
873 JS_SetGlobalJitCompilerOption(
874 cx,
875 JSJitCompilerOption::JSJITCOMPILER_BASELINE_WARMUP_TRIGGER,
876 if pref!(js_baseline_jit_unsafe_eager_compilation_enabled) {
877 0
878 } else {
879 u32::MAX
880 },
881 );
882 JS_SetGlobalJitCompilerOption(
883 cx,
884 JSJitCompilerOption::JSJITCOMPILER_ION_NORMAL_WARMUP_TRIGGER,
885 if pref!(js_ion_unsafe_eager_compilation_enabled) {
886 0
887 } else {
888 u32::MAX
889 },
890 );
891 JS_SetGCParameter(
897 cx,
898 JSGCParamKey::JSGC_MAX_BYTES,
899 in_range(pref!(js_mem_max), 1, 0x100)
900 .map(|val| (val * 1024 * 1024) as u32)
901 .unwrap_or(u32::MAX),
902 );
903 JS_SetGCParameter(
905 cx,
906 JSGCParamKey::JSGC_INCREMENTAL_GC_ENABLED,
907 pref!(js_mem_gc_incremental_enabled) as u32,
908 );
909 JS_SetGCParameter(
910 cx,
911 JSGCParamKey::JSGC_PER_ZONE_GC_ENABLED,
912 pref!(js_mem_gc_per_zone_enabled) as u32,
913 );
914 if let Some(val) = in_range(pref!(js_mem_gc_incremental_slice_ms), 0, 100_000) {
915 JS_SetGCParameter(cx, JSGCParamKey::JSGC_SLICE_TIME_BUDGET_MS, val as u32);
916 }
917 JS_SetGCParameter(
918 cx,
919 JSGCParamKey::JSGC_COMPACTING_ENABLED,
920 pref!(js_mem_gc_compacting_enabled) as u32,
921 );
922
923 if let Some(val) = in_range(pref!(js_mem_gc_high_frequency_time_limit_ms), 0, 10_000) {
924 JS_SetGCParameter(cx, JSGCParamKey::JSGC_HIGH_FREQUENCY_TIME_LIMIT, val as u32);
925 }
926 if let Some(val) = in_range(pref!(js_mem_gc_low_frequency_heap_growth), 0, 10_000) {
927 JS_SetGCParameter(cx, JSGCParamKey::JSGC_LOW_FREQUENCY_HEAP_GROWTH, val as u32);
928 }
929 if let Some(val) = in_range(pref!(js_mem_gc_high_frequency_heap_growth_min), 0, 10_000)
930 {
931 JS_SetGCParameter(
932 cx,
933 JSGCParamKey::JSGC_HIGH_FREQUENCY_LARGE_HEAP_GROWTH,
934 val as u32,
935 );
936 }
937 if let Some(val) = in_range(pref!(js_mem_gc_high_frequency_heap_growth_max), 0, 10_000)
938 {
939 JS_SetGCParameter(
940 cx,
941 JSGCParamKey::JSGC_HIGH_FREQUENCY_SMALL_HEAP_GROWTH,
942 val as u32,
943 );
944 }
945 if let Some(val) = in_range(pref!(js_mem_gc_high_frequency_low_limit_mb), 0, 10_000) {
946 JS_SetGCParameter(cx, JSGCParamKey::JSGC_SMALL_HEAP_SIZE_MAX, val as u32);
947 }
948 if let Some(val) = in_range(pref!(js_mem_gc_high_frequency_high_limit_mb), 0, 10_000) {
949 JS_SetGCParameter(cx, JSGCParamKey::JSGC_LARGE_HEAP_SIZE_MIN, val as u32);
950 }
951 if let Some(val) = in_range(pref!(js_mem_gc_empty_chunk_count_min), 0, 10_000) {
962 JS_SetGCParameter(cx, JSGCParamKey::JSGC_MIN_EMPTY_CHUNK_COUNT, val as u32);
963 }
964 }
965 Runtime {
966 rt: runtime,
967 microtask_queue,
968 job_queue,
969 script_event_loop_sender: (!script_event_loop_sender_pointer.is_null())
970 .then(|| unsafe { Box::from_raw(script_event_loop_sender_pointer) }),
971 }
972 }
973
974 pub(crate) fn thread_safe_js_context(&self) -> ThreadSafeJSContext {
975 self.rt.thread_safe_js_context()
976 }
977}
978
979impl Drop for Runtime {
980 #[expect(unsafe_code)]
981 fn drop(&mut self) {
982 self.microtask_queue.clear();
984
985 unsafe {
987 DeleteJobQueue(self.job_queue);
988 }
989 LiveDOMReferences::destruct();
990 mark_runtime_dead();
991 }
992}
993
994impl Deref for Runtime {
995 type Target = RustRuntime;
996 fn deref(&self) -> &RustRuntime {
997 &self.rt
998 }
999}
1000
1001impl DerefMut for Runtime {
1002 fn deref_mut(&mut self) -> &mut RustRuntime {
1003 &mut self.rt
1004 }
1005}
1006
1007pub struct JSEngineSetup(JSEngine);
1008
1009impl Default for JSEngineSetup {
1010 fn default() -> Self {
1011 let engine = JSEngine::init().unwrap();
1012 *JS_ENGINE.lock().unwrap() = Some(engine.handle());
1013 Self(engine)
1014 }
1015}
1016
1017impl Drop for JSEngineSetup {
1018 fn drop(&mut self) {
1019 *JS_ENGINE.lock().unwrap() = None;
1020
1021 while !self.0.can_shutdown() {
1022 thread::sleep(Duration::from_millis(50));
1023 }
1024 }
1025}
1026
1027static JS_ENGINE: Mutex<Option<JSEngineHandle>> = Mutex::new(None);
1028
1029fn in_range<T: PartialOrd + Copy>(val: T, min: T, max: T) -> Option<T> {
1030 if val < min || val >= max {
1031 None
1032 } else {
1033 Some(val)
1034 }
1035}
1036
1037thread_local!(static MALLOC_SIZE_OF_OPS: Cell<*mut MallocSizeOfOps> = const { Cell::new(ptr::null_mut()) });
1038
1039#[expect(unsafe_code)]
1040unsafe extern "C" fn get_size(obj: *mut JSObject) -> usize {
1041 match unsafe { get_dom_class(obj) } {
1042 Ok(v) => {
1043 let dom_object = unsafe { private_from_object(obj) as *const c_void };
1044
1045 if dom_object.is_null() {
1046 return 0;
1047 }
1048 let ops = MALLOC_SIZE_OF_OPS.get();
1049 unsafe { (v.malloc_size_of)(&mut *ops, dom_object) }
1050 },
1051 Err(_e) => 0,
1052 }
1053}
1054
1055thread_local!(static GC_CYCLE_START: Cell<Option<Instant>> = const { Cell::new(None) });
1056thread_local!(static GC_SLICE_START: Cell<Option<Instant>> = const { Cell::new(None) });
1057
1058#[expect(unsafe_code)]
1059unsafe extern "C" fn gc_slice_callback(
1060 _cx: *mut RawJSContext,
1061 progress: GCProgress,
1062 desc: *const GCDescription,
1063) {
1064 match progress {
1065 GCProgress::GC_CYCLE_BEGIN => GC_CYCLE_START.with(|start| {
1066 start.set(Some(Instant::now()));
1067 println!("GC cycle began");
1068 }),
1069 GCProgress::GC_SLICE_BEGIN => GC_SLICE_START.with(|start| {
1070 start.set(Some(Instant::now()));
1071 println!("GC slice began");
1072 }),
1073 GCProgress::GC_SLICE_END => GC_SLICE_START.with(|start| {
1074 let duration = start.get().unwrap().elapsed();
1075 start.set(None);
1076 println!("GC slice ended: duration={:?}", duration);
1077 }),
1078 GCProgress::GC_CYCLE_END => GC_CYCLE_START.with(|start| {
1079 let duration = start.get().unwrap().elapsed();
1080 start.set(None);
1081 println!("GC cycle ended: duration={:?}", duration);
1082 }),
1083 };
1084 if !desc.is_null() {
1085 let desc: &GCDescription = unsafe { &*desc };
1086 let options = match desc.options_ {
1087 GCOptions::Normal => "Normal",
1088 GCOptions::Shrink => "Shrink",
1089 GCOptions::Shutdown => "Shutdown",
1090 };
1091 println!(" isZone={}, options={}", desc.isZone_, options);
1092 }
1093 let _ = stdout().flush();
1094}
1095
1096#[expect(unsafe_code)]
1097unsafe extern "C" fn debug_gc_callback(
1098 _cx: *mut RawJSContext,
1099 status: JSGCStatus,
1100 _reason: GCReason,
1101 _data: *mut os::raw::c_void,
1102) {
1103 match status {
1104 JSGCStatus::JSGC_BEGIN => thread_state::enter(ThreadState::IN_GC),
1105 JSGCStatus::JSGC_END => thread_state::exit(ThreadState::IN_GC),
1106 }
1107}
1108
1109#[expect(unsafe_code)]
1110unsafe extern "C" fn trace_rust_roots(tr: *mut JSTracer, _data: *mut os::raw::c_void) {
1111 if !runtime_is_alive() {
1112 return;
1113 }
1114 trace!("starting custom root handler");
1115 unsafe {
1116 trace_thread(tr);
1117 trace_roots(tr);
1118 trace_refcounted_objects(tr);
1119 settings_stack::trace(tr);
1120 }
1121 trace!("done custom root handler");
1122}
1123
1124#[expect(unsafe_code)]
1125unsafe extern "C" fn servo_build_id(build_id: *mut BuildIdCharVector) -> bool {
1126 let servo_id = b"Servo\0";
1127 unsafe { SetBuildId(build_id, servo_id[0] as *const c_char, servo_id.len()) }
1128}
1129
1130#[expect(unsafe_code)]
1131#[cfg(feature = "debugmozjs")]
1132unsafe fn set_gc_zeal_options(cx: *mut RawJSContext) {
1133 use js::jsapi::SetGCZeal;
1134
1135 let level = match pref!(js_mem_gc_zeal_level) {
1136 level @ 0..=14 => level as u8,
1137 _ => return,
1138 };
1139 let frequency = match pref!(js_mem_gc_zeal_frequency) {
1140 frequency if frequency >= 0 => frequency as u32,
1141 _ => 5000,
1143 };
1144 SetGCZeal(cx, level, frequency);
1145}
1146
1147#[expect(unsafe_code)]
1148#[cfg(not(feature = "debugmozjs"))]
1149unsafe fn set_gc_zeal_options(_: *mut RawJSContext) {}
1150
1151pub(crate) use script_bindings::script_runtime::JSContext;
1152
1153pub(crate) trait JSContextHelper {
1156 fn get_reports(&self, path_seg: String, ops: &mut MallocSizeOfOps) -> Vec<Report>;
1157}
1158
1159impl JSContextHelper for JSContext {
1160 #[expect(unsafe_code)]
1161 fn get_reports(&self, path_seg: String, ops: &mut MallocSizeOfOps) -> Vec<Report> {
1162 MALLOC_SIZE_OF_OPS.with(|ops_tls| ops_tls.set(ops));
1163 let stats = unsafe {
1164 let mut stats = ::std::mem::zeroed();
1165 if !CollectServoSizes(**self, &mut stats, Some(get_size)) {
1166 return vec![];
1167 }
1168 stats
1169 };
1170 MALLOC_SIZE_OF_OPS.with(|ops| ops.set(ptr::null_mut()));
1171
1172 let mut reports = vec![];
1173 let mut report = |mut path_suffix, kind, size| {
1174 let mut path = path![path_seg, "js"];
1175 path.append(&mut path_suffix);
1176 reports.push(Report { path, kind, size })
1177 };
1178
1179 report(
1183 path!["gc-heap", "used"],
1184 ReportKind::ExplicitNonHeapSize,
1185 stats.gcHeapUsed,
1186 );
1187
1188 report(
1189 path!["gc-heap", "unused"],
1190 ReportKind::ExplicitNonHeapSize,
1191 stats.gcHeapUnused,
1192 );
1193
1194 report(
1195 path!["gc-heap", "admin"],
1196 ReportKind::ExplicitNonHeapSize,
1197 stats.gcHeapAdmin,
1198 );
1199
1200 report(
1201 path!["gc-heap", "decommitted"],
1202 ReportKind::ExplicitNonHeapSize,
1203 stats.gcHeapDecommitted,
1204 );
1205
1206 report(
1208 path!["malloc-heap"],
1209 ReportKind::ExplicitSystemHeapSize,
1210 stats.mallocHeap,
1211 );
1212
1213 report(
1214 path!["non-heap"],
1215 ReportKind::ExplicitNonHeapSize,
1216 stats.nonHeap,
1217 );
1218 reports
1219 }
1220}
1221
1222pub(crate) struct StreamConsumer(*mut JSStreamConsumer);
1223
1224#[expect(unsafe_code)]
1225impl StreamConsumer {
1226 pub(crate) fn consume_chunk(&self, stream: &[u8]) -> bool {
1227 unsafe {
1228 let stream_ptr = stream.as_ptr();
1229 StreamConsumerConsumeChunk(self.0, stream_ptr, stream.len())
1230 }
1231 }
1232
1233 pub(crate) fn stream_end(&self) {
1234 unsafe {
1235 StreamConsumerStreamEnd(self.0);
1236 }
1237 }
1238
1239 pub(crate) fn stream_error(&self, error_code: usize) {
1240 unsafe {
1241 StreamConsumerStreamError(self.0, error_code);
1242 }
1243 }
1244
1245 pub(crate) fn note_response_urls(
1246 &self,
1247 maybe_url: Option<String>,
1248 maybe_source_map_url: Option<String>,
1249 ) {
1250 unsafe {
1251 let maybe_url = maybe_url.map(|url| CString::new(url).unwrap());
1252 let maybe_source_map_url = maybe_source_map_url.map(|url| CString::new(url).unwrap());
1253
1254 let maybe_url_param = match maybe_url.as_ref() {
1255 Some(url) => url.as_ptr(),
1256 None => ptr::null(),
1257 };
1258 let maybe_source_map_url_param = match maybe_source_map_url.as_ref() {
1259 Some(url) => url.as_ptr(),
1260 None => ptr::null(),
1261 };
1262
1263 StreamConsumerNoteResponseURLs(self.0, maybe_url_param, maybe_source_map_url_param);
1264 }
1265 }
1266}
1267
1268#[expect(unsafe_code)]
1271unsafe extern "C" fn consume_stream(
1272 cx: *mut RawJSContext,
1273 obj: HandleObject,
1274 _mime_type: MimeType,
1275 _consumer: *mut JSStreamConsumer,
1276) -> bool {
1277 let cx = unsafe { JSContext::from_ptr(cx) };
1278 let in_realm_proof = AlreadyInRealm::assert_for_cx(cx);
1279 let global = unsafe { GlobalScope::from_context(*cx, InRealm::Already(&in_realm_proof)) };
1280
1281 if let Ok(unwrapped_source) =
1283 root_from_handleobject::<Response>(unsafe { RustHandleObject::from_raw(obj) }, *cx)
1284 {
1285 let mimetype = unwrapped_source.Headers(CanGc::note()).extract_mime_type();
1287
1288 if !&mimetype[..].eq_ignore_ascii_case(b"application/wasm") {
1290 throw_dom_exception(
1291 cx,
1292 &global,
1293 Error::Type("Response has unsupported MIME type".to_string()),
1294 CanGc::note(),
1295 );
1296 return false;
1297 }
1298
1299 match unwrapped_source.Type() {
1301 DOMResponseType::Basic | DOMResponseType::Cors | DOMResponseType::Default => {},
1302 _ => {
1303 throw_dom_exception(
1304 cx,
1305 &global,
1306 Error::Type("Response.type must be 'basic', 'cors' or 'default'".to_string()),
1307 CanGc::note(),
1308 );
1309 return false;
1310 },
1311 }
1312
1313 if !unwrapped_source.Ok() {
1315 throw_dom_exception(
1316 cx,
1317 &global,
1318 Error::Type("Response does not have ok status".to_string()),
1319 CanGc::note(),
1320 );
1321 return false;
1322 }
1323
1324 if unwrapped_source.is_locked() {
1326 throw_dom_exception(
1327 cx,
1328 &global,
1329 Error::Type("There was an error consuming the Response".to_string()),
1330 CanGc::note(),
1331 );
1332 return false;
1333 }
1334
1335 if unwrapped_source.is_disturbed() {
1337 throw_dom_exception(
1338 cx,
1339 &global,
1340 Error::Type("Response already consumed".to_string()),
1341 CanGc::note(),
1342 );
1343 return false;
1344 }
1345 unwrapped_source.set_stream_consumer(Some(StreamConsumer(_consumer)));
1346 } else {
1347 throw_dom_exception(
1349 cx,
1350 &global,
1351 Error::Type("expected Response or Promise resolving to Response".to_string()),
1352 CanGc::note(),
1353 );
1354 return false;
1355 }
1356 true
1357}
1358
1359#[expect(unsafe_code)]
1360unsafe extern "C" fn report_stream_error(_cx: *mut RawJSContext, error_code: usize) {
1361 error!("Error initializing StreamConsumer: {:?}", unsafe {
1362 RUST_js_GetErrorMessage(ptr::null_mut(), error_code as u32)
1363 });
1364}
1365
1366pub(crate) struct Runnable(*mut DispatchablePointer);
1367
1368#[expect(unsafe_code)]
1369unsafe impl Sync for Runnable {}
1370#[expect(unsafe_code)]
1371unsafe impl Send for Runnable {}
1372
1373#[expect(unsafe_code)]
1374impl Runnable {
1375 fn run(&self, cx: *mut RawJSContext, maybe_shutting_down: Dispatchable_MaybeShuttingDown) {
1376 unsafe {
1377 DispatchableRun(cx, self.0, maybe_shutting_down);
1378 }
1379 }
1380}
1381
1382pub(crate) use script_bindings::script_runtime::CanGc;
1383
1384pub(crate) struct IntroductionType;
1390impl IntroductionType {
1391 pub const EVAL: &CStr = c"eval";
1393 pub const EVAL_STR: &str = "eval";
1394
1395 pub const DEBUGGER_EVAL: &CStr = c"debugger eval";
1398 pub const DEBUGGER_EVAL_STR: &str = "debugger eval";
1399
1400 pub const FUNCTION: &CStr = c"Function";
1402 pub const FUNCTION_STR: &str = "Function";
1403
1404 pub const WORKLET: &CStr = c"Worklet";
1406 pub const WORKLET_STR: &str = "Worklet";
1407
1408 pub const EVENT_HANDLER: &CStr = c"eventHandler";
1410 pub const EVENT_HANDLER_STR: &str = "eventHandler";
1411
1412 pub const SRC_SCRIPT: &CStr = c"srcScript";
1415 pub const SRC_SCRIPT_STR: &str = "srcScript";
1416
1417 pub const INLINE_SCRIPT: &CStr = c"inlineScript";
1420 pub const INLINE_SCRIPT_STR: &str = "inlineScript";
1421
1422 pub const INJECTED_SCRIPT: &CStr = c"injectedScript";
1428 pub const INJECTED_SCRIPT_STR: &str = "injectedScript";
1429
1430 pub const IMPORTED_MODULE: &CStr = c"importedModule";
1433 pub const IMPORTED_MODULE_STR: &str = "importedModule";
1434
1435 pub const JAVASCRIPT_URL: &CStr = c"javascriptURL";
1437 pub const JAVASCRIPT_URL_STR: &str = "javascriptURL";
1438
1439 pub const DOM_TIMER: &CStr = c"domTimer";
1441 pub const DOM_TIMER_STR: &str = "domTimer";
1442
1443 pub const WORKER: &CStr = c"Worker";
1447 pub const WORKER_STR: &str = "Worker";
1448}