lingxia_browser_shell/
facade.rs1use lingxia_browser::{
2 BrowserNavigationPolicyRequest, BrowserNavigationPolicyResponse, LxAppError,
3 is_lingxia_startup_url,
4};
5
6pub const APP_ID: &str = lingxia_browser::BUILTIN_BROWSER_APPID;
7
8pub fn classify_navigation(
9 request: BrowserNavigationPolicyRequest,
10) -> BrowserNavigationPolicyResponse {
11 lingxia_browser::classify_navigation(request)
12}
13
14pub fn classify_navigation_json(request_json: &str) -> Option<String> {
15 lingxia_browser::classify_navigation_json(request_json)
16}
17
18pub fn should_hide_url(raw: &str) -> bool {
19 let trimmed = raw.trim();
20 if trimmed.is_empty() {
21 return true;
22 }
23 let lowered = trimmed.to_ascii_lowercase();
24 if lowered.starts_with("lx:")
25 || lowered.starts_with("data:")
26 || lowered.starts_with("javascript:")
27 || lowered.starts_with("blob:")
28 || lowered == "about:blank"
29 {
30 return true;
31 }
32 matches!(is_lingxia_startup_url(trimmed), Some(true))
33}
34
35pub fn open(url: &str, tab_id: Option<&str>) -> Result<String, LxAppError> {
36 lingxia_browser::open(url, tab_id)
37}
38
39pub fn open_for_app(
40 appid: &str,
41 session_id: u64,
42 url: &str,
43 tab_id: Option<&str>,
44) -> Result<String, LxAppError> {
45 lingxia_browser::open_for_app(appid, session_id, url, tab_id)
46}
47
48pub fn close(tab_id: &str) -> Result<(), LxAppError> {
49 lingxia_browser::close(tab_id)
50}
51
52pub fn tab_path(tab_id: &str) -> String {
53 lingxia_browser::tab_path(tab_id)
54}
55
56pub fn update_tab(tab_id: &str, current_url: Option<&str>, title: Option<&str>) -> bool {
57 lingxia_browser::update_tab(tab_id, current_url, title)
58}
59
60pub fn download(
61 tab_id: &str,
62 url: &str,
63 user_agent: Option<&str>,
64 suggested_filename: Option<&str>,
65 source_page_url: Option<&str>,
66 cookie: Option<&str>,
67) -> Result<(), LxAppError> {
68 lingxia_browser::start_download(
69 tab_id,
70 url,
71 user_agent,
72 suggested_filename,
73 source_page_url,
74 cookie,
75 )
76}
77
78#[cfg(test)]
79mod tests {
80 use super::{classify_navigation, classify_navigation_json, should_hide_url};
81 use lingxia_browser::{BrowserNavigationPolicyDecision, BrowserNavigationPolicyRequest};
82
83 #[test]
84 fn browser_nav_policy_allows_lark_with_gesture() {
85 let response = classify_navigation(BrowserNavigationPolicyRequest {
86 raw_url: "lark://client/auth?code=1".to_string(),
87 has_user_gesture: true,
88 is_main_frame: true,
89 });
90
91 assert_eq!(
92 response.decision,
93 BrowserNavigationPolicyDecision::OpenExternal
94 );
95 }
96
97 #[test]
98 fn browser_nav_policy_denies_non_external_scheme() {
99 let response = classify_navigation(BrowserNavigationPolicyRequest {
100 raw_url: "javascript:alert(1)".to_string(),
101 has_user_gesture: true,
102 is_main_frame: true,
103 });
104
105 assert_eq!(response.decision, BrowserNavigationPolicyDecision::Deny);
106 assert_eq!(response.reason.as_deref(), Some("non_external_scheme"));
107 }
108
109 #[test]
110 fn startup_page_url_is_hidden() {
111 assert!(should_hide_url("lingxia://newtab"));
112 assert!(should_hide_url("lingxia://"));
113 assert!(!should_hide_url("lingxia://downloads"));
114 }
115
116 #[test]
117 fn nav_policy_json_round_trips() {
118 let json = serde_json::to_string(&BrowserNavigationPolicyRequest {
119 raw_url: "lingxia://settings".to_string(),
120 has_user_gesture: false,
121 is_main_frame: true,
122 })
123 .unwrap();
124 let out = classify_navigation_json(&json).unwrap();
125 assert!(out.contains("\"in_webview\""));
126 }
127}