From bce36846451fc12fe9909bebb728c59f19b67701 Mon Sep 17 00:00:00 2001 From: Euclid Ye Date: Mon, 2 Jun 2025 15:00:21 +0800 Subject: [PATCH] script: Upgrade `node_ids` to `pipeline_to_node_ids` to track the owner pipeline of the node Signed-off-by: Euclid Ye --- components/script/devtools.rs | 2 +- components/script/dom/node.rs | 15 +++---- components/script/script_thread.rs | 28 +++++++++---- components/script/webdriver_handlers.rs | 53 +++++++++++++++---------- 4 files changed, 61 insertions(+), 37 deletions(-) diff --git a/components/script/devtools.rs b/components/script/devtools.rs index 93212887dc8..945470194e2 100644 --- a/components/script/devtools.rs +++ b/components/script/devtools.rs @@ -132,7 +132,7 @@ fn find_node_by_unique_id( document .upcast::() .traverse_preorder(ShadowIncluding::Yes) - .find(|candidate| candidate.unique_id() == node_id) + .find(|candidate| candidate.unique_id(pipeline) == node_id) }) } diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index cd0a41ab9f6..9c14a840849 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -1261,13 +1261,13 @@ impl Node { } } - pub(crate) fn unique_id(&self) -> String { + pub(crate) fn unique_id(&self, pipeline: PipelineId) -> String { let mut rare_data = self.ensure_rare_data(); if rare_data.unique_id.is_none() { - let id = UniqueId::new(); - ScriptThread::save_node_id(id.borrow().simple().to_string()); - rare_data.unique_id = Some(id); + let node_id = UniqueId::new(); + ScriptThread::save_node_id(pipeline, node_id.borrow().simple().to_string()); + rare_data.unique_id = Some(node_id); } rare_data .unique_id @@ -1281,6 +1281,7 @@ impl Node { pub(crate) fn summarize(&self, can_gc: CanGc) -> NodeInfo { let USVString(base_uri) = self.BaseURI(); let node_type = self.NodeType(); + let pipeline = self.owner_document().window().pipeline_id(); let maybe_shadow_root = self.downcast::(); let shadow_root_mode = maybe_shadow_root @@ -1288,7 +1289,7 @@ impl Node { .map(ShadowRootMode::convert); let host = maybe_shadow_root .map(ShadowRoot::Host) - .map(|host| host.upcast::().unique_id()); + .map(|host| host.upcast::().unique_id(pipeline)); let is_shadow_host = self.downcast::().is_some_and(|potential_host| { let Some(root) = potential_host.shadow_root() else { return false; @@ -1310,12 +1311,12 @@ impl Node { .map(|style| style.Display().into()); NodeInfo { - unique_id: self.unique_id(), + unique_id: self.unique_id(pipeline), host, base_uri, parent: self .GetParentNode() - .map_or("".to_owned(), |node| node.unique_id()), + .map_or("".to_owned(), |node| node.unique_id(pipeline)), node_type, is_top_level_document: node_type == NodeConstants::DOCUMENT_NODE, node_name: String::from(self.NodeName()), diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index e138e7114d3..e1ef3d5455f 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -194,6 +194,8 @@ pub(crate) struct IncompleteParserContexts(RefCell); +type NodeIdSet = HashSet; + #[derive(JSTraceable)] // ScriptThread instances are rooted on creation, so this is okay #[cfg_attr(crown, allow(crown::unrooted_must_root))] @@ -315,8 +317,9 @@ pub struct ScriptThread { #[no_trace] player_context: WindowGLContext, - /// A set of all nodes ever created in this script thread - node_ids: DomRefCell>, + /// A map from pipelines to all owned nodes ever created in this script thread + #[no_trace] + pipeline_to_node_ids: DomRefCell>, /// Code is running as a consequence of a user interaction is_user_interacting: Cell, @@ -818,14 +821,25 @@ impl ScriptThread { }) } - pub(crate) fn save_node_id(node_id: String) { + pub(crate) fn save_node_id(pipeline: PipelineId, node_id: String) { with_script_thread(|script_thread| { - script_thread.node_ids.borrow_mut().insert(node_id); + script_thread + .pipeline_to_node_ids + .borrow_mut() + .entry(pipeline) + .or_insert_with(HashSet::new) + .insert(node_id); }) } - pub(crate) fn has_node_id(node_id: &str) -> bool { - with_script_thread(|script_thread| script_thread.node_ids.borrow().contains(node_id)) + pub(crate) fn has_node_id(pipeline: PipelineId, node_id: &str) -> bool { + with_script_thread(|script_thread| { + script_thread + .pipeline_to_node_ids + .borrow() + .get(&pipeline) + .is_some_and(|node_ids| node_ids.contains(node_id)) + }) } /// Creates a new script thread. @@ -945,7 +959,7 @@ impl ScriptThread { unminify_css: opts.unminify_css, user_content_manager: state.user_content_manager, player_context: state.player_context, - node_ids: Default::default(), + pipeline_to_node_ids: Default::default(), is_user_interacting: Cell::new(false), #[cfg(feature = "webgpu")] gpu_id_hub: Arc::new(IdentityHub::default()), diff --git a/components/script/webdriver_handlers.rs b/components/script/webdriver_handlers.rs index 6b4264d945e..980c924e5e4 100644 --- a/components/script/webdriver_handlers.rs +++ b/components/script/webdriver_handlers.rs @@ -81,7 +81,7 @@ fn find_node_by_unique_id( match documents.find_document(pipeline) { Some(doc) => find_node_by_unique_id_in_document(&doc, node_id), None => { - if ScriptThread::has_node_id(&node_id) { + if ScriptThread::has_node_id(pipeline, &node_id) { Err(ErrorStatus::StaleElementReference) } else { Err(ErrorStatus::NoSuchElement) @@ -94,14 +94,15 @@ pub(crate) fn find_node_by_unique_id_in_document( document: &Document, node_id: String, ) -> Result, ErrorStatus> { + let pipeline = document.window().pipeline_id(); match document .upcast::() .traverse_preorder(ShadowIncluding::Yes) - .find(|node| node.unique_id() == node_id) + .find(|node| node.unique_id(pipeline) == node_id) { Some(node) => Ok(node), None => { - if ScriptThread::has_node_id(&node_id) { + if ScriptThread::has_node_id(pipeline, &node_id) { Err(ErrorStatus::StaleElementReference) } else { Err(ErrorStatus::NoSuchElement) @@ -129,7 +130,10 @@ fn matching_links( content == link_text } }) - .map(|node| node.upcast::().unique_id()) + .map(|node| { + node.upcast::() + .unique_id(node.owner_doc().window().pipeline_id()) + }) } fn all_matching_links( @@ -329,20 +333,25 @@ unsafe fn jsval_to_webdriver_inner( Ok(WebDriverJSValue::ArrayLike(result)) } else if let Ok(element) = root_from_object::(*object, cx) { Ok(WebDriverJSValue::Element(WebElement( - element.upcast::().unique_id(), + element + .upcast::() + .unique_id(element.owner_document().window().pipeline_id()), ))) } else if let Ok(window) = root_from_object::(*object, cx) { let window_proxy = window.window_proxy(); if window_proxy.is_browsing_context_discarded() { return Err(WebDriverJSError::StaleElementReference); - } else if window_proxy.browsing_context_id() == window_proxy.webview_id() { - Ok(WebDriverJSValue::Window(WebWindow( - window.Document().upcast::().unique_id(), - ))) } else { - Ok(WebDriverJSValue::Frame(WebFrame( - window.Document().upcast::().unique_id(), - ))) + let pipeline = window.pipeline_id(); + if window_proxy.browsing_context_id() == window_proxy.webview_id() { + Ok(WebDriverJSValue::Window(WebWindow( + window.Document().upcast::().unique_id(pipeline), + ))) + } else { + Ok(WebDriverJSValue::Frame(WebFrame( + window.Document().upcast::().unique_id(pipeline), + ))) + } } } else if object_has_to_json_property(cx, global_scope, object.handle()) { let name = CString::new("toJSON").unwrap(); @@ -598,7 +607,7 @@ pub(crate) fn handle_find_element_css( .QuerySelector(DOMString::from(selector)) .map_err(|_| ErrorStatus::InvalidSelector) }) - .map(|node| node.map(|x| x.upcast::().unique_id())), + .map(|node| node.map(|x| x.upcast::().unique_id(pipeline))), ) .unwrap(); } @@ -640,7 +649,7 @@ pub(crate) fn handle_find_element_tag_name( .elements_iter() .next() }) - .map(|node| node.map(|x| x.upcast::().unique_id())), + .map(|node| node.map(|x| x.upcast::().unique_id(pipeline))), ) .unwrap(); } @@ -664,7 +673,7 @@ pub(crate) fn handle_find_elements_css( .map(|nodes| { nodes .iter() - .map(|x| x.upcast::().unique_id()) + .map(|x| x.upcast::().unique_id(pipeline)) .collect() }), ) @@ -706,7 +715,7 @@ pub(crate) fn handle_find_elements_tag_name( .map(|nodes| { nodes .elements_iter() - .map(|x| x.upcast::().unique_id()) + .map(|x| x.upcast::().unique_id(pipeline)) .collect::>() }), ) @@ -725,7 +734,7 @@ pub(crate) fn handle_find_element_element_css( find_node_by_unique_id(documents, pipeline, element_id).and_then(|node| { node.query_selector(DOMString::from(selector)) .map_err(|_| ErrorStatus::InvalidSelector) - .map(|node| node.map(|x| x.upcast::().unique_id())) + .map(|node| node.map(|x| x.upcast::().unique_id(pipeline))) }), ) .unwrap(); @@ -764,7 +773,7 @@ pub(crate) fn handle_find_element_element_tag_name( .GetElementsByTagName(DOMString::from(selector), can_gc) .elements_iter() .next() - .map(|x| x.upcast::().unique_id())), + .map(|x| x.upcast::().unique_id(pipeline))), None => Err(ErrorStatus::UnknownError), }), ) @@ -786,7 +795,7 @@ pub(crate) fn handle_find_element_elements_css( .map(|nodes| { nodes .iter() - .map(|x| x.upcast::().unique_id()) + .map(|x| x.upcast::().unique_id(pipeline)) .collect() }) }), @@ -826,7 +835,7 @@ pub(crate) fn handle_find_element_elements_tag_name( Some(element) => Ok(element .GetElementsByTagName(DOMString::from(selector), can_gc) .elements_iter() - .map(|x| x.upcast::().unique_id()) + .map(|x| x.upcast::().unique_id(pipeline)) .collect::>()), None => Err(ErrorStatus::UnknownError), }), @@ -867,7 +876,7 @@ pub(crate) fn handle_get_active_element( documents .find_document(pipeline) .and_then(|document| document.GetActiveElement()) - .map(|element| element.upcast::().unique_id()), + .map(|element| element.upcast::().unique_id(pipeline)), ) .unwrap(); } @@ -1395,7 +1404,7 @@ pub(crate) fn handle_element_click( Ok(None) }, - None => Ok(Some(node.unique_id())), + None => Ok(Some(node.unique_id(pipeline))), } }), )