From 3eddfeaee237c076afccd0caafc481e72c662592 Mon Sep 17 00:00:00 2001 From: shuppy Date: Tue, 5 Aug 2025 20:41:14 +0800 Subject: [PATCH] script: Tell SpiderMonkey whether scripts are inline (#38363) to use the [SpiderMonkey Debugger API](https://firefox-source-docs.mozilla.org/js/Debugger/) as the single source of truth about scripts and their sources for devtools purposes (servo/servo#38334), the debugger script needs to be able to distinguish inline scripts from other scripts, because inline scripts are a special case where the source contents need to come from the Servo parser. the mechanism for this is [Debugger.Script.prototype.**introductionType**](https://firefox-source-docs.mozilla.org/js/Debugger/Debugger.Source.html#introductiontype), which is `inlineScript` for inline scripts or a variety of other values for other kinds of scripts, but only the embedder can provide this information. this patch bumps mozjs to servo/mozjs#603, which expands on CompileOptionsWrapper, making it a safe wrapper around CompileOptions. to construct one from safe code, use Runtime::new_compile_options(). then you can call `set_introduction_type(&'static CStr)` on the new instance. we also make Runtime::evaluate_script() take a CompileOptionsWrapper from the caller, instead of constructing one internally. in this patch, we set the introductionType to `c"inlineScript"` when calling run_a_classic_script() and compile_module_script() for inline scripts, and leave it unset all other cases. Testing: will undergo automated tests in #38334 Fixes: part of #36027, part of servo/servo#38378 --------- Signed-off-by: Delan Azabani Co-authored-by: atbrakhi --- Cargo.lock | 4 ++-- components/script/devtools.rs | 1 + components/script/dom/globalscope.rs | 8 +++++++- components/script/dom/htmlscriptelement.rs | 14 +++++++++++--- components/script/dom/userscripts.rs | 1 + components/script/dom/workerglobalscope.rs | 18 ++++++++++++++---- components/script/script_module.rs | 11 +++++++++-- components/script/script_runtime.rs | 8 +++++++- 8 files changed, 52 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fc4d12e6704..860f2d16672 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5241,7 +5241,7 @@ dependencies = [ [[package]] name = "mozjs" version = "0.14.1" -source = "git+https://github.com/servo/mozjs#903a158b36c10902a40c7fda766406d698f98bf4" +source = "git+https://github.com/servo/mozjs#eb4268a973f2334a18f97be0c6def8b1a7143431" dependencies = [ "bindgen 0.71.1", "cc", @@ -5253,7 +5253,7 @@ dependencies = [ [[package]] name = "mozjs_sys" version = "0.128.13-3" -source = "git+https://github.com/servo/mozjs#903a158b36c10902a40c7fda766406d698f98bf4" +source = "git+https://github.com/servo/mozjs#eb4268a973f2334a18f97be0c6def8b1a7143431" dependencies = [ "bindgen 0.71.1", "cc", diff --git a/components/script/devtools.rs b/components/script/devtools.rs index 37554210cf4..6b7cac9a086 100644 --- a/components/script/devtools.rs +++ b/components/script/devtools.rs @@ -65,6 +65,7 @@ pub(crate) fn handle_evaluate_js( ScriptFetchOptions::default_classic_script(global), global.api_base_url(), can_gc, + None, ); if rval.is_undefined() { diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs index b2ab674e29d..6eb6b2e249b 100644 --- a/components/script/dom/globalscope.rs +++ b/components/script/dom/globalscope.rs @@ -5,6 +5,7 @@ use std::cell::{Cell, OnceCell, Ref}; use std::collections::hash_map::Entry; use std::collections::{HashMap, HashSet, VecDeque}; +use std::ffi::CStr; use std::ops::Index; use std::rc::Rc; use std::sync::atomic::{AtomicBool, Ordering}; @@ -2768,6 +2769,7 @@ impl GlobalScope { fetch_options, script_base_url, can_gc, + None, ) } @@ -2783,6 +2785,7 @@ impl GlobalScope { fetch_options: ScriptFetchOptions, script_base_url: ServoUrl, can_gc: CanGc, + introduction_type: Option<&'static CStr>, ) -> bool { let cx = GlobalScope::get_cx(); @@ -2794,7 +2797,10 @@ impl GlobalScope { rooted!(in(*cx) let mut compiled_script = std::ptr::null_mut::()); match code { SourceCode::Text(text_code) => { - let options = CompileOptionsWrapper::new(*cx, filename, line_number); + let mut options = CompileOptionsWrapper::new(*cx, filename, line_number); + if let Some(introduction_type) = introduction_type { + options.set_introduction_type(introduction_type); + } debug!("compiling dom string"); compiled_script.set(Compile1( diff --git a/components/script/dom/htmlscriptelement.rs b/components/script/dom/htmlscriptelement.rs index 0ec0a30a9e4..752434e1987 100644 --- a/components/script/dom/htmlscriptelement.rs +++ b/components/script/dom/htmlscriptelement.rs @@ -4,6 +4,7 @@ #![allow(unused_imports)] use core::ffi::c_void; use std::cell::Cell; +use std::ffi::CStr; use std::fs::read_to_string; use std::path::PathBuf; use std::process::Command; @@ -80,7 +81,7 @@ use crate::script_module::{ ImportMap, ModuleOwner, ScriptFetchOptions, fetch_external_module_script, fetch_inline_module_script, parse_an_import_map_string, register_import_map, }; -use crate::script_runtime::CanGc; +use crate::script_runtime::{CanGc, IntroductionType}; use crate::task_source::{SendableTaskSource, TaskSourceName}; use crate::unminify::{ScriptSource, unminify_js}; @@ -1145,6 +1146,7 @@ impl HTMLScriptElement { // Step 6. let document = self.owner_document(); let old_script = document.GetCurrentScript(); + let introduction_type = (!script.external).then_some(IntroductionType::INLINE_SCRIPT); match script.type_ { ScriptType::Classic => { @@ -1153,7 +1155,7 @@ impl HTMLScriptElement { } else { document.set_current_script(Some(self)) } - self.run_a_classic_script(&script, can_gc); + self.run_a_classic_script(&script, can_gc, introduction_type); document.set_current_script(old_script.as_deref()); }, ScriptType::Module => { @@ -1179,7 +1181,12 @@ impl HTMLScriptElement { } // https://html.spec.whatwg.org/multipage/#run-a-classic-script - pub(crate) fn run_a_classic_script(&self, script: &ScriptOrigin, can_gc: CanGc) { + pub(crate) fn run_a_classic_script( + &self, + script: &ScriptOrigin, + can_gc: CanGc, + introduction_type: Option<&'static CStr>, + ) { // TODO use a settings object rather than this element's document/window // Step 2 let document = self.owner_document(); @@ -1205,6 +1212,7 @@ impl HTMLScriptElement { script.fetch_options.clone(), script.url.clone(), can_gc, + introduction_type, ); } diff --git a/components/script/dom/userscripts.rs b/components/script/dom/userscripts.rs index bf8febc8ad2..4433bfe4d64 100644 --- a/components/script/dom/userscripts.rs +++ b/components/script/dom/userscripts.rs @@ -39,6 +39,7 @@ pub(crate) fn load_script(head: &HTMLHeadElement) { ScriptFetchOptions::default_classic_script(global_scope), global_scope.api_base_url(), CanGc::note(), + None, ); } })); diff --git a/components/script/dom/workerglobalscope.rs b/components/script/dom/workerglobalscope.rs index 82fefa1180b..f338439bcf6 100644 --- a/components/script/dom/workerglobalscope.rs +++ b/components/script/dom/workerglobalscope.rs @@ -427,12 +427,17 @@ impl WorkerGlobalScopeMethods for WorkerGlobalScope { Ok((metadata, bytes)) => (metadata.final_url, String::from_utf8(bytes).unwrap()), }; + let options = self + .runtime + .borrow() + .as_ref() + .unwrap() + .new_compile_options(url.as_str(), 1); let result = self.runtime.borrow().as_ref().unwrap().evaluate_script( self.reflector().get_jsobject(), &source, - url.as_str(), - 1, rval.handle_mut(), + options, ); maybe_resume_unwind(); @@ -639,12 +644,17 @@ impl WorkerGlobalScope { let _aes = AutoEntryScript::new(self.upcast()); let cx = self.runtime.borrow().as_ref().unwrap().cx(); rooted!(in(cx) let mut rval = UndefinedValue()); + let options = self + .runtime + .borrow() + .as_ref() + .unwrap() + .new_compile_options(self.worker_url.borrow().as_str(), 1); match self.runtime.borrow().as_ref().unwrap().evaluate_script( self.reflector().get_jsobject(), &source, - self.worker_url.borrow().as_str(), - 1, rval.handle_mut(), + options, ) { Ok(_) => (), Err(_) => { diff --git a/components/script/script_module.rs b/components/script/script_module.rs index 918138a6b68..4328b7e974c 100644 --- a/components/script/script_module.rs +++ b/components/script/script_module.rs @@ -6,6 +6,7 @@ //! related to `type=module` for script thread or worker threads. use std::collections::{HashMap, HashSet}; +use std::ffi::CStr; use std::rc::Rc; use std::str::FromStr; use std::sync::{Arc, Mutex}; @@ -77,7 +78,7 @@ use crate::dom::window::Window; use crate::dom::worker::TrustedWorkerAddress; use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingListener}; use crate::realms::{AlreadyInRealm, InRealm, enter_realm}; -use crate::script_runtime::{CanGc, JSContext as SafeJSContext}; +use crate::script_runtime::{CanGc, IntroductionType, JSContext as SafeJSContext}; use crate::task::TaskBox; fn gen_type_error(global: &GlobalScope, string: String, can_gc: CanGc) -> RethrowError { @@ -467,11 +468,15 @@ impl ModuleTree { mut module_script: RustMutableHandleObject, inline: bool, can_gc: CanGc, + introduction_type: Option<&'static CStr>, ) -> Result<(), RethrowError> { let cx = GlobalScope::get_cx(); let _ac = JSAutoRealm::new(*cx, *global.reflector().get_jsobject()); - let compile_options = unsafe { CompileOptionsWrapper::new(*cx, url.as_str(), 1) }; + let mut compile_options = unsafe { CompileOptionsWrapper::new(*cx, url.as_str(), 1) }; + if let Some(introduction_type) = introduction_type { + compile_options.set_introduction_type(introduction_type); + } let mut module_source = ModuleSource { source: module_script_text, unminified_dir: global.unminified_js_dir(), @@ -1331,6 +1336,7 @@ impl FetchResponseListener for ModuleContext { compiled_module.handle_mut(), false, CanGc::note(), + None, ); match compiled_module_result { @@ -1895,6 +1901,7 @@ pub(crate) fn fetch_inline_module_script( compiled_module.handle_mut(), true, can_gc, + Some(IntroductionType::INLINE_SCRIPT), ); match compiled_module_result { diff --git a/components/script/script_runtime.rs b/components/script/script_runtime.rs index 283aa9329f1..69e6df46e0c 100644 --- a/components/script/script_runtime.rs +++ b/components/script/script_runtime.rs @@ -9,7 +9,7 @@ use core::ffi::c_char; use std::cell::Cell; -use std::ffi::CString; +use std::ffi::{CStr, CString}; use std::io::{Write, stdout}; use std::ops::Deref; use std::os::raw::c_void; @@ -1194,3 +1194,9 @@ impl Runnable { } pub(crate) use script_bindings::script_runtime::CanGc; + +/// `introductionType` values in SpiderMonkey TransitiveCompileOptions. +pub(crate) struct IntroductionType; +impl IntroductionType { + pub const INLINE_SCRIPT: &'static CStr = c"inlineScript"; +}