1 extern crate chrono;
2 #[macro_use]
3 extern crate clap;
4 #[macro_use]
5 extern crate lazy_static;
6 extern crate hyper;
7 extern crate mozprofile;
8 extern crate mozrunner;
9 extern crate mozversion;
10 extern crate regex;
11 extern crate rustc_serialize;
12 extern crate uuid;
13 extern crate zip;
14 extern crate webdriver;
15
16 #[macro_use]
17 extern crate log;
18
19 use std::fmt;
20 use std::io::Write;
21 use std::net::{IpAddr, SocketAddr};
22 use std::path::PathBuf;
23 use std::str::FromStr;
24
25 use clap::{App, Arg};
26
27 macro_rules! try_opt {
28 ($expr:expr, $err_type:expr, $err_msg:expr) => ({
29 match $expr {
30 Some(x) => x,
31 None => return Err(WebDriverError::new($err_type, $err_msg))
32 }
33 })
34 }
35
36 mod logging;
37 mod prefs;
38 mod marionette;
39 mod capabilities;
40
41 use marionette::{MarionetteHandler, MarionetteSettings, extension_routes};
42
43 include!(concat!(env!("OUT_DIR"), "/build-info.rs"));
44
45 type ProgramResult = std::result::Result<(), (ExitCode, String)>;
46
47 enum ExitCode {
48 Ok = 0,
49 Usage = 64,
50 Unavailable = 69,
51 }
52
53 struct BuildInfo;
54 impl BuildInfo {
version() -> &'static str55 pub fn version() -> &'static str {
56 crate_version!()
57 }
58
hash() -> Option<&'static str>59 pub fn hash() -> Option<&'static str> {
60 COMMIT_HASH
61 }
62
date() -> Option<&'static str>63 pub fn date() -> Option<&'static str> {
64 COMMIT_DATE
65 }
66 }
67
68 impl fmt::Display for BuildInfo {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result69 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
70 write!(f, "{}", BuildInfo::version())?;
71 match (BuildInfo::hash(), BuildInfo::date()) {
72 (Some(hash), Some(date)) => write!(f, " ({} {})", hash, date)?,
73 (Some(hash), None) => write!(f, " ({})", hash)?,
74 _ => {}
75 }
76 Ok(())
77 }
78 }
79
print_version()80 fn print_version() {
81 println!("geckodriver {}", BuildInfo);
82 println!("");
83 println!("The source code of this program is available from");
84 println!("testing/geckodriver in https://hg.mozilla.org/mozilla-central.");
85 println!("");
86 println!("This program is subject to the terms of the Mozilla Public License 2.0.");
87 println!("You can obtain a copy of the license at https://mozilla.org/MPL/2.0/.");
88 }
89
app<'a, 'b>() -> App<'a, 'b>90 fn app<'a, 'b>() -> App<'a, 'b> {
91 App::new(format!("geckodriver {}", crate_version!()))
92 .about("WebDriver implementation for Firefox.")
93 .arg(Arg::with_name("webdriver_host")
94 .long("host")
95 .value_name("HOST")
96 .help("Host ip to use for WebDriver server (default: 127.0.0.1)")
97 .takes_value(true))
98 .arg(Arg::with_name("webdriver_port")
99 .short("p")
100 .long("port")
101 .value_name("PORT")
102 .help("Port to use for WebDriver server (default: 4444)")
103 .takes_value(true)
104 .alias("webdriver-port"))
105 .arg(Arg::with_name("binary")
106 .short("b")
107 .long("binary")
108 .value_name("BINARY")
109 .help("Path to the Firefox binary")
110 .takes_value(true))
111 .arg(Arg::with_name("marionette_port")
112 .long("marionette-port")
113 .value_name("PORT")
114 .help("Port to use to connect to Gecko (default: random free port)")
115 .takes_value(true))
116 .arg(Arg::with_name("connect_existing")
117 .long("connect-existing")
118 .requires("marionette_port")
119 .help("Connect to an existing Firefox instance"))
120 .arg(Arg::with_name("jsdebugger")
121 .long("jsdebugger")
122 .takes_value(false)
123 .help("Attach browser toolbox debugger for Firefox"))
124 .arg(Arg::with_name("verbosity")
125 .short("v")
126 .multiple(true)
127 .conflicts_with("log_level")
128 .help("Log level verbosity (-v for debug and -vv for trace level)"))
129 .arg(Arg::with_name("log_level")
130 .long("log")
131 .takes_value(true)
132 .value_name("LEVEL")
133 .possible_values(&["fatal", "error", "warn", "info", "config", "debug", "trace"])
134 .help("Set Gecko log level"))
135 .arg(Arg::with_name("version")
136 .short("V")
137 .long("version")
138 .help("Prints version and copying information"))
139 }
140
run() -> ProgramResult141 fn run() -> ProgramResult {
142 let matches = app().get_matches();
143
144 if matches.is_present("version") {
145 print_version();
146 return Ok(());
147 }
148
149 let host = matches.value_of("webdriver_host").unwrap_or("127.0.0.1");
150 let port = match u16::from_str(
151 matches
152 .value_of("webdriver_port")
153 .or(matches.value_of("webdriver_port_alias"))
154 .unwrap_or("4444"),
155 ) {
156 Ok(x) => x,
157 Err(_) => return Err((ExitCode::Usage, "invalid WebDriver port".into())),
158 };
159 let addr = match IpAddr::from_str(host) {
160 Ok(addr) => SocketAddr::new(addr, port),
161 Err(_) => return Err((ExitCode::Usage, "invalid host address".into())),
162 };
163
164 let binary = matches.value_of("binary").map(|x| PathBuf::from(x));
165
166 let marionette_port = match matches.value_of("marionette_port") {
167 Some(x) => {
168 match u16::from_str(x) {
169 Ok(x) => Some(x),
170 Err(_) => return Err((ExitCode::Usage, "invalid Marionette port".into())),
171 }
172 }
173 None => None,
174 };
175
176 let log_level = if matches.is_present("log_level") {
177 logging::Level::from_str(matches.value_of("log_level").unwrap()).ok()
178 } else {
179 match matches.occurrences_of("verbosity") {
180 0 => Some(logging::Level::Info),
181 1 => Some(logging::Level::Debug),
182 _ => Some(logging::Level::Trace),
183 }
184 };
185 if let Some(ref level) = log_level {
186 logging::init_with_level(level.clone()).unwrap();
187 } else {
188 logging::init().unwrap();
189 }
190
191 info!("geckodriver {}", BuildInfo);
192
193 let settings = MarionetteSettings {
194 port: marionette_port,
195 binary,
196 connect_existing: matches.is_present("connect_existing"),
197 jsdebugger: matches.is_present("jsdebugger"),
198 };
199 let handler = MarionetteHandler::new(settings);
200 let listening = webdriver::server::start(addr, handler, &extension_routes()[..])
201 .map_err(|err| (ExitCode::Unavailable, err.to_string()))?;
202 info!("Listening on {}", listening.socket);
203
204 Ok(())
205 }
206
main()207 fn main() {
208 let exit_code = match run() {
209 Ok(_) => ExitCode::Ok,
210 Err((exit_code, reason)) => {
211 error!("{}", reason);
212 exit_code
213 }
214 };
215
216 std::io::stdout().flush().unwrap();
217 std::process::exit(exit_code as i32);
218 }
219