script: Set correct introductionType values in more places (#38550)

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), we need to keep track of whether scripts come from
an actual file or from things like setTimeout(), because for some
[introductionType](https://firefox-source-docs.mozilla.org/js/Debugger/Debugger.Source.html#introductiontype)
[values](https://firefox-source-docs.mozilla.org/devtools-user/debugger-api/debugger.source/#accessor-properties-of-the-debugger-source-prototype-object),
we want to disregard the script unless it has a [`//# sourceURL=`
override](https://tc39.es/ecma426/#sec-linking-eval)
([displayURL](https://firefox-source-docs.mozilla.org/js/Debugger/Debugger.Source.html#displayurl)).

this patch builds on #38363, setting the correct introductionType value
in several more cases.

Testing: will undergo many automated tests in #38334
Fixes: part of #36027

---------

Signed-off-by: Delan Azabani <dazabani@igalia.com>
Co-authored-by: atbrakhi <atbrakhi@igalia.com>
This commit is contained in:
shuppy 2025-08-09 13:05:54 +08:00 committed by GitHub
parent ff4971012f
commit 6471587fb4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 77 additions and 15 deletions

View file

@ -42,7 +42,7 @@ use crate::dom::node::{Node, NodeTraits, ShadowIncluding};
use crate::dom::types::HTMLElement; use crate::dom::types::HTMLElement;
use crate::realms::enter_realm; use crate::realms::enter_realm;
use crate::script_module::ScriptFetchOptions; use crate::script_module::ScriptFetchOptions;
use crate::script_runtime::CanGc; use crate::script_runtime::{CanGc, IntroductionType};
#[allow(unsafe_code)] #[allow(unsafe_code)]
pub(crate) fn handle_evaluate_js( pub(crate) fn handle_evaluate_js(
@ -57,6 +57,8 @@ pub(crate) fn handle_evaluate_js(
let _ac = enter_realm(global); let _ac = enter_realm(global);
rooted!(in(*cx) let mut rval = UndefinedValue()); rooted!(in(*cx) let mut rval = UndefinedValue());
let source_code = SourceCode::Text(Rc::new(DOMString::from_string(eval))); let source_code = SourceCode::Text(Rc::new(DOMString::from_string(eval)));
// TODO: run code with SpiderMonkey Debugger API, like Firefox does
// <https://searchfox.org/mozilla-central/rev/f6a806c38c459e0e0d797d264ca0e8ad46005105/devtools/server/actors/webconsole/eval-with-debugger.js#270>
global.evaluate_script_on_global_with_result( global.evaluate_script_on_global_with_result(
&source_code, &source_code,
"<eval>", "<eval>",
@ -65,7 +67,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, Some(IntroductionType::DEBUGGER_EVAL),
); );
if rval.is_undefined() { if rval.is_undefined() {

View file

@ -112,6 +112,7 @@ impl DebuggerGlobalScope {
ScriptFetchOptions::default_classic_script(&self.global_scope), ScriptFetchOptions::default_classic_script(&self.global_scope),
self.global_scope.api_base_url(), self.global_scope.api_base_url(),
can_gc, can_gc,
None,
) )
} }

View file

@ -2759,6 +2759,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 source_code = SourceCode::Text(Rc::new(DOMString::from_string((*code).to_string()))); let source_code = SourceCode::Text(Rc::new(DOMString::from_string((*code).to_string())));
self.evaluate_script_on_global_with_result( self.evaluate_script_on_global_with_result(
@ -2769,7 +2770,7 @@ impl GlobalScope {
fetch_options, fetch_options,
script_base_url, script_base_url,
can_gc, can_gc,
None, introduction_type,
) )
} }

View file

@ -213,6 +213,11 @@ pub(crate) struct HTMLScriptElement {
/// <https://w3c.github.io/trusted-types/dist/spec/#htmlscriptelement-script-text> /// <https://w3c.github.io/trusted-types/dist/spec/#htmlscriptelement-script-text>
script_text: DomRefCell<DOMString>, script_text: DomRefCell<DOMString>,
/// `introductionType` value to set in the `CompileOptionsWrapper`, overriding the usual
/// `srcScript` or `inlineScript` that this script would normally use.
#[no_trace]
introduction_type_override: Cell<Option<&'static CStr>>,
} }
impl HTMLScriptElement { impl HTMLScriptElement {
@ -232,6 +237,7 @@ impl HTMLScriptElement {
preparation_time_document: MutNullableDom::new(None), preparation_time_document: MutNullableDom::new(None),
line_number: creator.return_line_number(), line_number: creator.return_line_number(),
script_text: DomRefCell::new(DOMString::new()), script_text: DomRefCell::new(DOMString::new()),
introduction_type_override: Cell::new(None),
} }
} }
@ -688,7 +694,10 @@ impl HTMLScriptElement {
} }
/// <https://html.spec.whatwg.org/multipage/#prepare-the-script-element> /// <https://html.spec.whatwg.org/multipage/#prepare-the-script-element>
pub(crate) fn prepare(&self, can_gc: CanGc) { pub(crate) fn prepare(&self, introduction_type_override: Option<&'static CStr>, can_gc: CanGc) {
self.introduction_type_override
.set(introduction_type_override);
// Step 1. If el's already started is true, then return. // Step 1. If el's already started is true, then return.
if self.already_started.get() { if self.already_started.get() {
return; return;
@ -1165,7 +1174,14 @@ 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); let introduction_type =
self.introduction_type_override
.get()
.unwrap_or(if script.external {
IntroductionType::SRC_SCRIPT
} else {
IntroductionType::INLINE_SCRIPT
});
match script.type_ { match script.type_ {
ScriptType::Classic => { ScriptType::Classic => {
@ -1174,7 +1190,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, introduction_type); self.run_a_classic_script(&script, can_gc, Some(introduction_type));
document.set_current_script(old_script.as_deref()); document.set_current_script(old_script.as_deref());
}, },
ScriptType::Module => { ScriptType::Module => {
@ -1428,7 +1444,7 @@ impl VirtualMethods for HTMLScriptElement {
if *attr.local_name() == local_name!("src") { if *attr.local_name() == local_name!("src") {
if let AttributeMutation::Set(_) = mutation { if let AttributeMutation::Set(_) = mutation {
if !self.parser_inserted.get() && self.upcast::<Node>().is_connected() { if !self.parser_inserted.get() && self.upcast::<Node>().is_connected() {
self.prepare(can_gc); self.prepare(Some(IntroductionType::INJECTED_SCRIPT), can_gc);
} }
} }
} }
@ -1447,7 +1463,7 @@ impl VirtualMethods for HTMLScriptElement {
// running any scripts until the DOM tree is safe for interactions. // running any scripts until the DOM tree is safe for interactions.
self.owner_document().add_delayed_task( self.owner_document().add_delayed_task(
task!(ScriptPrepare: |script: DomRoot<HTMLScriptElement>| { task!(ScriptPrepare: |script: DomRoot<HTMLScriptElement>| {
script.prepare(CanGc::note()); script.prepare(Some(IntroductionType::INJECTED_SCRIPT), CanGc::note());
}), }),
); );
} }
@ -1460,7 +1476,7 @@ impl VirtualMethods for HTMLScriptElement {
} }
if self.upcast::<Node>().is_connected() && !self.parser_inserted.get() { if self.upcast::<Node>().is_connected() && !self.parser_inserted.get() {
self.prepare(CanGc::note()); self.prepare(Some(IntroductionType::INJECTED_SCRIPT), CanGc::note());
} }
} }

View file

@ -76,7 +76,7 @@ use crate::dom::text::Text;
use crate::dom::virtualmethods::vtable_for; use crate::dom::virtualmethods::vtable_for;
use crate::network_listener::PreInvoke; use crate::network_listener::PreInvoke;
use crate::realms::enter_realm; use crate::realms::enter_realm;
use crate::script_runtime::CanGc; use crate::script_runtime::{CanGc, IntroductionType};
use crate::script_thread::ScriptThread; use crate::script_thread::ScriptThread;
mod async_html; mod async_html;
@ -682,7 +682,9 @@ impl ServoParser {
self.script_nesting_level.set(script_nesting_level + 1); self.script_nesting_level.set(script_nesting_level + 1);
script.set_initial_script_text(); script.set_initial_script_text();
script.prepare(can_gc); let introduction_type_override =
(script_nesting_level > 0).then_some(IntroductionType::INJECTED_SCRIPT);
script.prepare(introduction_type_override, can_gc);
self.script_nesting_level.set(script_nesting_level); self.script_nesting_level.set(script_nesting_level);
if self.document.has_pending_parsing_blocking_script() { if self.document.has_pending_parsing_blocking_script() {

View file

@ -34,7 +34,7 @@ use crate::dom::worklet::WorkletExecutor;
use crate::messaging::MainThreadScriptMsg; use crate::messaging::MainThreadScriptMsg;
use crate::realms::enter_realm; use crate::realms::enter_realm;
use crate::script_module::ScriptFetchOptions; use crate::script_module::ScriptFetchOptions;
use crate::script_runtime::{CanGc, JSContext}; use crate::script_runtime::{CanGc, IntroductionType, JSContext};
#[dom_struct] #[dom_struct]
/// <https://drafts.css-houdini.org/worklets/#workletglobalscope> /// <https://drafts.css-houdini.org/worklets/#workletglobalscope>
@ -133,6 +133,7 @@ impl WorkletGlobalScope {
ScriptFetchOptions::default_classic_script(&self.globalscope), ScriptFetchOptions::default_classic_script(&self.globalscope),
self.globalscope.api_base_url(), self.globalscope.api_base_url(),
can_gc, can_gc,
Some(IntroductionType::WORKLET),
) )
} }

View file

@ -836,6 +836,7 @@ impl ModuleTree {
} }
#[allow(unsafe_code)] #[allow(unsafe_code)]
// FIXME: spec links in this function are all broken, so its unclear what this algorithm does
/// <https://html.spec.whatwg.org/multipage/#fetch-the-descendants-of-a-module-script> /// <https://html.spec.whatwg.org/multipage/#fetch-the-descendants-of-a-module-script>
fn fetch_module_descendants( fn fetch_module_descendants(
&self, &self,
@ -929,6 +930,8 @@ impl ModuleTree {
Some(parent_identity.clone()), Some(parent_identity.clone()),
false, false,
None, None,
// TODO: is this correct?
Some(IntroductionType::IMPORTED_MODULE),
can_gc, can_gc,
); );
} }
@ -1200,6 +1203,8 @@ struct ModuleContext {
status: Result<(), NetworkError>, status: Result<(), NetworkError>,
/// Timing object for this resource /// Timing object for this resource
resource_timing: ResourceFetchTiming, resource_timing: ResourceFetchTiming,
/// `introductionType` value to set in the `CompileOptionsWrapper`.
introduction_type: Option<&'static CStr>,
} }
impl FetchResponseListener for ModuleContext { impl FetchResponseListener for ModuleContext {
@ -1336,7 +1341,7 @@ impl FetchResponseListener for ModuleContext {
compiled_module.handle_mut(), compiled_module.handle_mut(),
false, false,
CanGc::note(), CanGc::note(),
None, self.introduction_type,
); );
match compiled_module_result { match compiled_module_result {
@ -1570,6 +1575,7 @@ fn fetch_an_import_module_script_graph(
None, None,
true, true,
Some(dynamic_module), Some(dynamic_module),
Some(IntroductionType::IMPORTED_MODULE),
can_gc, can_gc,
); );
Ok(()) Ok(())
@ -1675,6 +1681,7 @@ pub(crate) fn fetch_external_module_script(
None, None,
true, true,
None, None,
Some(IntroductionType::SRC_SCRIPT),
can_gc, can_gc,
) )
} }
@ -1738,6 +1745,7 @@ fn fetch_single_module_script(
parent_identity: Option<ModuleIdentity>, parent_identity: Option<ModuleIdentity>,
top_level_module_fetch: bool, top_level_module_fetch: bool,
dynamic_module: Option<RootedTraceableBox<DynamicModule>>, dynamic_module: Option<RootedTraceableBox<DynamicModule>>,
introduction_type: Option<&'static CStr>,
can_gc: CanGc, can_gc: CanGc,
) { ) {
{ {
@ -1857,6 +1865,7 @@ fn fetch_single_module_script(
options, options,
status: Ok(()), status: Ok(()),
resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource), resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource),
introduction_type,
})); }));
let network_listener = NetworkListener { let network_listener = NetworkListener {

View file

@ -1255,6 +1255,13 @@ pub(crate) use script_bindings::script_runtime::CanGc;
// TODO: squish `scriptElement` <https://searchfox.org/mozilla-central/rev/202069c4c5113a1a9052d84fa4679d4c1b22113e/devtools/server/actors/source.js#199-201> // TODO: squish `scriptElement` <https://searchfox.org/mozilla-central/rev/202069c4c5113a1a9052d84fa4679d4c1b22113e/devtools/server/actors/source.js#199-201>
pub(crate) struct IntroductionType; pub(crate) struct IntroductionType;
impl IntroductionType { impl IntroductionType {
/// `introductionType` for code evaluated by debugger.
/// This includes code run via the devtools repl, even if the thread is not paused.
pub const DEBUGGER_EVAL: &CStr = c"debugger eval";
/// `introductionType` for code loaded by worklet.
pub const WORKLET: &CStr = c"Worklet";
/// `introductionType` for code belonging to `<script src="file.js">` elements. /// `introductionType` for code belonging to `<script src="file.js">` elements.
/// This includes `<script type="module" src="...">`. /// This includes `<script type="module" src="...">`.
pub const SRC_SCRIPT: &CStr = c"srcScript"; pub const SRC_SCRIPT: &CStr = c"srcScript";
@ -1263,6 +1270,23 @@ impl IntroductionType {
/// This includes `<script type="module" src="...">`. /// This includes `<script type="module" src="...">`.
pub const INLINE_SCRIPT: &CStr = c"inlineScript"; pub const INLINE_SCRIPT: &CStr = c"inlineScript";
/// `introductionType` for code belonging to scripts that *would* be `"inlineScript"` except that they were not
/// part of the initial file itself.
/// For example, scripts created via:
/// - `document.write("<script>code;</script>")`
/// - `var s = document.createElement("script"); s.text = "code";`
pub const INJECTED_SCRIPT: &CStr = c"injectedScript";
/// `introductionType` for code that was loaded indirectly by being imported by another script
/// using ESM static or dynamic imports.
pub const IMPORTED_MODULE: &CStr = c"importedModule";
/// `introductionType` for code presented in `javascript:` URLs.
pub const JAVASCRIPT_URL: &CStr = c"javascriptURL";
/// `introductionType` for code passed to `setTimeout`/`setInterval` as a string.
pub const DOM_TIMER: &CStr = c"domTimer";
/// `introductionType` for web workers. /// `introductionType` for web workers.
/// <https://searchfox.org/mozilla-central/rev/202069c4c5113a1a9052d84fa4679d4c1b22113e/devtools/docs/user/debugger-api/debugger.source/index.rst#96> /// <https://searchfox.org/mozilla-central/rev/202069c4c5113a1a9052d84fa4679d4c1b22113e/devtools/docs/user/debugger-api/debugger.source/index.rst#96>
pub const WORKER: &CStr = c"Worker"; pub const WORKER: &CStr = c"Worker";

View file

@ -154,7 +154,8 @@ use crate::navigation::{InProgressLoad, NavigationListener};
use crate::realms::enter_realm; use crate::realms::enter_realm;
use crate::script_module::ScriptFetchOptions; use crate::script_module::ScriptFetchOptions;
use crate::script_runtime::{ use crate::script_runtime::{
CanGc, JSContext, JSContextHelper, Runtime, ScriptThreadEventCategory, ThreadSafeJSContext, CanGc, IntroductionType, JSContext, JSContextHelper, Runtime, ScriptThreadEventCategory,
ThreadSafeJSContext,
}; };
use crate::task_queue::TaskQueue; use crate::task_queue::TaskQueue;
use crate::task_source::{SendableTaskSource, TaskSourceName}; use crate::task_source::{SendableTaskSource, TaskSourceName};
@ -3739,6 +3740,7 @@ impl ScriptThread {
ScriptFetchOptions::default_classic_script(global_scope), ScriptFetchOptions::default_classic_script(global_scope),
global_scope.api_base_url(), global_scope.api_base_url(),
can_gc, can_gc,
Some(IntroductionType::JAVASCRIPT_URL),
); );
load_data.js_eval_result = if jsval.get().is_string() { load_data.js_eval_result = if jsval.get().is_string() {
@ -4093,6 +4095,7 @@ impl ScriptThread {
ScriptFetchOptions::default_classic_script(global_scope), ScriptFetchOptions::default_classic_script(global_scope),
global_scope.api_base_url(), global_scope.api_base_url(),
can_gc, can_gc,
None, // No known `introductionType` for JS code from embedder
); );
let result = match jsval_to_webdriver( let result = match jsval_to_webdriver(
context, context,

View file

@ -35,7 +35,7 @@ use crate::dom::testbinding::TestBindingCallback;
use crate::dom::types::{Window, WorkerGlobalScope}; use crate::dom::types::{Window, WorkerGlobalScope};
use crate::dom::xmlhttprequest::XHRTimeoutCallback; use crate::dom::xmlhttprequest::XHRTimeoutCallback;
use crate::script_module::ScriptFetchOptions; use crate::script_module::ScriptFetchOptions;
use crate::script_runtime::CanGc; use crate::script_runtime::{CanGc, IntroductionType};
use crate::script_thread::ScriptThread; use crate::script_thread::ScriptThread;
use crate::task_source::SendableTaskSource; use crate::task_source::SendableTaskSource;
@ -562,6 +562,7 @@ impl JsTimerTask {
ScriptFetchOptions::default_classic_script(&global), ScriptFetchOptions::default_classic_script(&global),
global.api_base_url(), global.api_base_url(),
can_gc, can_gc,
Some(IntroductionType::DOM_TIMER),
); );
}, },
InternalTimerCallback::FunctionTimerCallback(ref function, ref arguments) => { InternalTimerCallback::FunctionTimerCallback(ref function, ref arguments) => {

View file

@ -529,6 +529,7 @@ pub(crate) fn handle_execute_script(
ScriptFetchOptions::default_classic_script(global), ScriptFetchOptions::default_classic_script(global),
global.api_base_url(), global.api_base_url(),
can_gc, can_gc,
None, // No known `introductionType` for JS code from WebDriver
) { ) {
jsval_to_webdriver(cx, global, rval.handle(), realm, can_gc) jsval_to_webdriver(cx, global, rval.handle(), realm, can_gc)
} else { } else {
@ -570,6 +571,7 @@ pub(crate) fn handle_execute_async_script(
ScriptFetchOptions::default_classic_script(global_scope), ScriptFetchOptions::default_classic_script(global_scope),
global_scope.api_base_url(), global_scope.api_base_url(),
can_gc, can_gc,
None, // No known `introductionType` for JS code from WebDriver
) { ) {
reply_sender.send(Err(WebDriverJSError::JSError)).unwrap(); reply_sender.send(Err(WebDriverJSError::JSError)).unwrap();
} }