mirror of
https://github.com/servo/servo.git
synced 2025-06-09 09:03:23 +00:00
516 lines
22 KiB
Rust
516 lines
22 KiB
Rust
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#![crate_name = "webdriver_server"]
|
|
#![crate_type = "rlib"]
|
|
|
|
#![feature(ip_addr)]
|
|
|
|
#[macro_use]
|
|
extern crate log;
|
|
|
|
extern crate webdriver;
|
|
extern crate msg;
|
|
extern crate png;
|
|
extern crate url;
|
|
extern crate util;
|
|
extern crate rustc_serialize;
|
|
extern crate uuid;
|
|
extern crate ipc_channel;
|
|
|
|
use msg::constellation_msg::{ConstellationChan, LoadData, FrameId, PipelineId, NavigationDirection,
|
|
WebDriverCommandMsg};
|
|
use msg::constellation_msg::Msg as ConstellationMsg;
|
|
use msg::webdriver_msg::{WebDriverFrameId, WebDriverScriptCommand, WebDriverJSError, WebDriverJSResult, LoadStatus};
|
|
|
|
use url::Url;
|
|
use webdriver::command::{WebDriverMessage, WebDriverCommand};
|
|
use webdriver::command::{GetParameters, JavascriptCommandParameters, LocatorParameters,
|
|
SwitchToFrameParameters, TimeoutsParameters};
|
|
use webdriver::common::{LocatorStrategy, WebElement};
|
|
use webdriver::response::{
|
|
WebDriverResponse, NewSessionResponse, ValueResponse};
|
|
use webdriver::server::{self, WebDriverHandler, Session};
|
|
use webdriver::error::{WebDriverResult, WebDriverError, ErrorStatus};
|
|
use util::task::spawn_named;
|
|
use uuid::Uuid;
|
|
use ipc_channel::ipc::{self, IpcReceiver};
|
|
|
|
use std::borrow::ToOwned;
|
|
use rustc_serialize::json::{Json, ToJson};
|
|
use rustc_serialize::base64::{Config, ToBase64, CharacterSet, Newline};
|
|
use std::collections::BTreeMap;
|
|
use std::net::SocketAddr;
|
|
|
|
use std::thread::{self, sleep_ms};
|
|
|
|
pub fn start_server(port: u16, constellation_chan: ConstellationChan) {
|
|
let handler = Handler::new(constellation_chan);
|
|
|
|
spawn_named("WebdriverHttpServer".to_owned(), move || {
|
|
server::start(SocketAddr::new("0.0.0.0".parse().unwrap(), port), handler);
|
|
});
|
|
}
|
|
|
|
struct WebDriverSession {
|
|
id: Uuid,
|
|
frame_id: Option<FrameId>
|
|
}
|
|
|
|
struct Handler {
|
|
session: Option<WebDriverSession>,
|
|
constellation_chan: ConstellationChan,
|
|
script_timeout: u32,
|
|
load_timeout: u32,
|
|
implicit_wait_timeout: u32
|
|
}
|
|
|
|
impl WebDriverSession {
|
|
pub fn new() -> WebDriverSession {
|
|
WebDriverSession {
|
|
id: Uuid::new_v4(),
|
|
frame_id: None
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Handler {
|
|
pub fn new(constellation_chan: ConstellationChan) -> Handler {
|
|
Handler {
|
|
session: None,
|
|
constellation_chan: constellation_chan,
|
|
script_timeout: 30_000,
|
|
load_timeout: 300_000,
|
|
implicit_wait_timeout: 0
|
|
}
|
|
}
|
|
|
|
fn get_root_pipeline(&self) -> WebDriverResult<PipelineId> {
|
|
let interval = 20;
|
|
let iterations = 30_000 / interval;
|
|
|
|
for _ in 0..iterations {
|
|
if let Some(x) = self.get_pipeline(None) {
|
|
return Ok(x)
|
|
};
|
|
|
|
sleep_ms(interval);
|
|
};
|
|
|
|
Err(WebDriverError::new(ErrorStatus::Timeout,
|
|
"Failed to get root window handle"))
|
|
}
|
|
|
|
fn get_frame_pipeline(&self) -> WebDriverResult<PipelineId> {
|
|
if let Some(ref session) = self.session {
|
|
match self.get_pipeline(session.frame_id) {
|
|
Some(x) => Ok(x),
|
|
None => Err(WebDriverError::new(ErrorStatus::NoSuchFrame,
|
|
"Frame got closed"))
|
|
}
|
|
} else {
|
|
panic!("Command tried to access session but session is None");
|
|
}
|
|
}
|
|
|
|
fn get_session(&self) -> WebDriverResult<&WebDriverSession> {
|
|
match self.session {
|
|
Some(ref x) => Ok(x),
|
|
None => Err(WebDriverError::new(ErrorStatus::SessionNotCreated,
|
|
"Session not created"))
|
|
}
|
|
}
|
|
|
|
fn set_frame_id(&mut self, frame_id: Option<FrameId>) -> WebDriverResult<()> {
|
|
match self.session {
|
|
Some(ref mut x) => {
|
|
x.frame_id = frame_id;
|
|
Ok(())
|
|
},
|
|
None => Err(WebDriverError::new(ErrorStatus::SessionNotCreated,
|
|
"Session not created"))
|
|
}
|
|
}
|
|
|
|
fn get_pipeline(&self, frame_id: Option<FrameId>) -> Option<PipelineId> {
|
|
let (sender, reciever) = ipc::channel().unwrap();
|
|
let ConstellationChan(ref const_chan) = self.constellation_chan;
|
|
const_chan.send(ConstellationMsg::GetPipeline(frame_id, sender)).unwrap();
|
|
|
|
|
|
reciever.recv().unwrap()
|
|
}
|
|
|
|
fn handle_new_session(&mut self) -> WebDriverResult<WebDriverResponse> {
|
|
if self.session.is_none() {
|
|
let session = WebDriverSession::new();
|
|
let mut capabilities = BTreeMap::new();
|
|
capabilities.insert("browserName".to_owned(), "servo".to_json());
|
|
capabilities.insert("browserVersion".to_owned(), "0.0.1".to_json());
|
|
let rv = Ok(WebDriverResponse::NewSession(
|
|
NewSessionResponse::new(
|
|
session.id.to_string(),
|
|
Json::Object(capabilities))));
|
|
self.session = Some(session);
|
|
rv
|
|
} else {
|
|
Err(WebDriverError::new(ErrorStatus::UnknownError,
|
|
"Session already created"))
|
|
}
|
|
}
|
|
|
|
fn handle_get(&self, parameters: &GetParameters) -> WebDriverResult<WebDriverResponse> {
|
|
let url = match Url::parse(¶meters.url[..]) {
|
|
Ok(url) => url,
|
|
Err(_) => return Err(WebDriverError::new(ErrorStatus::InvalidArgument,
|
|
"Invalid URL"))
|
|
};
|
|
|
|
let pipeline_id = try!(self.get_root_pipeline());
|
|
|
|
let (sender, reciever) = ipc::channel().unwrap();
|
|
|
|
let load_data = LoadData::new(url);
|
|
let ConstellationChan(ref const_chan) = self.constellation_chan;
|
|
let cmd_msg = WebDriverCommandMsg::LoadUrl(pipeline_id, load_data, sender.clone());
|
|
const_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap();
|
|
|
|
let timeout = self.load_timeout;
|
|
let timeout_chan = sender.clone();
|
|
thread::spawn(move || {
|
|
sleep_ms(timeout);
|
|
let _ = timeout_chan.send(LoadStatus::LoadTimeout);
|
|
});
|
|
|
|
//Wait to get a load event
|
|
match reciever.recv().unwrap() {
|
|
LoadStatus::LoadComplete => Ok(WebDriverResponse::Void),
|
|
LoadStatus::LoadTimeout => Err(WebDriverError::new(ErrorStatus::Timeout,
|
|
"Load timed out"))
|
|
}
|
|
}
|
|
|
|
fn handle_go_back(&self) -> WebDriverResult<WebDriverResponse> {
|
|
let ConstellationChan(ref const_chan) = self.constellation_chan;
|
|
const_chan.send(ConstellationMsg::Navigate(None, NavigationDirection::Back)).unwrap();
|
|
Ok(WebDriverResponse::Void)
|
|
}
|
|
|
|
fn handle_go_forward(&self) -> WebDriverResult<WebDriverResponse> {
|
|
let ConstellationChan(ref const_chan) = self.constellation_chan;
|
|
const_chan.send(ConstellationMsg::Navigate(None, NavigationDirection::Forward)).unwrap();
|
|
Ok(WebDriverResponse::Void)
|
|
}
|
|
|
|
fn handle_get_title(&self) -> WebDriverResult<WebDriverResponse> {
|
|
let pipeline_id = try!(self.get_root_pipeline());
|
|
|
|
let (sender, reciever) = ipc::channel().unwrap();
|
|
let ConstellationChan(ref const_chan) = self.constellation_chan;
|
|
let cmd_msg = WebDriverCommandMsg::ScriptCommand(pipeline_id,
|
|
WebDriverScriptCommand::GetTitle(sender));
|
|
const_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap();
|
|
let value = reciever.recv().unwrap();
|
|
Ok(WebDriverResponse::Generic(ValueResponse::new(value.to_json())))
|
|
}
|
|
|
|
fn handle_get_window_handle(&self) -> WebDriverResult<WebDriverResponse> {
|
|
// For now we assume there's only one window so just use the session
|
|
// id as the window id
|
|
let handle = self.session.as_ref().unwrap().id.to_string();
|
|
Ok(WebDriverResponse::Generic(ValueResponse::new(handle.to_json())))
|
|
}
|
|
|
|
fn handle_get_window_handles(&self) -> WebDriverResult<WebDriverResponse> {
|
|
// For now we assume there's only one window so just use the session
|
|
// id as the window id
|
|
let handles = vec![self.session.as_ref().unwrap().id.to_string().to_json()];
|
|
Ok(WebDriverResponse::Generic(ValueResponse::new(handles.to_json())))
|
|
}
|
|
|
|
fn handle_find_element(&self, parameters: &LocatorParameters) -> WebDriverResult<WebDriverResponse> {
|
|
let pipeline_id = try!(self.get_frame_pipeline());
|
|
|
|
if parameters.using != LocatorStrategy::CSSSelector {
|
|
return Err(WebDriverError::new(ErrorStatus::UnsupportedOperation,
|
|
"Unsupported locator strategy"))
|
|
}
|
|
|
|
let (sender, reciever) = ipc::channel().unwrap();
|
|
let ConstellationChan(ref const_chan) = self.constellation_chan;
|
|
let cmd = WebDriverScriptCommand::FindElementCSS(parameters.value.clone(), sender);
|
|
let cmd_msg = WebDriverCommandMsg::ScriptCommand(pipeline_id, cmd);
|
|
const_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap();
|
|
match reciever.recv().unwrap() {
|
|
Ok(value) => {
|
|
let value_resp = value.map(|x| WebElement::new(x).to_json()).to_json();
|
|
Ok(WebDriverResponse::Generic(ValueResponse::new(value_resp)))
|
|
}
|
|
Err(_) => Err(WebDriverError::new(ErrorStatus::InvalidSelector,
|
|
"Invalid selector"))
|
|
}
|
|
}
|
|
|
|
fn handle_switch_to_frame(&mut self, parameters: &SwitchToFrameParameters) -> WebDriverResult<WebDriverResponse> {
|
|
use webdriver::common::FrameId;
|
|
let frame_id = match parameters.id {
|
|
FrameId::Null => {
|
|
self.set_frame_id(None).unwrap();
|
|
return Ok(WebDriverResponse::Void)
|
|
},
|
|
FrameId::Short(ref x) => WebDriverFrameId::Short(*x),
|
|
FrameId::Element(ref x) => WebDriverFrameId::Element(x.id.clone())
|
|
};
|
|
|
|
self.switch_to_frame(frame_id)
|
|
}
|
|
|
|
|
|
fn handle_switch_to_parent_frame(&mut self) -> WebDriverResult<WebDriverResponse> {
|
|
self.switch_to_frame(WebDriverFrameId::Parent)
|
|
}
|
|
|
|
fn switch_to_frame(&mut self, frame_id: WebDriverFrameId) -> WebDriverResult<WebDriverResponse> {
|
|
if let WebDriverFrameId::Short(_) = frame_id {
|
|
return Err(WebDriverError::new(ErrorStatus::UnsupportedOperation,
|
|
"Selecting frame by id not supported"));
|
|
}
|
|
let pipeline_id = try!(self.get_frame_pipeline());
|
|
let (sender, reciever) = ipc::channel().unwrap();
|
|
let cmd = WebDriverScriptCommand::GetFrameId(frame_id, sender);
|
|
{
|
|
let ConstellationChan(ref const_chan) = self.constellation_chan;
|
|
const_chan.send(ConstellationMsg::WebDriverCommand(
|
|
WebDriverCommandMsg::ScriptCommand(pipeline_id, cmd))).unwrap();
|
|
}
|
|
|
|
let frame = match reciever.recv().unwrap() {
|
|
Ok(Some((pipeline_id, subpage_id))) => {
|
|
let (sender, reciever) = ipc::channel().unwrap();
|
|
let ConstellationChan(ref const_chan) = self.constellation_chan;
|
|
const_chan.send(ConstellationMsg::GetFrame(pipeline_id, subpage_id, sender)).unwrap();
|
|
reciever.recv().unwrap()
|
|
},
|
|
Ok(None) => None,
|
|
Err(_) => {
|
|
return Err(WebDriverError::new(ErrorStatus::NoSuchFrame,
|
|
"Frame does not exist"));
|
|
}
|
|
};
|
|
|
|
self.set_frame_id(frame).unwrap();
|
|
Ok(WebDriverResponse::Void)
|
|
}
|
|
|
|
|
|
fn handle_find_elements(&self, parameters: &LocatorParameters) -> WebDriverResult<WebDriverResponse> {
|
|
let pipeline_id = try!(self.get_frame_pipeline());
|
|
|
|
if parameters.using != LocatorStrategy::CSSSelector {
|
|
return Err(WebDriverError::new(ErrorStatus::UnsupportedOperation,
|
|
"Unsupported locator strategy"))
|
|
}
|
|
|
|
let (sender, reciever) = ipc::channel().unwrap();
|
|
let ConstellationChan(ref const_chan) = self.constellation_chan;
|
|
let cmd = WebDriverScriptCommand::FindElementsCSS(parameters.value.clone(), sender);
|
|
let cmd_msg = WebDriverCommandMsg::ScriptCommand(pipeline_id, cmd);
|
|
const_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap();
|
|
match reciever.recv().unwrap() {
|
|
Ok(value) => {
|
|
let resp_value: Vec<Json> = value.into_iter().map(
|
|
|x| WebElement::new(x).to_json()).collect();
|
|
Ok(WebDriverResponse::Generic(ValueResponse::new(resp_value.to_json())))
|
|
}
|
|
Err(_) => Err(WebDriverError::new(ErrorStatus::InvalidSelector,
|
|
"Invalid selector"))
|
|
}
|
|
}
|
|
|
|
fn handle_get_element_text(&self, element: &WebElement) -> WebDriverResult<WebDriverResponse> {
|
|
let pipeline_id = try!(self.get_frame_pipeline());
|
|
|
|
let (sender, reciever) = ipc::channel().unwrap();
|
|
let ConstellationChan(ref const_chan) = self.constellation_chan;
|
|
let cmd = WebDriverScriptCommand::GetElementText(element.id.clone(), sender);
|
|
let cmd_msg = WebDriverCommandMsg::ScriptCommand(pipeline_id, cmd);
|
|
const_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap();
|
|
match reciever.recv().unwrap() {
|
|
Ok(value) => Ok(WebDriverResponse::Generic(ValueResponse::new(value.to_json()))),
|
|
Err(_) => Err(WebDriverError::new(ErrorStatus::StaleElementReference,
|
|
"Unable to find element in document"))
|
|
}
|
|
}
|
|
|
|
fn handle_get_active_element(&self) -> WebDriverResult<WebDriverResponse> {
|
|
let pipeline_id = try!(self.get_frame_pipeline());
|
|
|
|
let (sender, reciever) = ipc::channel().unwrap();
|
|
let ConstellationChan(ref const_chan) = self.constellation_chan;
|
|
let cmd = WebDriverScriptCommand::GetActiveElement(sender);
|
|
let cmd_msg = WebDriverCommandMsg::ScriptCommand(pipeline_id, cmd);
|
|
const_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap();
|
|
let value = reciever.recv().unwrap().map(|x| WebElement::new(x).to_json());
|
|
Ok(WebDriverResponse::Generic(ValueResponse::new(value.to_json())))
|
|
}
|
|
|
|
fn handle_get_element_tag_name(&self, element: &WebElement) -> WebDriverResult<WebDriverResponse> {
|
|
let pipeline_id = try!(self.get_frame_pipeline());
|
|
|
|
let (sender, reciever) = ipc::channel().unwrap();
|
|
let ConstellationChan(ref const_chan) = self.constellation_chan;
|
|
let cmd = WebDriverScriptCommand::GetElementTagName(element.id.clone(), sender);
|
|
let cmd_msg = WebDriverCommandMsg::ScriptCommand(pipeline_id, cmd);
|
|
const_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap();
|
|
match reciever.recv().unwrap() {
|
|
Ok(value) => Ok(WebDriverResponse::Generic(ValueResponse::new(value.to_json()))),
|
|
Err(_) => Err(WebDriverError::new(ErrorStatus::StaleElementReference,
|
|
"Unable to find element in document"))
|
|
}
|
|
}
|
|
|
|
fn handle_set_timeouts(&mut self, parameters: &TimeoutsParameters) -> WebDriverResult<WebDriverResponse> {
|
|
//TODO: this conversion is crazy, spec should limit these to u32 and check upstream
|
|
let value = parameters.ms as u32;
|
|
match ¶meters.type_[..] {
|
|
"implicit" => self.implicit_wait_timeout = value,
|
|
"page load" => self.load_timeout = value,
|
|
"script" => self.script_timeout = value,
|
|
x @ _ => return Err(WebDriverError::new(ErrorStatus::InvalidSelector,
|
|
&format!("Unknown timeout type {}", x)))
|
|
}
|
|
Ok(WebDriverResponse::Void)
|
|
}
|
|
|
|
fn handle_execute_script(&self, parameters: &JavascriptCommandParameters)
|
|
-> WebDriverResult<WebDriverResponse> {
|
|
let func_body = ¶meters.script;
|
|
let args_string = "";
|
|
|
|
// This is pretty ugly; we really want something that acts like
|
|
// new Function() and then takes the resulting function and executes
|
|
// it with a vec of arguments.
|
|
let script = format!("(function() {{ {} }})({})", func_body, args_string);
|
|
|
|
let (sender, reciever) = ipc::channel().unwrap();
|
|
let command = WebDriverScriptCommand::ExecuteScript(script, sender);
|
|
self.execute_script(command, reciever)
|
|
}
|
|
|
|
fn handle_execute_async_script(&self,
|
|
parameters: &JavascriptCommandParameters) -> WebDriverResult<WebDriverResponse> {
|
|
let func_body = ¶meters.script;
|
|
let args_string = "window.webdriverCallback";
|
|
|
|
let script = format!(
|
|
"setTimeout(webdriverTimeout, {}); (function(callback) {{ {} }})({})",
|
|
self.script_timeout, func_body, args_string);
|
|
|
|
let (sender, reciever) = ipc::channel().unwrap();
|
|
let command = WebDriverScriptCommand::ExecuteAsyncScript(script, sender);
|
|
self.execute_script(command, reciever)
|
|
}
|
|
|
|
fn execute_script(&self,
|
|
command: WebDriverScriptCommand,
|
|
reciever: IpcReceiver<WebDriverJSResult>)
|
|
-> WebDriverResult<WebDriverResponse> {
|
|
let pipeline_id = try!(self.get_frame_pipeline());
|
|
|
|
let ConstellationChan(ref const_chan) = self.constellation_chan;
|
|
let cmd_msg = WebDriverCommandMsg::ScriptCommand(pipeline_id, command);
|
|
const_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap();
|
|
|
|
match reciever.recv().unwrap() {
|
|
Ok(value) => Ok(WebDriverResponse::Generic(ValueResponse::new(value.to_json()))),
|
|
Err(WebDriverJSError::Timeout) => Err(WebDriverError::new(ErrorStatus::Timeout, "")),
|
|
Err(WebDriverJSError::UnknownType) => Err(WebDriverError::new(
|
|
ErrorStatus::UnsupportedOperation, "Unsupported return type"))
|
|
}
|
|
}
|
|
|
|
fn handle_take_screenshot(&self) -> WebDriverResult<WebDriverResponse> {
|
|
let mut img = None;
|
|
let pipeline_id = try!(self.get_root_pipeline());
|
|
|
|
let interval = 20;
|
|
let iterations = 30_000 / interval;
|
|
|
|
for _ in 0..iterations {
|
|
let (sender, reciever) = ipc::channel().unwrap();
|
|
let ConstellationChan(ref const_chan) = self.constellation_chan;
|
|
let cmd_msg = WebDriverCommandMsg::TakeScreenshot(pipeline_id, sender);
|
|
const_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap();
|
|
|
|
if let Some(x) = reciever.recv().unwrap() {
|
|
img = Some(x);
|
|
break;
|
|
};
|
|
|
|
sleep_ms(interval)
|
|
}
|
|
|
|
if img.is_none() {
|
|
return Err(WebDriverError::new(ErrorStatus::Timeout,
|
|
"Taking screenshot timed out"));
|
|
}
|
|
|
|
let img_vec = match png::to_vec(&mut img.unwrap()) {
|
|
Ok(x) => x,
|
|
Err(_) => return Err(WebDriverError::new(ErrorStatus::UnknownError,
|
|
"Taking screenshot failed"))
|
|
};
|
|
let config = Config {
|
|
char_set:CharacterSet::Standard,
|
|
newline: Newline::LF,
|
|
pad: true,
|
|
line_length: None
|
|
};
|
|
let encoded = img_vec.to_base64(config);
|
|
Ok(WebDriverResponse::Generic(ValueResponse::new(encoded.to_json())))
|
|
}
|
|
}
|
|
|
|
impl WebDriverHandler for Handler {
|
|
fn handle_command(&mut self,
|
|
_session: &Option<Session>,
|
|
msg: &WebDriverMessage) -> WebDriverResult<WebDriverResponse> {
|
|
|
|
// Unless we are trying to create a new session, we need to ensure that a
|
|
// session has previously been created
|
|
match msg.command {
|
|
WebDriverCommand::NewSession => {},
|
|
_ => {
|
|
try!(self.get_session());
|
|
}
|
|
}
|
|
|
|
match msg.command {
|
|
WebDriverCommand::NewSession => self.handle_new_session(),
|
|
WebDriverCommand::Get(ref parameters) => self.handle_get(parameters),
|
|
WebDriverCommand::GoBack => self.handle_go_back(),
|
|
WebDriverCommand::GoForward => self.handle_go_forward(),
|
|
WebDriverCommand::GetTitle => self.handle_get_title(),
|
|
WebDriverCommand::GetWindowHandle => self.handle_get_window_handle(),
|
|
WebDriverCommand::GetWindowHandles => self.handle_get_window_handles(),
|
|
WebDriverCommand::SwitchToFrame(ref parameters) => self.handle_switch_to_frame(parameters),
|
|
WebDriverCommand::SwitchToParentFrame => self.handle_switch_to_parent_frame(),
|
|
WebDriverCommand::FindElement(ref parameters) => self.handle_find_element(parameters),
|
|
WebDriverCommand::FindElements(ref parameters) => self.handle_find_elements(parameters),
|
|
WebDriverCommand::GetActiveElement => self.handle_get_active_element(),
|
|
WebDriverCommand::GetElementText(ref element) => self.handle_get_element_text(element),
|
|
WebDriverCommand::GetElementTagName(ref element) => self.handle_get_element_tag_name(element),
|
|
WebDriverCommand::ExecuteScript(ref x) => self.handle_execute_script(x),
|
|
WebDriverCommand::ExecuteAsyncScript(ref x) => self.handle_execute_async_script(x),
|
|
WebDriverCommand::SetTimeouts(ref x) => self.handle_set_timeouts(x),
|
|
WebDriverCommand::TakeScreenshot => self.handle_take_screenshot(),
|
|
_ => Err(WebDriverError::new(ErrorStatus::UnsupportedOperation,
|
|
"Command not implemented"))
|
|
}
|
|
}
|
|
|
|
fn delete_session(&mut self, _session: &Option<Session>) {
|
|
self.session = None;
|
|
}
|
|
}
|