Add method to Page to retrieve Element by some ID (NodeId, BackendNodeId, RemoteObjectId?) (for interacting with iframes)
kind:featuretopic:interfacetopic:interaction
I have a use case where I need to interact with elements in a same-origin iframe. I devised a mechanism in to a `Page` extension impl where I can get the node id for this iframe that looks like this:
```rust
async fn wait_for_iframe(
&self,
name: impl Into<String> + Send,
) -> chromiumoxide::Result<NodeId> {
let name = name.into();
let started_at = Instant::now();
let timeout = 60;
loop {
if started_at.elapsed().as_secs() >= timeout as u64 {
return Err(CdpError::msg(
"Timeout waiting for iframe content".to_string(),
));
}
let iframe = self
.wait_for_element(format!("iframe[name='{name}']"), None)
.await?;
let iframe_description = self
.execute(
DescribeNodeParams::builder()
.backend_node_id(iframe.backend_node_id)
.build(),
)
.await?;
let content_doc_remote = self
.execute(
ResolveNodeParams::builder()
.backend_node_id(
iframe_description
.node
.content_document
.as_ref()
.unwrap()
.backend_node_id,
)
.build(),
)
.await?;
let content_doc_node = self
.execute(
RequestNodeParams::builder()
.object_id(content_doc_remote.object.object_id.clone().unwrap())
.build()
.unwrap(),
)
.await?;
let inner_html = self
.execute(
CallFunctionOnParams::builder()
.object_id(content_doc_remote.object.object_id.clone().unwrap())
.function_declaration("function() { return this.body.innerHTML; }")
.generate_preview(true)
.build()
.unwrap(),
)
.await?;
if let Some(inner_html) = inner_html.result.result.value
&& let Some(inner_html) = inner_html.as_str()
&& !inner_html.is_empty()
{
return Ok(content_doc_node.node_id);
}
sleep(Duration::from_millis(200)).await;
}
}
```
In my implementation, I wait for the iframe to have content in the body because the first time I get the node id from my `wait_for_element` (which just calls `find_element` in a time loop), it often resolves to a node that has no body and never gets one. I'm not sure if the site I'm interacting with creates an iframe then replaces it or what, but that doesn't matter much.
From this function, I can use this `NodeId` in future CDP calls which is fine, but I basically have to reimplement Page and Element with new near-identical methods that can take a `RemoteObjectId` parameter and don't support chaining. So I have a `find_element_object_id` returning a RemoteObjectId, `click_object_id()` that has to accept one, etc, etc.
I'd love to be able to create an `Element` out of the `content_document` I retrieve in the above function, but I don't have access to `Element::new()` or `PageInner` in order to build a new `Element`. If it helps, the content_document type is of type `HTMLDocument`. I'm also not sure if it makes sense to use the `NodeId` to construct a new `Page` or `Element`, given the node type
0 条评论