Skip to main content

lingxia_browser_shell/
facade.rs

1use 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}