mod android; mod browser; mod build; mod capabilities; mod command; mod logging; mod marionette; mod prefs;
#[cfg(test)] pubmod test;
usecrate::command::extension_routes; usecrate::logging::Level; usecrate::marionette::{MarionetteHandler, MarionetteSettings}; use anyhow::{bail, Result as ProgramResult}; use clap::ArgMatches; use mozdevice::AndroidStorageInput; use url::{Host, Url};
/// Get a socket address from the provided host and port /// /// # Arguments /// * `webdriver_host` - The hostname on which the server will listen /// * `webdriver_port` - The port on which the server will listen /// /// When the host and port resolve to multiple addresses, prefer /// IPv4 addresses vs IPv6. fn server_address(webdriver_host: &str, webdriver_port: u16) -> ProgramResult<SocketAddr> { letmut socket_addrs = match format!("{}:{}", webdriver_host, webdriver_port).to_socket_addrs()
{
Ok(addrs) => addrs.collect::<Vec<_>>(),
Err(e) => bail!("{}: {}:{}", e, webdriver_host, webdriver_port),
}; if socket_addrs.is_empty() {
bail!( "Unable to resolve host: {}:{}",
webdriver_host,
webdriver_port
)
} // Prefer ipv4 address
socket_addrs.sort_by(|a, b| { let a_val = i32::from(!a.ip().is_ipv4()); let b_val = i32::from(!b.ip().is_ipv4());
a_val.partial_cmp(&b_val).expect("Comparison failed")
});
Ok(socket_addrs.remove(0))
}
/// Parse a given string into a Host fn parse_hostname(webdriver_host: &str) -> Result<Host, url::ParseError> { let host_str = iflet Ok(ip_addr) = IpAddr::from_str(webdriver_host) { // In this case we have an IP address as the host if ip_addr.is_ipv6() { // Convert to quoted form
format!("[{}]", &webdriver_host)
} else {
webdriver_host.into()
}
} else {
webdriver_host.into()
};
Host::parse(&host_str)
}
/// Get a list of default hostnames to allow /// /// This only covers domain names, not IP addresses, since IP adresses /// are always accepted. fn get_default_allowed_hosts(ip: IpAddr) -> Vec<Host> { let localhost_is_loopback = ("localhost".to_string(), 80)
.to_socket_addrs()
.map(|addr_iter| {
addr_iter
.map(|addr| addr.ip())
.filter(|ip| ip.is_loopback())
})
.iter()
.len()
> 0; if ip.is_loopback() && localhost_is_loopback {
vec![Host::parse("localhost").unwrap()]
} else {
vec![]
}
}
let webdriver_host = args.get_one::<String>("webdriver_host").unwrap(); let webdriver_port = { let s = args.get_one::<String>("webdriver_port").unwrap(); match u16::from_str(s) {
Ok(n) => n,
Err(e) => bail!("invalid --port: {}: {}", e, s),
}
};
let android_storage = args
.get_one::<String>("android_storage")
.and_then(|arg| AndroidStorageInput::from_str(arg).ok())
.unwrap_or(AndroidStorageInput::Auto);
let binary = args.get_one::<String>("binary").map(PathBuf::from);
let profile_root = args.get_one::<String>("profile_root").map(PathBuf::from);
// Try to create a temporary directory on startup to check that the directory exists and is writable
{ let tmp_dir = iflet Some(ref tmp_root) = profile_root {
tempfile::tempdir_in(tmp_root)
} else {
tempfile::tempdir()
}; if tmp_dir.is_err() {
bail!("Unable to write to temporary directory; consider --profile-root with a writeable directory")
}
}
let marionette_host = args.get_one::<String>("marionette_host").unwrap(); let marionette_port = match args.get_one::<String>("marionette_port") {
Some(s) => match u16::from_str(s) {
Ok(n) => Some(n),
Err(e) => bail!("invalid --marionette-port: {}", e),
},
None => None,
};
// For Android the port on the device must be the same as the one on the // host. For now default to 9222, which is the default for --remote-debugging-port. let websocket_port = match args.get_one::<String>("websocket_port") {
Some(s) => match u16::from_str(s) {
Ok(n) => n,
Err(e) => bail!("invalid --websocket-port: {}", e),
},
None => 9222,
};
let host = match parse_hostname(webdriver_host) {
Ok(name) => name,
Err(e) => bail!("invalid --host {}: {}", webdriver_host, e),
};
let allow_hosts = get_allowed_hosts(host, args.get_many("allow_hosts"));
let allow_origins = get_allowed_origins(args.get_many("allow_origins"));
let address = server_address(webdriver_host, webdriver_port)?;
fn make_command() -> Command {
Command::new(format!("geckodriver {}", build::build_info()))
.disable_help_flag(true)
.disable_version_flag(true)
.about("WebDriver implementation for Firefox")
.arg(
Arg::new("allow_hosts")
.long("allow-hosts")
.num_args(1..)
.value_parser(clap::builder::ValueParser::new(Host::parse))
.value_name("ALLOW_HOSTS")
.help("List of hostnames to allow. By default the value of --host is allowed, and in addition if that's a well known local address, other variations on well known local addresses are allowed. If --allow-hosts is provided only exactly those hosts are allowed."),
)
.arg(
Arg::new("allow_origins")
.long("allow-origins")
.num_args(1..)
.value_parser(clap::builder::ValueParser::new(Url::parse))
.value_name("ALLOW_ORIGINS")
.help("List of request origins to allow. These must be formatted as scheme://host:port. By default any request with an origin header is rejected. If --allow-origins is provided then only exactly those origins are allowed."),
)
.arg(
Arg::new("android_storage")
.long("android-storage")
.value_parser(["auto", "app", "internal", "sdcard"])
.value_name("ANDROID_STORAGE")
.help("Selects storage location to be used for test data (deprecated)."),
)
.arg(
Arg::new("binary")
.short('b')
.long("binary")
.num_args(1)
.value_name("BINARY")
.help("Path to the Firefox binary"),
)
.arg(
Arg::new("connect_existing")
.long("connect-existing")
.requires("marionette_port")
.action(ArgAction::SetTrue)
.help("Connect to an existing Firefox instance"),
)
.arg(
Arg::new("enable_crash_reporter")
.long("enable-crash-reporter")
.action(ArgAction::SetTrue)
.help("Enable the Firefox crash reporter for diagnostic purposes (deprecated)"),
)
.arg(
Arg::new("help")
.short('h')
.long("help")
.action(ArgAction::SetTrue)
.help("Prints this message"),
)
.arg(
Arg::new("webdriver_host")
.long("host")
.num_args(1)
.value_name("HOST")
.default_value("127.0.0.1")
.help("Host IP to use for WebDriver server"),
)
.arg(
Arg::new("jsdebugger")
.long("jsdebugger")
.action(ArgAction::SetTrue)
.help("Attach browser toolbox debugger for Firefox"),
)
.arg(
Arg::new("log_level")
.long("log")
.num_args(1)
.value_name("LEVEL")
.value_parser(["fatal", "error", "warn", "info", "config", "debug", "trace"])
.help("Set Gecko log level"),
)
.arg(
Arg::new("log_no_truncate")
.long("log-no-truncate")
.action(ArgAction::SetTrue)
.help("Disable truncation of long log lines"),
)
.arg(
Arg::new("marionette_host")
.long("marionette-host")
.num_args(1)
.value_name("HOST")
.default_value("127.0.0.1")
.help("Host to use to connect to Gecko"),
)
.arg(
Arg::new("marionette_port")
.long("marionette-port")
.num_args(1)
.value_name("PORT")
.help("Port to use to connect to Gecko [default: system-allocated port]"),
)
.arg(
Arg::new("webdriver_port")
.short('p')
.long("port")
.num_args(1)
.value_name("PORT")
.default_value("4444")
.help("Port to use for WebDriver server"),
)
.arg(
Arg::new("profile_root")
.long("profile-root")
.num_args(1)
.value_name("PROFILE_ROOT")
.help("Directory in which to create profiles. Defaults to the system temporary directory."),
)
.arg(
Arg::new("verbosity")
.conflicts_with("log_level")
.short('v')
.action(ArgAction::Count)
.help("Log level verbosity (-v for debug and -vv for trace level)"),
)
.arg(
Arg::new("version")
.short('V')
.long("version")
.action(ArgAction::SetTrue)
.help("Prints version and copying information"),
)
.arg(
Arg::new("websocket_port")
.long("websocket-port")
.num_args(1)
.value_name("PORT")
.conflicts_with("connect_existing")
.help("Port to use to connect to WebDriver BiDi [default: 9222]"),
)
}
fn print_version() {
println!("geckodriver {}", build::build_info());
println!();
println!("The source code of this program is available from");
println!("testing/geckodriver in https://hg.mozilla.org/mozilla-central.");
println!();
println!("This program is subject to the terms of the Mozilla Public License 2.0.");
println!("You can obtain a copy of the license at https://mozilla.org/MPL/2.0/.");
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet am 2026-06-18)
¤
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.