diff options
Diffstat (limited to 'src/browser.rs')
-rw-r--r-- | src/browser.rs | 186 |
1 files changed, 152 insertions, 34 deletions
diff --git a/src/browser.rs b/src/browser.rs index b777a0a..306bfe1 100644 --- a/src/browser.rs +++ b/src/browser.rs @@ -3,16 +3,16 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use crate::android::AndroidHandler; -use crate::capabilities::FirefoxOptions; +use crate::capabilities::{FirefoxOptions, ProfileType}; use crate::logging; use crate::prefs; use mozprofile::preferences::Pref; use mozprofile::profile::{PrefFile, Profile}; use mozrunner::runner::{FirefoxProcess, FirefoxRunner, Runner, RunnerProcess}; use std::fs; -use std::path::PathBuf; +use std::io::Read; +use std::path::{Path, PathBuf}; use std::time; - use webdriver::error::{ErrorStatus, WebDriverError, WebDriverResult}; /// A running Gecko instance. @@ -22,7 +22,7 @@ pub(crate) enum Browser { Remote(RemoteBrowser), /// An existing browser instance not controlled by GeckoDriver - Existing, + Existing(u16), } impl Browser { @@ -30,7 +30,29 @@ impl Browser { match self { Browser::Local(x) => x.close(wait_for_shutdown), Browser::Remote(x) => x.close(), - Browser::Existing => Ok(()), + Browser::Existing(_) => Ok(()), + } + } + + pub(crate) fn marionette_port(&mut self) -> WebDriverResult<Option<u16>> { + match self { + Browser::Local(x) => x.marionette_port(), + Browser::Remote(x) => x.marionette_port(), + Browser::Existing(x) => Ok(Some(*x)), + } + } + + pub(crate) fn update_marionette_port(&mut self, port: u16) { + match self { + Browser::Local(x) => x.update_marionette_port(port), + Browser::Remote(x) => x.update_marionette_port(port), + Browser::Existing(x) => { + if port != *x { + error!( + "Cannot re-assign Marionette port when connected to an existing browser" + ); + } + } } } } @@ -38,8 +60,10 @@ impl Browser { #[derive(Debug)] /// A local Firefox process, running on this (host) device. pub(crate) struct LocalBrowser { - process: FirefoxProcess, + marionette_port: u16, prefs_backup: Option<PrefsBackup>, + process: FirefoxProcess, + profile_path: Option<PathBuf>, } impl LocalBrowser { @@ -58,26 +82,34 @@ impl LocalBrowser { ) })?; - let is_custom_profile = options.profile.is_some(); + let is_custom_profile = matches!(options.profile, ProfileType::Path(_)); let mut profile = match options.profile { - Some(x) => x, - None => Profile::new()?, + ProfileType::Named => None, + ProfileType::Path(x) => Some(x), + ProfileType::Temporary => Some(Profile::new()?), }; - let prefs_backup = set_prefs( - marionette_port, - &mut profile, - is_custom_profile, - options.prefs, - jsdebugger, - ) - .map_err(|e| { - WebDriverError::new( - ErrorStatus::SessionNotCreated, - format!("Failed to set preferences: {}", e), + let (profile_path, prefs_backup) = if let Some(ref mut profile) = profile { + let profile_path = profile.path.clone(); + let prefs_backup = set_prefs( + marionette_port, + profile, + is_custom_profile, + options.prefs, + jsdebugger, ) - })?; + .map_err(|e| { + WebDriverError::new( + ErrorStatus::SessionNotCreated, + format!("Failed to set preferences: {}", e), + ) + })?; + (Some(profile_path), prefs_backup) + } else { + warn!("Unable to set geckodriver prefs when using a named profile"); + (None, None) + }; let mut runner = FirefoxRunner::new(&binary, profile); @@ -109,8 +141,10 @@ impl LocalBrowser { }; Ok(LocalBrowser { - process, + marionette_port, prefs_backup, + process, + profile_path, }) } @@ -132,6 +166,26 @@ impl LocalBrowser { Ok(()) } + fn marionette_port(&mut self) -> WebDriverResult<Option<u16>> { + if self.marionette_port != 0 { + return Ok(Some(self.marionette_port)); + } + + if let Some(profile_path) = self.profile_path.as_ref() { + return Ok(read_marionette_port(profile_path)); + } + + // This should be impossible, but it isn't enforced + Err(WebDriverError::new( + ErrorStatus::SessionNotCreated, + "Port not known when using named profile", + )) + } + + fn update_marionette_port(&mut self, port: u16) { + self.marionette_port = port; + } + pub(crate) fn check_status(&mut self) -> Option<String> { match self.process.try_wait() { Ok(Some(status)) => Some( @@ -146,10 +200,33 @@ impl LocalBrowser { } } +fn read_marionette_port(profile_path: &Path) -> Option<u16> { + let port_file = profile_path.join("MarionetteActivePort"); + let mut port_str = String::with_capacity(6); + let mut file = match fs::File::open(&port_file) { + Ok(file) => file, + Err(_) => { + trace!("Failed to open {}", &port_file.to_string_lossy()); + return None; + } + }; + if let Err(e) = file.read_to_string(&mut port_str) { + trace!("Failed to read {}: {}", &port_file.to_string_lossy(), e); + return None; + }; + println!("Read port: {}", port_str); + let port = port_str.parse::<u16>().ok(); + if port.is_none() { + warn!("Failed fo convert {} to u16", &port_str); + } + port +} + #[derive(Debug)] /// A remote instance, running on a (target) Android device. pub(crate) struct RemoteBrowser { handler: AndroidHandler, + marionette_port: u16, } impl RemoteBrowser { @@ -163,9 +240,16 @@ impl RemoteBrowser { let handler = AndroidHandler::new(&android_options, marionette_port, websocket_port)?; // Profile management. - let is_custom_profile = options.profile.is_some(); - - let mut profile = options.profile.unwrap_or(Profile::new()?); + let (mut profile, is_custom_profile) = match options.profile { + ProfileType::Named => { + return Err(WebDriverError::new( + ErrorStatus::SessionNotCreated, + "Cannot use a named profile on Android", + )); + } + ProfileType::Path(x) => (x, true), + ProfileType::Temporary => (Profile::new()?, false), + }; set_prefs( handler.marionette_target_port, @@ -185,13 +269,24 @@ impl RemoteBrowser { handler.launch()?; - Ok(RemoteBrowser { handler }) + Ok(RemoteBrowser { + handler, + marionette_port, + }) } fn close(self) -> WebDriverResult<()> { self.handler.force_stop()?; Ok(()) } + + fn marionette_port(&mut self) -> WebDriverResult<Option<u16>> { + Ok(Some(self.marionette_port)) + } + + fn update_marionette_port(&mut self, port: u16) { + self.marionette_port = port; + } } fn set_prefs( @@ -209,14 +304,14 @@ fn set_prefs( })?; let backup_prefs = if custom_profile && prefs.path.exists() { - Some(PrefsBackup::new(&prefs)?) + Some(PrefsBackup::new(prefs)?) } else { None }; - for &(ref name, ref value) in prefs::DEFAULT.iter() { + for &(name, ref value) in prefs::DEFAULT.iter() { if !custom_profile || !prefs.contains_key(name) { - prefs.insert((*name).to_string(), (*value).clone()); + prefs.insert(name.to_string(), (*value).clone()); } } @@ -232,9 +327,6 @@ fn set_prefs( prefs.insert("marionette.port", Pref::new(port)); prefs.insert("remote.log.level", logging::max_level().into()); - // Deprecated since Firefox 91. - prefs.insert("marionette.log.level", logging::max_level().into()); - prefs.write().map_err(|e| { WebDriverError::new( ErrorStatus::UnknownError, @@ -284,12 +376,15 @@ impl PrefsBackup { #[cfg(test)] mod tests { use super::set_prefs; - use crate::capabilities::FirefoxOptions; + use crate::browser::read_marionette_port; + use crate::capabilities::{FirefoxOptions, ProfileType}; use mozprofile::preferences::{Pref, PrefValue}; use mozprofile::profile::Profile; use serde_json::{Map, Value}; use std::fs::File; use std::io::{Read, Write}; + use std::path::Path; + use tempfile::tempdir; fn example_profile() -> Value { let mut profile_data = Vec::with_capacity(1024); @@ -342,7 +437,10 @@ mod tests { let opts = FirefoxOptions::from_capabilities(None, &marionette_settings, &mut caps) .expect("Valid profile and prefs"); - let mut profile = opts.profile.expect("valid firefox profile"); + let mut profile = match opts.profile { + ProfileType::Path(profile) => profile, + _ => panic!("Expected ProfileType::Path"), + }; set_prefs(2828, &mut profile, true, opts.prefs, false).expect("set preferences"); @@ -420,4 +518,24 @@ mod tests { .unwrap(); assert_eq!(final_prefs_data, initial_prefs_data); } + + #[test] + fn test_local_read_marionette_port() { + fn create_port_file(profile_path: &Path, data: &[u8]) { + let port_path = profile_path.join("MarionetteActivePort"); + let mut file = File::create(&port_path).unwrap(); + file.write_all(data).unwrap(); + } + + let profile_dir = tempdir().unwrap(); + let profile_path = profile_dir.path(); + assert_eq!(read_marionette_port(profile_path), None); + assert_eq!(read_marionette_port(profile_path), None); + create_port_file(profile_path, b""); + assert_eq!(read_marionette_port(profile_path), None); + create_port_file(profile_path, b"1234"); + assert_eq!(read_marionette_port(profile_path), Some(1234)); + create_port_file(profile_path, b"1234abc"); + assert_eq!(read_marionette_port(profile_path), None); + } } |