mirror of
https://github.com/servo/servo.git
synced 2025-07-07 07:23:38 +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,
|
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) => {
|
WebDriverScriptCommand::FindElementElementsCSSSelector(selector, element_id, reply) => {
|
||||||
webdriver_handlers::handle_find_element_elements_css_selector(
|
webdriver_handlers::handle_find_element_elements_css_selector(
|
||||||
&documents,
|
&documents,
|
||||||
|
@ -2303,6 +2312,18 @@ impl ScriptThread {
|
||||||
can_gc,
|
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(
|
WebDriverScriptCommand::FindShadowElementsCSSSelector(
|
||||||
selector,
|
selector,
|
||||||
shadow_root_id,
|
shadow_root_id,
|
||||||
|
@ -2337,6 +2358,18 @@ impl ScriptThread {
|
||||||
reply,
|
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) => {
|
WebDriverScriptCommand::GetElementShadowRoot(element_id, reply) => {
|
||||||
webdriver_handlers::handle_get_element_shadow_root(
|
webdriver_handlers::handle_get_element_shadow_root(
|
||||||
&documents,
|
&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::NodeBinding::{GetRootNodeOptions, NodeMethods};
|
||||||
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
|
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
|
||||||
use crate::dom::bindings::codegen::Bindings::XMLSerializerBinding::XMLSerializerMethods;
|
use crate::dom::bindings::codegen::Bindings::XMLSerializerBinding::XMLSerializerMethods;
|
||||||
|
use crate::dom::bindings::codegen::Bindings::XPathResultBinding::{
|
||||||
|
XPathResultConstants, XPathResultMethods,
|
||||||
|
};
|
||||||
use crate::dom::bindings::conversions::{
|
use crate::dom::bindings::conversions::{
|
||||||
ConversionBehavior, ConversionResult, FromJSValConvertible, StringificationBehavior,
|
ConversionBehavior, ConversionResult, FromJSValConvertible, StringificationBehavior,
|
||||||
get_property, get_property_jsval, jsid_to_string, jsstring_to_str, root_from_object,
|
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(
|
pub(crate) fn handle_find_element_elements_css_selector(
|
||||||
documents: &DocumentCollection,
|
documents: &DocumentCollection,
|
||||||
pipeline: PipelineId,
|
pipeline: PipelineId,
|
||||||
|
@ -823,6 +907,31 @@ pub(crate) fn handle_find_element_elements_tag_name(
|
||||||
.unwrap();
|
.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>
|
/// <https://w3c.github.io/webdriver/#find-elements-from-shadow-root>
|
||||||
pub(crate) fn handle_find_shadow_elements_css_selector(
|
pub(crate) fn handle_find_shadow_elements_css_selector(
|
||||||
documents: &DocumentCollection,
|
documents: &DocumentCollection,
|
||||||
|
@ -902,6 +1011,31 @@ pub(crate) fn handle_find_shadow_elements_tag_name(
|
||||||
.unwrap();
|
.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>
|
/// <https://www.w3.org/TR/webdriver2/#dfn-get-element-shadow-root>
|
||||||
pub(crate) fn handle_get_element_shadow_root(
|
pub(crate) fn handle_get_element_shadow_root(
|
||||||
documents: &DocumentCollection,
|
documents: &DocumentCollection,
|
||||||
|
|
|
@ -134,6 +134,7 @@ pub enum WebDriverScriptCommand {
|
||||||
FindElementsCSSSelector(String, IpcSender<Result<Vec<String>, ErrorStatus>>),
|
FindElementsCSSSelector(String, IpcSender<Result<Vec<String>, ErrorStatus>>),
|
||||||
FindElementsLinkText(String, bool, IpcSender<Result<Vec<String>, ErrorStatus>>),
|
FindElementsLinkText(String, bool, IpcSender<Result<Vec<String>, ErrorStatus>>),
|
||||||
FindElementsTagName(String, 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>>),
|
FindElementElementsCSSSelector(String, String, IpcSender<Result<Vec<String>, ErrorStatus>>),
|
||||||
FindElementElementsLinkText(
|
FindElementElementsLinkText(
|
||||||
String,
|
String,
|
||||||
|
@ -142,6 +143,7 @@ pub enum WebDriverScriptCommand {
|
||||||
IpcSender<Result<Vec<String>, ErrorStatus>>,
|
IpcSender<Result<Vec<String>, ErrorStatus>>,
|
||||||
),
|
),
|
||||||
FindElementElementsTagName(String, String, 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>>),
|
FindShadowElementsCSSSelector(String, String, IpcSender<Result<Vec<String>, ErrorStatus>>),
|
||||||
FindShadowElementsLinkText(
|
FindShadowElementsLinkText(
|
||||||
String,
|
String,
|
||||||
|
@ -150,6 +152,7 @@ pub enum WebDriverScriptCommand {
|
||||||
IpcSender<Result<Vec<String>, ErrorStatus>>,
|
IpcSender<Result<Vec<String>, ErrorStatus>>,
|
||||||
),
|
),
|
||||||
FindShadowElementsTagName(String, String, 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>>),
|
GetElementShadowRoot(String, IpcSender<Result<Option<String>, ErrorStatus>>),
|
||||||
ElementClick(String, IpcSender<Result<Option<String>, ErrorStatus>>),
|
ElementClick(String, IpcSender<Result<Option<String>, ErrorStatus>>),
|
||||||
GetActiveElement(IpcSender<Option<String>>),
|
GetActiveElement(IpcSender<Option<String>>),
|
||||||
|
|
|
@ -1172,11 +1172,12 @@ impl Handler {
|
||||||
WebDriverScriptCommand::FindElementsTagName(parameters.value.clone(), sender);
|
WebDriverScriptCommand::FindElementsTagName(parameters.value.clone(), sender);
|
||||||
self.browsing_context_script_command::<true>(cmd)?;
|
self.browsing_context_script_command::<true>(cmd)?;
|
||||||
},
|
},
|
||||||
_ => {
|
LocatorStrategy::XPath => {
|
||||||
return Err(WebDriverError::new(
|
let cmd = WebDriverScriptCommand::FindElementsXpathSelector(
|
||||||
ErrorStatus::UnsupportedOperation,
|
parameters.value.clone(),
|
||||||
"Unsupported locator strategy",
|
sender,
|
||||||
));
|
);
|
||||||
|
self.browsing_context_script_command::<true>(cmd)?;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1242,11 +1243,13 @@ impl Handler {
|
||||||
);
|
);
|
||||||
self.browsing_context_script_command::<true>(cmd)?;
|
self.browsing_context_script_command::<true>(cmd)?;
|
||||||
},
|
},
|
||||||
_ => {
|
LocatorStrategy::XPath => {
|
||||||
return Err(WebDriverError::new(
|
let cmd = WebDriverScriptCommand::FindElementElementsXPathSelector(
|
||||||
ErrorStatus::UnsupportedOperation,
|
parameters.value.clone(),
|
||||||
"Unsupported locator strategy",
|
element.to_string(),
|
||||||
));
|
sender,
|
||||||
|
);
|
||||||
|
self.browsing_context_script_command::<true>(cmd)?;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1302,11 +1305,13 @@ impl Handler {
|
||||||
);
|
);
|
||||||
self.browsing_context_script_command::<true>(cmd)?;
|
self.browsing_context_script_command::<true>(cmd)?;
|
||||||
},
|
},
|
||||||
_ => {
|
LocatorStrategy::XPath => {
|
||||||
return Err(WebDriverError::new(
|
let cmd = WebDriverScriptCommand::FindShadowElementsXPathSelector(
|
||||||
ErrorStatus::UnsupportedOperation,
|
parameters.value.clone(),
|
||||||
"Unsupported locator strategy",
|
shadow_root.to_string(),
|
||||||
));
|
sender,
|
||||||
|
);
|
||||||
|
self.browsing_context_script_command::<true>(cmd)?;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,3 @@
|
||||||
[find.py]
|
[find.py]
|
||||||
[test_no_browsing_context]
|
[test_no_browsing_context]
|
||||||
expected: FAIL
|
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]
|
[test_no_browsing_context]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[test_find_element[xpath-//a\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[test_xhtml_namespace[xpath-//*[name()='a'\]\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[test_parent_htmldocument]
|
[test_parent_htmldocument]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,6 @@
|
||||||
[test_no_browsing_context]
|
[test_no_browsing_context]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[test_find_element[open-xpath-//a\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[test_find_element[closed-css selector-#linkText\]]
|
[test_find_element[closed-css selector-#linkText\]]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,3 @@
|
||||||
[find.py]
|
[find.py]
|
||||||
[test_no_browsing_context]
|
[test_no_browsing_context]
|
||||||
expected: FAIL
|
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]
|
[test_no_browsing_context]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[test_find_elements[xpath-//a\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[test_xhtml_namespace[xpath-//*[name()='a'\]\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[test_parent_htmldocument]
|
[test_parent_htmldocument]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,6 @@
|
||||||
[test_no_browsing_context]
|
[test_no_browsing_context]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[test_find_elements[open-xpath-//a\]]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[test_find_elements[closed-css selector-#linkText\]]
|
[test_find_elements[closed-css selector-#linkText\]]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue