Match selectors inside ShadowRoot trees

This commit is contained in:
SGavrl committed 2026-07-31 10:34:06 +00:00
1 parent a4a3ea49ba
commit 6fdcd19898
4 files changed
+33 -9

No files matched your search

+23
View File
@@ -743,6 +743,29 @@ impl DomTree {
Ok(results)
}
/// Test one element as the selector subject, including when it is
/// detached or is a direct child of a shadow-tree compatibility root.
pub fn matches_selector(&self, nid: NodeId, selector: &str) -> Result<bool, String> {
if !self.with_node(nid, |node| node.is_element()).unwrap_or(false) {
return Ok(false);
}
let selector_list = parse_selector(selector)?;
let mut caches = selectors::context::SelectorCaches::default();
let mut context = MatchingContext::new(
MatchingMode::Normal,
None,
&mut caches,
QuirksMode::NoQuirks,
NeedsSelectorFlags::No,
MatchingForInvalidation::No,
);
Ok(selectors::matching::matches_selector_list(
&selector_list,
&DomElement::new(self, nid),
&mut context,
))
}
/// Parse a single selector once for repeated single-element matching, and
/// precompute its specificity, ancestor hashes, and "subject key" (the
/// rightmost id/class/tag used to bucket the rule for fast candidate
+1 -7
View File
@@ -2059,13 +2059,7 @@ class Element extends Node {
if (rest === "") return true;
return this.matches(rest);
}
const parent = this.parentNode;
if (!parent || !parent.querySelectorAll) return false;
const matches = parent.querySelectorAll(s);
for (let i = 0; i < matches.length; i++) {
if (matches[i]._nid === this._nid) return true;
}
return false;
return _dom("matches_selector", this._nid, String(s)) === "true";
}
closest(s) {
let el = this;
+4
View File
@@ -288,6 +288,10 @@ fn op_dom_inner(state: &OpState, cmd: String, arg1: String, arg2: String) -> Str
.map(|ids| ids.iter().map(|id| id.index() as i32).collect()).unwrap_or_default();
serde_json::to_string(&ids).unwrap_or("[]".into())
}
"matches_selector" => {
let nid = NodeId::new(arg1.parse::<u32>().unwrap_or(0));
dom.matches_selector(nid, &arg2).unwrap_or(false).to_string()
}
"node_type" => {
let nid = arg1.parse::<u32>().unwrap_or(0);
dom.with_node(NodeId::new(nid), |n| match &n.data {
+5 -2
View File
@@ -1991,7 +1991,10 @@ mod tests {
const parsed = [
inside.parentNode === root,
inside.getRootNode() === root,
root.textContent
root.textContent,
inside.matches("span#inside"),
root.querySelector("span#inside") === inside,
root.querySelectorAll("span#inside").length === 1
];
const a = document.createElement("a");
@@ -2059,7 +2062,7 @@ mod tests {
],
[true, true, true],
[true, true, true],
[true, true, "inside"],
[true, true, "inside", true, true, true],
["b", "a", "i"],
[["b", "a", "x-one", "x-two", "i"], 0, true, true],
[["a", "x-one", "x-two", "b"], true, true],