mirror of
https://github.com/servo/servo.git
synced 2025-07-06 23:13:39 +01:00
[WebDriver] Implement XPath Locator Strategy (#37783)
1. Implement XPath Locator Strategy 2. Use it for "Find Element(s)", "Find Element(s) from Element", "Find Element(s) from Shadow Root" Testing: `tests\wpt\tests\webdriver\tests\classic\find_element*\find.py` --------- Signed-off-by: Euclid Ye <yezhizhenjiakang@gmail.com>
This commit is contained in:
parent
f682f9d6f5
commit
d781d1b1cb
10 changed files with 190 additions and 51 deletions
|
@ -2270,6 +2270,15 @@ impl ScriptThread {
|
|||
can_gc,
|
||||
)
|
||||
},
|
||||
WebDriverScriptCommand::FindElementsXpathSelector(selector, reply) => {
|
||||
webdriver_handlers::handle_find_elements_xpath_selector(
|
||||
&documents,
|
||||
pipeline_id,
|
||||
selector,
|
||||
reply,
|
||||
can_gc,
|
||||
)
|
||||
},
|
||||
WebDriverScriptCommand::FindElementElementsCSSSelector(selector, element_id, reply) => {
|
||||
webdriver_handlers::handle_find_element_elements_css_selector(
|
||||
&documents,
|
||||
|
@ -2303,6 +2312,18 @@ impl ScriptThread {
|
|||
can_gc,
|
||||
)
|
||||
},
|
||||
WebDriverScriptCommand::FindElementElementsXPathSelector(
|
||||
selector,
|
||||
element_id,
|
||||
reply,
|
||||
) => webdriver_handlers::handle_find_element_elements_xpath_selector(
|
||||
&documents,
|
||||
pipeline_id,
|
||||
element_id,
|
||||
selector,
|
||||
reply,
|
||||
can_gc,
|
||||
),
|
||||
WebDriverScriptCommand::FindShadowElementsCSSSelector(
|
||||
selector,
|
||||
shadow_root_id,
|
||||
|
@ -2337,6 +2358,18 @@ impl ScriptThread {
|
|||
reply,
|
||||
)
|
||||
},
|
||||
WebDriverScriptCommand::FindShadowElementsXPathSelector(
|
||||
selector,
|
||||
shadow_root_id,
|
||||
reply,
|
||||
) => webdriver_handlers::handle_find_shadow_elements_xpath_selector(
|
||||
&documents,
|
||||
pipeline_id,
|
||||
shadow_root_id,
|
||||
selector,
|
||||
reply,
|
||||
can_gc,
|
||||
),
|
||||
WebDriverScriptCommand::GetElementShadowRoot(element_id, reply) => {
|
||||
webdriver_handlers::handle_get_element_shadow_root(
|
||||
&documents,
|
||||
|
|
|
@ -43,6 +43,9 @@ use crate::dom::bindings::codegen::Bindings::HTMLSelectElementBinding::HTMLSelec
|
|||
use crate::dom::bindings::codegen::Bindings::NodeBinding::{GetRootNodeOptions, NodeMethods};
|
||||
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::XMLSerializerBinding::XMLSerializerMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::XPathResultBinding::{
|
||||
XPathResultConstants, XPathResultMethods,
|
||||
};
|
||||
use crate::dom::bindings::conversions::{
|
||||
ConversionBehavior, ConversionResult, FromJSValConvertible, StringificationBehavior,
|
||||
get_property, get_property_jsval, jsid_to_string, jsstring_to_str, root_from_object,
|
||||
|
@ -759,6 +762,87 @@ pub(crate) fn handle_find_elements_tag_name(
|
|||
}
|
||||
}
|
||||
|
||||
/// <https://w3c.github.io/webdriver/#xpath>
|
||||
fn find_elements_xpath_strategy(
|
||||
document: &Document,
|
||||
start_node: &Node,
|
||||
selector: String,
|
||||
pipeline: PipelineId,
|
||||
can_gc: CanGc,
|
||||
) -> Result<Vec<String>, ErrorStatus> {
|
||||
// Step 1. Let evaluateResult be the result of calling evaluate,
|
||||
// with arguments selector, start node, null, ORDERED_NODE_SNAPSHOT_TYPE, and null.
|
||||
|
||||
// A snapshot is used to promote operation atomicity.
|
||||
let evaluate_result = match document.Evaluate(
|
||||
DOMString::from(selector),
|
||||
start_node,
|
||||
None,
|
||||
XPathResultConstants::ORDERED_NODE_SNAPSHOT_TYPE,
|
||||
None,
|
||||
can_gc,
|
||||
) {
|
||||
Ok(res) => res,
|
||||
Err(_) => return Err(ErrorStatus::InvalidSelector),
|
||||
};
|
||||
// Step 2. Let index be 0. (Handled altogether in Step 5.)
|
||||
|
||||
// Step 3: Let length be the result of getting the property "snapshotLength"
|
||||
// from evaluateResult.
|
||||
|
||||
let length = match evaluate_result.GetSnapshotLength() {
|
||||
Ok(len) => len,
|
||||
Err(_) => return Err(ErrorStatus::InvalidSelector),
|
||||
};
|
||||
|
||||
// Step 4: Prepare result vector
|
||||
let mut result = Vec::new();
|
||||
|
||||
// Step 5: Repeat, while index is less than length:
|
||||
for index in 0..length {
|
||||
// Step 5.1. Let node be the result of calling snapshotItem with
|
||||
// evaluateResult as this and index as the argument.
|
||||
let node = match evaluate_result.SnapshotItem(index) {
|
||||
Ok(node) => node.expect(
|
||||
"Node should always exist as ORDERED_NODE_SNAPSHOT_TYPE \
|
||||
gives static result and we verified the length!",
|
||||
),
|
||||
Err(_) => return Err(ErrorStatus::InvalidSelector),
|
||||
};
|
||||
|
||||
// Step 5.2. If node is not an element return an error with error code invalid selector.
|
||||
if !node.is::<Element>() {
|
||||
return Err(ErrorStatus::InvalidSelector);
|
||||
}
|
||||
|
||||
// Step 5.3. Append node to result.
|
||||
result.push(node.unique_id(pipeline));
|
||||
}
|
||||
// Step 6. Return success with data result.
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub(crate) fn handle_find_elements_xpath_selector(
|
||||
documents: &DocumentCollection,
|
||||
pipeline: PipelineId,
|
||||
selector: String,
|
||||
reply: IpcSender<Result<Vec<String>, ErrorStatus>>,
|
||||
can_gc: CanGc,
|
||||
) {
|
||||
match retrieve_document_and_check_root_existence(documents, pipeline) {
|
||||
Ok(document) => reply
|
||||
.send(find_elements_xpath_strategy(
|
||||
&document,
|
||||
document.upcast::<Node>(),
|
||||
selector,
|
||||
pipeline,
|
||||
can_gc,
|
||||
))
|
||||
.unwrap(),
|
||||
Err(error) => reply.send(Err(error)).unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn handle_find_element_elements_css_selector(
|
||||
documents: &DocumentCollection,
|
||||
pipeline: PipelineId,
|
||||
|
@ -823,6 +907,31 @@ pub(crate) fn handle_find_element_elements_tag_name(
|
|||
.unwrap();
|
||||
}
|
||||
|
||||
pub(crate) fn handle_find_element_elements_xpath_selector(
|
||||
documents: &DocumentCollection,
|
||||
pipeline: PipelineId,
|
||||
element_id: String,
|
||||
selector: String,
|
||||
reply: IpcSender<Result<Vec<String>, ErrorStatus>>,
|
||||
can_gc: CanGc,
|
||||
) {
|
||||
reply
|
||||
.send(
|
||||
get_known_element(documents, pipeline, element_id).and_then(|element| {
|
||||
find_elements_xpath_strategy(
|
||||
&documents
|
||||
.find_document(pipeline)
|
||||
.expect("Document existence guaranteed by `get_known_element`"),
|
||||
element.upcast::<Node>(),
|
||||
selector,
|
||||
pipeline,
|
||||
can_gc,
|
||||
)
|
||||
}),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
/// <https://w3c.github.io/webdriver/#find-elements-from-shadow-root>
|
||||
pub(crate) fn handle_find_shadow_elements_css_selector(
|
||||
documents: &DocumentCollection,
|
||||
|
@ -902,6 +1011,31 @@ pub(crate) fn handle_find_shadow_elements_tag_name(
|
|||
.unwrap();
|
||||
}
|
||||
|
||||
pub(crate) fn handle_find_shadow_elements_xpath_selector(
|
||||
documents: &DocumentCollection,
|
||||
pipeline: PipelineId,
|
||||
shadow_root_id: String,
|
||||
selector: String,
|
||||
reply: IpcSender<Result<Vec<String>, ErrorStatus>>,
|
||||
can_gc: CanGc,
|
||||
) {
|
||||
reply
|
||||
.send(
|
||||
get_known_shadow_root(documents, pipeline, shadow_root_id).and_then(|shadow_root| {
|
||||
find_elements_xpath_strategy(
|
||||
&documents
|
||||
.find_document(pipeline)
|
||||
.expect("Document existence guaranteed by `get_known_shadow_root`"),
|
||||
shadow_root.upcast::<Node>(),
|
||||
selector,
|
||||
pipeline,
|
||||
can_gc,
|
||||
)
|
||||
}),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
/// <https://www.w3.org/TR/webdriver2/#dfn-get-element-shadow-root>
|
||||
pub(crate) fn handle_get_element_shadow_root(
|
||||
documents: &DocumentCollection,
|
||||
|
|
|
@ -134,6 +134,7 @@ pub enum WebDriverScriptCommand {
|
|||
FindElementsCSSSelector(String, IpcSender<Result<Vec<String>, ErrorStatus>>),
|
||||
FindElementsLinkText(String, bool, IpcSender<Result<Vec<String>, ErrorStatus>>),
|
||||
FindElementsTagName(String, IpcSender<Result<Vec<String>, ErrorStatus>>),
|
||||
FindElementsXpathSelector(String, IpcSender<Result<Vec<String>, ErrorStatus>>),
|
||||
FindElementElementsCSSSelector(String, String, IpcSender<Result<Vec<String>, ErrorStatus>>),
|
||||
FindElementElementsLinkText(
|
||||
String,
|
||||
|
@ -142,6 +143,7 @@ pub enum WebDriverScriptCommand {
|
|||
IpcSender<Result<Vec<String>, ErrorStatus>>,
|
||||
),
|
||||
FindElementElementsTagName(String, String, IpcSender<Result<Vec<String>, ErrorStatus>>),
|
||||
FindElementElementsXPathSelector(String, String, IpcSender<Result<Vec<String>, ErrorStatus>>),
|
||||
FindShadowElementsCSSSelector(String, String, IpcSender<Result<Vec<String>, ErrorStatus>>),
|
||||
FindShadowElementsLinkText(
|
||||
String,
|
||||
|
@ -150,6 +152,7 @@ pub enum WebDriverScriptCommand {
|
|||
IpcSender<Result<Vec<String>, ErrorStatus>>,
|
||||
),
|
||||
FindShadowElementsTagName(String, String, IpcSender<Result<Vec<String>, ErrorStatus>>),
|
||||
FindShadowElementsXPathSelector(String, String, IpcSender<Result<Vec<String>, ErrorStatus>>),
|
||||
GetElementShadowRoot(String, IpcSender<Result<Option<String>, ErrorStatus>>),
|
||||
ElementClick(String, IpcSender<Result<Option<String>, ErrorStatus>>),
|
||||
GetActiveElement(IpcSender<Option<String>>),
|
||||
|
|
|
@ -1172,11 +1172,12 @@ impl Handler {
|
|||
WebDriverScriptCommand::FindElementsTagName(parameters.value.clone(), sender);
|
||||
self.browsing_context_script_command::<true>(cmd)?;
|
||||
},
|
||||
_ => {
|
||||
return Err(WebDriverError::new(
|
||||
ErrorStatus::UnsupportedOperation,
|
||||
"Unsupported locator strategy",
|
||||
));
|
||||
LocatorStrategy::XPath => {
|
||||
let cmd = WebDriverScriptCommand::FindElementsXpathSelector(
|
||||
parameters.value.clone(),
|
||||
sender,
|
||||
);
|
||||
self.browsing_context_script_command::<true>(cmd)?;
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -1242,11 +1243,13 @@ impl Handler {
|
|||
);
|
||||
self.browsing_context_script_command::<true>(cmd)?;
|
||||
},
|
||||
_ => {
|
||||
return Err(WebDriverError::new(
|
||||
ErrorStatus::UnsupportedOperation,
|
||||
"Unsupported locator strategy",
|
||||
));
|
||||
LocatorStrategy::XPath => {
|
||||
let cmd = WebDriverScriptCommand::FindElementElementsXPathSelector(
|
||||
parameters.value.clone(),
|
||||
element.to_string(),
|
||||
sender,
|
||||
);
|
||||
self.browsing_context_script_command::<true>(cmd)?;
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -1302,11 +1305,13 @@ impl Handler {
|
|||
);
|
||||
self.browsing_context_script_command::<true>(cmd)?;
|
||||
},
|
||||
_ => {
|
||||
return Err(WebDriverError::new(
|
||||
ErrorStatus::UnsupportedOperation,
|
||||
"Unsupported locator strategy",
|
||||
));
|
||||
LocatorStrategy::XPath => {
|
||||
let cmd = WebDriverScriptCommand::FindShadowElementsXPathSelector(
|
||||
parameters.value.clone(),
|
||||
shadow_root.to_string(),
|
||||
sender,
|
||||
);
|
||||
self.browsing_context_script_command::<true>(cmd)?;
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,3 @@
|
|||
[find.py]
|
||||
[test_no_browsing_context]
|
||||
expected: FAIL
|
||||
|
||||
[test_find_element[xpath-//a\]]
|
||||
expected: FAIL
|
||||
|
||||
[test_xhtml_namespace[xpath-//*[name()='a'\]\]]
|
||||
expected: FAIL
|
||||
|
||||
[test_htmldocument[xpath-/html\]]
|
||||
expected: FAIL
|
||||
|
|
|
@ -2,12 +2,6 @@
|
|||
[test_no_browsing_context]
|
||||
expected: FAIL
|
||||
|
||||
[test_find_element[xpath-//a\]]
|
||||
expected: FAIL
|
||||
|
||||
[test_xhtml_namespace[xpath-//*[name()='a'\]\]]
|
||||
expected: FAIL
|
||||
|
||||
[test_parent_htmldocument]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -2,9 +2,6 @@
|
|||
[test_no_browsing_context]
|
||||
expected: FAIL
|
||||
|
||||
[test_find_element[open-xpath-//a\]]
|
||||
expected: FAIL
|
||||
|
||||
[test_find_element[closed-css selector-#linkText\]]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,12 +1,3 @@
|
|||
[find.py]
|
||||
[test_no_browsing_context]
|
||||
expected: FAIL
|
||||
|
||||
[test_find_elements[xpath-//a\]]
|
||||
expected: FAIL
|
||||
|
||||
[test_xhtml_namespace[xpath-//*[name()='a'\]\]]
|
||||
expected: FAIL
|
||||
|
||||
[test_htmldocument[xpath-/html\]]
|
||||
expected: FAIL
|
||||
|
|
|
@ -2,12 +2,6 @@
|
|||
[test_no_browsing_context]
|
||||
expected: FAIL
|
||||
|
||||
[test_find_elements[xpath-//a\]]
|
||||
expected: FAIL
|
||||
|
||||
[test_xhtml_namespace[xpath-//*[name()='a'\]\]]
|
||||
expected: FAIL
|
||||
|
||||
[test_parent_htmldocument]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -2,9 +2,6 @@
|
|||
[test_no_browsing_context]
|
||||
expected: FAIL
|
||||
|
||||
[test_find_elements[open-xpath-//a\]]
|
||||
expected: FAIL
|
||||
|
||||
[test_find_elements[closed-css selector-#linkText\]]
|
||||
expected: FAIL
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue