/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
usecrate::android::AndroidHandler; usecrate::capabilities::{FirefoxOptions, ProfileType}; usecrate::logging; usecrate::prefs; use mozprofile::preferences::Pref; use mozprofile::profile::{PrefFile, Profile}; use mozrunner::runner::{FirefoxProcess, FirefoxRunner, Runner, RunnerProcess}; use std::env; use std::fs; use std::io::prelude::*; use std::path::{Path, PathBuf}; use std::time; use webdriver::error::{ErrorStatus, WebDriverError, WebDriverResult};
// Status of the browser process. pub(crate) enum BrowserStatus {
Exited(Option<i32>),
Running,
}
let (profile_path, prefs_backup) = iflet Some(refmut 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)
};
// This should be impossible, but it isn't enforced
Err(WebDriverError::new(
ErrorStatus::SessionNotCreated, "Port not known when using named profile",
))
}
pub(crate) fn check_status(&mutself) -> (u32, BrowserStatus) { let pid = self.process.pid(); let status = matchself.process.try_wait() {
Ok(Some(exit_status)) => BrowserStatus::Exited(exit_status.code()),
Ok(None) => BrowserStatus::Running,
Err(_) => BrowserStatus::Exited(None),
};
(pid, status)
}
}
impl Drop for LocalBrowser { fn drop(&mutself) { iflet Some(prefs_backup) = self.prefs_backup.take() {
debug!("Restore user preferences");
prefs_backup.restore();
}
iflet Some(profile_path) = &self.profile_path { // Save minidump files of potential crashes from the profile if requested. iflet Err(e) = copy_minidumps_files(profile_path) {
error!( "Failed to save crash minidumps to the specified location: {}",
e
);
}
}
}
}
fn read_marionette_port(profile_path: &Path) -> Option<u16> { let port_file = profile_path.join("MarionetteActivePort"); letmut port_str = String::with_capacity(6); letmut file = match fs::File::open(&port_file) {
Ok(file) => file,
Err(_) => {
trace!("Failed to open {}", &port_file.to_string_lossy()); return None;
}
}; iflet 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 { pub(crate) handler: AndroidHandler,
marionette_port: u16,
pid: u32,
prefs_backup: Option<PrefsBackup>,
}
pub(crate) fn check_status(&self) -> (u32, BrowserStatus) { let command = format!("kill -0 {} 2>/dev/null; echo $?", self.pid); let status = matchself
.handler
.process
.device
.execute_host_shell_command(&command)
{
Ok(output) if output.trim() != "0" => BrowserStatus::Exited(None),
Err(e) => {
warn!("Failed to check browser status via adb: {}", e);
BrowserStatus::Running
}
_ => BrowserStatus::Running,
};
(self.pid, status)
}
}
impl Drop for RemoteBrowser { fn drop(&mutself) { // Restore preferences which had custom values set. iflet Some(prefs_backup) = self.prefs_backup.take() {
prefs_backup.restore();
}
// Save minidump files of potential crashes from the profile if requested. iflet Err(e) = self.handler.copy_minidumps_files() {
error!( "Failed to save crash minidumps to the specified location: {}",
e
);
}
}
}
usesuper::*; usecrate::browser::read_marionette_port; usecrate::capabilities::{FirefoxOptions, ProfileType}; use base64::prelude::BASE64_STANDARD; use base64::Engine; 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 std::sync::{LazyLock, Mutex, MutexGuard}; use tempfile::TempDir;
// This is not a pretty test, mostly due to the nature of // mozprofile's and MarionetteHandler's APIs, but we have had // several regressions related to remote.log.level. #[test] fn test_remote_log_level() { letmut profile = Profile::new(None).unwrap();
set_prefs(2828, &mut profile, false, vec![], false).ok(); let user_prefs = profile.user_prefs().unwrap();
let pref = user_prefs.get("remote.log.level").unwrap(); let value = match pref.value {
PrefValue::String(ref s) => s,
_ => panic!(),
}; for (i, ch) in value.chars().enumerate() { if i == 0 {
assert!(ch.is_uppercase());
} else {
assert!(ch.is_lowercase());
}
}
}
#[test] fn test_prefs() { let marionette_settings = Default::default();
// Create some prefs in the profile let initial_prefs = profile.user_prefs().unwrap();
initial_prefs.insert("geckodriver.example", Pref::new("example"));
initial_prefs.write().unwrap();
// Ensure the actual prefs contain both the existing ones and the ones we added let pref = user_prefs.get("marionette.port").unwrap();
assert_eq!(pref.value, PrefValue::Int(2828));
let pref = user_prefs.get("geckodriver.example").unwrap();
assert_eq!(pref.value, PrefValue::String("example".into()));
// Ensure the backup prefs don't contain the new settings letmut backup_data = String::new();
File::open(&backup_path)
.expect("Backup prefs exist")
.read_to_string(&mut backup_data)
.unwrap();
assert_eq!(backup_data, initial_prefs_data);
for result_entry in std::fs::read_dir(minidumps_path).unwrap() { let entry = result_entry.unwrap();
let path: PathBuf = entry.path(); let filename_from_path = path.file_stem().unwrap().to_str().unwrap(); if filename == filename_from_path { let extension = path.extension().and_then(|ext| ext.to_str()).unwrap();
if extension == "dmp" {
dmp_file_present = true;
}
pub(crate) fn set(&self, value: Option<&str>) { fn set_env(key: &str, value: Option<&str>) { // SAFETY: Safe as long as no other threads try to modify the environment // This is enforced by Environment taking a mutex, so tests can't run // in parallel. unsafe { match value {
Some(value) => env::set_var(key, value),
None => env::remove_var(key),
}
}
}
set_env(MINIDUMP_KEY, value);
}
}
impl Drop for MinidumpEnvironment<'_> { fn drop(&mutself) { self.set(self.initial_environment.clone().as_deref());
}
}
#[test] fn test_copy_minidumps_with_no_minidump_files() { let env = MinidumpEnvironment::new();
let tmp_dir_profile = TempDir::new().unwrap(); let profile_path = tmp_dir_profile.path();
let minidumps_folder = create_minidump_folder(profile_path);
// Create a folder. let test_folder_binding = profile_path.join("test"); let test_folder = test_folder_binding.as_path();
fs::create_dir(test_folder).unwrap();
// Create a file with non minidumps extension.
create_file(&minidumps_folder, "test.txt");
let tmp_dir_minidumps = TempDir::new().unwrap(); let minidumps_path = tmp_dir_minidumps.path();
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.