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 <dazabani@igalia.com>
Co-authored-by: atbrakhi <atbrakhi@igalia.com>
This commit is contained in:
shuppy 2025-08-05 20:41:14 +08:00 committed by GitHub
parent 0bf8676345
commit 3eddfeaee2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 52 additions and 13 deletions

4
Cargo.lock generated
View file

@ -5241,7 +5241,7 @@ dependencies = [
[[package]] [[package]]
name = "mozjs" name = "mozjs"
version = "0.14.1" version = "0.14.1"
source = "git+https://github.com/servo/mozjs#903a158b36c10902a40c7fda766406d698f98bf4" source = "git+https://github.com/servo/mozjs#eb4268a973f2334a18f97be0c6def8b1a7143431"
dependencies = [ dependencies = [
"bindgen 0.71.1", "bindgen 0.71.1",
"cc", "cc",
@ -5253,7 +5253,7 @@ dependencies = [
[[package]] [[package]]
name = "mozjs_sys" name = "mozjs_sys"
version = "0.128.13-3" version = "0.128.13-3"
source = "git+https://github.com/servo/mozjs#903a158b36c10902a40c7fda766406d698f98bf4" source = "git+https://github.com/servo/mozjs#eb4268a973f2334a18f97be0c6def8b1a7143431"
dependencies = [ dependencies = [
"bindgen 0.71.1", "bindgen 0.71.1",
"cc", "cc",

View file

@ -65,6 +65,7 @@ pub(crate) fn handle_evaluate_js(
ScriptFetchOptions::default_classic_script(global), ScriptFetchOptions::default_classic_script(global),
global.api_base_url(), global.api_base_url(),
can_gc, can_gc,
None,
); );
if rval.is_undefined() { if rval.is_undefined() {

View file

@ -5,6 +5,7 @@
use std::cell::{Cell, OnceCell, Ref}; use std::cell::{Cell, OnceCell, Ref};
use std::collections::hash_map::Entry; use std::collections::hash_map::Entry;
use std::collections::{HashMap, HashSet, VecDeque}; use std::collections::{HashMap, HashSet, VecDeque};
use std::ffi::CStr;
use std::ops::Index; use std::ops::Index;
use std::rc::Rc; use std::rc::Rc;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
@ -2768,6 +2769,7 @@ impl GlobalScope {
fetch_options, fetch_options,
script_base_url, script_base_url,
can_gc, can_gc,
None,
) )
} }
@ -2783,6 +2785,7 @@ impl GlobalScope {
fetch_options: ScriptFetchOptions, fetch_options: ScriptFetchOptions,
script_base_url: ServoUrl, script_base_url: ServoUrl,
can_gc: CanGc, can_gc: CanGc,
introduction_type: Option<&'static CStr>,
) -> bool { ) -> bool {
let cx = GlobalScope::get_cx(); let cx = GlobalScope::get_cx();
@ -2794,7 +2797,10 @@ impl GlobalScope {
rooted!(in(*cx) let mut compiled_script = std::ptr::null_mut::<JSScript>()); rooted!(in(*cx) let mut compiled_script = std::ptr::null_mut::<JSScript>());
match code { match code {
SourceCode::Text(text_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"); debug!("compiling dom string");
compiled_script.set(Compile1( compiled_script.set(Compile1(

View file

@ -4,6 +4,7 @@
#![allow(unused_imports)] #![allow(unused_imports)]
use core::ffi::c_void; use core::ffi::c_void;
use std::cell::Cell; use std::cell::Cell;
use std::ffi::CStr;
use std::fs::read_to_string; use std::fs::read_to_string;
use std::path::PathBuf; use std::path::PathBuf;
use std::process::Command; use std::process::Command;
@ -80,7 +81,7 @@ use crate::script_module::{
ImportMap, ModuleOwner, ScriptFetchOptions, fetch_external_module_script, ImportMap, ModuleOwner, ScriptFetchOptions, fetch_external_module_script,
fetch_inline_module_script, parse_an_import_map_string, register_import_map, 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::task_source::{SendableTaskSource, TaskSourceName};
use crate::unminify::{ScriptSource, unminify_js}; use crate::unminify::{ScriptSource, unminify_js};
@ -1145,6 +1146,7 @@ impl HTMLScriptElement {
// Step 6. // Step 6.
let document = self.owner_document(); let document = self.owner_document();
let old_script = document.GetCurrentScript(); let old_script = document.GetCurrentScript();
let introduction_type = (!script.external).then_some(IntroductionType::INLINE_SCRIPT);
match script.type_ { match script.type_ {
ScriptType::Classic => { ScriptType::Classic => {
@ -1153,7 +1155,7 @@ impl HTMLScriptElement {
} else { } else {
document.set_current_script(Some(self)) 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()); document.set_current_script(old_script.as_deref());
}, },
ScriptType::Module => { ScriptType::Module => {
@ -1179,7 +1181,12 @@ impl HTMLScriptElement {
} }
// https://html.spec.whatwg.org/multipage/#run-a-classic-script // 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 // TODO use a settings object rather than this element's document/window
// Step 2 // Step 2
let document = self.owner_document(); let document = self.owner_document();
@ -1205,6 +1212,7 @@ impl HTMLScriptElement {
script.fetch_options.clone(), script.fetch_options.clone(),
script.url.clone(), script.url.clone(),
can_gc, can_gc,
introduction_type,
); );
} }

View file

@ -39,6 +39,7 @@ pub(crate) fn load_script(head: &HTMLHeadElement) {
ScriptFetchOptions::default_classic_script(global_scope), ScriptFetchOptions::default_classic_script(global_scope),
global_scope.api_base_url(), global_scope.api_base_url(),
CanGc::note(), CanGc::note(),
None,
); );
} }
})); }));

View file

@ -427,12 +427,17 @@ impl WorkerGlobalScopeMethods<crate::DomTypeHolder> for WorkerGlobalScope {
Ok((metadata, bytes)) => (metadata.final_url, String::from_utf8(bytes).unwrap()), 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( let result = self.runtime.borrow().as_ref().unwrap().evaluate_script(
self.reflector().get_jsobject(), self.reflector().get_jsobject(),
&source, &source,
url.as_str(),
1,
rval.handle_mut(), rval.handle_mut(),
options,
); );
maybe_resume_unwind(); maybe_resume_unwind();
@ -639,12 +644,17 @@ impl WorkerGlobalScope {
let _aes = AutoEntryScript::new(self.upcast()); let _aes = AutoEntryScript::new(self.upcast());
let cx = self.runtime.borrow().as_ref().unwrap().cx(); let cx = self.runtime.borrow().as_ref().unwrap().cx();
rooted!(in(cx) let mut rval = UndefinedValue()); 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( match self.runtime.borrow().as_ref().unwrap().evaluate_script(
self.reflector().get_jsobject(), self.reflector().get_jsobject(),
&source, &source,
self.worker_url.borrow().as_str(),
1,
rval.handle_mut(), rval.handle_mut(),
options,
) { ) {
Ok(_) => (), Ok(_) => (),
Err(_) => { Err(_) => {

View file

@ -6,6 +6,7 @@
//! related to `type=module` for script thread or worker threads. //! related to `type=module` for script thread or worker threads.
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::ffi::CStr;
use std::rc::Rc; use std::rc::Rc;
use std::str::FromStr; use std::str::FromStr;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
@ -77,7 +78,7 @@ use crate::dom::window::Window;
use crate::dom::worker::TrustedWorkerAddress; use crate::dom::worker::TrustedWorkerAddress;
use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingListener}; use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingListener};
use crate::realms::{AlreadyInRealm, InRealm, enter_realm}; 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; use crate::task::TaskBox;
fn gen_type_error(global: &GlobalScope, string: String, can_gc: CanGc) -> RethrowError { fn gen_type_error(global: &GlobalScope, string: String, can_gc: CanGc) -> RethrowError {
@ -467,11 +468,15 @@ impl ModuleTree {
mut module_script: RustMutableHandleObject, mut module_script: RustMutableHandleObject,
inline: bool, inline: bool,
can_gc: CanGc, can_gc: CanGc,
introduction_type: Option<&'static CStr>,
) -> Result<(), RethrowError> { ) -> Result<(), RethrowError> {
let cx = GlobalScope::get_cx(); let cx = GlobalScope::get_cx();
let _ac = JSAutoRealm::new(*cx, *global.reflector().get_jsobject()); 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 { let mut module_source = ModuleSource {
source: module_script_text, source: module_script_text,
unminified_dir: global.unminified_js_dir(), unminified_dir: global.unminified_js_dir(),
@ -1331,6 +1336,7 @@ impl FetchResponseListener for ModuleContext {
compiled_module.handle_mut(), compiled_module.handle_mut(),
false, false,
CanGc::note(), CanGc::note(),
None,
); );
match compiled_module_result { match compiled_module_result {
@ -1895,6 +1901,7 @@ pub(crate) fn fetch_inline_module_script(
compiled_module.handle_mut(), compiled_module.handle_mut(),
true, true,
can_gc, can_gc,
Some(IntroductionType::INLINE_SCRIPT),
); );
match compiled_module_result { match compiled_module_result {

View file

@ -9,7 +9,7 @@
use core::ffi::c_char; use core::ffi::c_char;
use std::cell::Cell; use std::cell::Cell;
use std::ffi::CString; use std::ffi::{CStr, CString};
use std::io::{Write, stdout}; use std::io::{Write, stdout};
use std::ops::Deref; use std::ops::Deref;
use std::os::raw::c_void; use std::os::raw::c_void;
@ -1194,3 +1194,9 @@ impl Runnable {
} }
pub(crate) use script_bindings::script_runtime::CanGc; 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";
}