mirror of
https://github.com/servo/servo.git
synced 2025-07-06 15:03:40 +01:00
[WebDriver] Reuse "Find Elements" handler in "Find element" (#37737)
All "Find Element ..." in [spec](https://w3c.github.io/webdriver/#find-element-from-element) has exactly same step has "Find Elements ...", except they extract the first element if there is any. We now reuse the handler instead of running numerous repetitive steps, same as what we did for ["Find Element from Shadow Root"](https://w3c.github.io/webdriver/#find-element-from-shadow-root) in #37578. This reduces binary size by 98KB for Release profile in Windows and improves maintainability. Testing: `./mach test-wpt -r .\tests\wpt\tests\webdriver\tests\classic\find_element* --product servodriver` --------- Signed-off-by: Euclid Ye <yezhizhenjiakang@gmail.com>
This commit is contained in:
parent
8e2ef5c248
commit
0329f33520
4 changed files with 23 additions and 325 deletions
|
@ -2247,33 +2247,6 @@ impl ScriptThread {
|
|||
WebDriverScriptCommand::DeleteCookie(name, reply) => {
|
||||
webdriver_handlers::handle_delete_cookie(&documents, pipeline_id, name, reply)
|
||||
},
|
||||
WebDriverScriptCommand::FindElementCSSSelector(selector, reply) => {
|
||||
webdriver_handlers::handle_find_element_css_selector(
|
||||
&documents,
|
||||
pipeline_id,
|
||||
selector,
|
||||
reply,
|
||||
)
|
||||
},
|
||||
WebDriverScriptCommand::FindElementLinkText(selector, partial, reply) => {
|
||||
webdriver_handlers::handle_find_element_link_text(
|
||||
&documents,
|
||||
pipeline_id,
|
||||
selector,
|
||||
partial,
|
||||
reply,
|
||||
can_gc,
|
||||
)
|
||||
},
|
||||
WebDriverScriptCommand::FindElementTagName(selector, reply) => {
|
||||
webdriver_handlers::handle_find_element_tag_name(
|
||||
&documents,
|
||||
pipeline_id,
|
||||
selector,
|
||||
reply,
|
||||
can_gc,
|
||||
)
|
||||
},
|
||||
WebDriverScriptCommand::FindElementsCSSSelector(selector, reply) => {
|
||||
webdriver_handlers::handle_find_elements_css_selector(
|
||||
&documents,
|
||||
|
@ -2301,39 +2274,6 @@ impl ScriptThread {
|
|||
can_gc,
|
||||
)
|
||||
},
|
||||
WebDriverScriptCommand::FindElementElementCSSSelector(selector, element_id, reply) => {
|
||||
webdriver_handlers::handle_find_element_element_css_selector(
|
||||
&documents,
|
||||
pipeline_id,
|
||||
element_id,
|
||||
selector,
|
||||
reply,
|
||||
)
|
||||
},
|
||||
WebDriverScriptCommand::FindElementElementLinkText(
|
||||
selector,
|
||||
element_id,
|
||||
partial,
|
||||
reply,
|
||||
) => webdriver_handlers::handle_find_element_element_link_text(
|
||||
&documents,
|
||||
pipeline_id,
|
||||
element_id,
|
||||
selector,
|
||||
partial,
|
||||
reply,
|
||||
can_gc,
|
||||
),
|
||||
WebDriverScriptCommand::FindElementElementTagName(selector, element_id, reply) => {
|
||||
webdriver_handlers::handle_find_element_element_tag_name(
|
||||
&documents,
|
||||
pipeline_id,
|
||||
element_id,
|
||||
selector,
|
||||
reply,
|
||||
can_gc,
|
||||
)
|
||||
},
|
||||
WebDriverScriptCommand::FindElementElementsCSSSelector(selector, element_id, reply) => {
|
||||
webdriver_handlers::handle_find_element_elements_css_selector(
|
||||
&documents,
|
||||
|
|
|
@ -217,25 +217,6 @@ fn all_matching_links(
|
|||
.map(|nodes| matching_links(&nodes, link_text, partial, can_gc).collect())
|
||||
}
|
||||
|
||||
fn first_matching_link(
|
||||
root_node: &Node,
|
||||
link_text: String,
|
||||
partial: bool,
|
||||
can_gc: CanGc,
|
||||
) -> Result<Option<String>, ErrorStatus> {
|
||||
// <https://w3c.github.io/webdriver/#dfn-find>
|
||||
// Step 7.2. If a DOMException, SyntaxError, XPathException, or other error occurs
|
||||
// during the execution of the element location strategy, return error invalid selector.
|
||||
root_node
|
||||
.query_selector_all(DOMString::from("a"))
|
||||
.map_err(|_| ErrorStatus::InvalidSelector)
|
||||
.map(|nodes| {
|
||||
matching_links(&nodes, link_text, partial, can_gc)
|
||||
.take(1)
|
||||
.next()
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
unsafe fn object_has_to_json_property(
|
||||
cx: *mut JSContext,
|
||||
|
@ -714,65 +695,6 @@ fn retrieve_document_and_check_root_existence(
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn handle_find_element_css_selector(
|
||||
documents: &DocumentCollection,
|
||||
pipeline: PipelineId,
|
||||
selector: String,
|
||||
reply: IpcSender<Result<Option<String>, ErrorStatus>>,
|
||||
) {
|
||||
match retrieve_document_and_check_root_existence(documents, pipeline) {
|
||||
Ok(document) => reply
|
||||
.send(
|
||||
document
|
||||
.QuerySelector(DOMString::from(selector))
|
||||
.map_err(|_| ErrorStatus::InvalidSelector)
|
||||
.map(|element| element.map(|x| x.upcast::<Node>().unique_id(pipeline))),
|
||||
)
|
||||
.unwrap(),
|
||||
Err(error) => reply.send(Err(error)).unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn handle_find_element_link_text(
|
||||
documents: &DocumentCollection,
|
||||
pipeline: PipelineId,
|
||||
selector: String,
|
||||
partial: bool,
|
||||
reply: IpcSender<Result<Option<String>, ErrorStatus>>,
|
||||
can_gc: CanGc,
|
||||
) {
|
||||
match retrieve_document_and_check_root_existence(documents, pipeline) {
|
||||
Ok(document) => reply
|
||||
.send(first_matching_link(
|
||||
document.upcast::<Node>(),
|
||||
selector.clone(),
|
||||
partial,
|
||||
can_gc,
|
||||
))
|
||||
.unwrap(),
|
||||
Err(error) => reply.send(Err(error)).unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn handle_find_element_tag_name(
|
||||
documents: &DocumentCollection,
|
||||
pipeline: PipelineId,
|
||||
selector: String,
|
||||
reply: IpcSender<Result<Option<String>, ErrorStatus>>,
|
||||
can_gc: CanGc,
|
||||
) {
|
||||
match retrieve_document_and_check_root_existence(documents, pipeline) {
|
||||
Ok(document) => reply
|
||||
.send(Ok(document
|
||||
.GetElementsByTagName(DOMString::from(selector), can_gc)
|
||||
.elements_iter()
|
||||
.next()
|
||||
.map(|element| element.upcast::<Node>().unique_id(pipeline))))
|
||||
.unwrap(),
|
||||
Err(error) => reply.send(Err(error)).unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn handle_find_elements_css_selector(
|
||||
documents: &DocumentCollection,
|
||||
pipeline: PipelineId,
|
||||
|
@ -837,65 +759,6 @@ pub(crate) fn handle_find_elements_tag_name(
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn handle_find_element_element_css_selector(
|
||||
documents: &DocumentCollection,
|
||||
pipeline: PipelineId,
|
||||
element_id: String,
|
||||
selector: String,
|
||||
reply: IpcSender<Result<Option<String>, ErrorStatus>>,
|
||||
) {
|
||||
reply
|
||||
.send(
|
||||
get_known_element(documents, pipeline, element_id).and_then(|element| {
|
||||
element
|
||||
.upcast::<Node>()
|
||||
.query_selector(DOMString::from(selector))
|
||||
.map_err(|_| ErrorStatus::InvalidSelector)
|
||||
.map(|element| element.map(|x| x.upcast::<Node>().unique_id(pipeline)))
|
||||
}),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub(crate) fn handle_find_element_element_link_text(
|
||||
documents: &DocumentCollection,
|
||||
pipeline: PipelineId,
|
||||
element_id: String,
|
||||
selector: String,
|
||||
partial: bool,
|
||||
reply: IpcSender<Result<Option<String>, ErrorStatus>>,
|
||||
can_gc: CanGc,
|
||||
) {
|
||||
reply
|
||||
.send(
|
||||
get_known_element(documents, pipeline, element_id).and_then(|element| {
|
||||
first_matching_link(element.upcast::<Node>(), selector.clone(), partial, can_gc)
|
||||
}),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub(crate) fn handle_find_element_element_tag_name(
|
||||
documents: &DocumentCollection,
|
||||
pipeline: PipelineId,
|
||||
element_id: String,
|
||||
selector: String,
|
||||
reply: IpcSender<Result<Option<String>, ErrorStatus>>,
|
||||
can_gc: CanGc,
|
||||
) {
|
||||
reply
|
||||
.send(
|
||||
get_known_element(documents, pipeline, element_id).map(|element| {
|
||||
element
|
||||
.GetElementsByTagName(DOMString::from(selector), can_gc)
|
||||
.elements_iter()
|
||||
.next()
|
||||
.map(|x| x.upcast::<Node>().unique_id(pipeline))
|
||||
}),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub(crate) fn handle_find_element_elements_css_selector(
|
||||
documents: &DocumentCollection,
|
||||
pipeline: PipelineId,
|
||||
|
|
|
@ -131,28 +131,9 @@ pub enum WebDriverScriptCommand {
|
|||
DeleteCookie(String, IpcSender<Result<(), ErrorStatus>>),
|
||||
ExecuteScript(String, IpcSender<WebDriverJSResult>),
|
||||
ExecuteAsyncScript(String, IpcSender<WebDriverJSResult>),
|
||||
FindElementCSSSelector(String, IpcSender<Result<Option<String>, ErrorStatus>>),
|
||||
FindElementLinkText(String, bool, IpcSender<Result<Option<String>, ErrorStatus>>),
|
||||
FindElementTagName(String, IpcSender<Result<Option<String>, ErrorStatus>>),
|
||||
FindElementsCSSSelector(String, IpcSender<Result<Vec<String>, ErrorStatus>>),
|
||||
FindElementsLinkText(String, bool, IpcSender<Result<Vec<String>, ErrorStatus>>),
|
||||
FindElementsTagName(String, IpcSender<Result<Vec<String>, ErrorStatus>>),
|
||||
FindElementElementCSSSelector(
|
||||
String,
|
||||
String,
|
||||
IpcSender<Result<Option<String>, ErrorStatus>>,
|
||||
),
|
||||
FindElementElementLinkText(
|
||||
String,
|
||||
String,
|
||||
bool,
|
||||
IpcSender<Result<Option<String>, ErrorStatus>>,
|
||||
),
|
||||
FindElementElementTagName(
|
||||
String,
|
||||
String,
|
||||
IpcSender<Result<Option<String>, ErrorStatus>>,
|
||||
),
|
||||
FindElementElementsCSSSelector(String, String, IpcSender<Result<Vec<String>, ErrorStatus>>),
|
||||
FindElementElementsLinkText(
|
||||
String,
|
||||
|
|
|
@ -970,53 +970,11 @@ impl Handler {
|
|||
&self,
|
||||
parameters: &LocatorParameters,
|
||||
) -> WebDriverResult<WebDriverResponse> {
|
||||
// Step 4. If selector is undefined, return error with error code invalid argument.
|
||||
if parameters.value.is_empty() {
|
||||
return Err(WebDriverError::new(ErrorStatus::InvalidArgument, ""));
|
||||
}
|
||||
let (sender, receiver) = ipc::channel().unwrap();
|
||||
|
||||
match parameters.using {
|
||||
LocatorStrategy::CSSSelector => {
|
||||
let cmd = WebDriverScriptCommand::FindElementCSSSelector(
|
||||
parameters.value.clone(),
|
||||
sender,
|
||||
);
|
||||
self.browsing_context_script_command::<true>(cmd)?;
|
||||
},
|
||||
LocatorStrategy::LinkText | LocatorStrategy::PartialLinkText => {
|
||||
let cmd = WebDriverScriptCommand::FindElementLinkText(
|
||||
parameters.value.clone(),
|
||||
parameters.using == LocatorStrategy::PartialLinkText,
|
||||
sender,
|
||||
);
|
||||
self.browsing_context_script_command::<true>(cmd)?;
|
||||
},
|
||||
LocatorStrategy::TagName => {
|
||||
let cmd =
|
||||
WebDriverScriptCommand::FindElementTagName(parameters.value.clone(), sender);
|
||||
self.browsing_context_script_command::<true>(cmd)?;
|
||||
},
|
||||
_ => {
|
||||
return Err(WebDriverError::new(
|
||||
ErrorStatus::UnsupportedOperation,
|
||||
"Unsupported locator strategy",
|
||||
));
|
||||
},
|
||||
}
|
||||
|
||||
// Step 1 - 9.
|
||||
let res = self.handle_find_elements(parameters)?;
|
||||
// Step 10. If result is empty, return error with error code no such element.
|
||||
// Otherwise, return the first element of result.
|
||||
match wait_for_script_response(receiver)? {
|
||||
Ok(value) => match value {
|
||||
Some(value) => {
|
||||
let value_resp = serde_json::to_value(WebElement(value)).unwrap();
|
||||
Ok(WebDriverResponse::Generic(ValueResponse(value_resp)))
|
||||
},
|
||||
None => Err(WebDriverError::new(ErrorStatus::NoSuchElement, "")),
|
||||
},
|
||||
Err(error) => Err(WebDriverError::new(error, "")),
|
||||
}
|
||||
unwrap_first_element_response(res)
|
||||
}
|
||||
|
||||
/// <https://w3c.github.io/webdriver/#close-window>
|
||||
|
@ -1234,62 +1192,16 @@ impl Handler {
|
|||
}
|
||||
|
||||
/// <https://w3c.github.io/webdriver/#find-element-from-element>
|
||||
fn handle_find_element_element(
|
||||
fn handle_find_element_from_element(
|
||||
&self,
|
||||
element: &WebElement,
|
||||
parameters: &LocatorParameters,
|
||||
) -> WebDriverResult<WebDriverResponse> {
|
||||
// Step 4. If selector is undefined, return error with error code invalid argument.
|
||||
if parameters.value.is_empty() {
|
||||
return Err(WebDriverError::new(ErrorStatus::InvalidArgument, ""));
|
||||
}
|
||||
let (sender, receiver) = ipc::channel().unwrap();
|
||||
|
||||
match parameters.using {
|
||||
LocatorStrategy::CSSSelector => {
|
||||
let cmd = WebDriverScriptCommand::FindElementElementCSSSelector(
|
||||
parameters.value.clone(),
|
||||
element.to_string(),
|
||||
sender,
|
||||
);
|
||||
self.browsing_context_script_command::<true>(cmd)?;
|
||||
},
|
||||
LocatorStrategy::LinkText | LocatorStrategy::PartialLinkText => {
|
||||
let cmd = WebDriverScriptCommand::FindElementElementLinkText(
|
||||
parameters.value.clone(),
|
||||
element.to_string(),
|
||||
parameters.using == LocatorStrategy::PartialLinkText,
|
||||
sender,
|
||||
);
|
||||
self.browsing_context_script_command::<true>(cmd)?;
|
||||
},
|
||||
LocatorStrategy::TagName => {
|
||||
let cmd = WebDriverScriptCommand::FindElementElementTagName(
|
||||
parameters.value.clone(),
|
||||
element.to_string(),
|
||||
sender,
|
||||
);
|
||||
self.browsing_context_script_command::<true>(cmd)?;
|
||||
},
|
||||
_ => {
|
||||
return Err(WebDriverError::new(
|
||||
ErrorStatus::UnsupportedOperation,
|
||||
"Unsupported locator strategy",
|
||||
));
|
||||
},
|
||||
}
|
||||
// Step 1 - 8.
|
||||
let res = self.handle_find_elements_from_element(element, parameters)?;
|
||||
// Step 9. If result is empty, return error with error code no such element.
|
||||
// Otherwise, return the first element of result.
|
||||
match wait_for_script_response(receiver)? {
|
||||
Ok(value) => match value {
|
||||
Some(value) => {
|
||||
let value_resp = serde_json::to_value(WebElement(value))?;
|
||||
Ok(WebDriverResponse::Generic(ValueResponse(value_resp)))
|
||||
},
|
||||
None => Err(WebDriverError::new(ErrorStatus::NoSuchElement, "")),
|
||||
},
|
||||
Err(error) => Err(WebDriverError::new(error, "")),
|
||||
}
|
||||
unwrap_first_element_response(res)
|
||||
}
|
||||
|
||||
/// <https://w3c.github.io/webdriver/#find-elements-from-element>
|
||||
|
@ -1418,22 +1330,11 @@ impl Handler {
|
|||
shadow_root: &ShadowRoot,
|
||||
parameters: &LocatorParameters,
|
||||
) -> WebDriverResult<WebDriverResponse> {
|
||||
// Step 1 - 8.
|
||||
let res = self.handle_find_elements_from_shadow_root(shadow_root, parameters)?;
|
||||
// Step 9. If result is empty, return error with error code no such element.
|
||||
// Otherwise, return the first element of result.
|
||||
if let WebDriverResponse::Generic(ValueResponse(values)) = res {
|
||||
let arr = values.as_array().unwrap();
|
||||
if let Some(first) = arr.first() {
|
||||
Ok(WebDriverResponse::Generic(ValueResponse(first.clone())))
|
||||
} else {
|
||||
Err(WebDriverError::new(ErrorStatus::NoSuchElement, ""))
|
||||
}
|
||||
} else {
|
||||
Err(WebDriverError::new(
|
||||
ErrorStatus::UnknownError,
|
||||
"Unexpected response",
|
||||
))
|
||||
}
|
||||
unwrap_first_element_response(res)
|
||||
}
|
||||
|
||||
fn handle_get_shadow_root(&self, element: WebElement) -> WebDriverResult<WebDriverResponse> {
|
||||
|
@ -2255,7 +2156,7 @@ impl WebDriverHandler<ServoExtensionRoute> for Handler {
|
|||
WebDriverCommand::FindElement(ref parameters) => self.handle_find_element(parameters),
|
||||
WebDriverCommand::FindElements(ref parameters) => self.handle_find_elements(parameters),
|
||||
WebDriverCommand::FindElementElement(ref element, ref parameters) => {
|
||||
self.handle_find_element_element(element, parameters)
|
||||
self.handle_find_element_from_element(element, parameters)
|
||||
},
|
||||
WebDriverCommand::FindElementElements(ref element, ref parameters) => {
|
||||
self.handle_find_elements_from_element(element, parameters)
|
||||
|
@ -2380,3 +2281,16 @@ where
|
|||
.recv()
|
||||
.map_err(|_| WebDriverError::new(ErrorStatus::NoSuchWindow, ""))
|
||||
}
|
||||
|
||||
fn unwrap_first_element_response(res: WebDriverResponse) -> WebDriverResult<WebDriverResponse> {
|
||||
if let WebDriverResponse::Generic(ValueResponse(values)) = res {
|
||||
let arr = values.as_array().unwrap();
|
||||
if let Some(first) = arr.first() {
|
||||
Ok(WebDriverResponse::Generic(ValueResponse(first.clone())))
|
||||
} else {
|
||||
Err(WebDriverError::new(ErrorStatus::NoSuchElement, ""))
|
||||
}
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue