Create source actors from Debugger API notifications

Co-authored-by: atbrakhi <atbrakhi@igalia.com>
Signed-off-by: Delan Azabani <dazabani@igalia.com>
This commit is contained in:
Delan Azabani 2025-07-29 18:45:20 +08:00
parent e2eabd41c9
commit 048d0b0538
13 changed files with 193 additions and 64 deletions

View file

@ -15,7 +15,7 @@ use crate::dom::bindings::codegen::Bindings::EventBinding::Event_Binding::EventM
use crate::dom::bindings::reflector::reflect_dom_object;
use crate::dom::bindings::root::{DomRoot, MutNullableDom};
use crate::dom::event::Event;
use crate::dom::types::GlobalScope;
use crate::dom::types::{GlobalScope, PipelineId};
use crate::script_runtime::CanGc;
#[dom_struct]
@ -23,20 +23,24 @@ use crate::script_runtime::CanGc;
pub(crate) struct DebuggerEvent {
event: Event,
global: MutNullableDom<GlobalScope>,
pipeline_id: MutNullableDom<PipelineId>,
}
impl DebuggerEvent {
pub(crate) fn new(
debugger_global: &GlobalScope,
global: &GlobalScope,
pipeline_id: &PipelineId,
can_gc: CanGc,
) -> DomRoot<Self> {
let result = Box::new(Self {
event: Event::new_inherited(),
global: Default::default(),
pipeline_id: Default::default(),
});
// TODO: make these fields not optional somehow?
result.global.set(Some(global));
result.pipeline_id.set(Some(pipeline_id));
result.event.init_event("addDebuggee".into(), false, false);
@ -58,6 +62,12 @@ impl DebuggerEventMethods<crate::DomTypeHolder> for DebuggerEvent {
NonNull::new(result.to_object()).unwrap()
}
fn PipelineId(
&self,
) -> DomRoot<<crate::DomTypeHolder as script_bindings::DomTypes>::PipelineId> {
self.pipeline_id.get().expect("Guaranteed by new")
}
fn IsTrusted(&self) -> bool {
self.event.IsTrusted()
}

View file

@ -2,18 +2,22 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use base::id::PipelineId;
use base::id::{Index, PipelineId, PipelineNamespaceId};
use constellation_traits::ScriptToConstellationChan;
use crossbeam_channel::Sender;
use devtools_traits::ScriptToDevtoolsControlMsg;
use devtools_traits::{ScriptToDevtoolsControlMsg, SourceInfo};
use dom_struct::dom_struct;
use embedder_traits::resources::{self, Resource};
use ipc_channel::ipc::IpcSender;
use js::jsval::UndefinedValue;
use js::gc::HandleValue;
use js::jsval::{UInt32Value, UndefinedValue};
use js::rust::Runtime;
use js::rust::wrappers::JS_DefineDebuggerObject;
use net_traits::ResourceThreads;
use profile_traits::{mem, time};
use script_bindings::codegen::GenericBindings::DebuggerGlobalScopeBinding::{
DebuggerGlobalScopeMethods, NotifyNewSource,
};
use script_bindings::realms::InRealm;
use script_bindings::reflector::DomObject;
use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl};
@ -104,6 +108,10 @@ impl DebuggerGlobalScope {
GlobalScope::get_cx()
}
pub(crate) fn as_global_scope(&self) -> &GlobalScope {
self.upcast::<GlobalScope>()
}
fn evaluate_js(&self, script: &str, can_gc: CanGc) -> bool {
rooted!(in (*Self::get_cx()) let mut rval = UndefinedValue());
self.global_scope.evaluate_js_on_global_with_result(
@ -123,8 +131,20 @@ impl DebuggerGlobalScope {
}
#[allow(unsafe_code)]
pub(crate) fn fire_add_debuggee(&self, can_gc: CanGc, global: &GlobalScope) {
let event = DomRoot::upcast::<Event>(DebuggerEvent::new(self.upcast(), global, can_gc));
pub(crate) fn fire_add_debuggee(
&self,
can_gc: CanGc,
global: &GlobalScope,
pipeline_id: PipelineId,
) {
let pipeline_id =
crate::dom::pipelineid::PipelineId::new(self.upcast(), pipeline_id, can_gc);
let event = DomRoot::upcast::<Event>(DebuggerEvent::new(
self.upcast(),
global,
&pipeline_id,
can_gc,
));
assert_eq!(
event.fire(self.upcast(), can_gc),
EventStatus::NotCanceled,
@ -132,3 +152,38 @@ impl DebuggerGlobalScope {
);
}
}
impl DebuggerGlobalScopeMethods<crate::DomTypeHolder> for DebuggerGlobalScope {
// check-tidy: no specs after this line
fn NotifyNewSource(&self, args: &NotifyNewSource) {
info!(
"NotifyNewSource: ({},{}) {} {} {}",
args.pipelineId.namespaceId,
args.pipelineId.index,
args.spidermonkeyId,
args.url,
args.text
);
if let Some(devtools_chan) = self.as_global_scope().devtools_chan() {
let pipeline_id = PipelineId {
namespace_id: PipelineNamespaceId(args.pipelineId.namespaceId),
index: Index::new(args.pipelineId.index)
.expect("`pipelineId.index` must not be zero"),
};
let source_info = SourceInfo {
url: ServoUrl::parse(args.url.str()).expect("Failed to parse url"),
external: true, // TODO
worker_id: None, // TODO
content: Some(args.text.to_string()),
content_type: None, // TODO
spidermonkey_id: args.spidermonkeyId,
};
devtools_chan
.send(ScriptToDevtoolsControlMsg::CreateSourceActor(
pipeline_id,
source_info,
))
.expect("Failed to send to devtools server");
}
}
}

View file

@ -9,7 +9,7 @@ use std::thread::{self, JoinHandle};
use base::id::{BrowsingContextId, PipelineId, WebViewId};
use constellation_traits::{WorkerGlobalScopeInit, WorkerScriptLoadOrigin};
use crossbeam_channel::{Receiver, Sender, unbounded};
use devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg, SourceInfo};
use devtools_traits::DevtoolScriptControlMsg;
use dom_struct::dom_struct;
use headers::{HeaderMapExt, ReferrerPolicy as ReferrerPolicyHeader};
use ipc_channel::ipc::IpcReceiver;
@ -513,20 +513,6 @@ impl DedicatedWorkerGlobalScope {
));
global_scope.set_https_state(metadata.https_state);
let source = String::from_utf8_lossy(&bytes);
if let Some(chan) = global_scope.devtools_chan() {
let pipeline_id = global_scope.pipeline_id();
let source_info = SourceInfo {
url: metadata.final_url,
external: true, // Worker scripts are always external.
worker_id: Some(global.upcast::<WorkerGlobalScope>().get_worker_id()),
content: Some(source.to_string()),
content_type: metadata.content_type.map(|c_type| c_type.0.to_string()),
};
let _ = chan.send(ScriptToDevtoolsControlMsg::CreateSourceActor(
pipeline_id,
source_info,
));
}
unsafe {
// Handle interrupt requests

View file

@ -1091,40 +1091,6 @@ impl HTMLScriptElement {
Ok(script) => script,
};
if let Some(chan) = self.global().devtools_chan() {
let pipeline_id = self.global().pipeline_id();
let (url, content, content_type, is_external) = if script.external {
let content = match &script.code {
SourceCode::Text(text) => text.to_string(),
SourceCode::Compiled(compiled) => compiled.original_text.to_string(),
};
// content_type: https://html.spec.whatwg.org/multipage/#scriptingLanguages
(script.url.clone(), Some(content), "text/javascript", true)
} else {
// TODO: if needed, fetch the page again, in the same way as in the original request.
// Fetch it from cache, even if the original request was non-idempotent (e.g. POST).
// If we cant fetch it from cache, we should probably give up, because with a real
// fetch, the server could return a different response.
// TODO: handle cases where Content-Type is not text/html.
(doc.url(), None, "text/html", false)
};
let source_info = SourceInfo {
url,
external: is_external,
worker_id: None,
content,
content_type: Some(content_type.to_string()),
};
let _ = chan.send(ScriptToDevtoolsControlMsg::CreateSourceActor(
pipeline_id,
source_info,
));
}
if script.type_ == ScriptType::Classic {
unminify_js(&mut script);
self.substitute_with_local_script(&mut script);

View file

@ -500,6 +500,7 @@ pub(crate) mod performancepainttiming;
pub(crate) mod performanceresourcetiming;
pub(crate) mod permissions;
pub(crate) mod permissionstatus;
pub(crate) mod pipelineid;
pub(crate) mod plugin;
pub(crate) mod pluginarray;
#[allow(dead_code)]

View file

@ -0,0 +1,52 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::cell::Cell;
use dom_struct::dom_struct;
use js::rust::HandleObject;
use crate::dom::bindings::codegen::Bindings::DebuggerEventBinding::PipelineIdMethods;
use crate::dom::bindings::error::Fallible;
use crate::dom::bindings::reflector::{
Reflector, reflect_dom_object, reflect_dom_object_with_proto,
};
use crate::dom::bindings::root::DomRoot;
use crate::dom::globalscope::GlobalScope;
use crate::script_runtime::CanGc;
#[dom_struct]
pub(crate) struct PipelineId {
reflector_: Reflector,
#[no_trace]
inner: base::id::PipelineId,
}
impl PipelineId {
pub(crate) fn new(
global: &GlobalScope,
pipeline_id: base::id::PipelineId,
can_gc: CanGc,
) -> DomRoot<Self> {
reflect_dom_object(
Box::new(Self {
reflector_: Reflector::new(),
inner: pipeline_id,
}),
global,
can_gc,
)
}
}
impl PipelineIdMethods<crate::DomTypeHolder> for PipelineId {
// check-tidy: no specs after this line
fn NamespaceId(&self) -> u32 {
self.inner.namespace_id.0
}
fn Index(&self) -> u32 {
self.inner.index.0.get()
}
}

View file

@ -3449,8 +3449,9 @@ impl ScriptThread {
incomplete.load_data.inherited_secure_context,
incomplete.theme,
);
// TODO: call this for workers too
self.debugger_global
.fire_add_debuggee(can_gc, window.upcast());
.fire_add_debuggee(can_gc, window.upcast(), incomplete.pipeline_id);
let _realm = enter_realm(&*window);