1 //! Argument string parsing and matching functions for Firefox.
2 //!
3 //! Which arguments Firefox accepts and in what style depends on the platform.
4 //! On Windows only, arguments can be prefixed with `/` (slash), such as
5 //! `/foreground`. Elsewhere, including Windows, arguments may be prefixed
6 //! with both single (`-foreground`) and double (`--foreground`) dashes.
7 //!
8 //! An argument's name is determined by a space or an assignment operator (`=`)
9 //! so that for the string `-foo=bar`, `foo` is considered the argument's
10 //! basename.
11
12 use std::ffi::{OsStr, OsString};
13
14 use crate::runner::platform;
15
parse_arg_name<T>(arg: T) -> Option<String> where T: AsRef<OsStr>,16 fn parse_arg_name<T>(arg: T) -> Option<String>
17 where
18 T: AsRef<OsStr>,
19 {
20 let arg_os_str: &OsStr = arg.as_ref();
21 let arg_str = arg_os_str.to_string_lossy();
22
23 let mut start = 0;
24 let mut end = 0;
25
26 for (i, c) in arg_str.chars().enumerate() {
27 if i == 0 {
28 if !platform::arg_prefix_char(c) {
29 break;
30 }
31 } else if i == 1 {
32 if name_end_char(c) {
33 break;
34 } else if c != '-' {
35 start = i;
36 end = start + 1;
37 } else {
38 start = i + 1;
39 end = start;
40 }
41 } else {
42 end += 1;
43 if name_end_char(c) {
44 end -= 1;
45 break;
46 }
47 }
48 }
49
50 if start > 0 && end > start {
51 Some(arg_str[start..end].into())
52 } else {
53 None
54 }
55 }
56
name_end_char(c: char) -> bool57 fn name_end_char(c: char) -> bool {
58 c == ' ' || c == '='
59 }
60
61 /// Represents a Firefox command-line argument.
62 #[derive(Debug, PartialEq)]
63 pub enum Arg {
64 /// `-foreground` ensures application window gets focus, which is not the
65 /// default on macOS.
66 Foreground,
67
68 /// `-no-remote` prevents remote commands to this instance of Firefox, and
69 /// ensure we always start a new instance.
70 NoRemote,
71
72 /// `-P NAME` starts Firefox with a profile with a given name.
73 NamedProfile,
74
75 /// `-profile PATH` starts Firefox with the profile at the specified path.
76 Profile,
77
78 /// `-ProfileManager` starts Firefox with the profile chooser dialogue.
79 ProfileManager,
80
81 /// All other arguments.
82 Other(String),
83
84 /// Not an argument.
85 None,
86 }
87
88 impl<'a> From<&'a OsString> for Arg {
from(arg_str: &OsString) -> Arg89 fn from(arg_str: &OsString) -> Arg {
90 if let Some(basename) = parse_arg_name(arg_str) {
91 match &*basename {
92 "profile" => Arg::Profile,
93 "P" => Arg::NamedProfile,
94 "ProfileManager" => Arg::ProfileManager,
95 "foreground" => Arg::Foreground,
96 "no-remote" => Arg::NoRemote,
97 _ => Arg::Other(basename),
98 }
99 } else {
100 Arg::None
101 }
102 }
103 }
104
105 #[cfg(test)]
106 mod tests {
107 use super::{parse_arg_name, Arg};
108 use std::ffi::OsString;
109
parse(arg: &str, name: Option<&str>)110 fn parse(arg: &str, name: Option<&str>) {
111 let result = parse_arg_name(arg);
112 assert_eq!(result, name.map(|x| x.to_string()));
113 }
114
115 #[test]
test_parse_arg_name()116 fn test_parse_arg_name() {
117 parse("-p", Some("p"));
118 parse("--p", Some("p"));
119 parse("--profile foo", Some("profile"));
120 parse("--profile", Some("profile"));
121 parse("--", None);
122 parse("", None);
123 parse("-=", None);
124 parse("--=", None);
125 parse("-- foo", None);
126 parse("foo", None);
127 parse("/ foo", None);
128 parse("/- foo", None);
129 parse("/=foo", None);
130 parse("foo", None);
131 parse("-profile", Some("profile"));
132 parse("-profile=foo", Some("profile"));
133 parse("-profile = foo", Some("profile"));
134 parse("-profile abc", Some("profile"));
135 parse("-profile /foo", Some("profile"));
136 }
137
138 #[cfg(target_os = "windows")]
139 #[test]
test_parse_arg_name_windows()140 fn test_parse_arg_name_windows() {
141 parse("/profile", Some("profile"));
142 }
143
144 #[cfg(not(target_os = "windows"))]
145 #[test]
test_parse_arg_name_non_windows()146 fn test_parse_arg_name_non_windows() {
147 parse("/profile", None);
148 }
149
150 #[test]
test_arg_from_osstring()151 fn test_arg_from_osstring() {
152 assert_eq!(Arg::from(&OsString::from("-- profile")), Arg::None);
153 assert_eq!(Arg::from(&OsString::from("profile")), Arg::None);
154 assert_eq!(Arg::from(&OsString::from("profile -P")), Arg::None);
155 assert_eq!(
156 Arg::from(&OsString::from("-profiled")),
157 Arg::Other("profiled".into())
158 );
159 assert_eq!(
160 Arg::from(&OsString::from("-PROFILEMANAGER")),
161 Arg::Other("PROFILEMANAGER".into())
162 );
163
164 assert_eq!(Arg::from(&OsString::from("--profile")), Arg::Profile);
165 assert_eq!(Arg::from(&OsString::from("-profile foo")), Arg::Profile);
166
167 assert_eq!(
168 Arg::from(&OsString::from("--ProfileManager")),
169 Arg::ProfileManager
170 );
171 assert_eq!(
172 Arg::from(&OsString::from("-ProfileManager")),
173 Arg::ProfileManager
174 );
175
176 // TODO: -Ptest is valid
177 //assert_eq!(Arg::from(&OsString::from("-Ptest")), Arg::NamedProfile);
178 assert_eq!(Arg::from(&OsString::from("-P")), Arg::NamedProfile);
179 assert_eq!(Arg::from(&OsString::from("-P test")), Arg::NamedProfile);
180
181 assert_eq!(Arg::from(&OsString::from("--foreground")), Arg::Foreground);
182 assert_eq!(Arg::from(&OsString::from("-foreground")), Arg::Foreground);
183
184 assert_eq!(Arg::from(&OsString::from("--no-remote")), Arg::NoRemote);
185 assert_eq!(Arg::from(&OsString::from("-no-remote")), Arg::NoRemote);
186 }
187 }
188