command.rs (11374B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 use crate::logging; 6 use base64::prelude::BASE64_STANDARD; 7 use base64::Engine; 8 use hyper::Method; 9 use serde::de::{self, Deserialize, Deserializer}; 10 use serde_json::Value; 11 use webdriver::command::{WebDriverCommand, WebDriverExtensionCommand}; 12 use webdriver::error::WebDriverResult; 13 use webdriver::httpapi::WebDriverExtensionRoute; 14 use webdriver::Parameters; 15 16 pub fn extension_routes() -> Vec<(Method, &'static str, GeckoExtensionRoute)> { 17 vec![ 18 ( 19 Method::GET, 20 "/session/{sessionId}/moz/context", 21 GeckoExtensionRoute::GetContext, 22 ), 23 ( 24 Method::POST, 25 "/session/{sessionId}/moz/context", 26 GeckoExtensionRoute::SetContext, 27 ), 28 ( 29 Method::POST, 30 "/session/{sessionId}/moz/addon/install", 31 GeckoExtensionRoute::InstallAddon, 32 ), 33 ( 34 Method::POST, 35 "/session/{sessionId}/moz/addon/uninstall", 36 GeckoExtensionRoute::UninstallAddon, 37 ), 38 ( 39 Method::GET, 40 "/session/{sessionId}/moz/screenshot/full", 41 GeckoExtensionRoute::TakeFullScreenshot, 42 ), 43 ] 44 } 45 46 #[derive(Clone, PartialEq, Eq)] 47 pub enum GeckoExtensionRoute { 48 GetContext, 49 SetContext, 50 InstallAddon, 51 UninstallAddon, 52 TakeFullScreenshot, 53 } 54 55 impl WebDriverExtensionRoute for GeckoExtensionRoute { 56 type Command = GeckoExtensionCommand; 57 58 fn command( 59 &self, 60 _params: &Parameters, 61 body_data: &Value, 62 ) -> WebDriverResult<WebDriverCommand<GeckoExtensionCommand>> { 63 use self::GeckoExtensionRoute::*; 64 65 let command = match *self { 66 GetContext => GeckoExtensionCommand::GetContext, 67 SetContext => { 68 GeckoExtensionCommand::SetContext(serde_json::from_value(body_data.clone())?) 69 } 70 InstallAddon => { 71 GeckoExtensionCommand::InstallAddon(serde_json::from_value(body_data.clone())?) 72 } 73 UninstallAddon => { 74 GeckoExtensionCommand::UninstallAddon(serde_json::from_value(body_data.clone())?) 75 } 76 TakeFullScreenshot => GeckoExtensionCommand::TakeFullScreenshot, 77 }; 78 79 Ok(WebDriverCommand::Extension(command)) 80 } 81 } 82 83 #[derive(Clone)] 84 pub enum GeckoExtensionCommand { 85 GetContext, 86 SetContext(GeckoContextParameters), 87 InstallAddon(AddonInstallParameters), 88 UninstallAddon(AddonUninstallParameters), 89 TakeFullScreenshot, 90 } 91 92 impl WebDriverExtensionCommand for GeckoExtensionCommand { 93 fn parameters_json(&self) -> Option<Value> { 94 use self::GeckoExtensionCommand::*; 95 match self { 96 GetContext => None, 97 InstallAddon(x) => Some(serde_json::to_value(x).unwrap()), 98 SetContext(x) => Some(serde_json::to_value(x).unwrap()), 99 UninstallAddon(x) => Some(serde_json::to_value(x).unwrap()), 100 TakeFullScreenshot => None, 101 } 102 } 103 } 104 105 #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] 106 #[serde(deny_unknown_fields)] 107 pub struct AddonBase64 { 108 #[serde(deserialize_with = "deserialize_base64")] 109 pub addon: Vec<u8>, 110 pub temporary: Option<bool>, 111 #[serde(rename = "allowPrivateBrowsing")] 112 pub allow_private_browsing: Option<bool>, 113 } 114 115 #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] 116 #[serde(deny_unknown_fields)] 117 pub struct AddonPath { 118 pub path: String, 119 pub temporary: Option<bool>, 120 #[serde(rename = "allowPrivateBrowsing")] 121 pub allow_private_browsing: Option<bool>, 122 } 123 124 #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] 125 #[serde(untagged)] 126 pub enum AddonInstallParameters { 127 AddonBase64(AddonBase64), 128 AddonPath(AddonPath), 129 } 130 131 fn deserialize_base64<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error> 132 where 133 D: Deserializer<'de>, 134 { 135 let encoded_str = String::deserialize(deserializer)?; 136 let decoded_str = BASE64_STANDARD 137 .decode(encoded_str) 138 .map_err(de::Error::custom)?; 139 140 Ok(decoded_str.clone()) 141 } 142 143 #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] 144 pub struct AddonUninstallParameters { 145 pub id: String, 146 } 147 148 #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] 149 #[serde(rename_all = "lowercase")] 150 pub enum GeckoContext { 151 Content, 152 Chrome, 153 } 154 155 #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] 156 pub struct GeckoContextParameters { 157 pub context: GeckoContext, 158 } 159 160 #[derive(Default, Debug, PartialEq, Eq)] 161 pub struct LogOptions { 162 pub level: Option<logging::Level>, 163 } 164 165 #[cfg(test)] 166 mod tests { 167 use serde_json::json; 168 169 use super::*; 170 use crate::test::assert_de; 171 172 #[test] 173 fn test_json_addon_install_parameters_invalid() { 174 assert!(serde_json::from_str::<AddonInstallParameters>("").is_err()); 175 assert!(serde_json::from_value::<AddonInstallParameters>(json!(null)).is_err()); 176 assert!(serde_json::from_value::<AddonInstallParameters>(json!({})).is_err()); 177 } 178 179 #[test] 180 fn test_json_addon_install_parameters_with_path_and_temporary() { 181 let params = AddonPath { 182 path: "/path/to.xpi".to_string(), 183 temporary: Some(true), 184 allow_private_browsing: None, 185 }; 186 assert_de( 187 &AddonInstallParameters::AddonPath(params), 188 json!({"path": "/path/to.xpi", "temporary": true}), 189 ); 190 } 191 192 #[test] 193 fn test_json_addon_install_parameters_with_path() { 194 let params = AddonPath { 195 path: "/path/to.xpi".to_string(), 196 temporary: None, 197 allow_private_browsing: None, 198 }; 199 assert_de( 200 &AddonInstallParameters::AddonPath(params), 201 json!({"path": "/path/to.xpi"}), 202 ); 203 } 204 205 #[test] 206 fn test_json_addon_install_parameters_with_path_and_allow_private_browsing() { 207 let params = AddonPath { 208 path: "/path/to.xpi".to_string(), 209 temporary: None, 210 allow_private_browsing: Some(true), 211 }; 212 assert_de( 213 &AddonInstallParameters::AddonPath(params), 214 json!({"path": "/path/to.xpi", "allowPrivateBrowsing": true}), 215 ); 216 } 217 218 #[test] 219 fn test_json_addon_install_parameters_with_path_invalid_type() { 220 let json = json!({"path": true, "temporary": true}); 221 assert!(serde_json::from_value::<AddonInstallParameters>(json).is_err()); 222 } 223 224 #[test] 225 fn test_json_addon_install_parameters_with_path_and_allow_private_browsing_invalid_type() { 226 let json = json!({"path": "/path/to.xpi", "allowPrivateBrowsing": "foo"}); 227 assert!(serde_json::from_value::<AddonInstallParameters>(json).is_err()); 228 } 229 230 #[test] 231 fn test_json_addon_install_parameters_with_path_and_temporary_invalid_type() { 232 let json = json!({"path": "/path/to.xpi", "temporary": "foo"}); 233 assert!(serde_json::from_value::<AddonInstallParameters>(json).is_err()); 234 } 235 236 #[test] 237 fn test_json_addon_install_parameters_with_addon() { 238 let json = json!({"addon": "aGVsbG8=", "temporary": true}); 239 let data = serde_json::from_value::<AddonInstallParameters>(json).unwrap(); 240 241 if let AddonInstallParameters::AddonBase64(data) = data { 242 assert_eq!(data.temporary, Some(true)); 243 assert_eq!(String::from_utf8(data.addon).unwrap(), "hello"); 244 } 245 } 246 247 #[test] 248 fn test_json_addon_install_parameters_with_addon_and_allow_private_browsing() { 249 let json = json!({"addon": "aGVsbG8=", "allowPrivateBrowsing": true}); 250 let data = serde_json::from_value::<AddonInstallParameters>(json).unwrap(); 251 252 if let AddonInstallParameters::AddonBase64(data) = data { 253 assert_eq!(data.allow_private_browsing, Some(true)); 254 assert_eq!(String::from_utf8(data.addon).unwrap(), "hello"); 255 } 256 } 257 258 #[test] 259 fn test_json_addon_install_parameters_with_addon_only() { 260 let json = json!({"addon": "aGVsbG8="}); 261 let data = serde_json::from_value::<AddonInstallParameters>(json).unwrap(); 262 263 if let AddonInstallParameters::AddonBase64(data) = data { 264 assert_eq!(data.temporary, None); 265 assert_eq!(String::from_utf8(data.addon).unwrap(), "hello"); 266 } 267 } 268 269 #[test] 270 fn test_json_addon_install_parameters_with_addon_invalid_type() { 271 let json = json!({"addon": true, "temporary": true}); 272 assert!(serde_json::from_value::<AddonInstallParameters>(json).is_err()); 273 } 274 275 #[test] 276 fn test_json_addon_install_parameters_with_addon_and_temporary_invalid_type() { 277 let json = json!({"addon": "aGVsbG8=", "temporary": "foo"}); 278 assert!(serde_json::from_value::<AddonInstallParameters>(json).is_err()); 279 } 280 281 #[test] 282 fn test_json_addon_install_parameters_with_addon_and_allow_private_browsing_invalid_type() { 283 let json = json!({"addon": "aGVsbG8=", "allowPrivateBrowsing": "foo"}); 284 assert!(serde_json::from_value::<AddonInstallParameters>(json).is_err()); 285 } 286 287 #[test] 288 fn test_json_install_parameters_with_temporary_only() { 289 let json = json!({"temporary": true}); 290 assert!(serde_json::from_value::<AddonInstallParameters>(json).is_err()); 291 } 292 293 #[test] 294 fn test_json_addon_install_parameters_with_both_path_and_addon() { 295 let json = json!({ 296 "path": "/path/to.xpi", 297 "addon": "aGVsbG8=", 298 "temporary": true, 299 }); 300 assert!(serde_json::from_value::<AddonInstallParameters>(json).is_err()); 301 } 302 303 #[test] 304 fn test_json_addon_uninstall_parameters_invalid() { 305 assert!(serde_json::from_str::<AddonUninstallParameters>("").is_err()); 306 assert!(serde_json::from_value::<AddonUninstallParameters>(json!(null)).is_err()); 307 assert!(serde_json::from_value::<AddonUninstallParameters>(json!({})).is_err()); 308 } 309 310 #[test] 311 fn test_json_addon_uninstall_parameters() { 312 let params = AddonUninstallParameters { 313 id: "foo".to_string(), 314 }; 315 assert_de(¶ms, json!({"id": "foo"})); 316 } 317 318 #[test] 319 fn test_json_addon_uninstall_parameters_id_invalid_type() { 320 let json = json!({"id": true}); 321 assert!(serde_json::from_value::<AddonUninstallParameters>(json).is_err()); 322 } 323 324 #[test] 325 fn test_json_gecko_context_parameters_content() { 326 let params = GeckoContextParameters { 327 context: GeckoContext::Content, 328 }; 329 assert_de(¶ms, json!({"context": "content"})); 330 } 331 332 #[test] 333 fn test_json_gecko_context_parameters_chrome() { 334 let params = GeckoContextParameters { 335 context: GeckoContext::Chrome, 336 }; 337 assert_de(¶ms, json!({"context": "chrome"})); 338 } 339 340 #[test] 341 fn test_json_gecko_context_parameters_context_invalid() { 342 type P = GeckoContextParameters; 343 assert!(serde_json::from_value::<P>(json!({})).is_err()); 344 assert!(serde_json::from_value::<P>(json!({ "context": null })).is_err()); 345 assert!(serde_json::from_value::<P>(json!({"context": "foo"})).is_err()); 346 } 347 }