1pub(crate) mod plugin;
8mod webview_window;
9
10pub use webview_window::{WebviewWindow, WebviewWindowBuilder};
11
12pub use cookie;
18use http::HeaderMap;
19use serde::Serialize;
20use tauri_macros::default_runtime;
21pub use tauri_runtime::webview::{NewWindowFeatures, PageLoadEvent};
22pub use tauri_runtime::Cookie;
24#[cfg(desktop)]
25use tauri_runtime::{
26 dpi::{PhysicalPosition, PhysicalSize, Position, Size},
27 WindowDispatch,
28};
29use tauri_runtime::{
30 webview::{DetachedWebview, InitializationScript, PendingWebview, WebviewAttributes},
31 WebviewDispatch,
32};
33pub use tauri_utils::config::Color;
34use tauri_utils::config::{BackgroundThrottlingPolicy, WebviewUrl, WindowConfig};
35pub use url::Url;
36
37use crate::{
38 app::{UriSchemeResponder, WebviewEvent},
39 event::{EmitArgs, EventTarget},
40 ipc::{
41 CallbackFn, CommandArg, CommandItem, CommandScope, GlobalScope, Invoke, InvokeBody,
42 InvokeError, InvokeMessage, InvokeResolver, Origin, OwnedInvokeResponder, ScopeObject,
43 },
44 manager::AppManager,
45 sealed::{ManagerBase, RuntimeOrDispatch},
46 AppHandle, Emitter, Event, EventId, EventLoopMessage, EventName, Listener, Manager,
47 ResourceTable, Runtime, Window,
48};
49
50use std::{
51 borrow::Cow,
52 hash::{Hash, Hasher},
53 path::{Path, PathBuf},
54 sync::{Arc, Mutex, MutexGuard},
55};
56
57pub(crate) type WebResourceRequestHandler =
58 dyn Fn(http::Request<Vec<u8>>, &mut http::Response<Cow<'static, [u8]>>) + Send + Sync;
59pub(crate) type NavigationHandler = dyn Fn(&Url) -> bool + Send;
60pub(crate) type NewWindowHandler<R> =
61 dyn Fn(Url, NewWindowFeatures) -> NewWindowResponse<R> + Send + Sync;
62pub(crate) type UriSchemeProtocolHandler =
63 Box<dyn Fn(&str, http::Request<Vec<u8>>, UriSchemeResponder) + Send + Sync>;
64pub(crate) type OnPageLoad<R> = dyn Fn(Webview<R>, PageLoadPayload<'_>) + Send + Sync + 'static;
65pub(crate) type OnDocumentTitleChanged<R> = dyn Fn(Webview<R>, String) + Send + 'static;
66pub(crate) type DownloadHandler<R> = dyn Fn(Webview<R>, DownloadEvent<'_>) -> bool + Send + Sync;
67
68#[derive(Clone, Serialize)]
69pub(crate) struct CreatedEvent {
70 pub(crate) label: String,
71}
72
73#[non_exhaustive]
75pub enum DownloadEvent<'a> {
76 Requested {
78 url: Url,
80 destination: &'a mut PathBuf,
84 },
85 Finished {
87 url: Url,
89 path: Option<PathBuf>,
100 success: bool,
102 },
103}
104
105#[derive(Debug, Clone)]
107pub struct PageLoadPayload<'a> {
108 pub(crate) url: &'a Url,
109 pub(crate) event: PageLoadEvent,
110}
111
112impl<'a> PageLoadPayload<'a> {
113 pub fn url(&self) -> &'a Url {
115 self.url
116 }
117
118 pub fn event(&self) -> PageLoadEvent {
120 self.event
121 }
122}
123
124#[derive(Debug)]
131pub struct InvokeRequest {
132 pub cmd: String,
134 pub callback: CallbackFn,
136 pub error: CallbackFn,
138 pub url: Url,
140 pub body: InvokeBody,
142 pub headers: HeaderMap,
144 pub invoke_key: String,
146}
147
148#[cfg(feature = "wry")]
150#[cfg_attr(docsrs, doc(cfg(feature = "wry")))]
151pub struct PlatformWebview(tauri_runtime_wry::Webview);
152
153#[cfg(feature = "wry")]
154impl PlatformWebview {
155 #[cfg(any(
157 target_os = "linux",
158 target_os = "dragonfly",
159 target_os = "freebsd",
160 target_os = "netbsd",
161 target_os = "openbsd"
162 ))]
163 #[cfg_attr(
164 docsrs,
165 doc(cfg(any(
166 target_os = "linux",
167 target_os = "dragonfly",
168 target_os = "freebsd",
169 target_os = "netbsd",
170 target_os = "openbsd"
171 )))
172 )]
173 pub fn inner(&self) -> webkit2gtk::WebView {
174 self.0.clone()
175 }
176
177 #[cfg(windows)]
179 #[cfg_attr(docsrs, doc(cfg(windows)))]
180 pub fn controller(
181 &self,
182 ) -> webview2_com::Microsoft::Web::WebView2::Win32::ICoreWebView2Controller {
183 self.0.controller.clone()
184 }
185
186 #[cfg(windows)]
188 #[cfg_attr(docsrs, doc(cfg(windows)))]
189 pub fn environment(
190 &self,
191 ) -> webview2_com::Microsoft::Web::WebView2::Win32::ICoreWebView2Environment {
192 self.0.environment.clone()
193 }
194
195 #[cfg(any(target_os = "macos", target_os = "ios"))]
199 #[cfg_attr(docsrs, doc(cfg(any(target_os = "macos", target_os = "ios"))))]
200 pub fn inner(&self) -> *mut std::ffi::c_void {
201 self.0.webview
202 }
203
204 #[cfg(any(target_os = "macos", target_os = "ios"))]
208 #[cfg_attr(docsrs, doc(cfg(any(target_os = "macos", target_os = "ios"))))]
209 pub fn controller(&self) -> *mut std::ffi::c_void {
210 self.0.manager
211 }
212
213 #[cfg(target_os = "macos")]
217 #[cfg_attr(docsrs, doc(cfg(target_os = "macos")))]
218 pub fn ns_window(&self) -> *mut std::ffi::c_void {
219 self.0.ns_window
220 }
221
222 #[cfg(target_os = "ios")]
226 #[cfg_attr(docsrs, doc(cfg(target_os = "ios")))]
227 pub fn view_controller(&self) -> *mut std::ffi::c_void {
228 self.0.view_controller
229 }
230
231 #[cfg(target_os = "android")]
233 pub fn jni_handle(&self) -> tauri_runtime_wry::wry::JniHandle {
234 self.0
235 }
236}
237
238pub enum NewWindowResponse<R: Runtime> {
240 Allow,
242 Create {
250 window: crate::WebviewWindow<R>,
252 },
253 Deny,
255}
256
257macro_rules! unstable_struct {
258 (#[doc = $doc:expr] $($tokens:tt)*) => {
259 #[cfg(any(test, feature = "unstable"))]
260 #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
261 #[doc = $doc]
262 pub $($tokens)*
263
264 #[cfg(not(any(test, feature = "unstable")))]
265 pub(crate) $($tokens)*
266 }
267}
268
269unstable_struct!(
270 #[doc = "A builder for a webview."]
271 struct WebviewBuilder<R: Runtime> {
272 pub(crate) label: String,
273 pub(crate) webview_attributes: WebviewAttributes,
274 pub(crate) web_resource_request_handler: Option<Box<WebResourceRequestHandler>>,
275 pub(crate) navigation_handler: Option<Box<NavigationHandler>>,
276 pub(crate) new_window_handler: Option<Box<NewWindowHandler<R>>>,
277 pub(crate) on_page_load_handler: Option<Box<OnPageLoad<R>>>,
278 pub(crate) document_title_changed_handler: Option<Box<OnDocumentTitleChanged<R>>>,
279 pub(crate) download_handler: Option<Arc<DownloadHandler<R>>>,
280 }
281);
282
283#[cfg_attr(not(feature = "unstable"), allow(dead_code))]
284impl<R: Runtime> WebviewBuilder<R> {
285 #[cfg_attr(
297 feature = "unstable",
298 doc = r####"
299```
300tauri::Builder::default()
301 .setup(|app| {
302 let window = tauri::window::WindowBuilder::new(app, "label").build()?;
303 let webview_builder = tauri::webview::WebviewBuilder::new("label", tauri::WebviewUrl::App("index.html".into()));
304 let webview = window.add_child(webview_builder, tauri::LogicalPosition::new(0, 0), window.inner_size().unwrap());
305 Ok(())
306 });
307```
308 "####
309 )]
310 #[cfg_attr(
314 feature = "unstable",
315 doc = r####"
316```
317tauri::Builder::default()
318 .setup(|app| {
319 let handle = app.handle().clone();
320 std::thread::spawn(move || {
321 let window = tauri::window::WindowBuilder::new(&handle, "label").build().unwrap();
322 let webview_builder = tauri::webview::WebviewBuilder::new("label", tauri::WebviewUrl::App("index.html".into()));
323 window.add_child(webview_builder, tauri::LogicalPosition::new(0, 0), window.inner_size().unwrap());
324 });
325 Ok(())
326 });
327```
328 "####
329 )]
330 #[cfg_attr(
334 feature = "unstable",
335 doc = r####"
336```
337#[tauri::command]
338async fn create_window(app: tauri::AppHandle) {
339 let window = tauri::window::WindowBuilder::new(&app, "label").build().unwrap();
340 let webview_builder = tauri::webview::WebviewBuilder::new("label", tauri::WebviewUrl::External("https://tauri.app/".parse().unwrap()));
341 window.add_child(webview_builder, tauri::LogicalPosition::new(0, 0), window.inner_size().unwrap());
342}
343```
344 "####
345 )]
346 pub fn new<L: Into<String>>(label: L, url: WebviewUrl) -> Self {
349 Self {
350 label: label.into(),
351 webview_attributes: WebviewAttributes::new(url),
352 web_resource_request_handler: None,
353 navigation_handler: None,
354 new_window_handler: None,
355 on_page_load_handler: None,
356 document_title_changed_handler: None,
357 download_handler: None,
358 }
359 }
360
361 #[cfg_attr(
375 feature = "unstable",
376 doc = r####"
377```
378#[tauri::command]
379async fn create_window(app: tauri::AppHandle) {
380 let window = tauri::window::WindowBuilder::new(&app, "label").build().unwrap();
381 let webview_builder = tauri::webview::WebviewBuilder::from_config(&app.config().app.windows.get(0).unwrap().clone());
382 window.add_child(webview_builder, tauri::LogicalPosition::new(0, 0), window.inner_size().unwrap());
383}
384```
385 "####
386 )]
387 pub fn from_config(config: &WindowConfig) -> Self {
390 Self {
391 label: config.label.clone(),
392 webview_attributes: WebviewAttributes::from(config),
393 web_resource_request_handler: None,
394 navigation_handler: None,
395 new_window_handler: None,
396 on_page_load_handler: None,
397 document_title_changed_handler: None,
398 download_handler: None,
399 }
400 }
401
402 #[cfg_attr(
412 feature = "unstable",
413 doc = r####"
414```rust,no_run
415use tauri::{
416 utils::config::{Csp, CspDirectiveSources, WebviewUrl},
417 window::WindowBuilder,
418 webview::WebviewBuilder,
419};
420use http::header::HeaderValue;
421use std::collections::HashMap;
422tauri::Builder::default()
423 .setup(|app| {
424 let window = tauri::window::WindowBuilder::new(app, "label").build()?;
425
426 let webview_builder = WebviewBuilder::new("core", WebviewUrl::App("index.html".into()))
427 .on_web_resource_request(|request, response| {
428 if request.uri().scheme_str() == Some("tauri") {
429 // if we have a CSP header, Tauri is loading an HTML file
430 // for this example, let's dynamically change the CSP
431 if let Some(csp) = response.headers_mut().get_mut("Content-Security-Policy") {
432 // use the tauri helper to parse the CSP policy to a map
433 let mut csp_map: HashMap<String, CspDirectiveSources> = Csp::Policy(csp.to_str().unwrap().to_string()).into();
434 csp_map.entry("script-src".to_string()).or_insert_with(Default::default).push("'unsafe-inline'");
435 // use the tauri helper to get a CSP string from the map
436 let csp_string = Csp::from(csp_map).to_string();
437 *csp = HeaderValue::from_str(&csp_string).unwrap();
438 }
439 }
440 });
441
442 let webview = window.add_child(webview_builder, tauri::LogicalPosition::new(0, 0), window.inner_size().unwrap())?;
443
444 Ok(())
445 });
446```
447 "####
448 )]
449 pub fn on_web_resource_request<
450 F: Fn(http::Request<Vec<u8>>, &mut http::Response<Cow<'static, [u8]>>) + Send + Sync + 'static,
451 >(
452 mut self,
453 f: F,
454 ) -> Self {
455 self.web_resource_request_handler.replace(Box::new(f));
456 self
457 }
458
459 #[cfg_attr(
464 feature = "unstable",
465 doc = r####"
466```rust,no_run
467use tauri::{
468 utils::config::{Csp, CspDirectiveSources, WebviewUrl},
469 window::WindowBuilder,
470 webview::WebviewBuilder,
471};
472use http::header::HeaderValue;
473use std::collections::HashMap;
474tauri::Builder::default()
475 .setup(|app| {
476 let window = tauri::window::WindowBuilder::new(app, "label").build()?;
477
478 let webview_builder = WebviewBuilder::new("core", WebviewUrl::App("index.html".into()))
479 .on_navigation(|url| {
480 // allow the production URL or localhost on dev
481 url.scheme() == "tauri" || (cfg!(dev) && url.host_str() == Some("localhost"))
482 });
483
484 let webview = window.add_child(webview_builder, tauri::LogicalPosition::new(0, 0), window.inner_size().unwrap())?;
485 Ok(())
486 });
487```
488 "####
489 )]
490 pub fn on_navigation<F: Fn(&Url) -> bool + Send + 'static>(mut self, f: F) -> Self {
491 self.navigation_handler.replace(Box::new(f));
492 self
493 }
494
495 #[cfg_attr(
502 feature = "unstable",
503 doc = r####"
504```rust,no_run
505use tauri::{
506 utils::config::{Csp, CspDirectiveSources, WebviewUrl},
507 window::WindowBuilder,
508 webview::WebviewBuilder,
509};
510use http::header::HeaderValue;
511use std::collections::HashMap;
512tauri::Builder::default()
513 .setup(|app| {
514 let window = tauri::window::WindowBuilder::new(app, "label").build()?;
515
516 let app_ = app.handle().clone();
517 let webview_builder = WebviewBuilder::new("core", WebviewUrl::App("index.html".into()))
518 .on_new_window(move |url, features| {
519 let builder = tauri::WebviewWindowBuilder::new(
520 &app_,
521 // note: add an ID counter or random label generator to support multiple opened windows at the same time
522 "opened-window",
523 tauri::WebviewUrl::External("about:blank".parse().unwrap()),
524 )
525 .window_features(features)
526 .on_document_title_changed(|window, title| {
527 window.set_title(&title).unwrap();
528 })
529 .title(url.as_str());
530
531 let window = builder.build().unwrap();
532 tauri::webview::NewWindowResponse::Create { window }
533 });
534
535 let webview = window.add_child(webview_builder, tauri::LogicalPosition::new(0, 0), window.inner_size().unwrap())?;
536 Ok(())
537 });
538```
539 "####
540 )]
541 pub fn on_new_window<
549 F: Fn(Url, NewWindowFeatures) -> NewWindowResponse<R> + Send + Sync + 'static,
550 >(
551 mut self,
552 f: F,
553 ) -> Self {
554 self.new_window_handler.replace(Box::new(f));
555 self
556 }
557
558 pub fn on_document_title_changed<F: Fn(Webview<R>, String) + Send + 'static>(
560 mut self,
561 f: F,
562 ) -> Self {
563 self.document_title_changed_handler.replace(Box::new(f));
564 self
565 }
566
567 #[cfg_attr(
574 feature = "unstable",
575 doc = r####"
576```rust,no_run
577use tauri::{
578 utils::config::{Csp, CspDirectiveSources, WebviewUrl},
579 window::WindowBuilder,
580 webview::{DownloadEvent, WebviewBuilder},
581};
582
583tauri::Builder::default()
584 .setup(|app| {
585 let window = WindowBuilder::new(app, "label").build()?;
586 let webview_builder = WebviewBuilder::new("core", WebviewUrl::App("index.html".into()))
587 .on_download(|webview, event| {
588 match event {
589 DownloadEvent::Requested { url, destination } => {
590 println!("downloading {}", url);
591 *destination = "/home/tauri/target/path".into();
592 }
593 DownloadEvent::Finished { url, path, success } => {
594 println!("downloaded {} to {:?}, success: {}", url, path, success);
595 }
596 _ => (),
597 }
598 // let the download start
599 true
600 });
601
602 let webview = window.add_child(webview_builder, tauri::LogicalPosition::new(0, 0), window.inner_size().unwrap())?;
603 Ok(())
604 });
605```
606 "####
607 )]
608 pub fn on_download<F: Fn(Webview<R>, DownloadEvent<'_>) -> bool + Send + Sync + 'static>(
609 mut self,
610 f: F,
611 ) -> Self {
612 self.download_handler.replace(Arc::new(f));
613 self
614 }
615
616 #[cfg_attr(
623 feature = "unstable",
624 doc = r####"
625```rust,no_run
626use tauri::{
627 utils::config::{Csp, CspDirectiveSources, WebviewUrl},
628 window::WindowBuilder,
629 webview::{PageLoadEvent, WebviewBuilder},
630};
631use http::header::HeaderValue;
632use std::collections::HashMap;
633tauri::Builder::default()
634 .setup(|app| {
635 let window = tauri::window::WindowBuilder::new(app, "label").build()?;
636 let webview_builder = WebviewBuilder::new("core", WebviewUrl::App("index.html".into()))
637 .on_page_load(|webview, payload| {
638 match payload.event() {
639 PageLoadEvent::Started => {
640 println!("{} finished loading", payload.url());
641 }
642 PageLoadEvent::Finished => {
643 println!("{} finished loading", payload.url());
644 }
645 }
646 });
647 let webview = window.add_child(webview_builder, tauri::LogicalPosition::new(0, 0), window.inner_size().unwrap())?;
648 Ok(())
649 });
650```
651 "####
652 )]
653 pub fn on_page_load<F: Fn(Webview<R>, PageLoadPayload<'_>) + Send + Sync + 'static>(
654 mut self,
655 f: F,
656 ) -> Self {
657 self.on_page_load_handler.replace(Box::new(f));
658 self
659 }
660
661 pub(crate) fn into_pending_webview<M: Manager<R>>(
662 mut self,
663 manager: &M,
664 window_label: &str,
665 ) -> crate::Result<PendingWebview<EventLoopMessage, R>> {
666 let mut pending = PendingWebview::new(self.webview_attributes, self.label.clone())?;
667 pending.navigation_handler = self.navigation_handler.take();
668 pending.new_window_handler = self.new_window_handler.take().map(|handler| {
669 Box::new(
670 move |url, features: NewWindowFeatures| match handler(url, features) {
671 NewWindowResponse::Allow => tauri_runtime::webview::NewWindowResponse::Allow,
672 #[cfg(mobile)]
673 NewWindowResponse::Create { window: _ } => {
674 tauri_runtime::webview::NewWindowResponse::Allow
675 }
676 #[cfg(desktop)]
677 NewWindowResponse::Create { window } => {
678 tauri_runtime::webview::NewWindowResponse::Create {
679 window_id: window.window.window.id,
680 }
681 }
682 NewWindowResponse::Deny => tauri_runtime::webview::NewWindowResponse::Deny,
683 },
684 )
685 as Box<
686 dyn Fn(Url, NewWindowFeatures) -> tauri_runtime::webview::NewWindowResponse
687 + Send
688 + Sync
689 + 'static,
690 >
691 });
692
693 if let Some(document_title_changed_handler) = self.document_title_changed_handler.take() {
694 let label = pending.label.clone();
695 let manager = manager.manager_owned();
696 pending
697 .document_title_changed_handler
698 .replace(Box::new(move |title| {
699 if let Some(w) = manager.get_webview(&label) {
700 document_title_changed_handler(w, title);
701 }
702 }));
703 }
704 pending.web_resource_request_handler = self.web_resource_request_handler.take();
705
706 if let Some(download_handler) = self.download_handler.take() {
707 let label = pending.label.clone();
708 let manager = manager.manager_owned();
709 pending.download_handler.replace(Arc::new(move |event| {
710 if let Some(w) = manager.get_webview(&label) {
711 download_handler(
712 w,
713 match event {
714 tauri_runtime::webview::DownloadEvent::Requested { url, destination } => {
715 DownloadEvent::Requested { url, destination }
716 }
717 tauri_runtime::webview::DownloadEvent::Finished { url, path, success } => {
718 DownloadEvent::Finished { url, path, success }
719 }
720 },
721 )
722 } else {
723 false
724 }
725 }));
726 }
727
728 let label_ = pending.label.clone();
729 let manager_ = manager.manager_owned();
730 pending
731 .on_page_load_handler
732 .replace(Box::new(move |url, event| {
733 if let Some(w) = manager_.get_webview(&label_) {
734 if let Some(handler) = self.on_page_load_handler.as_ref() {
735 handler(w, PageLoadPayload { url: &url, event });
736 }
737 }
738 }));
739
740 manager
741 .manager()
742 .webview
743 .prepare_webview(manager, pending, window_label)
744 }
745
746 #[cfg(desktop)]
748 pub(crate) fn build(
749 self,
750 window: Window<R>,
751 position: Position,
752 size: Size,
753 ) -> crate::Result<Webview<R>> {
754 let app_manager = window.manager();
755
756 let mut pending = self.into_pending_webview(&window, window.label())?;
757
758 pending.webview_attributes.bounds = Some(tauri_runtime::dpi::Rect { size, position });
759
760 let use_https_scheme = pending.webview_attributes.use_https_scheme;
761
762 let webview = match &mut window.runtime() {
763 RuntimeOrDispatch::Dispatch(dispatcher) => dispatcher.create_webview(pending),
764 _ => unimplemented!(),
765 }
766 .map(|webview| {
767 app_manager
768 .webview
769 .attach_webview(window.clone(), webview, use_https_scheme)
770 })?;
771
772 Ok(webview)
773 }
774}
775
776impl<R: Runtime> WebviewBuilder<R> {
778 #[must_use]
780 pub fn accept_first_mouse(mut self, accept: bool) -> Self {
781 self.webview_attributes.accept_first_mouse = accept;
782 self
783 }
784
785 #[cfg_attr(
804 feature = "unstable",
805 doc = r####"
806```rust
807use tauri::{WindowBuilder, Runtime};
808
809const INIT_SCRIPT: &str = r#"
810 if (window.location.origin === 'https://tauri.app') {
811 console.log("hello world from js init script");
812
813 window.__MY_CUSTOM_PROPERTY__ = { foo: 'bar' };
814 }
815"#;
816
817fn main() {
818 tauri::Builder::default()
819 .setup(|app| {
820 let window = tauri::window::WindowBuilder::new(app, "label").build()?;
821 let webview_builder = tauri::webview::WebviewBuilder::new("label", tauri::WebviewUrl::App("index.html".into()))
822 .initialization_script(INIT_SCRIPT);
823 let webview = window.add_child(webview_builder, tauri::LogicalPosition::new(0, 0), window.inner_size().unwrap())?;
824 Ok(())
825 });
826}
827```
828 "####
829 )]
830 #[must_use]
834 pub fn initialization_script(mut self, script: impl Into<String>) -> Self {
835 self
836 .webview_attributes
837 .initialization_scripts
838 .push(InitializationScript {
839 script: script.into(),
840 for_main_frame_only: true,
841 });
842 self
843 }
844
845 #[cfg_attr(
863 feature = "unstable",
864 doc = r####"
865```rust
866use tauri::{WindowBuilder, Runtime};
867
868const INIT_SCRIPT: &str = r#"
869 if (window.location.origin === 'https://tauri.app') {
870 console.log("hello world from js init script");
871
872 window.__MY_CUSTOM_PROPERTY__ = { foo: 'bar' };
873 }
874"#;
875
876fn main() {
877 tauri::Builder::default()
878 .setup(|app| {
879 let window = tauri::window::WindowBuilder::new(app, "label").build()?;
880 let webview_builder = tauri::webview::WebviewBuilder::new("label", tauri::WebviewUrl::App("index.html".into()))
881 .initialization_script_for_all_frames(INIT_SCRIPT);
882 let webview = window.add_child(webview_builder, tauri::LogicalPosition::new(0, 0), window.inner_size().unwrap())?;
883 Ok(())
884 });
885}
886```
887 "####
888 )]
889 #[must_use]
893 pub fn initialization_script_for_all_frames(mut self, script: impl Into<String>) -> Self {
894 self
895 .webview_attributes
896 .initialization_scripts
897 .push(InitializationScript {
898 script: script.into(),
899 for_main_frame_only: false,
900 });
901 self
902 }
903
904 #[must_use]
906 pub fn user_agent(mut self, user_agent: &str) -> Self {
907 self.webview_attributes.user_agent = Some(user_agent.to_string());
908 self
909 }
910
911 #[must_use]
922 pub fn additional_browser_args(mut self, additional_args: &str) -> Self {
923 self.webview_attributes.additional_browser_args = Some(additional_args.to_string());
924 self
925 }
926
927 #[must_use]
929 pub fn data_directory(mut self, data_directory: PathBuf) -> Self {
930 self
931 .webview_attributes
932 .data_directory
933 .replace(data_directory);
934 self
935 }
936
937 #[must_use]
939 pub fn disable_drag_drop_handler(mut self) -> Self {
940 self.webview_attributes.drag_drop_handler_enabled = false;
941 self
942 }
943
944 #[must_use]
949 pub fn enable_clipboard_access(mut self) -> Self {
950 self.webview_attributes.clipboard = true;
951 self
952 }
953
954 #[must_use]
963 pub fn incognito(mut self, incognito: bool) -> Self {
964 self.webview_attributes.incognito = incognito;
965 self
966 }
967
968 #[must_use]
976 pub fn proxy_url(mut self, url: Url) -> Self {
977 self.webview_attributes.proxy_url = Some(url);
978 self
979 }
980
981 #[cfg(any(not(target_os = "macos"), feature = "macos-private-api"))]
983 #[cfg_attr(
984 docsrs,
985 doc(cfg(any(not(target_os = "macos"), feature = "macos-private-api")))
986 )]
987 #[must_use]
988 pub fn transparent(mut self, transparent: bool) -> Self {
989 self.webview_attributes.transparent = transparent;
990 self
991 }
992
993 #[must_use]
995 pub fn focused(mut self, focus: bool) -> Self {
996 self.webview_attributes.focus = focus;
997 self
998 }
999
1000 #[must_use]
1002 pub fn auto_resize(mut self) -> Self {
1003 self.webview_attributes.auto_resize = true;
1004 self
1005 }
1006
1007 #[must_use]
1017 pub fn zoom_hotkeys_enabled(mut self, enabled: bool) -> Self {
1018 self.webview_attributes.zoom_hotkeys_enabled = enabled;
1019 self
1020 }
1021
1022 #[must_use]
1029 pub fn browser_extensions_enabled(mut self, enabled: bool) -> Self {
1030 self.webview_attributes.browser_extensions_enabled = enabled;
1031 self
1032 }
1033
1034 #[must_use]
1041 pub fn extensions_path(mut self, path: impl AsRef<Path>) -> Self {
1042 self.webview_attributes.extensions_path = Some(path.as_ref().to_path_buf());
1043 self
1044 }
1045
1046 #[must_use]
1054 pub fn data_store_identifier(mut self, data_store_identifier: [u8; 16]) -> Self {
1055 self.webview_attributes.data_store_identifier = Some(data_store_identifier);
1056 self
1057 }
1058
1059 #[must_use]
1069 pub fn use_https_scheme(mut self, enabled: bool) -> Self {
1070 self.webview_attributes.use_https_scheme = enabled;
1071 self
1072 }
1073
1074 #[must_use]
1084 pub fn devtools(mut self, enabled: bool) -> Self {
1085 self.webview_attributes.devtools.replace(enabled);
1086 self
1087 }
1088
1089 #[must_use]
1097 pub fn background_color(mut self, color: Color) -> Self {
1098 self.webview_attributes.background_color = Some(color);
1099 self
1100 }
1101
1102 #[must_use]
1117 pub fn background_throttling(mut self, policy: BackgroundThrottlingPolicy) -> Self {
1118 self.webview_attributes.background_throttling = Some(policy);
1119 self
1120 }
1121
1122 #[must_use]
1124 pub fn disable_javascript(mut self) -> Self {
1125 self.webview_attributes.javascript_disabled = true;
1126 self
1127 }
1128
1129 #[cfg(target_os = "macos")]
1139 #[must_use]
1140 pub fn allow_link_preview(mut self, allow_link_preview: bool) -> Self {
1141 self.webview_attributes = self
1142 .webview_attributes
1143 .allow_link_preview(allow_link_preview);
1144 self
1145 }
1146
1147 #[cfg(target_os = "ios")]
1159 pub fn with_input_accessory_view_builder<
1160 F: Fn(&objc2_ui_kit::UIView) -> Option<objc2::rc::Retained<objc2_ui_kit::UIView>>
1161 + Send
1162 + Sync
1163 + 'static,
1164 >(
1165 mut self,
1166 builder: F,
1167 ) -> Self {
1168 self
1169 .webview_attributes
1170 .input_accessory_view_builder
1171 .replace(tauri_runtime::webview::InputAccessoryViewBuilder::new(
1172 Box::new(builder),
1173 ));
1174 self
1175 }
1176
1177 #[cfg(all(feature = "wry", windows))]
1180 pub fn with_environment(
1181 mut self,
1182 environment: webview2_com::Microsoft::Web::WebView2::Win32::ICoreWebView2Environment,
1183 ) -> Self {
1184 self.webview_attributes.environment.replace(environment);
1185 self
1186 }
1187
1188 #[cfg(all(
1191 feature = "wry",
1192 any(
1193 target_os = "linux",
1194 target_os = "dragonfly",
1195 target_os = "freebsd",
1196 target_os = "netbsd",
1197 target_os = "openbsd",
1198 )
1199 ))]
1200 pub fn with_related_view(mut self, related_view: webkit2gtk::WebView) -> Self {
1201 self.webview_attributes.related_view.replace(related_view);
1202 self
1203 }
1204
1205 #[cfg(target_os = "macos")]
1208 pub fn with_webview_configuration(
1209 mut self,
1210 webview_configuration: objc2::rc::Retained<objc2_web_kit::WKWebViewConfiguration>,
1211 ) -> Self {
1212 self
1213 .webview_attributes
1214 .webview_configuration
1215 .replace(webview_configuration);
1216 self
1217 }
1218}
1219
1220#[default_runtime(crate::Wry, wry)]
1222pub struct Webview<R: Runtime> {
1223 pub(crate) window: Arc<Mutex<Window<R>>>,
1224 pub(crate) webview: DetachedWebview<EventLoopMessage, R>,
1226 pub(crate) manager: Arc<AppManager<R>>,
1228 pub(crate) app_handle: AppHandle<R>,
1229 pub(crate) resources_table: Arc<Mutex<ResourceTable>>,
1230 use_https_scheme: bool,
1231}
1232
1233impl<R: Runtime> std::fmt::Debug for Webview<R> {
1234 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1235 f.debug_struct("Window")
1236 .field("window", &self.window.lock().unwrap())
1237 .field("webview", &self.webview)
1238 .field("use_https_scheme", &self.use_https_scheme)
1239 .finish()
1240 }
1241}
1242
1243impl<R: Runtime> Clone for Webview<R> {
1244 fn clone(&self) -> Self {
1245 Self {
1246 window: self.window.clone(),
1247 webview: self.webview.clone(),
1248 manager: self.manager.clone(),
1249 app_handle: self.app_handle.clone(),
1250 resources_table: self.resources_table.clone(),
1251 use_https_scheme: self.use_https_scheme,
1252 }
1253 }
1254}
1255
1256impl<R: Runtime> Hash for Webview<R> {
1257 fn hash<H: Hasher>(&self, state: &mut H) {
1259 self.webview.label.hash(state)
1260 }
1261}
1262
1263impl<R: Runtime> Eq for Webview<R> {}
1264impl<R: Runtime> PartialEq for Webview<R> {
1265 fn eq(&self, other: &Self) -> bool {
1267 self.webview.label.eq(&other.webview.label)
1268 }
1269}
1270
1271impl<R: Runtime> Webview<R> {
1273 pub(crate) fn new(
1275 window: Window<R>,
1276 webview: DetachedWebview<EventLoopMessage, R>,
1277 use_https_scheme: bool,
1278 ) -> Self {
1279 Self {
1280 manager: window.manager.clone(),
1281 app_handle: window.app_handle.clone(),
1282 window: Arc::new(Mutex::new(window)),
1283 webview,
1284 resources_table: Default::default(),
1285 use_https_scheme,
1286 }
1287 }
1288
1289 #[cfg(feature = "unstable")]
1293 #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
1294 pub fn builder<L: Into<String>>(label: L, url: WebviewUrl) -> WebviewBuilder<R> {
1295 WebviewBuilder::new(label.into(), url)
1296 }
1297
1298 pub fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> crate::Result<()> {
1300 self
1301 .webview
1302 .dispatcher
1303 .run_on_main_thread(f)
1304 .map_err(Into::into)
1305 }
1306
1307 pub fn label(&self) -> &str {
1309 &self.webview.label
1310 }
1311
1312 pub(crate) fn use_https_scheme(&self) -> bool {
1314 self.use_https_scheme
1315 }
1316
1317 pub fn on_webview_event<F: Fn(&WebviewEvent) + Send + 'static>(&self, f: F) {
1319 self
1320 .webview
1321 .dispatcher
1322 .on_webview_event(move |event| f(&event.clone().into()));
1323 }
1324
1325 pub fn resolve_command_scope<T: ScopeObject>(
1364 &self,
1365 plugin: &str,
1366 command: &str,
1367 ) -> crate::Result<Option<ResolvedScope<T>>> {
1368 let current_url = self.url()?;
1369 let is_local = self.is_local_url(¤t_url);
1370 let origin = if is_local {
1371 Origin::Local
1372 } else {
1373 Origin::Remote { url: current_url }
1374 };
1375
1376 let cmd_name = format!("plugin:{plugin}|{command}");
1377 let resolved_access = self
1378 .manager()
1379 .runtime_authority
1380 .lock()
1381 .unwrap()
1382 .resolve_access(&cmd_name, self.window().label(), self.label(), &origin);
1383
1384 if let Some(access) = resolved_access {
1385 let scope_ids = access
1386 .iter()
1387 .filter_map(|cmd| cmd.scope_id)
1388 .collect::<Vec<_>>();
1389
1390 let command_scope = CommandScope::resolve(self, scope_ids)?;
1391 let global_scope = GlobalScope::resolve(self, plugin)?;
1392
1393 Ok(Some(ResolvedScope {
1394 global_scope,
1395 command_scope,
1396 }))
1397 } else {
1398 Ok(None)
1399 }
1400 }
1401}
1402
1403#[cfg(desktop)]
1405impl<R: Runtime> Webview<R> {
1406 pub fn print(&self) -> crate::Result<()> {
1410 self.webview.dispatcher.print().map_err(Into::into)
1411 }
1412
1413 pub fn cursor_position(&self) -> crate::Result<PhysicalPosition<f64>> {
1422 self.app_handle.cursor_position()
1423 }
1424
1425 pub fn close(&self) -> crate::Result<()> {
1427 self.webview.dispatcher.close()?;
1428 self.manager().on_webview_close(self.label());
1429 Ok(())
1430 }
1431
1432 pub fn set_bounds(&self, bounds: tauri_runtime::dpi::Rect) -> crate::Result<()> {
1434 self
1435 .webview
1436 .dispatcher
1437 .set_bounds(bounds)
1438 .map_err(Into::into)
1439 }
1440
1441 pub fn set_size<S: Into<Size>>(&self, size: S) -> crate::Result<()> {
1443 self
1444 .webview
1445 .dispatcher
1446 .set_size(size.into())
1447 .map_err(Into::into)
1448 }
1449
1450 pub fn set_position<Pos: Into<Position>>(&self, position: Pos) -> crate::Result<()> {
1452 self
1453 .webview
1454 .dispatcher
1455 .set_position(position.into())
1456 .map_err(Into::into)
1457 }
1458
1459 pub fn set_focus(&self) -> crate::Result<()> {
1461 self.webview.dispatcher.set_focus().map_err(Into::into)
1462 }
1463
1464 pub fn hide(&self) -> crate::Result<()> {
1466 self.webview.dispatcher.hide().map_err(Into::into)
1467 }
1468
1469 pub fn show(&self) -> crate::Result<()> {
1471 self.webview.dispatcher.show().map_err(Into::into)
1472 }
1473
1474 pub fn reparent(&self, window: &Window<R>) -> crate::Result<()> {
1476 #[cfg(not(feature = "unstable"))]
1477 {
1478 if self.window_ref().is_webview_window() || window.is_webview_window() {
1479 return Err(crate::Error::CannotReparentWebviewWindow);
1480 }
1481 }
1482
1483 *self.window.lock().unwrap() = window.clone();
1484 self.webview.dispatcher.reparent(window.window.id)?;
1485 Ok(())
1486 }
1487
1488 pub fn set_auto_resize(&self, auto_resize: bool) -> crate::Result<()> {
1490 self
1491 .webview
1492 .dispatcher
1493 .set_auto_resize(auto_resize)
1494 .map_err(Into::into)
1495 }
1496
1497 pub fn bounds(&self) -> crate::Result<tauri_runtime::dpi::Rect> {
1499 self.webview.dispatcher.bounds().map_err(Into::into)
1500 }
1501
1502 pub fn position(&self) -> crate::Result<PhysicalPosition<i32>> {
1507 self.webview.dispatcher.position().map_err(Into::into)
1508 }
1509
1510 pub fn size(&self) -> crate::Result<PhysicalSize<u32>> {
1512 self.webview.dispatcher.size().map_err(Into::into)
1513 }
1514}
1515
1516impl<R: Runtime> Webview<R> {
1518 pub fn window(&self) -> Window<R> {
1520 self.window.lock().unwrap().clone()
1521 }
1522
1523 pub fn window_ref(&self) -> MutexGuard<'_, Window<R>> {
1525 self.window.lock().unwrap()
1526 }
1527
1528 pub(crate) fn window_label(&self) -> String {
1529 self.window_ref().label().to_string()
1530 }
1531
1532 #[cfg_attr(
1542 feature = "unstable",
1543 doc = r####"
1544```rust,no_run
1545use tauri::Manager;
1546
1547tauri::Builder::default()
1548 .setup(|app| {
1549 let main_webview = app.get_webview("main").unwrap();
1550 main_webview.with_webview(|webview| {
1551 #[cfg(target_os = "linux")]
1552 {
1553 // see <https://docs.rs/webkit2gtk/2.0.0/webkit2gtk/struct.WebView.html>
1554 // and <https://docs.rs/webkit2gtk/2.0.0/webkit2gtk/trait.WebViewExt.html>
1555 use webkit2gtk::WebViewExt;
1556 webview.inner().set_zoom_level(4.);
1557 }
1558
1559 #[cfg(windows)]
1560 unsafe {
1561 // see https://docs.rs/webview2-com/0.19.1/webview2_com/Microsoft/Web/WebView2/Win32/struct.ICoreWebView2Controller.html
1562 webview.controller().SetZoomFactor(4.).unwrap();
1563 }
1564
1565 #[cfg(target_os = "macos")]
1566 unsafe {
1567 let view: &objc2_web_kit::WKWebView = &*webview.inner().cast();
1568 let controller: &objc2_web_kit::WKUserContentController = &*webview.controller().cast();
1569 let window: &objc2_app_kit::NSWindow = &*webview.ns_window().cast();
1570
1571 view.setPageZoom(4.);
1572 controller.removeAllUserScripts();
1573 let bg_color = objc2_app_kit::NSColor::colorWithDeviceRed_green_blue_alpha(0.5, 0.2, 0.4, 1.);
1574 window.setBackgroundColor(Some(&bg_color));
1575 }
1576
1577 #[cfg(target_os = "android")]
1578 {
1579 use jni::objects::JValue;
1580 webview.jni_handle().exec(|env, _, webview| {
1581 env.call_method(webview, "zoomBy", "(F)V", &[JValue::Float(4.)]).unwrap();
1582 })
1583 }
1584 });
1585 Ok(())
1586});
1587```
1588 "####
1589 )]
1590 #[cfg(feature = "wry")]
1591 #[cfg_attr(docsrs, doc(feature = "wry"))]
1592 pub fn with_webview<F: FnOnce(PlatformWebview) + Send + 'static>(
1593 &self,
1594 f: F,
1595 ) -> crate::Result<()> {
1596 self
1597 .webview
1598 .dispatcher
1599 .with_webview(|w| f(PlatformWebview(*w.downcast().unwrap())))
1600 .map_err(Into::into)
1601 }
1602
1603 pub fn url(&self) -> crate::Result<Url> {
1605 self
1606 .webview
1607 .dispatcher
1608 .url()
1609 .map(|url| url.parse().map_err(crate::Error::InvalidUrl))?
1610 }
1611
1612 pub fn navigate(&self, url: Url) -> crate::Result<()> {
1614 self.webview.dispatcher.navigate(url).map_err(Into::into)
1615 }
1616
1617 pub fn reload(&self) -> crate::Result<()> {
1619 self.webview.dispatcher.reload().map_err(Into::into)
1620 }
1621
1622 fn is_local_url(&self, current_url: &Url) -> bool {
1623 let uses_https = current_url.scheme() == "https";
1624
1625 ({
1627 let protocol_url = self.manager().protocol_url(uses_https);
1628 current_url.scheme() == protocol_url.scheme()
1629 && current_url.domain() == protocol_url.domain()
1630 }) ||
1631
1632 self
1634 .manager()
1635 .get_url(uses_https)
1636 .make_relative(current_url)
1637 .is_some()
1638
1639 || ({
1641 let scheme = current_url.scheme();
1642 let protocols = self.manager().webview.uri_scheme_protocols.lock().unwrap();
1643
1644 #[cfg(all(not(windows), not(target_os = "android")))]
1645 let local = protocols.contains_key(scheme);
1646
1647 #[cfg(any(windows, target_os = "android"))]
1650 let local = {
1651 let protocol_url = self.manager().protocol_url(uses_https);
1652 let maybe_protocol = current_url
1653 .domain()
1654 .and_then(|d| d .split_once('.'))
1655 .unwrap_or_default()
1656 .0;
1657
1658 protocols.contains_key(maybe_protocol) && scheme == protocol_url.scheme()
1659 };
1660
1661 local
1662 })
1663 }
1664
1665 pub fn on_message(self, request: InvokeRequest, responder: Box<OwnedInvokeResponder<R>>) {
1667 let manager = self.manager_owned();
1668 let is_local = self.is_local_url(&request.url);
1669
1670 let expected = manager.invoke_key();
1672 if request.invoke_key != expected {
1673 #[cfg(feature = "tracing")]
1674 tracing::error!(
1675 "__TAURI_INVOKE_KEY__ expected {expected} but received {}",
1676 request.invoke_key
1677 );
1678
1679 #[cfg(not(feature = "tracing"))]
1680 eprintln!(
1681 "__TAURI_INVOKE_KEY__ expected {expected} but received {}",
1682 request.invoke_key
1683 );
1684
1685 return;
1686 }
1687
1688 let resolver = InvokeResolver::new(
1689 self.clone(),
1690 Arc::new(Mutex::new(Some(Box::new(
1691 move |webview: Webview<R>, cmd, response, callback, error| {
1692 responder(webview, cmd, response, callback, error);
1693 },
1694 )))),
1695 request.cmd.clone(),
1696 request.callback,
1697 request.error,
1698 );
1699
1700 #[cfg(mobile)]
1701 let app_handle = self.app_handle.clone();
1702
1703 let message = InvokeMessage::new(
1704 self,
1705 manager.state(),
1706 request.cmd.to_string(),
1707 request.body,
1708 request.headers,
1709 );
1710
1711 let acl_origin = if is_local {
1712 Origin::Local
1713 } else {
1714 Origin::Remote {
1715 url: request.url.clone(),
1716 }
1717 };
1718 let (resolved_acl, has_app_acl_manifest) = {
1719 let runtime_authority = manager.runtime_authority.lock().unwrap();
1720 let acl = runtime_authority.resolve_access(
1721 &request.cmd,
1722 message.webview.window_ref().label(),
1723 message.webview.label(),
1724 &acl_origin,
1725 );
1726 (acl, runtime_authority.has_app_manifest())
1727 };
1728
1729 let mut invoke = Invoke {
1730 message,
1731 resolver: resolver.clone(),
1732 acl: resolved_acl,
1733 };
1734
1735 let plugin_command = request.cmd.strip_prefix("plugin:").map(|raw_command| {
1736 let mut tokens = raw_command.split('|');
1737 let plugin = tokens.next().unwrap();
1739 let command = tokens.next().map(|c| c.to_string()).unwrap_or_default();
1740 (plugin, command)
1741 });
1742
1743 if (plugin_command.is_some() || has_app_acl_manifest)
1745 && request.cmd != crate::ipc::channel::FETCH_CHANNEL_DATA_COMMAND
1747 && invoke.acl.is_none()
1748 {
1749 #[cfg(debug_assertions)]
1750 {
1751 let (key, command_name) = plugin_command
1752 .clone()
1753 .unwrap_or_else(|| (tauri_utils::acl::APP_ACL_KEY, request.cmd.clone()));
1754 invoke.resolver.reject(
1755 manager
1756 .runtime_authority
1757 .lock()
1758 .unwrap()
1759 .resolve_access_message(
1760 key,
1761 &command_name,
1762 invoke.message.webview.window().label(),
1763 invoke.message.webview.label(),
1764 &acl_origin,
1765 ),
1766 );
1767 }
1768 #[cfg(not(debug_assertions))]
1769 invoke
1770 .resolver
1771 .reject(format!("Command {} not allowed by ACL", request.cmd));
1772 return;
1773 }
1774
1775 if let Some((plugin, command_name)) = plugin_command {
1776 invoke.message.command = command_name;
1777
1778 let command = invoke.message.command.clone();
1779
1780 #[cfg(mobile)]
1781 let message = invoke.message.clone();
1782
1783 #[allow(unused_mut)]
1784 let mut handled = manager.extend_api(plugin, invoke);
1785
1786 #[cfg(mobile)]
1787 {
1788 if !handled {
1789 handled = true;
1790
1791 fn load_channels<R: Runtime>(payload: &serde_json::Value, webview: &Webview<R>) {
1792 use std::str::FromStr;
1793
1794 if let serde_json::Value::Object(map) = payload {
1795 for v in map.values() {
1796 if let serde_json::Value::String(s) = v {
1797 let _ = crate::ipc::JavaScriptChannelId::from_str(s)
1798 .map(|id| id.channel_on::<R, ()>(webview.clone()));
1799 }
1800 }
1801 }
1802 }
1803
1804 let payload = message.payload.into_json();
1805 load_channels(&payload, &message.webview);
1807
1808 let resolver_ = resolver.clone();
1809 if let Err(e) = crate::plugin::mobile::run_command(
1810 plugin,
1811 &app_handle,
1812 heck::AsLowerCamelCase(message.command).to_string(),
1813 payload,
1814 move |response| match response {
1815 Ok(r) => resolver_.resolve(r),
1816 Err(e) => resolver_.reject(e),
1817 },
1818 ) {
1819 resolver.reject(e.to_string());
1820 return;
1821 }
1822 }
1823 }
1824
1825 if !handled {
1826 resolver.reject(format!("Command {command} not found"));
1827 }
1828 } else {
1829 let command = invoke.message.command.clone();
1830 let handled = manager.run_invoke_handler(invoke);
1831 if !handled {
1832 resolver.reject(format!("Command {command} not found"));
1833 }
1834 }
1835 }
1836
1837 pub fn eval(&self, js: impl Into<String>) -> crate::Result<()> {
1839 self
1840 .webview
1841 .dispatcher
1842 .eval_script(js.into())
1843 .map_err(Into::into)
1844 }
1845
1846 pub(crate) fn listen_js(
1848 &self,
1849 event: EventName<&str>,
1850 target: EventTarget,
1851 handler: CallbackFn,
1852 ) -> crate::Result<EventId> {
1853 let listeners = self.manager().listeners();
1854
1855 let id = listeners.next_event_id();
1856
1857 self.eval(crate::event::listen_js_script(
1858 listeners.listeners_object_name(),
1859 &serde_json::to_string(&target)?,
1860 event,
1861 id,
1862 handler,
1863 ))?;
1864
1865 listeners.listen_js(event, self.label(), target, id);
1866
1867 Ok(id)
1868 }
1869
1870 pub(crate) fn unlisten_js(&self, event: EventName<&str>, id: EventId) -> crate::Result<()> {
1872 let listeners = self.manager().listeners();
1873
1874 listeners.unlisten_js(event, id);
1875
1876 Ok(())
1877 }
1878
1879 pub(crate) fn emit_js(&self, emit_args: &EmitArgs, ids: &[u32]) -> crate::Result<()> {
1880 self.eval(crate::event::emit_js_script(
1881 self.manager().listeners().function_name(),
1882 emit_args,
1883 &serde_json::to_string(ids)?,
1884 )?)?;
1885 Ok(())
1886 }
1887
1888 #[cfg_attr(
1899 feature = "unstable",
1900 doc = r####"
1901```rust,no_run
1902use tauri::Manager;
1903tauri::Builder::default()
1904 .setup(|app| {
1905 #[cfg(debug_assertions)]
1906 app.get_webview("main").unwrap().open_devtools();
1907 Ok(())
1908 });
1909```
1910 "####
1911 )]
1912 #[cfg(any(debug_assertions, feature = "devtools"))]
1913 #[cfg_attr(docsrs, doc(cfg(any(debug_assertions, feature = "devtools"))))]
1914 pub fn open_devtools(&self) {
1915 self.webview.dispatcher.open_devtools();
1916 }
1917
1918 #[cfg_attr(
1930 feature = "unstable",
1931 doc = r####"
1932```rust,no_run
1933use tauri::Manager;
1934tauri::Builder::default()
1935 .setup(|app| {
1936 #[cfg(debug_assertions)]
1937 {
1938 let webview = app.get_webview("main").unwrap();
1939 webview.open_devtools();
1940 std::thread::spawn(move || {
1941 std::thread::sleep(std::time::Duration::from_secs(10));
1942 webview.close_devtools();
1943 });
1944 }
1945 Ok(())
1946 });
1947```
1948 "####
1949 )]
1950 #[cfg(any(debug_assertions, feature = "devtools"))]
1951 #[cfg_attr(docsrs, doc(cfg(any(debug_assertions, feature = "devtools"))))]
1952 pub fn close_devtools(&self) {
1953 self.webview.dispatcher.close_devtools();
1954 }
1955
1956 #[cfg_attr(
1968 feature = "unstable",
1969 doc = r####"
1970```rust,no_run
1971use tauri::Manager;
1972tauri::Builder::default()
1973 .setup(|app| {
1974 #[cfg(debug_assertions)]
1975 {
1976 let webview = app.get_webview("main").unwrap();
1977 if !webview.is_devtools_open() {
1978 webview.open_devtools();
1979 }
1980 }
1981 Ok(())
1982 });
1983```
1984 "####
1985 )]
1986 #[cfg(any(debug_assertions, feature = "devtools"))]
1987 #[cfg_attr(docsrs, doc(cfg(any(debug_assertions, feature = "devtools"))))]
1988 pub fn is_devtools_open(&self) -> bool {
1989 self
1990 .webview
1991 .dispatcher
1992 .is_devtools_open()
1993 .unwrap_or_default()
1994 }
1995
1996 pub fn set_zoom(&self, scale_factor: f64) -> crate::Result<()> {
2004 self
2005 .webview
2006 .dispatcher
2007 .set_zoom(scale_factor)
2008 .map_err(Into::into)
2009 }
2010
2011 pub fn set_background_color(&self, color: Option<Color>) -> crate::Result<()> {
2020 self
2021 .webview
2022 .dispatcher
2023 .set_background_color(color)
2024 .map_err(Into::into)
2025 }
2026
2027 pub fn clear_all_browsing_data(&self) -> crate::Result<()> {
2029 self
2030 .webview
2031 .dispatcher
2032 .clear_all_browsing_data()
2033 .map_err(Into::into)
2034 }
2035
2036 pub fn cookies_for_url(&self, url: Url) -> crate::Result<Vec<Cookie<'static>>> {
2050 self
2051 .webview
2052 .dispatcher
2053 .cookies_for_url(url)
2054 .map_err(Into::into)
2055 }
2056
2057 pub fn cookies(&self) -> crate::Result<Vec<Cookie<'static>>> {
2079 self.webview.dispatcher.cookies().map_err(Into::into)
2080 }
2081
2082 pub fn set_cookie(&self, cookie: Cookie<'_>) -> crate::Result<()> {
2088 self
2089 .webview
2090 .dispatcher
2091 .set_cookie(cookie)
2092 .map_err(Into::into)
2093 }
2094
2095 pub fn delete_cookie(&self, cookie: Cookie<'_>) -> crate::Result<()> {
2101 self
2102 .webview
2103 .dispatcher
2104 .delete_cookie(cookie)
2105 .map_err(Into::into)
2106 }
2107}
2108
2109impl<R: Runtime> Listener<R> for Webview<R> {
2110 #[cfg_attr(
2114 feature = "unstable",
2115 doc = r####"
2116```
2117use tauri::{Manager, Listener};
2118
2119tauri::Builder::default()
2120 .setup(|app| {
2121 let webview = app.get_webview("main").unwrap();
2122 webview.listen("component-loaded", move |event| {
2123 println!("webview just loaded a component");
2124 });
2125
2126 Ok(())
2127 });
2128```
2129 "####
2130 )]
2131 fn listen<F>(&self, event: impl Into<String>, handler: F) -> EventId
2132 where
2133 F: Fn(Event) + Send + 'static,
2134 {
2135 let event = EventName::new(event.into()).unwrap();
2136 self.manager.listen(
2137 event,
2138 EventTarget::Webview {
2139 label: self.label().to_string(),
2140 },
2141 handler,
2142 )
2143 }
2144
2145 fn once<F>(&self, event: impl Into<String>, handler: F) -> EventId
2149 where
2150 F: FnOnce(Event) + Send + 'static,
2151 {
2152 let event = EventName::new(event.into()).unwrap();
2153 self.manager.once(
2154 event,
2155 EventTarget::Webview {
2156 label: self.label().to_string(),
2157 },
2158 handler,
2159 )
2160 }
2161
2162 #[cfg_attr(
2166 feature = "unstable",
2167 doc = r####"
2168```
2169use tauri::{Manager, Listener};
2170
2171tauri::Builder::default()
2172 .setup(|app| {
2173 let webview = app.get_webview("main").unwrap();
2174 let webview_ = webview.clone();
2175 let handler = webview.listen("component-loaded", move |event| {
2176 println!("webview just loaded a component");
2177
2178 // we no longer need to listen to the event
2179 // we also could have used `webview.once` instead
2180 webview_.unlisten(event.id());
2181 });
2182
2183 // stop listening to the event when you do not need it anymore
2184 webview.unlisten(handler);
2185
2186 Ok(())
2187 });
2188```
2189 "####
2190 )]
2191 fn unlisten(&self, id: EventId) {
2192 self.manager.unlisten(id)
2193 }
2194}
2195
2196impl<R: Runtime> Emitter<R> for Webview<R> {}
2197
2198impl<R: Runtime> Manager<R> for Webview<R> {
2199 fn resources_table(&self) -> MutexGuard<'_, ResourceTable> {
2200 self
2201 .resources_table
2202 .lock()
2203 .expect("poisoned window resources table")
2204 }
2205}
2206
2207impl<R: Runtime> ManagerBase<R> for Webview<R> {
2208 fn manager(&self) -> &AppManager<R> {
2209 &self.manager
2210 }
2211
2212 fn manager_owned(&self) -> Arc<AppManager<R>> {
2213 self.manager.clone()
2214 }
2215
2216 fn runtime(&self) -> RuntimeOrDispatch<'_, R> {
2217 self.app_handle.runtime()
2218 }
2219
2220 fn managed_app_handle(&self) -> &AppHandle<R> {
2221 &self.app_handle
2222 }
2223}
2224
2225impl<'de, R: Runtime> CommandArg<'de, R> for Webview<R> {
2226 fn from_command(command: CommandItem<'de, R>) -> Result<Self, InvokeError> {
2228 Ok(command.message.webview())
2229 }
2230}
2231
2232pub struct ResolvedScope<T: ScopeObject> {
2234 command_scope: CommandScope<T>,
2235 global_scope: GlobalScope<T>,
2236}
2237
2238impl<T: ScopeObject> ResolvedScope<T> {
2239 pub fn global_scope(&self) -> &GlobalScope<T> {
2241 &self.global_scope
2242 }
2243
2244 pub fn command_scope(&self) -> &CommandScope<T> {
2246 &self.command_scope
2247 }
2248}
2249
2250#[cfg(test)]
2251mod tests {
2252 #[test]
2253 fn webview_is_send_sync() {
2254 crate::test_utils::assert_send::<super::Webview>();
2255 crate::test_utils::assert_sync::<super::Webview>();
2256 }
2257}