diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 5001b1efe6d..86571029bef 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -2436,9 +2436,6 @@ impl ScriptThread { WebDriverScriptCommand::RemoveLoadStatusSender(_) => { webdriver_handlers::handle_remove_load_status_sender(&documents, pipeline_id) }, - WebDriverScriptCommand::GetWindowHandle(reply) => { - webdriver_handlers::handle_get_window_handle(pipeline_id, reply) - }, _ => (), } } diff --git a/components/script/webdriver_handlers.rs b/components/script/webdriver_handlers.rs index 4b33aeab5df..23d73371bf1 100644 --- a/components/script/webdriver_handlers.rs +++ b/components/script/webdriver_handlers.rs @@ -460,9 +460,7 @@ unsafe fn jsval_to_webdriver_inner( } else { let pipeline = window.pipeline_id(); if window_proxy.browsing_context_id() == window_proxy.webview_id() { - Ok(JSValue::Window( - window.Document().upcast::().unique_id(pipeline), - )) + Ok(JSValue::Window(window.webview_id().to_string())) } else { Ok(JSValue::Frame( window.Document().upcast::().unique_id(pipeline), @@ -2076,16 +2074,3 @@ pub(crate) fn handle_remove_load_status_sender( window.set_webdriver_load_status_sender(None); } } - -pub(crate) fn handle_get_window_handle( - pipeline_id: PipelineId, - reply: IpcSender>, -) { - if let Some(res) = ScriptThread::find_document(pipeline_id) - .map(|document| document.upcast::().unique_id(pipeline_id)) - { - reply.send(Ok(res)).ok(); - } else { - reply.send(Err(ErrorStatus::NoSuchWindow)).ok(); - } -} diff --git a/components/shared/embedder/webdriver.rs b/components/shared/embedder/webdriver.rs index aec3dca6881..7bd11a1d163 100644 --- a/components/shared/embedder/webdriver.rs +++ b/components/shared/embedder/webdriver.rs @@ -156,8 +156,8 @@ pub enum WebDriverCommandMsg { FocusWebView(WebViewId, IpcSender), /// Get focused webview. For now, this is only used when start new session. GetFocusedWebView(IpcSender>), - /// Get all webviews - GetAllWebViews(IpcSender, ErrorStatus>>), + /// Get webviews state + GetAllWebViews(IpcSender>), /// Check whether top-level browsing context is open. IsWebViewOpen(WebViewId, IpcSender), /// Check whether browsing context is open. @@ -246,7 +246,6 @@ pub enum WebDriverScriptCommand { WillSendKeys(String, String, bool, IpcSender>), AddLoadStatusSender(WebViewId, IpcSender), RemoveLoadStatusSender(WebViewId), - GetWindowHandle(IpcSender>), } #[derive(Debug, Deserialize, Serialize)] diff --git a/components/webdriver_server/actions.rs b/components/webdriver_server/actions.rs index 1aa1c3fe609..01ecfaa27e9 100644 --- a/components/webdriver_server/actions.rs +++ b/components/webdriver_server/actions.rs @@ -216,6 +216,8 @@ impl Handler { tick_actions: &TickActions, tick_duration: u64, ) -> Result<(), ErrorStatus> { + let session = self.session().unwrap(); + // Step 1. For each action object in tick actions: // Step 1.1. Let input_id be the value of the id property of action object. for (input_id, action) in tick_actions.iter() { @@ -233,16 +235,12 @@ impl Handler { // Step 9. If subtype is "keyDown", append a copy of action // object with the subtype property changed to "keyUp" to // input state's input cancel list. - self.session() - .unwrap() - .input_cancel_list - .borrow_mut() - .push(( - input_id.clone(), - ActionItem::Key(KeyActionItem::Key(KeyAction::Up(KeyUpAction { - value: keydown_action.value.clone(), - }))), - )); + session.input_cancel_list_mut().push(( + input_id.clone(), + ActionItem::Key(KeyActionItem::Key(KeyAction::Up(KeyUpAction { + value: keydown_action.value.clone(), + }))), + )); }, ActionItem::Key(KeyActionItem::Key(KeyAction::Up(keyup_action))) => { self.dispatch_keyup_action(input_id, keyup_action); @@ -257,19 +255,15 @@ impl Handler { // Step 10. If subtype is "pointerDown", append a copy of action // object with the subtype property changed to "pointerUp" to // input state's input cancel list. - self.session() - .unwrap() - .input_cancel_list - .borrow_mut() - .push(( - input_id.clone(), - ActionItem::Pointer(PointerActionItem::Pointer(PointerAction::Up( - PointerUpAction { - button: pointer_down_action.button, - ..Default::default() - }, - ))), - )); + session.input_cancel_list_mut().push(( + input_id.clone(), + ActionItem::Pointer(PointerActionItem::Pointer(PointerAction::Up( + PointerUpAction { + button: pointer_down_action.button, + ..Default::default() + }, + ))), + )); }, ActionItem::Pointer(PointerActionItem::Pointer(PointerAction::Move( pointer_move_action, @@ -298,8 +292,7 @@ impl Handler { fn dispatch_general_action(&self, source_id: &str) { self.session() .unwrap() - .input_state_table - .borrow_mut() + .input_state_table_mut() .entry(source_id.to_string()) .or_insert(InputSourceState::Null); } @@ -307,9 +300,9 @@ impl Handler { /// fn dispatch_keydown_action(&self, source_id: &str, action: &KeyDownAction) { let session = self.session().unwrap(); + let mut input_state_table = session.input_state_table_mut(); let raw_key = action.value.chars().next().unwrap(); - let mut input_state_table = session.input_state_table.borrow_mut(); let key_input_state = match input_state_table.get_mut(source_id).unwrap() { InputSourceState::Key(key_input_state) => key_input_state, _ => unreachable!(), @@ -321,7 +314,7 @@ impl Handler { self.increment_num_pending_actions(); let msg_id = self.current_action_id.get(); let cmd_msg = - WebDriverCommandMsg::KeyboardAction(session.webview_id, keyboard_event, msg_id); + WebDriverCommandMsg::KeyboardAction(self.verified_webview_id(), keyboard_event, msg_id); let _ = self.send_message_to_embedder(cmd_msg); } @@ -333,7 +326,7 @@ impl Handler { // See https://github.com/w3c/webdriver/issues/1905 && // https://github.com/servo/servo/issues/37579#issuecomment-2990762713 { - let mut input_cancel_list = session.input_cancel_list.borrow_mut(); + let mut input_cancel_list = session.input_cancel_list_mut(); if let Some(pos) = input_cancel_list.iter().rposition(|(id, item)| { id == source_id && matches!(item, @@ -346,7 +339,7 @@ impl Handler { } let raw_key = action.value.chars().next().unwrap(); - let mut input_state_table = session.input_state_table.borrow_mut(); + let mut input_state_table = session.input_state_table_mut(); let key_input_state = match input_state_table.get_mut(source_id).unwrap() { InputSourceState::Key(key_input_state) => key_input_state, _ => unreachable!(), @@ -356,8 +349,11 @@ impl Handler { // Step 12 self.increment_num_pending_actions(); let msg_id = self.current_action_id.get(); - let cmd_msg = - WebDriverCommandMsg::KeyboardAction(session.webview_id, keyboard_event, msg_id); + let cmd_msg = WebDriverCommandMsg::KeyboardAction( + self.verified_webview_id(), + keyboard_event, + msg_id, + ); let _ = self.send_message_to_embedder(cmd_msg); } } @@ -366,7 +362,7 @@ impl Handler { pub(crate) fn dispatch_pointerdown_action(&self, source_id: &str, action: &PointerDownAction) { let session = self.session().unwrap(); - let mut input_state_table = session.input_state_table.borrow_mut(); + let mut input_state_table = session.input_state_table_mut(); let pointer_input_state = match input_state_table.get_mut(source_id).unwrap() { InputSourceState::Pointer(pointer_input_state) => pointer_input_state, _ => unreachable!(), @@ -380,7 +376,7 @@ impl Handler { self.increment_num_pending_actions(); let msg_id = self.current_action_id.get(); let cmd_msg = WebDriverCommandMsg::MouseButtonAction( - session.webview_id, + self.verified_webview_id(), MouseButtonAction::Down, action.button.into(), pointer_input_state.x as f32, @@ -394,7 +390,7 @@ impl Handler { pub(crate) fn dispatch_pointerup_action(&self, source_id: &str, action: &PointerUpAction) { let session = self.session().unwrap(); - let mut input_state_table = session.input_state_table.borrow_mut(); + let mut input_state_table = session.input_state_table_mut(); let pointer_input_state = match input_state_table.get_mut(source_id).unwrap() { InputSourceState::Pointer(pointer_input_state) => pointer_input_state, _ => unreachable!(), @@ -409,7 +405,7 @@ impl Handler { // See https://github.com/w3c/webdriver/issues/1905 && // https://github.com/servo/servo/issues/37579#issuecomment-2990762713 { - let mut input_cancel_list = session.input_cancel_list.borrow_mut(); + let mut input_cancel_list = session.input_cancel_list_mut(); if let Some(pos) = input_cancel_list.iter().position(|(id, item)| { id == source_id && matches!(item, ActionItem::Pointer(PointerActionItem::Pointer(PointerAction::Up( @@ -426,7 +422,7 @@ impl Handler { self.increment_num_pending_actions(); let msg_id = self.current_action_id.get(); let cmd_msg = WebDriverCommandMsg::MouseButtonAction( - session.webview_id, + self.verified_webview_id(), MouseButtonAction::Up, action.button.into(), pointer_input_state.x as f32, @@ -452,8 +448,7 @@ impl Handler { let (start_x, start_y) = match self .session() .unwrap() - .input_state_table - .borrow() + .input_state_table() .get(source_id) .unwrap() { @@ -517,8 +512,7 @@ impl Handler { let (start_x, start_y) = match self .session() .unwrap() - .input_state_table - .borrow() + .input_state_table() .get(source_id) .unwrap() { @@ -548,7 +542,7 @@ impl Handler { tick_start: Instant, ) { let session = self.session().unwrap(); - let mut input_state_table = session.input_state_table.borrow_mut(); + let mut input_state_table = session.input_state_table_mut(); let pointer_input_state = match input_state_table.get_mut(source_id).unwrap() { InputSourceState::Pointer(pointer_input_state) => pointer_input_state, _ => unreachable!(), @@ -594,7 +588,7 @@ impl Handler { None }; let cmd_msg = WebDriverCommandMsg::MouseMoveAction( - session.webview_id, + self.verified_webview_id(), x as f32, y as f32, msg_id, @@ -710,8 +704,6 @@ impl Handler { mut curr_delta_y: f64, tick_start: Instant, ) { - let session = self.session().unwrap(); - // Step 1. Let time delta be the time since the beginning of the current tick, // measured in milliseconds on a monotonic clock. let time_delta = tick_start.elapsed().as_millis(); @@ -751,7 +743,7 @@ impl Handler { None }; let cmd_msg = WebDriverCommandMsg::WheelScrollAction( - session.webview_id, + self.verified_webview_id(), x, y, delta_x, @@ -795,8 +787,7 @@ impl Handler { return Err(ErrorStatus::MoveTargetOutOfBounds); } let (sender, receiver) = ipc::channel().unwrap(); - let cmd_msg = - WebDriverCommandMsg::GetViewportSize(self.session.as_ref().unwrap().webview_id, sender); + let cmd_msg = WebDriverCommandMsg::GetViewportSize(self.verified_webview_id(), sender); self.send_message_to_embedder(cmd_msg) .map_err(|_| ErrorStatus::UnknownError)?; @@ -878,7 +869,7 @@ impl Handler { // Step 2. Let id be the value of the id property of action sequence. let id = action_sequence.id.clone(); - let mut input_state_table = self.session().unwrap().input_state_table.borrow_mut(); + let mut input_state_table = self.session().unwrap().input_state_table_mut(); match action_sequence.actions { ActionsType::Null { diff --git a/components/webdriver_server/lib.rs b/components/webdriver_server/lib.rs index 3ed82e35871..70b8512c148 100644 --- a/components/webdriver_server/lib.rs +++ b/components/webdriver_server/lib.rs @@ -14,7 +14,7 @@ mod timeout; mod user_prompt; use std::borrow::ToOwned; -use std::cell::{Cell, LazyCell, RefCell}; +use std::cell::{Cell, LazyCell}; use std::collections::{BTreeMap, HashMap}; use std::io::Cursor; use std::net::{SocketAddr, SocketAddrV4}; @@ -71,10 +71,8 @@ use webdriver::response::{ }; use webdriver::server::{self, Session, SessionTeardownKind, WebDriverHandler}; -use crate::actions::{ActionItem, InputSourceState, PointerInputState}; -use crate::session::PageLoadStrategy; -use crate::timeout::TimeoutsConfiguration; -use crate::user_prompt::UserPromptHandler; +use crate::actions::{InputSourceState, PointerInputState}; +use crate::session::{PageLoadStrategy, WebDriverSession}; #[derive(Default)] pub struct WebDriverMessageIdGenerator { @@ -162,56 +160,6 @@ pub fn start_server( .expect("Thread spawning failed"); } -/// Represents the current WebDriver session and holds relevant session state. -/// Currently, only 1 webview is supported per session. -/// So only there is only 1 InputState. -pub struct WebDriverSession { - /// - id: Uuid, - - /// - webview_id: WebViewId, - - /// - browsing_context_id: BrowsingContextId, - - /// - /// The spec said each browsing context has an associated window handle. - /// Actually, each webview has a unique window handle. - window_handles: HashMap, - - timeouts: TimeoutsConfiguration, - - page_loading_strategy: PageLoadStrategy, - - strict_file_interactability: bool, - - user_prompt_handler: UserPromptHandler, - - /// - input_state_table: RefCell>, - - /// - input_cancel_list: RefCell>, -} - -impl WebDriverSession { - pub fn new(browsing_context_id: BrowsingContextId, webview_id: WebViewId) -> WebDriverSession { - WebDriverSession { - id: Uuid::new_v4(), - webview_id, - browsing_context_id, - window_handles: HashMap::new(), - timeouts: TimeoutsConfiguration::default(), - page_loading_strategy: PageLoadStrategy::Normal, - strict_file_interactability: false, - user_prompt_handler: UserPromptHandler::new(), - input_state_table: RefCell::new(HashMap::new()), - input_cancel_list: RefCell::new(Vec::new()), - } - } -} - struct Handler { /// The threaded receiver on which we can block for a load-status. /// It will receive messages sent on the load_status_sender, @@ -482,6 +430,20 @@ impl Handler { } } + pub fn browsing_context_id(&self) -> WebDriverResult { + self.session()? + .current_browsing_context_id() + .ok_or_else(|| { + WebDriverError::new(ErrorStatus::UnknownError, "No browsing context available") + }) + } + + pub fn webview_id(&self) -> WebDriverResult { + self.session()? + .current_webview_id() + .ok_or_else(|| WebDriverError::new(ErrorStatus::UnknownError, "No webview available")) + } + fn increment_num_pending_actions(&self) { // Increase the num_pending_actions by one every time we dispatch non null actions. self.num_pending_actions @@ -501,9 +463,9 @@ impl Handler { fn add_load_status_sender(&self) -> WebDriverResult<()> { self.send_message_to_embedder(WebDriverCommandMsg::ScriptCommand( - self.session()?.browsing_context_id, + self.browsing_context_id()?, WebDriverScriptCommand::AddLoadStatusSender( - self.session()?.webview_id, + self.webview_id()?, self.load_status_sender.clone(), ), )) @@ -511,14 +473,14 @@ impl Handler { fn clear_load_status_sender(&self) -> WebDriverResult<()> { self.send_message_to_embedder(WebDriverCommandMsg::ScriptCommand( - self.session()?.browsing_context_id, - WebDriverScriptCommand::RemoveLoadStatusSender(self.session()?.webview_id), + self.browsing_context_id()?, + WebDriverScriptCommand::RemoveLoadStatusSender(self.webview_id()?), )) } // This function is called only if session and webview are verified. fn verified_webview_id(&self) -> WebViewId { - self.session().unwrap().webview_id + self.session().unwrap().current_webview_id().unwrap() } fn focused_webview_id(&self) -> WebDriverResult> { @@ -590,6 +552,11 @@ impl Handler { }; // Step 6. Create a session + let session_id = self.create_session(&mut capabilities, &servo_capabilities)?; + + // Step 7. Let response be a JSON Object initialized with session's session ID and capabilities + let response = NewSessionResponse::new(session_id.to_string(), Value::Object(capabilities)); + // Step 8. Set session' current top-level browsing context let webview_id = match self.focused_webview_id()? { Some(webview_id) => webview_id, @@ -605,19 +572,9 @@ impl Handler { webview_id }, }; - let browsing_context_id = BrowsingContextId::from(webview_id); - - // Create and append session to the handler - let session_id = self.create_session( - &mut capabilities, - &servo_capabilities, - webview_id, - browsing_context_id, - )?; - self.session_mut()?.window_handles = self.get_window_handles()?; - - // Step 7. Let response be a JSON Object initialized with session's session ID and capabilities - let response = NewSessionResponse::new(session_id.to_string(), Value::Object(capabilities)); + self.session_mut()?.set_webview_id(Some(webview_id)); + self.session_mut()? + .set_browsing_context_id(Some(BrowsingContextId::from(webview_id))); // Step 9. Set the request queue to a new queue. // Skip here because the requests are handled in the external crate. @@ -654,7 +611,7 @@ impl Handler { cmd_msg: WebDriverScriptCommand, verify: VerifyBrowsingContextIsOpen, ) -> WebDriverResult<()> { - let browsing_context_id = self.session()?.browsing_context_id; + let browsing_context_id = self.browsing_context_id()?; if let VerifyBrowsingContextIsOpen::Yes = verify { self.verify_browsing_context_is_open(browsing_context_id)?; } @@ -673,7 +630,7 @@ impl Handler { cmd_msg: WebDriverScriptCommand, verify: VerifyBrowsingContextIsOpen, ) -> WebDriverResult<()> { - let webview_id = self.session()?.webview_id; + let webview_id = self.webview_id()?; if let VerifyBrowsingContextIsOpen::Yes = verify { self.verify_top_level_browsing_context_is_open(webview_id)?; } @@ -687,7 +644,7 @@ impl Handler { /// fn handle_get(&mut self, parameters: &GetParameters) -> WebDriverResult { - let webview_id = self.session()?.webview_id; + let webview_id = self.webview_id()?; // Step 2. If session's current top-level browsing context is no longer open, // return error with error code no such window. self.verify_top_level_browsing_context_is_open(webview_id)?; @@ -714,7 +671,8 @@ impl Handler { self.wait_for_document_ready_state()?; // Step 8.3. Set current browsing context with session and current top browsing context - self.session_mut()?.browsing_context_id = BrowsingContextId::from(webview_id); + self.session_mut()? + .set_browsing_context_id(Some(BrowsingContextId::from(webview_id))); Ok(WebDriverResponse::Void) } @@ -726,21 +684,21 @@ impl Handler { // Step 1. If session's page loading strategy is "none", // return success with data null. - if session.page_loading_strategy == PageLoadStrategy::None { + if session.page_loading_strategy() == PageLoadStrategy::None { return Ok(WebDriverResponse::Void); } // Step 2. If session's current browsing context is no longer open, // return success with data null. if self - .verify_browsing_context_is_open(session.browsing_context_id) + .verify_browsing_context_is_open(self.browsing_context_id()?) .is_err() { return Ok(WebDriverResponse::Void); } // Step 3. let timeout be the session's page load timeout. - let timeout = self.session()?.timeouts.page_load; + let timeout = session.session_timeouts().page_load; // TODO: Step 4. Implement timer parameter @@ -809,11 +767,14 @@ impl Handler { /// fn handle_current_url(&self) -> WebDriverResult { + let webview_id = self.webview_id()?; + // Step 1. If session's current top-level browsing context is no longer open, // return error with error code no such window. - self.verify_top_level_browsing_context_is_open(self.session()?.webview_id)?; + self.verify_top_level_browsing_context_is_open(webview_id)?; + // Step 2. Handle any user prompt. - self.handle_any_user_prompts(self.session()?.webview_id)?; + self.handle_any_user_prompts(webview_id)?; let (sender, receiver) = ipc::channel().unwrap(); self.top_level_script_command( @@ -834,7 +795,7 @@ impl Handler { verify: VerifyBrowsingContextIsOpen, ) -> WebDriverResult { let (sender, receiver) = ipc::channel().unwrap(); - let webview_id = self.session()?.webview_id; + let webview_id = self.webview_id()?; // Step 1. If session's current top-level browsing context is no longer open, // return error with error code no such window. if let VerifyBrowsingContextIsOpen::Yes = verify { @@ -868,7 +829,7 @@ impl Handler { // the implmentation is expected to continue with the remaining steps. // DO NOT return "unsupported operation". - let webview_id = self.session()?.webview_id; + let webview_id = self.webview_id()?; // Step 12. If session's current top-level browsing context is no longer open, // return error with error code no such window. self.verify_top_level_browsing_context_is_open(webview_id)?; @@ -926,13 +887,13 @@ impl Handler { // Step 1. If the remote end does not support the Maximize Window command for session's // current top-level browsing context for any reason, // return error with error code unsupported operation. - let webview_id = self.session()?.webview_id; + let webview_id = self.webview_id()?; // Step 2. If session's current top-level browsing context is no longer open, // return error with error code no such window. self.verify_top_level_browsing_context_is_open(webview_id)?; // Step 3. Try to handle any user prompts with session. - self.handle_any_user_prompts(self.session()?.webview_id)?; + self.handle_any_user_prompts(self.webview_id()?)?; // Step 4. (TODO) Fully exit fullscreen. @@ -956,11 +917,11 @@ impl Handler { fn handle_is_enabled(&self, element: &WebElement) -> WebDriverResult { // Step 1. If session's current browsing context is no longer open, // return error with error code no such window. - let browsing_context = self.session()?.browsing_context_id; + let browsing_context = self.browsing_context_id()?; self.verify_browsing_context_is_open(browsing_context)?; // Step 2. Try to handle any user prompts with session. - let webview_id = self.session()?.webview_id; + let webview_id = self.webview_id()?; self.handle_any_user_prompts(webview_id)?; let (sender, receiver) = ipc::channel().unwrap(); @@ -980,11 +941,11 @@ impl Handler { fn handle_is_selected(&self, element: &WebElement) -> WebDriverResult { // Step 1. If session's current browsing context is no longer open, // return error with error code no such window. - let browsing_context = self.session()?.browsing_context_id; + let browsing_context = self.browsing_context_id()?; self.verify_browsing_context_is_open(browsing_context)?; // Step 2. Try to handle any user prompts with session. - let webview_id = self.session()?.webview_id; + let webview_id = self.webview_id()?; self.handle_any_user_prompts(webview_id)?; let (sender, receiver) = ipc::channel().unwrap(); @@ -1003,7 +964,7 @@ impl Handler { /// fn handle_go_back(&self) -> WebDriverResult { - let webview_id = self.session()?.webview_id; + let webview_id = self.webview_id()?; // Step 1. If session's current top-level browsing context is no longer open, // return error with error code no such window. self.verify_top_level_browsing_context_is_open(webview_id)?; @@ -1020,7 +981,7 @@ impl Handler { /// fn handle_go_forward(&self) -> WebDriverResult { - let webview_id = self.session()?.webview_id; + let webview_id = self.webview_id()?; // Step 1. If session's current top-level browsing context is no longer open, // return error with error code no such window. self.verify_top_level_browsing_context_is_open(webview_id)?; @@ -1037,7 +998,7 @@ impl Handler { /// fn handle_refresh(&mut self) -> WebDriverResult { - let webview_id = self.session()?.webview_id; + let webview_id = self.webview_id()?; // Step 1. If session's current top-level browsing context is no longer open, // return error with error code no such window. self.verify_top_level_browsing_context_is_open(webview_id)?; @@ -1052,18 +1013,21 @@ impl Handler { self.wait_for_document_ready_state()?; // Step 5. Set current browsing context with session and current top browsing context. - self.session_mut()?.browsing_context_id = BrowsingContextId::from(webview_id); + self.session_mut()? + .set_browsing_context_id(Some(BrowsingContextId::from(webview_id))); Ok(WebDriverResponse::Void) } /// fn handle_title(&self) -> WebDriverResult { + let webview_id = self.webview_id()?; + // Step 1. If session's current top-level browsing context is no longer open, // return error with error code no such window. - self.verify_top_level_browsing_context_is_open(self.session()?.webview_id)?; + self.verify_top_level_browsing_context_is_open(webview_id)?; // Step 2. Handle any user prompt. - self.handle_any_user_prompts(self.session()?.webview_id)?; + self.handle_any_user_prompts(webview_id)?; let (sender, receiver) = ipc::channel().unwrap(); @@ -1082,35 +1046,29 @@ impl Handler { /// fn handle_window_handle(&mut self) -> WebDriverResult { - let webview_id = self.session()?.webview_id; - let browsing_context_id = self.session()?.browsing_context_id; + let webview_id = self.webview_id()?; // Step 1. If session's current top-level browsing context is no longer open, // return error with error code no such window. self.verify_top_level_browsing_context_is_open(webview_id)?; - let handle = self.get_window_handle(browsing_context_id)?; - self.session_mut()? - .window_handles - .insert(webview_id, handle); - match self.session()?.window_handles.get(&webview_id) { - Some(handle) => Ok(WebDriverResponse::Generic(ValueResponse( - serde_json::to_value(handle)?, - ))), - None => Ok(WebDriverResponse::Void), - } + // Step 2. Return success with the window handle. + let handle = self + .get_window_handle(webview_id) + .expect("Failed to get window handle of an existing webview"); + + Ok(WebDriverResponse::Generic(ValueResponse( + serde_json::to_value(handle)?, + ))) } /// fn handle_window_handles(&mut self) -> WebDriverResult { - self.handle_any_user_prompts(self.session()?.webview_id)?; + let mut handles = self.get_window_handles()?; + handles.sort(); - self.session_mut()?.window_handles = self.get_window_handles()?; - - let handles = self - .session()? - .window_handles - .values() + let handles = handles + .into_iter() .map(serde_json::to_value) .collect::, _>>()?; @@ -1119,40 +1077,35 @@ impl Handler { ))) } - fn get_window_handles(&self) -> WebDriverResult> { - let (sender, receiver) = ipc::channel().unwrap(); - self.send_message_to_embedder(WebDriverCommandMsg::GetAllWebViews(sender))?; - - let webviews = match wait_for_ipc_response(receiver)? { - Ok(webviews) => webviews, - Err(_) => { - return Err(WebDriverError::new( + fn get_window_handle(&mut self, webview_id: WebViewId) -> WebDriverResult { + self.get_window_handles()? + .iter() + .find(|id| id == &&webview_id.to_string()) + .cloned() + .ok_or_else(|| { + WebDriverError::new( ErrorStatus::UnknownError, - "Failed to get window handles", - )); - }, - }; - - let mut res = HashMap::new(); - for id in webviews.iter() { - let handle = self.get_window_handle(BrowsingContextId::from(*id))?; - res.insert(*id, handle); - } - - Ok(res) + "No such window while getting window handle", + ) + }) } - fn get_window_handle(&self, browsing_context_id: BrowsingContextId) -> WebDriverResult { - let (sender, receiver) = ipc::channel().unwrap(); - self.send_message_to_embedder(WebDriverCommandMsg::ScriptCommand( - browsing_context_id, - WebDriverScriptCommand::GetWindowHandle(sender), - ))?; + fn get_window_handles(&self) -> WebDriverResult> { + let handles = self + .get_all_webview_ids()? + .into_iter() + .map(|id| id.to_string()) + .collect::>(); - match wait_for_ipc_response(receiver)? { - Ok(handle) => Ok(handle), - Err(err) => Err(WebDriverError::new(err, "Failed to get window handle")), - } + Ok(handles) + } + + fn get_all_webview_ids(&self) -> WebDriverResult> { + let (sender, receiver) = ipc::channel().unwrap(); + self.send_message_to_embedder(WebDriverCommandMsg::GetAllWebViews(sender))?; + let webviews = wait_for_ipc_response(receiver)?; + + Ok(webviews) } /// @@ -1169,7 +1122,7 @@ impl Handler { /// fn handle_close_window(&mut self) -> WebDriverResult { - let webview_id = self.session()?.webview_id; + let webview_id = self.webview_id()?; // Step 1. If session's current top-level browsing context is no longer open, // return error with error code no such window. self.verify_top_level_browsing_context_is_open(webview_id)?; @@ -1184,11 +1137,9 @@ impl Handler { self.send_message_to_embedder(cmd_msg)?; wait_for_ipc_response(receiver)?; - self.session_mut()?.window_handles.remove(&webview_id); // Step 4. If there are no more open top-level browsing contexts, try to close the session. - let window_handles: Vec = - self.session()?.window_handles.values().cloned().collect(); + let window_handles = self.get_window_handles()?; if window_handles.is_empty() { self.session = None; @@ -1207,14 +1158,14 @@ impl Handler { ) -> WebDriverResult { let (sender, receiver) = ipc::channel().unwrap(); - let session = self.session()?; + let webview_id = self.webview_id()?; // Step 2. If session's current top-level browsing context is no longer open, // return error with error code no such window. - self.verify_top_level_browsing_context_is_open(session.webview_id)?; + self.verify_top_level_browsing_context_is_open(webview_id)?; // Step 3. Handle any user prompt. - self.handle_any_user_prompts(session.webview_id)?; + self.handle_any_user_prompts(webview_id)?; let cmd_msg = WebDriverCommandMsg::NewWebView(sender, Some(self.load_status_sender.clone())); @@ -1224,10 +1175,8 @@ impl Handler { if let Ok(webview_id) = receiver.recv() { let _ = self.wait_for_document_ready_state(); - let handle = self.get_window_handle(BrowsingContextId::from(webview_id))?; - self.session_mut()? - .window_handles - .insert(webview_id, handle.clone()); + let handle = self.get_window_handle(webview_id)?; + Ok(WebDriverResponse::NewWindow(NewWindowResponse { handle, typ: "tab".to_string(), @@ -1249,7 +1198,7 @@ impl Handler { let frame_id = match parameters.id { // id is null FrameId::Top => { - let webview_id = self.session()?.webview_id; + let webview_id = self.webview_id()?; // Step 1. If session's current top-level browsing context is no longer open, // return error with error code no such window. self.verify_top_level_browsing_context_is_open(webview_id)?; @@ -1257,7 +1206,8 @@ impl Handler { self.handle_any_user_prompts(webview_id)?; // Step 3. Set the current browsing context with session and // session's current top-level browsing context. - self.session_mut()?.browsing_context_id = BrowsingContextId::from(webview_id); + self.session_mut()? + .set_browsing_context_id(Some(BrowsingContextId::from(webview_id))); return Ok(WebDriverResponse::Void); }, // id is a Number object @@ -1275,8 +1225,9 @@ impl Handler { /// fn handle_switch_to_parent_frame(&mut self) -> WebDriverResult { - let webview_id = self.session()?.webview_id; - let browsing_context = self.session()?.browsing_context_id; + let webview_id = self.webview_id()?; + let browsing_context = self.browsing_context_id()?; + // Step 1. If session's current browsing context is already the top-level browsing context: if browsing_context == webview_id { // Step 1.1. If session's current browsing context is no longer open, @@ -1299,7 +1250,8 @@ impl Handler { // set the current browsing context with session and current parent browsing context. match wait_for_ipc_response(receiver)? { Ok(browsing_context_id) => { - self.session_mut()?.browsing_context_id = browsing_context_id; + self.session_mut()? + .set_browsing_context_id(Some(browsing_context_id)); Ok(WebDriverResponse::Void) }, Err(error) => Err(WebDriverError::new(error, "")), @@ -1311,26 +1263,21 @@ impl Handler { &mut self, parameters: &SwitchToWindowParameters, ) -> WebDriverResult { - let session = self.session_mut()?; - if session.id.to_string() == parameters.handle { - // There's only one main window, so there's nothing to do here. - Ok(WebDriverResponse::Void) - } else if let Some((webview_id, _)) = session - .window_handles + let window_handles = self.get_all_webview_ids()?; + let Some(webview_id) = window_handles .iter() - .find(|(_k, v)| **v == parameters.handle) - { - let webview_id = *webview_id; - session.webview_id = webview_id; - session.browsing_context_id = BrowsingContextId::from(webview_id); - self.focus_webview(webview_id)?; - Ok(WebDriverResponse::Void) - } else { - Err(WebDriverError::new( + .find(|id| id.to_string() == parameters.handle) + else { + return Err(WebDriverError::new( ErrorStatus::NoSuchWindow, - "No such window", - )) - } + "No such window while switching to window", + )); + }; + + let session = self.session_mut()?; + session.set_webview_id(Some(*webview_id)); + session.set_browsing_context_id(Some(BrowsingContextId::from(*webview_id))); + Ok(WebDriverResponse::Void) } fn switch_to_frame( @@ -1340,11 +1287,12 @@ impl Handler { let (sender, receiver) = ipc::channel().unwrap(); let cmd = WebDriverScriptCommand::GetBrowsingContextId(frame_id, sender); self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::Yes)?; - self.handle_any_user_prompts(self.session()?.webview_id)?; + self.handle_any_user_prompts(self.webview_id()?)?; match wait_for_ipc_response(receiver)? { Ok(browsing_context_id) => { - self.session_mut()?.browsing_context_id = browsing_context_id; + self.session_mut()? + .set_browsing_context_id(Some(browsing_context_id)); Ok(WebDriverResponse::Void) }, Err(error) => Err(WebDriverError::new(error, "")), @@ -1362,10 +1310,10 @@ impl Handler { } // Step 5. If session's current browsing context is no longer open, // return error with error code no such window. - self.verify_browsing_context_is_open(self.session()?.browsing_context_id)?; + self.verify_browsing_context_is_open(self.browsing_context_id()?)?; // Step 6. Handle any user prompt. - self.handle_any_user_prompts(self.session()?.webview_id)?; + self.handle_any_user_prompts(self.webview_id()?)?; let (sender, receiver) = ipc::channel().unwrap(); match parameters.using { @@ -1434,10 +1382,10 @@ impl Handler { } // Step 5. If session's current browsing context is no longer open, // return error with error code no such window. - self.verify_browsing_context_is_open(self.session()?.browsing_context_id)?; + self.verify_browsing_context_is_open(self.browsing_context_id()?)?; // Step 6. Handle any user prompt. - self.handle_any_user_prompts(self.session()?.webview_id)?; + self.handle_any_user_prompts(self.webview_id()?)?; let (sender, receiver) = ipc::channel().unwrap(); @@ -1504,10 +1452,10 @@ impl Handler { // Step 5. If session's current browsing context is no longer open, // return error with error code no such window. - self.verify_browsing_context_is_open(self.session()?.browsing_context_id)?; + self.verify_browsing_context_is_open(self.browsing_context_id()?)?; // Step 6. Handle any user prompt. - self.handle_any_user_prompts(self.session()?.webview_id)?; + self.handle_any_user_prompts(self.webview_id()?)?; let (sender, receiver) = ipc::channel().unwrap(); @@ -1578,9 +1526,9 @@ impl Handler { fn handle_get_shadow_root(&self, element: WebElement) -> WebDriverResult { // Step 1. If session's current browsing context is no longer open, // return error with error code no such window. - self.verify_browsing_context_is_open(self.session()?.browsing_context_id)?; + self.verify_browsing_context_is_open(self.browsing_context_id()?)?; // Step 2. Handle any user prompt. - self.handle_any_user_prompts(self.session()?.webview_id)?; + self.handle_any_user_prompts(self.webview_id()?)?; let (sender, receiver) = ipc::channel().unwrap(); let cmd = WebDriverScriptCommand::GetElementShadowRoot(element.to_string(), sender); self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?; @@ -1601,9 +1549,9 @@ impl Handler { fn handle_element_rect(&self, element: &WebElement) -> WebDriverResult { // Step 1. If session's current browsing context is no longer open, // return error with error code no such window. - self.verify_browsing_context_is_open(self.session()?.browsing_context_id)?; + self.verify_browsing_context_is_open(self.browsing_context_id()?)?; // Step 2. Handle any user prompt. - self.handle_any_user_prompts(self.session()?.webview_id)?; + self.handle_any_user_prompts(self.webview_id()?)?; let (sender, receiver) = ipc::channel().unwrap(); let cmd = WebDriverScriptCommand::GetElementRect(element.to_string(), sender); self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?; @@ -1625,9 +1573,9 @@ impl Handler { fn handle_element_text(&self, element: &WebElement) -> WebDriverResult { // Step 1. If session's current browsing context is no longer open, // return error with error code no such window. - self.verify_browsing_context_is_open(self.session()?.browsing_context_id)?; + self.verify_browsing_context_is_open(self.browsing_context_id()?)?; // Step 2. Handle any user prompt. - self.handle_any_user_prompts(self.session()?.webview_id)?; + self.handle_any_user_prompts(self.webview_id()?)?; let (sender, receiver) = ipc::channel().unwrap(); let cmd = WebDriverScriptCommand::GetElementText(element.to_string(), sender); self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?; @@ -1643,9 +1591,9 @@ impl Handler { fn handle_active_element(&self) -> WebDriverResult { // Step 1. If session's current browsing context is no longer open, // return error with error code no such window. - self.verify_browsing_context_is_open(self.session()?.browsing_context_id)?; + self.verify_browsing_context_is_open(self.browsing_context_id()?)?; // Step 2. Handle any user prompt. - self.handle_any_user_prompts(self.session()?.webview_id)?; + self.handle_any_user_prompts(self.webview_id()?)?; let (sender, receiver) = ipc::channel().unwrap(); let cmd = WebDriverScriptCommand::GetActiveElement(sender); self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?; @@ -1670,9 +1618,9 @@ impl Handler { fn handle_computed_role(&self, element: &WebElement) -> WebDriverResult { // Step 1. If session's current browsing context is no longer open, // return error with error code no such window. - self.verify_browsing_context_is_open(self.session()?.browsing_context_id)?; + self.verify_browsing_context_is_open(self.browsing_context_id()?)?; // Step 2. Handle any user prompt. - self.handle_any_user_prompts(self.session()?.webview_id)?; + self.handle_any_user_prompts(self.webview_id()?)?; let (sender, receiver) = ipc::channel().unwrap(); let cmd = WebDriverScriptCommand::GetComputedRole(element.to_string(), sender); self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?; @@ -1688,9 +1636,9 @@ impl Handler { fn handle_element_tag_name(&self, element: &WebElement) -> WebDriverResult { // Step 1. If session's current browsing context is no longer open, // return error with error code no such window. - self.verify_browsing_context_is_open(self.session()?.browsing_context_id)?; + self.verify_browsing_context_is_open(self.browsing_context_id()?)?; // Step 2. Handle any user prompt. - self.handle_any_user_prompts(self.session()?.webview_id)?; + self.handle_any_user_prompts(self.webview_id()?)?; let (sender, receiver) = ipc::channel().unwrap(); let cmd = WebDriverScriptCommand::GetElementTagName(element.to_string(), sender); self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?; @@ -1710,9 +1658,9 @@ impl Handler { ) -> WebDriverResult { // Step 1. If session's current browsing context is no longer open, // return error with error code no such window. - self.verify_browsing_context_is_open(self.session()?.browsing_context_id)?; + self.verify_browsing_context_is_open(self.browsing_context_id()?)?; // Step 2. Handle any user prompt. - self.handle_any_user_prompts(self.session()?.webview_id)?; + self.handle_any_user_prompts(self.webview_id()?)?; let (sender, receiver) = ipc::channel().unwrap(); let cmd = WebDriverScriptCommand::GetElementAttribute( element.to_string(), @@ -1736,9 +1684,9 @@ impl Handler { ) -> WebDriverResult { // Step 1. If session's current browsing context is no longer open, // return error with error code no such window. - self.verify_browsing_context_is_open(self.session()?.browsing_context_id)?; + self.verify_browsing_context_is_open(self.browsing_context_id()?)?; // Step 2. Handle any user prompt. - self.handle_any_user_prompts(self.session()?.webview_id)?; + self.handle_any_user_prompts(self.webview_id()?)?; let (sender, receiver) = ipc::channel().unwrap(); let cmd = WebDriverScriptCommand::GetElementProperty( @@ -1764,9 +1712,9 @@ impl Handler { ) -> WebDriverResult { // Step 1. If session's current browsing context is no longer open, // return error with error code no such window. - self.verify_browsing_context_is_open(self.session()?.browsing_context_id)?; + self.verify_browsing_context_is_open(self.browsing_context_id()?)?; // Step 2. Handle any user prompt. - self.handle_any_user_prompts(self.session()?.webview_id)?; + self.handle_any_user_prompts(self.webview_id()?)?; let (sender, receiver) = ipc::channel().unwrap(); let cmd = WebDriverScriptCommand::GetElementCSS(element.to_string(), name.to_owned(), sender); @@ -1783,9 +1731,9 @@ impl Handler { fn handle_get_cookies(&self) -> WebDriverResult { // Step 1. If session's current browsing context is no longer open, // return error with error code no such window. - self.verify_browsing_context_is_open(self.session()?.browsing_context_id)?; + self.verify_browsing_context_is_open(self.browsing_context_id()?)?; // Step 2. Handle any user prompt. - self.handle_any_user_prompts(self.session()?.webview_id)?; + self.handle_any_user_prompts(self.webview_id()?)?; let (sender, receiver) = ipc::channel().unwrap(); let cmd = WebDriverScriptCommand::GetCookies(sender); self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?; @@ -1804,9 +1752,9 @@ impl Handler { fn handle_get_cookie(&self, name: String) -> WebDriverResult { // Step 1. If session's current browsing context is no longer open, // return error with error code no such window. - self.verify_browsing_context_is_open(self.session()?.browsing_context_id)?; + self.verify_browsing_context_is_open(self.browsing_context_id()?)?; // Step 2. Handle any user prompt. - self.handle_any_user_prompts(self.session()?.webview_id)?; + self.handle_any_user_prompts(self.webview_id()?)?; let (sender, receiver) = ipc::channel().unwrap(); let cmd = WebDriverScriptCommand::GetCookie(name, sender); self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?; @@ -1831,9 +1779,9 @@ impl Handler { ) -> WebDriverResult { // Step 1. If session's current browsing context is no longer open, // return error with error code no such window. - self.verify_browsing_context_is_open(self.session()?.browsing_context_id)?; + self.verify_browsing_context_is_open(self.browsing_context_id()?)?; // Step 2. Handle any user prompt. - self.handle_any_user_prompts(self.session()?.webview_id)?; + self.handle_any_user_prompts(self.webview_id()?)?; let (sender, receiver) = ipc::channel().unwrap(); let mut cookie_builder = @@ -1875,9 +1823,9 @@ impl Handler { fn handle_delete_cookie(&self, name: String) -> WebDriverResult { // Step 1. If session's current browsing context is no longer open, // return error with error code no such window. - self.verify_browsing_context_is_open(self.session()?.browsing_context_id)?; + self.verify_browsing_context_is_open(self.browsing_context_id()?)?; // Step 2. Handle any user prompt. - self.handle_any_user_prompts(self.session()?.webview_id)?; + self.handle_any_user_prompts(self.webview_id()?)?; let (sender, receiver) = ipc::channel().unwrap(); let cmd = WebDriverScriptCommand::DeleteCookie(name, sender); self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?; @@ -1891,9 +1839,9 @@ impl Handler { fn handle_delete_cookies(&self) -> WebDriverResult { // Step 1. If session's current browsing context is no longer open, // return error with error code no such window. - self.verify_browsing_context_is_open(self.session()?.browsing_context_id)?; + self.verify_browsing_context_is_open(self.browsing_context_id()?)?; // Step 2. Handle any user prompt. - self.handle_any_user_prompts(self.session()?.webview_id)?; + self.handle_any_user_prompts(self.webview_id()?)?; let (sender, receiver) = ipc::channel().unwrap(); let cmd = WebDriverScriptCommand::DeleteCookies(sender); self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::Yes)?; @@ -1904,12 +1852,12 @@ impl Handler { } fn handle_get_timeouts(&mut self) -> WebDriverResult { - let session = self.session()?; + let timeouts = self.session()?.session_timeouts(); let timeouts = TimeoutsResponse { - script: session.timeouts.script, - page_load: session.timeouts.page_load, - implicit: session.timeouts.implicit_wait, + script: timeouts.script, + page_load: timeouts.page_load, + implicit: timeouts.implicit_wait, }; Ok(WebDriverResponse::Timeouts(timeouts)) @@ -1919,19 +1867,16 @@ impl Handler { &mut self, parameters: &TimeoutsParameters, ) -> WebDriverResult { - let session = self - .session - .as_mut() - .ok_or(WebDriverError::new(ErrorStatus::SessionNotCreated, ""))?; + let session = self.session_mut()?; if let Some(timeout) = parameters.script { - session.timeouts.script = timeout; + session.session_timeouts_mut().script = timeout; } if let Some(timeout) = parameters.page_load { - session.timeouts.page_load = timeout + session.session_timeouts_mut().page_load = timeout; } if let Some(timeout) = parameters.implicit { - session.timeouts.implicit_wait = timeout + session.session_timeouts_mut().implicit_wait = timeout } Ok(WebDriverResponse::Void) @@ -1941,9 +1886,9 @@ impl Handler { fn handle_get_page_source(&self) -> WebDriverResult { // Step 1. If session's current browsing context is no longer open, // return error with error code no such window. - self.verify_browsing_context_is_open(self.session()?.browsing_context_id)?; + self.verify_browsing_context_is_open(self.browsing_context_id()?)?; // Step 2. Handle any user prompt. - self.handle_any_user_prompts(self.session()?.webview_id)?; + self.handle_any_user_prompts(self.webview_id()?)?; let (sender, receiver) = ipc::channel().unwrap(); let cmd = WebDriverScriptCommand::GetPageSource(sender); @@ -1962,13 +1907,13 @@ impl Handler { &mut self, parameters: ActionsParameters, ) -> WebDriverResult { - let browsing_context = self.session()?.browsing_context_id; + let browsing_context = self.browsing_context_id()?; // Step 1. If session's current browsing context is no longer open, // return error with error code no such window. self.verify_browsing_context_is_open(browsing_context)?; // Step 2. Handle any user prompt. - self.handle_any_user_prompts(self.session()?.webview_id)?; + self.handle_any_user_prompts(self.webview_id()?)?; // Step 5. Let actions by tick be the result of trying to extract an action sequence let actions_by_tick = self.extract_an_action_sequence(parameters); @@ -1983,13 +1928,14 @@ impl Handler { /// fn handle_release_actions(&mut self) -> WebDriverResult { let session = self.session()?; + let browsing_context_id = self.browsing_context_id()?; // Step 1. If session's current browsing context is no longer open, // return error with error code no such window. - self.verify_browsing_context_is_open(session.browsing_context_id)?; + self.verify_browsing_context_is_open(browsing_context_id)?; // Step 2. User prompts. No user prompt implemented yet. - self.handle_any_user_prompts(self.session()?.webview_id)?; + self.handle_any_user_prompts(self.webview_id()?)?; // Skip: Step 3. We don't support "browsing context input state map" yet. @@ -2001,19 +1947,18 @@ impl Handler { // Step 6. Let undo actions be input cancel list in reverse order. let undo_actions = session - .input_cancel_list - .borrow_mut() + .input_cancel_list_mut() .drain(..) .rev() .map(|(id, action_item)| Vec::from([(id, action_item)])) .collect(); // Step 7. Dispatch undo actions with current browsing context. - if let Err(err) = self.dispatch_actions(undo_actions, session.browsing_context_id) { + if let Err(err) = self.dispatch_actions(undo_actions, browsing_context_id) { return Err(WebDriverError::new(err, "Failed to dispatch undo actions")); } // Step 8. Reset the input state of session's current top-level browsing context. - session.input_state_table.borrow_mut().clear(); + session.input_state_table_mut().clear(); Ok(WebDriverResponse::Void) } @@ -2038,10 +1983,10 @@ impl Handler { // Step 2. If session's current browsing context is no longer open, // return error with error code no such window. - self.verify_browsing_context_is_open(self.session()?.browsing_context_id)?; + self.verify_browsing_context_is_open(self.browsing_context_id()?)?; // Step 3. Handle any user prompt. - self.handle_any_user_prompts(self.session()?.webview_id)?; + self.handle_any_user_prompts(self.webview_id()?)?; let (sender, receiver) = ipc::channel().unwrap(); let cmd = WebDriverScriptCommand::ExecuteScript(script, sender); @@ -2059,7 +2004,8 @@ impl Handler { let (func_body, mut args_string) = self.extract_script_arguments(parameters)?; args_string.push("resolve".to_string()); - let timeout_script = if let Some(script_timeout) = self.session()?.timeouts.script { + let timeout_script = if let Some(script_timeout) = self.session()?.session_timeouts().script + { format!("setTimeout(webdriverTimeout, {});", script_timeout) } else { "".into() @@ -2084,10 +2030,10 @@ impl Handler { // Step 2. If session's current browsing context is no longer open, // return error with error code no such window. - self.verify_browsing_context_is_open(self.session()?.browsing_context_id)?; + self.verify_browsing_context_is_open(self.browsing_context_id()?)?; // Step 3. Handle any user prompt. - self.handle_any_user_prompts(self.session()?.webview_id)?; + self.handle_any_user_prompts(self.webview_id()?)?; let (sender, receiver) = ipc::channel().unwrap(); let cmd = WebDriverScriptCommand::ExecuteAsyncScript(script, sender); @@ -2138,15 +2084,15 @@ impl Handler { ) -> WebDriverResult { // Step 3. If session's current browsing context is no longer open, // return error with error code no such window. - self.verify_browsing_context_is_open(self.session()?.browsing_context_id)?; + self.verify_browsing_context_is_open(self.browsing_context_id()?)?; // Step 4. Handle any user prompt. - self.handle_any_user_prompts(self.session()?.webview_id)?; + self.handle_any_user_prompts(self.webview_id()?)?; let (sender, receiver) = ipc::channel().unwrap(); let cmd = WebDriverScriptCommand::WillSendKeys( element.to_string(), keys.text.to_string(), - self.session()?.strict_file_interactability, + self.session()?.strict_file_interactability(), sender, ); self.browsing_context_script_command(cmd, VerifyBrowsingContextIsOpen::No)?; @@ -2164,7 +2110,7 @@ impl Handler { // send keys command being two separate messages, // so the constellation may have changed state between them. // TODO: We should use `dispatch_action` to send the keys. - let cmd_msg = WebDriverCommandMsg::SendKeys(self.session()?.webview_id, input_events); + let cmd_msg = WebDriverCommandMsg::SendKeys(self.webview_id()?, input_events); self.send_message_to_embedder(cmd_msg)?; Ok(WebDriverResponse::Void) @@ -2174,10 +2120,10 @@ impl Handler { fn handle_element_clear(&self, element: &WebElement) -> WebDriverResult { // Step 1. If session's current browsing context is no longer open, // return ErrorStatus::NoSuchWindow. - self.verify_browsing_context_is_open(self.session()?.browsing_context_id)?; + self.verify_browsing_context_is_open(self.browsing_context_id()?)?; // Step 2. Try to handle any user prompt. - self.handle_any_user_prompts(self.session()?.webview_id)?; + self.handle_any_user_prompts(self.webview_id()?)?; // Step 3-11 handled in script thread. let (sender, receiver) = ipc::channel().unwrap(); @@ -2194,11 +2140,11 @@ impl Handler { fn handle_element_click(&mut self, element: &WebElement) -> WebDriverResult { // Step 1. If session's current browsing context is no longer open, // return error with error code no such window. - let browsing_context_id = self.session()?.browsing_context_id; + let browsing_context_id = self.browsing_context_id()?; self.verify_browsing_context_is_open(browsing_context_id)?; // Step 2. Handle any user prompts. - self.handle_any_user_prompts(self.session()?.webview_id)?; + self.handle_any_user_prompts(self.webview_id()?)?; let (sender, receiver) = ipc::channel().unwrap(); @@ -2236,7 +2182,7 @@ impl Handler { let id = Uuid::new_v4().to_string(); // Step 8.1 - self.session_mut()?.input_state_table.borrow_mut().insert( + self.session_mut()?.input_state_table_mut().insert( id.clone(), InputSourceState::Pointer(PointerInputState::new(PointerType::Mouse)), ); @@ -2283,22 +2229,18 @@ impl Handler { // Step 8.16. Dispatch a list of actions with session's current browsing context let actions_by_tick = self.actions_by_tick_from_sequence(vec![action_sequence]); - if let Err(e) = self.dispatch_actions(actions_by_tick, self.session()?.browsing_context_id) - { + if let Err(e) = self.dispatch_actions(actions_by_tick, self.browsing_context_id()?) { log::error!("handle_element_click: dispatch_actions failed: {:?}", e); } // Step 8.17 Remove an input source with input state and input id. - self.session_mut()? - .input_state_table - .borrow_mut() - .remove(&id); + self.session_mut()?.input_state_table_mut().remove(&id); Ok(WebDriverResponse::Void) } fn take_screenshot(&self, rect: Option>) -> WebDriverResult { - let webview_id = self.session()?.webview_id; + let webview_id = self.webview_id()?; let mut img = None; let interval = 1000; @@ -2353,7 +2295,7 @@ impl Handler { fn handle_take_screenshot(&self) -> WebDriverResult { // Step 1. If session's current top-level browsing context is no longer open, // return error with error code no such window. - let webview_id = self.session()?.webview_id; + let webview_id = self.webview_id()?; self.verify_top_level_browsing_context_is_open(webview_id)?; self.handle_any_user_prompts(webview_id)?; @@ -2374,7 +2316,7 @@ impl Handler { // Step 1. If session's current top-level browsing context is no longer open, // return error with error code no such window. - let webview_id = self.session()?.webview_id; + let webview_id = self.webview_id()?; self.verify_top_level_browsing_context_is_open(webview_id)?; // Step 2. Try to handle any user prompts with session. @@ -2516,8 +2458,7 @@ impl WebDriverHandler for Handler { ) -> WebDriverResult { info!("{:?}", msg.command); - // Drain the load status receiver to avoid blocking - // the command processing. + // Drain the load status receiver to avoid incorrect status handling while self.load_status_receiver.try_recv().is_ok() {} // Unless we are trying to create a new session, we need to ensure that a diff --git a/components/webdriver_server/session.rs b/components/webdriver_server/session.rs index a455754a9c4..1e5cf06e598 100644 --- a/components/webdriver_server/session.rs +++ b/components/webdriver_server/session.rs @@ -2,19 +2,25 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +use std::cell::{Ref, RefCell, RefMut}; +use std::collections::HashMap; + use base::id::{BrowsingContextId, WebViewId}; use serde_json::{Map, Value, json}; use uuid::Uuid; use webdriver::error::WebDriverResult; +use crate::Handler; +use crate::actions::{ActionItem, InputSourceState}; use crate::capabilities::ServoCapabilities; -use crate::timeout::{deserialize_as_timeouts_configuration, serialize_timeouts_configuration}; -use crate::user_prompt::{ - default_unhandled_prompt_behavior, deserialize_unhandled_prompt_behaviour, +use crate::timeout::{ + TimeoutsConfiguration, deserialize_as_timeouts_configuration, serialize_timeouts_configuration, +}; +use crate::user_prompt::{ + UserPromptHandler, default_unhandled_prompt_behavior, deserialize_unhandled_prompt_behaviour, }; -use crate::{Handler, WebDriverSession}; -#[derive(Debug, PartialEq, serde::Serialize)] +#[derive(Debug, Clone, PartialEq, serde::Serialize)] pub enum PageLoadStrategy { None, Eager, @@ -33,17 +39,109 @@ impl ToString for PageLoadStrategy { } } +/// Represents the current WebDriver session and holds relevant session state. +/// Currently, only 1 webview is supported per session. +/// So only there is only 1 InputState. +pub struct WebDriverSession { + /// + id: Uuid, + + /// + /// The id of the current top-level browsing context + webview_id: Option, + + /// + /// The id of the current browsing context + browsing_context_id: Option, + + timeouts: TimeoutsConfiguration, + + page_loading_strategy: PageLoadStrategy, + + strict_file_interactability: bool, + + user_prompt_handler: UserPromptHandler, + + /// + input_state_table: RefCell>, + + /// + input_cancel_list: RefCell>, +} + +impl WebDriverSession { + pub fn new() -> WebDriverSession { + WebDriverSession { + id: Uuid::new_v4(), + webview_id: None, + browsing_context_id: None, + timeouts: TimeoutsConfiguration::default(), + page_loading_strategy: PageLoadStrategy::Normal, + strict_file_interactability: false, + user_prompt_handler: UserPromptHandler::new(), + input_state_table: RefCell::new(HashMap::new()), + input_cancel_list: RefCell::new(Vec::new()), + } + } + + pub fn set_webview_id(&mut self, webview_id: Option) { + self.webview_id = webview_id; + } + + pub fn set_browsing_context_id(&mut self, browsing_context_id: Option) { + self.browsing_context_id = browsing_context_id; + } + + pub fn current_webview_id(&self) -> Option { + self.webview_id + } + + pub fn current_browsing_context_id(&self) -> Option { + self.browsing_context_id + } + + pub fn session_timeouts(&self) -> &TimeoutsConfiguration { + &self.timeouts + } + + pub fn session_timeouts_mut(&mut self) -> &mut TimeoutsConfiguration { + &mut self.timeouts + } + + pub fn page_loading_strategy(&self) -> PageLoadStrategy { + self.page_loading_strategy.clone() + } + + pub fn strict_file_interactability(&self) -> bool { + self.strict_file_interactability + } + + pub fn user_prompt_handler(&self) -> &UserPromptHandler { + &self.user_prompt_handler + } + + pub fn input_state_table(&self) -> Ref<'_, HashMap> { + self.input_state_table.borrow() + } + + pub fn input_state_table_mut(&self) -> RefMut<'_, HashMap> { + self.input_state_table.borrow_mut() + } + + pub fn input_cancel_list_mut(&self) -> RefMut<'_, Vec<(String, ActionItem)>> { + self.input_cancel_list.borrow_mut() + } +} + impl Handler { /// pub(crate) fn create_session( &mut self, capabilities: &mut Map, servo_capabilities: &ServoCapabilities, - webview_id: WebViewId, - browsing_context_id: BrowsingContextId, ) -> WebDriverResult { // Step 2. Let session be a new session - let mut session = WebDriverSession::new(browsing_context_id, webview_id); + let mut session = WebDriverSession::new(); // Step 3. Let proxy be the result of getting property "proxy" from capabilities match capabilities.get("proxy") { diff --git a/components/webdriver_server/user_prompt.rs b/components/webdriver_server/user_prompt.rs index c3da8ffb128..d5b42a5ea17 100644 --- a/components/webdriver_server/user_prompt.rs +++ b/components/webdriver_server/user_prompt.rs @@ -178,7 +178,7 @@ impl Handler { pub(crate) fn handle_dismiss_alert(&self) -> WebDriverResult { // Step 1. If session's current top-level browsing context is no longer open, // return error with error code no such window. - self.verify_top_level_browsing_context_is_open(self.session()?.webview_id)?; + self.verify_top_level_browsing_context_is_open(self.webview_id()?)?; // Step 3. Dismiss the current user prompt. let (sender, receiver) = ipc::channel().unwrap(); @@ -203,7 +203,7 @@ impl Handler { pub(crate) fn handle_accept_alert(&self) -> WebDriverResult { // Step 1. If session's current top-level browsing context is no longer open, // return error with error code no such window. - self.verify_top_level_browsing_context_is_open(self.session()?.webview_id)?; + self.verify_top_level_browsing_context_is_open(self.webview_id()?)?; // Step 3. Accept the current user prompt. let (sender, receiver) = ipc::channel().unwrap(); @@ -228,7 +228,7 @@ impl Handler { pub(crate) fn handle_get_alert_text(&self) -> WebDriverResult { // Step 1. If session's current top-level browsing context is no longer open, // return error with error code no such window. - self.verify_top_level_browsing_context_is_open(self.session()?.webview_id)?; + self.verify_top_level_browsing_context_is_open(self.webview_id()?)?; let (sender, receiver) = ipc::channel().unwrap(); self.send_message_to_embedder(WebDriverCommandMsg::GetAlertText( @@ -261,7 +261,7 @@ impl Handler { &self, text: String, ) -> WebDriverResult { - let webview_id = self.session()?.webview_id; + let webview_id = self.webview_id()?; // Step 3. If session's current top-level browsing context is no longer open, // return error with error code no such window. @@ -321,7 +321,7 @@ impl Handler { Some(prompt_type) => { // Step 2 - 4. Get user prompt handler for the prompt type. let handler = - get_user_prompt_handler(&self.session()?.user_prompt_handler, prompt_type); + get_user_prompt_handler(self.session()?.user_prompt_handler(), prompt_type); // Step 5. Perform the substeps based on handler's handler let (sender, receiver) = ipc::channel().unwrap(); diff --git a/ports/servoshell/desktop/app.rs b/ports/servoshell/desktop/app.rs index 08c943b2829..4a4abe1d2fa 100644 --- a/ports/servoshell/desktop/app.rs +++ b/ports/servoshell/desktop/app.rs @@ -382,13 +382,9 @@ impl App { } }, WebDriverCommandMsg::GetAllWebViews(response_sender) => { - let webviews = running_state - .webviews() - .iter() - .map(|(id, _)| *id) - .collect::>(); + let webviews = running_state.webviews().iter().map(|(id, _)| *id).collect(); - if let Err(error) = response_sender.send(Ok(webviews)) { + if let Err(error) = response_sender.send(webviews) { warn!("Failed to send response of GetAllWebViews: {error}"); } }, diff --git a/tests/wpt/meta/webdriver/tests/classic/accept_alert/accept.py.ini b/tests/wpt/meta/webdriver/tests/classic/accept_alert/accept.py.ini deleted file mode 100644 index 44d5bd2b6ed..00000000000 --- a/tests/wpt/meta/webdriver/tests/classic/accept_alert/accept.py.ini +++ /dev/null @@ -1,3 +0,0 @@ -[accept.py] - [test_accept_in_popup_window] - expected: FAIL diff --git a/tests/wpt/meta/webdriver/tests/classic/back/back.py.ini b/tests/wpt/meta/webdriver/tests/classic/back/back.py.ini index 074e67aef1b..6f12d53580c 100644 --- a/tests/wpt/meta/webdriver/tests/classic/back/back.py.ini +++ b/tests/wpt/meta/webdriver/tests/classic/back/back.py.ini @@ -7,3 +7,6 @@ [test_seen_nodes[https coop\]] expected: FAIL + + [test_history_pushstate] + expected: FAIL diff --git a/tests/wpt/meta/webdriver/tests/classic/dismiss_alert/dismiss.py.ini b/tests/wpt/meta/webdriver/tests/classic/dismiss_alert/dismiss.py.ini deleted file mode 100644 index 3bb980bd3e1..00000000000 --- a/tests/wpt/meta/webdriver/tests/classic/dismiss_alert/dismiss.py.ini +++ /dev/null @@ -1,3 +0,0 @@ -[dismiss.py] - [test_dismiss_in_popup_window] - expected: FAIL diff --git a/tests/wpt/meta/webdriver/tests/classic/execute_async_script/arguments.py.ini b/tests/wpt/meta/webdriver/tests/classic/execute_async_script/arguments.py.ini index ec1cbb50358..aac667cc691 100644 --- a/tests/wpt/meta/webdriver/tests/classic/execute_async_script/arguments.py.ini +++ b/tests/wpt/meta/webdriver/tests/classic/execute_async_script/arguments.py.ini @@ -37,3 +37,6 @@ [test_element_reference[shadow-root\]] expected: FAIL + + [test_element_reference[window\]] + expected: FAIL diff --git a/tests/wpt/meta/webdriver/tests/classic/execute_script/arguments.py.ini b/tests/wpt/meta/webdriver/tests/classic/execute_script/arguments.py.ini index ec1cbb50358..aac667cc691 100644 --- a/tests/wpt/meta/webdriver/tests/classic/execute_script/arguments.py.ini +++ b/tests/wpt/meta/webdriver/tests/classic/execute_script/arguments.py.ini @@ -37,3 +37,6 @@ [test_element_reference[shadow-root\]] expected: FAIL + + [test_element_reference[window\]] + expected: FAIL diff --git a/tests/wpt/meta/webdriver/tests/classic/execute_script/window.py.ini b/tests/wpt/meta/webdriver/tests/classic/execute_script/window.py.ini deleted file mode 100644 index 8368e28165a..00000000000 --- a/tests/wpt/meta/webdriver/tests/classic/execute_script/window.py.ini +++ /dev/null @@ -1,3 +0,0 @@ -[window.py] - [test_same_id_after_cross_origin_navigation] - expected: FAIL diff --git a/tests/wpt/meta/webdriver/tests/classic/forward/forward.py.ini b/tests/wpt/meta/webdriver/tests/classic/forward/forward.py.ini index 13032a4e96e..ac3532fc471 100644 --- a/tests/wpt/meta/webdriver/tests/classic/forward/forward.py.ini +++ b/tests/wpt/meta/webdriver/tests/classic/forward/forward.py.ini @@ -13,3 +13,6 @@ [test_removed_iframe] expected: FAIL + + [test_history_pushstate] + expected: FAIL diff --git a/tests/wpt/meta/webdriver/tests/classic/get_alert_text/get.py.ini b/tests/wpt/meta/webdriver/tests/classic/get_alert_text/get.py.ini deleted file mode 100644 index eb473ba2e37..00000000000 --- a/tests/wpt/meta/webdriver/tests/classic/get_alert_text/get.py.ini +++ /dev/null @@ -1,6 +0,0 @@ -[get.py] - [test_get_confirm_text] - expected: FAIL - - [test_get_prompt_text] - expected: FAIL diff --git a/tests/wpt/meta/webdriver/tests/classic/perform_actions/navigation.py.ini b/tests/wpt/meta/webdriver/tests/classic/perform_actions/navigation.py.ini deleted file mode 100644 index 08dd1964663..00000000000 --- a/tests/wpt/meta/webdriver/tests/classic/perform_actions/navigation.py.ini +++ /dev/null @@ -1,4 +0,0 @@ -[navigation.py] - expected: TIMEOUT - [test_pointer] - expected: FAIL diff --git a/tests/wpt/meta/webdriver/tests/classic/send_alert_text/send.py.ini b/tests/wpt/meta/webdriver/tests/classic/send_alert_text/send.py.ini index b5f761ee43d..fa060153cc0 100644 --- a/tests/wpt/meta/webdriver/tests/classic/send_alert_text/send.py.ini +++ b/tests/wpt/meta/webdriver/tests/classic/send_alert_text/send.py.ini @@ -13,3 +13,6 @@ [test_send_alert_text[Fed\\terer\]] expected: FAIL + + [test_chained_alert_element_not_interactable[confirm\]] + expected: FAIL