DevelopmentNodeEnvironment_MicrosoftVSCodeDependency_22NodeVersion_Bundle_Clean_Debug_ElectronProfile_EsbuildCompiler_Mountain/IPC/WindServiceHandlers/NativeHost/
OpenExternal.rs1use std::sync::Arc;
5
6use serde_json::Value;
7
8use crate::{RunTime::ApplicationRunTime::ApplicationRunTime, dev_log};
9
10pub async fn Fn(RunTime:Arc<ApplicationRunTime>, Arguments:Vec<Value>) -> Result<Value, String> {
11 let url_str = match Arguments.first() {
14 Some(Value::String(S)) => S.as_str(),
15
16 Some(Value::Object(Obj)) => Obj.get("uri").or_else(|| Obj.get("url")).and_then(|V| V.as_str()).unwrap_or(""),
17
18 _ => return Ok(Value::Bool(false)),
19 };
20
21 if url_str.is_empty() {
22 return Ok(Value::Bool(false));
23 }
24
25 dev_log!("lifecycle", "openExternal: {}", url_str);
26
27 let Scheme = url_str.splitn(2, ':').next().unwrap_or("").to_lowercase();
31
32 let AllowedSchemes = [
33 "http",
34 "https",
35 "mailto",
36 "ftp",
37 "vscode",
38 "fiddee",
39 "ssh",
40 "git",
41 "x-github-client",
42 "github-windows",
43 "slack",
44 "teams",
45 "zoommtg",
46 "tel",
47 "callto",
48 ];
49
50 if Scheme == "file" || Scheme.is_empty() || !url_str.contains(':') {
51 dev_log!(
52 "lifecycle",
53 "warn: [OpenExternal] blocked scheme '{}' for uri '{}'",
54 Scheme,
55 url_str
56 );
57
58 return Ok(Value::Bool(false));
59 }
60
61 let IsKnownScheme = AllowedSchemes.contains(&Scheme.as_str());
62
63 if !IsKnownScheme {
64 dev_log!(
65 "lifecycle",
66 "[OpenExternal] unknown scheme '{}' - forwarding to OS anyway",
67 Scheme
68 );
69 }
70
71 #[cfg(target_os = "macos")]
72 {
73 use std::process::Command;
74
75 let result = Command::new("open")
76 .arg(url_str)
77 .output()
78 .map_err(|Error| format!("Failed to execute open command: {}", Error))?;
79
80 if !result.status.success() {
81 return Err(format!("Failed to open URL: {}", String::from_utf8_lossy(&result.stderr)));
82 }
83 }
84
85 #[cfg(target_os = "windows")]
86 {
87 use std::process::Command;
88
89 let result = Command::new("cmd")
90 .arg("/c")
91 .arg("start")
92 .arg(url_str)
93 .output()
94 .map_err(|Error| format!("Failed to execute start command: {}", Error))?;
95
96 if !result.status.success() {
97 return Err(format!("Failed to open URL: {}", String::from_utf8_lossy(&result.stderr)));
98 }
99 }
100
101 #[cfg(target_os = "linux")]
102 {
103 use std::process::Command;
104
105 let handlers = ["xdg-open", "gnome-open", "kde-open", "x-www-browser"];
106
107 let mut last_error = String::new();
108
109 for handler in handlers.iter() {
110 let result = Command::new(handler).arg(url_str).output();
111
112 match result {
113 Ok(output) if output.status.success() => {
114 dev_log!("lifecycle", "opened with {}", handler);
115
116 break;
117 },
118
119 Err(e) => {
120 last_error = e.to_string();
121
122 continue;
123 },
124
125 _ => continue,
126 }
127 }
128
129 if !last_error.is_empty() {
130 return Err(format!("Failed to open URL with any handler: {}", last_error));
131 }
132 }
133
134 dev_log!("lifecycle", "opened URL: {}", url_str);
135
136 Ok(Value::Bool(true))
137}