Skip to main content

lingxia_browser/
lib.rs

1mod automation;
2mod chooser;
3mod downloads;
4mod internal_pages;
5mod policy;
6mod tabs;
7mod types;
8mod webview;
9
10pub use lingxia_webview::{
11    NetworkBody, NetworkCaptureSnapshot, NetworkEntry, WebViewCookie, WebViewCookieSameSite,
12    WebViewCookieSetRequest,
13};
14pub use policy::{extract_url_scheme, is_lingxia_startup_url, normalize_url_for_wait_compare};
15use std::sync::Arc;
16use std::time::Duration;
17pub use types::{
18    BrowserAddressAction, BrowserAddressInputContext, BrowserAddressInputError,
19    BrowserAddressInputRequest, BrowserAddressInputResponse, BrowserAddressInputTrigger,
20    BrowserAddressNavigation, BrowserAddressState, BrowserAddressSuggestion,
21    BrowserAddressValueKind, BrowserAutomationError, BrowserElementInfo, BrowserNativeInputHost,
22    BrowserNavigationPolicyDecision, BrowserNavigationPolicyRequest,
23    BrowserNavigationPolicyResponse, BrowserNavigationTarget, BrowserRect, BrowserTabInfo,
24    BrowserWaitCondition, BrowserWaitResult,
25};
26
27pub use lxapp::LxAppError;
28
29pub const BUILTIN_BROWSER_APPID: &str = "app.lingxia.browser";
30/// `(url, title)` observer; `title` is empty when no title has been reported
31/// for that URL yet.
32pub type BrowserPageMetadataHandler = Arc<dyn Fn(&str, &str) + Send + Sync>;
33
34pub fn classify_navigation(
35    request: BrowserNavigationPolicyRequest,
36) -> BrowserNavigationPolicyResponse {
37    policy::handle_browser_navigation_policy(request)
38}
39
40pub fn classify_navigation_json(request_json: &str) -> Option<String> {
41    policy::handle_browser_navigation_policy_json(request_json)
42}
43
44#[doc(hidden)]
45pub fn set_navigation_finished_handler(handler: BrowserPageMetadataHandler) {
46    tabs::set_navigation_finished_handler(handler);
47}
48
49#[doc(hidden)]
50pub fn set_title_changed_handler(handler: BrowserPageMetadataHandler) {
51    tabs::set_title_changed_handler(handler);
52}
53
54#[doc(hidden)]
55pub fn register_document_script(js: impl Into<String>) {
56    internal_pages::register_browser_document_script(js);
57}
58
59#[doc(hidden)]
60pub fn install_runtime() {
61    lxapp::register_page_resolver(internal_pages::browser_logic_page_path_for_tab_path);
62    lingxia_transfer::runtime::register_browser_tab_path_resolver(tabs::browser_tab_path_for_id);
63    lingxia_transfer::runtime::register_browser_retry_handler(
64        downloads::retry_browser_owned_download,
65    );
66}
67
68#[doc(hidden)]
69pub fn register_internal_page(
70    route: impl Into<String>,
71    entry_asset: impl Into<String>,
72) -> Result<(), LxAppError> {
73    internal_pages::register_browser_internal_page(route, entry_asset)
74}
75
76pub fn open(url: &str, tab_id: Option<&str>) -> Result<String, LxAppError> {
77    tabs::open_internal_browser_tab(url, tab_id)
78}
79
80pub fn open_for_app(
81    appid: &str,
82    session_id: u64,
83    url: &str,
84    tab_id: Option<&str>,
85) -> Result<String, LxAppError> {
86    tabs::open_internal_browser_tab_for_owner(
87        appid,
88        session_id,
89        url,
90        tab_id,
91        false,
92        false,
93        lingxia_webview::WebViewDataMode::ProfileDefault,
94        false,
95    )
96}
97
98/// Open a tab in the API-managed aside browser group.
99pub fn open_aside_for_app(
100    appid: &str,
101    session_id: u64,
102    url: &str,
103    tab_id: Option<&str>,
104) -> Result<String, LxAppError> {
105    tabs::open_internal_browser_tab_for_owner(
106        appid,
107        session_id,
108        url,
109        tab_id,
110        false,
111        true,
112        lingxia_webview::WebViewDataMode::ProfileDefault,
113        false,
114    )
115}
116
117/// Open a standalone browser tab hosted outside the product browser chrome.
118/// This supports docked asides and URL surfaces. New-window requests load
119/// inline in the same WebView rather than spawning a main-area tab.
120pub fn open_standalone_for_app(
121    appid: &str,
122    session_id: u64,
123    url: &str,
124    tab_id: Option<&str>,
125    data_mode: lingxia_webview::WebViewDataMode,
126    url_callback: bool,
127) -> Result<String, LxAppError> {
128    tabs::open_internal_browser_tab_for_owner(
129        appid,
130        session_id,
131        url,
132        tab_id,
133        true,
134        false,
135        data_mode,
136        url_callback,
137    )
138}
139
140/// Whether `tab_id` belongs to the API-managed aside browser group.
141pub fn tab_is_aside(tab_id: &str) -> bool {
142    tabs::is_aside_tab(tab_id)
143}
144
145/// Whether `tab_id` is hosted outside the product browser chrome, such as a
146/// docked aside or URL surface. It remains visible to browser automation.
147pub fn tab_is_standalone(tab_id: &str) -> bool {
148    tabs::is_standalone_tab(tab_id)
149}
150
151pub fn close(tab_id: &str) -> Result<(), LxAppError> {
152    tabs::close_browser_tab(tab_id)
153}
154
155/// Retire browser tabs owned by previous sessions of an lxapp.
156pub fn prune_stale_owner_tabs(owner_appid: &str, current_session_id: u64) -> usize {
157    tabs::prune_stale_owner_tabs(owner_appid, current_session_id)
158}
159
160/// Discard a tab's WebView to free memory while keeping its sidebar entry.
161pub fn discard(tab_id: &str) -> Result<(), LxAppError> {
162    tabs::discard_browser_tab(tab_id)
163}
164
165/// Recreate a discarded tab's WebView, reload its URL, and activate it.
166pub fn reactivate(tab_id: &str) -> Result<(), LxAppError> {
167    tabs::reactivate_browser_tab(tab_id)
168}
169
170/// Sync the Rust-side active tab when the SDK switches to an already-live tab.
171pub fn mark_active(tab_id: &str) {
172    tabs::mark_browser_tab_active(tab_id)
173}
174
175/// Clear active browser state when the host leaves browser UI entirely.
176pub fn clear_active() {
177    tabs::clear_active_browser_tab()
178}
179
180pub fn tabs() -> Vec<BrowserTabInfo> {
181    tabs::browser_tabs()
182}
183
184pub fn current_tab() -> Option<BrowserTabInfo> {
185    tabs::browser_current_tab()
186}
187
188/// Current tab selected by browser automation. Standalone surface tabs can be
189/// selected here without changing the product browser's active tab.
190pub fn automation_current_tab() -> Option<BrowserTabInfo> {
191    tabs::browser_automation_current_tab()
192}
193
194pub fn activate(tab_id: &str) -> Result<BrowserTabInfo, BrowserAutomationError> {
195    tabs::browser_activate_tab(tab_id)
196}
197
198/// Registers a process-wide observer invoked whenever the browser tab set
199/// or tab metadata changes: tab opened/closed, active tab switched, or a
200/// tab's URL/title updated. Intended for shell UIs that mirror the tab
201/// list (e.g. sidebar tab rows); the previous handler (if any) is replaced.
202///
203/// The callback may fire from arbitrary runtime threads (webview UI
204/// threads included) and must not block; query [`tabs`]/[`current_tab`]
205/// from it to read the new state.
206pub fn set_tabs_changed_handler(handler: Arc<dyn Fn() + Send + Sync>) {
207    tabs::set_tabs_changed_handler(handler);
208}
209
210/// Registers a process-wide observer used to bring a browser tab onscreen.
211///
212/// Browser core owns tab lifecycle and active-tab state, but only a host shell
213/// knows how to present that tab's WebView in its UI. Devtools and other
214/// automation surfaces call [`present`] to request this handoff.
215pub fn set_tab_present_handler(handler: Arc<dyn Fn(&str) + Send + Sync>) {
216    tabs::set_tab_present_handler(handler);
217}
218
219/// Mark `tab_id` active and ask the host shell to bring it onscreen.
220pub fn present(tab_id: &str) -> Result<BrowserTabInfo, BrowserAutomationError> {
221    tabs::browser_present_tab(tab_id)
222}
223
224/// PNG-encoded favicon of `tab_id`'s current page, if the platform webview
225/// reported one (see `WebViewDelegate::on_favicon_changed`). Kept out of
226/// [`BrowserTabInfo`] so the serialized tab projection stays byte-free;
227/// shell sidebars query it per tab when mirroring the tab list.
228pub fn tab_favicon(tab_id: &str) -> Option<Arc<Vec<u8>>> {
229    tabs::browser_tab_favicon(tab_id)
230}
231
232pub fn register_native_input_host(host: Arc<dyn BrowserNativeInputHost>) -> bool {
233    automation::register_native_input_host(host)
234}
235
236pub async fn evaluate_javascript(
237    tab_id: &str,
238    js: &str,
239) -> Result<serde_json::Value, BrowserAutomationError> {
240    automation::browser_evaluate_javascript(tab_id, js).await
241}
242
243pub async fn take_screenshot(tab_id: &str) -> Result<Vec<u8>, BrowserAutomationError> {
244    automation::browser_take_screenshot(tab_id).await
245}
246
247pub async fn current_url(tab_id: &str) -> Result<Option<String>, BrowserAutomationError> {
248    automation::browser_current_url(tab_id).await
249}
250
251pub fn reload(tab_id: &str) -> Result<(), BrowserAutomationError> {
252    automation::browser_reload(tab_id)
253}
254
255pub fn go_back(tab_id: &str) -> Result<(), BrowserAutomationError> {
256    automation::browser_go_back(tab_id)
257}
258
259pub fn go_forward(tab_id: &str) -> Result<(), BrowserAutomationError> {
260    automation::browser_go_forward(tab_id)
261}
262
263pub async fn list_cookies(tab_id: &str) -> Result<Vec<WebViewCookie>, BrowserAutomationError> {
264    automation::browser_list_cookies(tab_id).await
265}
266
267pub async fn list_all_cookies(tab_id: &str) -> Result<Vec<WebViewCookie>, BrowserAutomationError> {
268    automation::browser_list_all_cookies(tab_id).await
269}
270
271pub async fn set_cookie(
272    tab_id: &str,
273    request: WebViewCookieSetRequest,
274) -> Result<(), BrowserAutomationError> {
275    automation::browser_set_cookie(tab_id, request).await
276}
277
278pub async fn delete_cookie(
279    tab_id: &str,
280    name: &str,
281    domain: &str,
282    path: &str,
283) -> Result<(), BrowserAutomationError> {
284    automation::browser_delete_cookie(tab_id, name, domain, path).await
285}
286
287pub async fn clear_cookies(tab_id: &str) -> Result<(), BrowserAutomationError> {
288    automation::browser_clear_cookies(tab_id).await
289}
290
291pub async fn clear_site_data(
292    tab_id: &str,
293    options: lingxia_webview::ClearSiteDataOptions,
294) -> Result<lingxia_webview::ClearSiteDataResult, BrowserAutomationError> {
295    automation::browser_clear_site_data(tab_id, options).await
296}
297
298pub async fn start_network_capture(tab_id: &str) -> Result<(), BrowserAutomationError> {
299    automation::browser_start_network_capture(tab_id).await
300}
301
302pub async fn stop_network_capture(tab_id: &str) -> Result<(), BrowserAutomationError> {
303    automation::browser_stop_network_capture(tab_id).await
304}
305
306pub async fn network_entries(
307    tab_id: &str,
308) -> Result<NetworkCaptureSnapshot, BrowserAutomationError> {
309    automation::browser_network_entries(tab_id).await
310}
311
312pub async fn clear_network_capture(tab_id: &str) -> Result<(), BrowserAutomationError> {
313    automation::browser_clear_network_capture(tab_id).await
314}
315
316pub async fn query(
317    tab_id: &str,
318    selector: &str,
319) -> Result<BrowserElementInfo, BrowserAutomationError> {
320    automation::browser_query(tab_id, selector).await
321}
322
323pub async fn query_with_max_text(
324    tab_id: &str,
325    selector: &str,
326    max_text_chars: Option<usize>,
327) -> Result<BrowserElementInfo, BrowserAutomationError> {
328    automation::browser_query_with_max_text(tab_id, selector, max_text_chars).await
329}
330
331pub async fn wait(
332    tab_id: &str,
333    condition: BrowserWaitCondition,
334    timeout: Duration,
335) -> Result<BrowserWaitResult, BrowserAutomationError> {
336    automation::browser_wait(tab_id, condition, timeout).await
337}
338
339pub async fn wait_for_url(
340    tab_id: &str,
341    url: &str,
342    timeout: Duration,
343) -> Result<BrowserWaitResult, BrowserAutomationError> {
344    automation::browser_wait_for_url(tab_id, url, timeout).await
345}
346
347pub async fn wait_for_url_contains(
348    tab_id: &str,
349    text: &str,
350    timeout: Duration,
351) -> Result<BrowserWaitResult, BrowserAutomationError> {
352    automation::browser_wait_for_url_contains(tab_id, text, timeout).await
353}
354
355pub async fn wait_for_navigation(
356    tab_id: &str,
357    timeout: Duration,
358    wait_until_complete: bool,
359) -> Result<BrowserWaitResult, BrowserAutomationError> {
360    automation::browser_wait_for_navigation(tab_id, timeout, wait_until_complete).await
361}
362
363pub async fn click(tab_id: &str, selector: &str) -> Result<(), BrowserAutomationError> {
364    automation::browser_click(tab_id, selector).await
365}
366
367pub async fn type_text(
368    tab_id: &str,
369    selector: &str,
370    text: &str,
371) -> Result<(), BrowserAutomationError> {
372    automation::browser_type_text(tab_id, selector, text).await
373}
374
375pub async fn fill(tab_id: &str, selector: &str, text: &str) -> Result<(), BrowserAutomationError> {
376    automation::browser_fill(tab_id, selector, text).await
377}
378
379pub async fn press(tab_id: &str, key: &str) -> Result<(), BrowserAutomationError> {
380    automation::browser_press(tab_id, key).await
381}
382
383pub async fn scroll(tab_id: &str, dx: f64, dy: f64) -> Result<(), BrowserAutomationError> {
384    automation::browser_scroll(tab_id, dx, dy).await
385}
386
387pub async fn scroll_to(tab_id: &str, selector: &str) -> Result<(), BrowserAutomationError> {
388    automation::browser_scroll_to(tab_id, selector).await
389}
390
391pub fn tab_path(tab_id: &str) -> String {
392    tabs::browser_tab_path_for_id(tab_id)
393}
394
395pub fn update_tab(tab_id: &str, current_url: Option<&str>, title: Option<&str>) -> bool {
396    tabs::browser_update_tab_info(tab_id, current_url, title)
397}
398
399pub fn start_download(
400    tab_id: &str,
401    url: &str,
402    user_agent: Option<&str>,
403    suggested_filename: Option<&str>,
404    source_page_url: Option<&str>,
405    cookie: Option<&str>,
406) -> Result<(), LxAppError> {
407    downloads::start_native_browser_download(
408        tab_id,
409        url,
410        user_agent,
411        suggested_filename,
412        source_page_url,
413        cookie,
414    )
415}
416
417#[doc(hidden)]
418pub fn register_bundled_app() {
419    tabs::register_builtin_browser_host();
420}
421
422#[doc(hidden)]
423pub fn warmup() {
424    if let Err(err) = internal_pages::warmup_builtin_browser_runtime() {
425        lxapp::warn!("[InternalBrowser] warmup failed: {}", err);
426    }
427}