1use std::borrow::Cow;
6use std::ffi::CStr;
7use std::ptr::NonNull;
8use std::rc::Rc;
9
10use content_security_policy::sandboxing_directive::SandboxingFlagSet;
11use js::jsapi::{
12 Compile1, ExceptionStackBehavior, JS_ClearPendingException, JSScript, SetScriptPrivate,
13};
14use js::jsval::{PrivateValue, UndefinedValue};
15use js::panic::maybe_resume_unwind;
16use js::rust::wrappers::{
17 JS_ExecuteScript, JS_GetPendingException, JS_GetScriptPrivate, JS_SetPendingException,
18};
19use js::rust::{CompileOptionsWrapper, MutableHandleValue, transform_str_to_source_text};
20use servo_url::ServoUrl;
21
22use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
23use crate::dom::bindings::error::{Error, ErrorResult};
24use crate::dom::bindings::inheritance::Castable;
25use crate::dom::bindings::settings_stack::AutoEntryScript;
26use crate::dom::bindings::str::DOMString;
27use crate::dom::globalscope::GlobalScope;
28use crate::dom::window::Window;
29use crate::script_module::{ModuleScript, ModuleSource, RethrowError, ScriptFetchOptions};
30use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
31use crate::unminify::unminify_js;
32
33#[derive(JSTraceable, MallocSizeOf)]
35pub(crate) struct ClassicScript {
36 #[no_trace]
39 #[ignore_malloc_size_of = "mozjs"]
40 pub record: Result<NonNull<JSScript>, RethrowError>,
41 fetch_options: ScriptFetchOptions,
43 #[no_trace]
45 url: ServoUrl,
46 muted_errors: ErrorReporting,
48}
49
50#[derive(JSTraceable, MallocSizeOf)]
51pub(crate) enum ErrorReporting {
52 Muted,
53 Unmuted,
54}
55
56impl From<bool> for ErrorReporting {
57 fn from(boolean: bool) -> Self {
58 if boolean {
59 ErrorReporting::Muted
60 } else {
61 ErrorReporting::Unmuted
62 }
63 }
64}
65
66pub(crate) enum RethrowErrors {
67 Yes,
68 No,
69}
70
71impl GlobalScope {
72 #[expect(clippy::too_many_arguments)]
74 pub(crate) fn create_a_classic_script(
75 &self,
76 source: Cow<'_, str>,
77 url: ServoUrl,
78 fetch_options: ScriptFetchOptions,
79 muted_errors: ErrorReporting,
80 introduction_type: Option<&'static CStr>,
81 line_number: u32,
82 external: bool,
83 ) -> ClassicScript {
84 let cx = GlobalScope::get_cx();
85
86 let mut script_source = ModuleSource {
87 source: Rc::new(DOMString::from(source)),
88 unminified_dir: self.unminified_js_dir(),
89 external,
90 url: url.clone(),
91 };
92 unminify_js(&mut script_source);
93
94 rooted!(in(*cx) let mut compiled_script = std::ptr::null_mut::<JSScript>());
95
96 compiled_script.set(compile_script(
106 cx,
107 &script_source.source.str(),
108 url.as_str(),
109 line_number,
110 introduction_type,
111 ));
112
113 let record = if compiled_script.get().is_null() {
115 Err(RethrowError::from_pending_exception(cx))
118 } else {
119 Ok(NonNull::new(*compiled_script).expect("Can't be null"))
120 };
121
122 ClassicScript {
129 record,
130 url,
131 fetch_options,
132 muted_errors,
133 }
134 }
135
136 #[expect(unsafe_code)]
138 pub(crate) fn run_a_classic_script(
139 &self,
140 script: ClassicScript,
141 rethrow_errors: RethrowErrors,
142 can_gc: CanGc,
143 ) -> ErrorResult {
144 let cx = GlobalScope::get_cx();
145 if !self.can_run_script() {
149 return Ok(());
150 }
151
152 let _aes = AutoEntryScript::new(self);
157
158 rooted!(in(*cx) let mut evaluation_status = UndefinedValue());
160 let mut result = false;
161
162 match script.record {
163 Err(error_to_rethrow) => unsafe {
165 JS_SetPendingException(
166 *cx,
167 error_to_rethrow.handle(),
168 ExceptionStackBehavior::Capture,
169 )
170 },
171 Ok(compiled_script) => {
173 rooted!(in(*cx) let mut rval = UndefinedValue());
174 result = evaluate_script(
175 cx,
176 compiled_script,
177 script.url,
178 script.fetch_options,
179 rval.handle_mut(),
180 );
181 },
182 }
183
184 unsafe { JS_GetPendingException(*cx, evaluation_status.handle_mut()) };
185
186 if !evaluation_status.is_undefined() {
188 warn!("Error evaluating script");
189
190 match (rethrow_errors, script.muted_errors) {
191 (RethrowErrors::Yes, ErrorReporting::Unmuted) => {
193 return Err(Error::JSFailed);
195 },
196 (RethrowErrors::Yes, ErrorReporting::Muted) => {
198 unsafe { JS_ClearPendingException(*cx) };
199 return Err(Error::Network(None));
201 },
202 _ => {
204 unsafe { JS_ClearPendingException(*cx) };
205 self.report_an_exception(cx, evaluation_status.handle(), can_gc);
207
208 return Err(Error::JSFailed);
210 },
211 }
212 }
213
214 maybe_resume_unwind();
215
216 if result {
218 return Ok(());
219 }
220
221 Err(Error::QuotaExceeded {
224 quota: None,
225 requested: None,
226 })
227 }
228
229 fn can_run_script(&self) -> bool {
231 if let Some(window) = self.downcast::<Window>() {
242 let doc = window.Document();
243 doc.is_fully_active() ||
244 !doc.has_active_sandboxing_flag(
245 SandboxingFlagSet::SANDBOXED_SCRIPTS_BROWSING_CONTEXT_FLAG,
246 )
247 } else {
248 true
249 }
250 }
251}
252
253#[expect(unsafe_code)]
254pub(crate) fn compile_script(
255 cx: SafeJSContext,
256 text: &str,
257 filename: &str,
258 line_number: u32,
259 introduction_type: Option<&'static CStr>,
260) -> *mut JSScript {
261 let mut options = unsafe { CompileOptionsWrapper::new_raw(*cx, filename, line_number) };
262 if let Some(introduction_type) = introduction_type {
263 options.set_introduction_type(introduction_type);
264 }
265
266 debug!("Compiling script");
267 unsafe { Compile1(*cx, options.ptr, &mut transform_str_to_source_text(text)) }
268}
269
270#[expect(unsafe_code)]
272pub(crate) fn evaluate_script(
273 cx: SafeJSContext,
274 compiled_script: NonNull<JSScript>,
275 url: ServoUrl,
276 fetch_options: ScriptFetchOptions,
277 rval: MutableHandleValue,
278) -> bool {
279 rooted!(in(*cx) let record = compiled_script.as_ptr());
280 rooted!(in(*cx) let mut script_private = UndefinedValue());
281
282 unsafe { JS_GetScriptPrivate(*record, script_private.handle_mut()) };
283
284 if script_private.is_undefined() {
287 debug!("Set script private for {}", url);
288 let module_script_data = Rc::new(ModuleScript::new(
289 url,
290 fetch_options,
291 None,
295 ));
296
297 unsafe {
298 SetScriptPrivate(
299 *record,
300 &PrivateValue(Rc::into_raw(module_script_data) as *const _),
301 );
302 }
303 }
304
305 unsafe { JS_ExecuteScript(*cx, record.handle(), rval) }
306}