tauri_plugin_cef_webview/
registry.rs1use std::{
2 collections::{HashMap, HashSet},
3 sync::{
4 atomic::{AtomicI32, AtomicU64, Ordering},
5 Arc, Mutex,
6 },
7};
8
9use tauri::{webview::Webview, LogicalPosition, LogicalSize, Rect, Runtime, Url};
10
11use crate::{scripts, Config};
12
13#[derive(Clone, Debug)]
15pub struct WebviewState {
16 pub url: String,
17 pub title: String,
18 pub loading: bool,
19 pub waiting_for_response: bool,
20 pub can_go_back: bool,
21 pub can_go_forward: bool,
22 pub history_index: i32,
23 pub crashed: bool,
24 pub audio_muted: bool,
25 pub audible: bool,
26 pub user_agent: String,
27 pub zoom_factor: f64,
28 pub zoom_level: f64,
29 pub web_contents_id: i32,
30 pub devtools_focused: bool,
31 pub last_finished_url: String,
32}
33
34impl Default for WebviewState {
35 fn default() -> Self {
36 Self {
37 url: String::new(),
38 title: String::new(),
39 loading: false,
40 waiting_for_response: false,
41 can_go_back: false,
42 can_go_forward: false,
43 history_index: 0,
44 crashed: false,
45 audio_muted: false,
46 audible: false,
47 user_agent: String::new(),
48 zoom_factor: 1.0,
49 zoom_level: 0.0,
50 web_contents_id: 0,
51 devtools_focused: false,
52 last_finished_url: String::new(),
53 }
54 }
55}
56
57#[derive(Clone, Default)]
59pub(crate) struct SharedState(Arc<Mutex<WebviewState>>);
60
61impl SharedState {
62 pub(crate) fn update(&self, update: impl FnOnce(&mut WebviewState)) {
63 let mut state = self.0.lock().unwrap_or_else(|error| error.into_inner());
64 update(&mut state);
65 }
66
67 pub(crate) fn snapshot(&self) -> WebviewState {
68 self.0
69 .lock()
70 .unwrap_or_else(|error| error.into_inner())
71 .clone()
72 }
73}
74
75pub struct Registry<R: Runtime> {
77 webviews: Mutex<HashMap<String, Webview<R>>>,
78 states: Mutex<HashMap<String, SharedState>>,
79 pending: Mutex<HashSet<String>>,
80 css_sequence: Arc<AtomicU64>,
81 contents_sequence: Arc<AtomicI32>,
82 config: Config,
83}
84
85impl<R: Runtime> Registry<R> {
86 pub(crate) fn new(config: Config) -> Self {
87 Self {
88 webviews: Mutex::new(HashMap::new()),
89 states: Mutex::new(HashMap::new()),
90 pending: Mutex::new(HashSet::new()),
91 css_sequence: Arc::new(AtomicU64::new(0)),
92 contents_sequence: Arc::new(AtomicI32::new(0)),
93 config,
94 }
95 }
96
97 pub(crate) fn config(&self) -> &Config {
98 &self.config
99 }
100
101 pub(crate) fn state(&self, label: &str) -> SharedState {
102 self.states
103 .lock()
104 .unwrap_or_else(|error| error.into_inner())
105 .entry(label.to_string())
106 .or_default()
107 .clone()
108 }
109
110 pub(crate) fn remove_state(&self, label: &str) -> Option<SharedState> {
111 self.states
112 .lock()
113 .unwrap_or_else(|error| error.into_inner())
114 .remove(label)
115 }
116
117 pub(crate) fn next_contents_id(&self) -> i32 {
118 self.contents_sequence.fetch_add(1, Ordering::Relaxed) + 1
119 }
120
121 pub(crate) fn mark_child(&self, label: &str) {
123 self.pending
124 .lock()
125 .unwrap_or_else(|error| error.into_inner())
126 .insert(label.to_string());
127 }
128
129 pub(crate) fn unmark_child(&self, label: &str) {
130 self.pending
131 .lock()
132 .unwrap_or_else(|error| error.into_inner())
133 .remove(label);
134 }
135
136 pub fn is_child(&self, label: &str) -> bool {
138 self.pending
139 .lock()
140 .unwrap_or_else(|error| error.into_inner())
141 .contains(label)
142 || self
143 .webviews
144 .lock()
145 .unwrap_or_else(|error| error.into_inner())
146 .contains_key(label)
147 }
148
149 pub(crate) fn insert(&self, label: &str, webview: Webview<R>) {
150 self.webviews
151 .lock()
152 .unwrap_or_else(|error| error.into_inner())
153 .insert(label.to_string(), webview);
154 }
155
156 pub(crate) fn remove(&self, label: &str) -> Option<Webview<R>> {
157 self.webviews
158 .lock()
159 .unwrap_or_else(|error| error.into_inner())
160 .remove(label)
161 }
162
163 pub fn get(&self, label: &str) -> Option<CefWebview<R>> {
165 self.webviews
166 .lock()
167 .unwrap_or_else(|error| error.into_inner())
168 .get(label)
169 .cloned()
170 .map(|webview| CefWebview {
171 webview,
172 css_sequence: Arc::clone(&self.css_sequence),
173 })
174 }
175
176 pub fn labels(&self) -> Vec<String> {
178 self.webviews
179 .lock()
180 .unwrap_or_else(|error| error.into_inner())
181 .keys()
182 .cloned()
183 .collect()
184 }
185}
186
187#[derive(Clone)]
189pub struct CefWebview<R: Runtime> {
190 pub(crate) webview: Webview<R>,
191 css_sequence: Arc<AtomicU64>,
192}
193
194impl<R: Runtime> CefWebview<R> {
195 pub fn label(&self) -> &str {
197 self.webview.label()
198 }
199
200 pub fn inner(&self) -> &Webview<R> {
202 &self.webview
203 }
204
205 pub fn eval(&self, js: impl Into<String>) -> tauri::Result<()> {
207 self.webview.eval(js)
208 }
209
210 pub fn insert_css(&self, css: &str) -> tauri::Result<String> {
212 let key = format!(
213 "cwv-css-{}",
214 self.css_sequence.fetch_add(1, Ordering::Relaxed)
215 );
216 self.webview.eval(scripts::insert_style(&key, css))?;
217 Ok(key)
218 }
219
220 pub fn remove_inserted_css(&self, key: &str) -> tauri::Result<()> {
222 self.webview.eval(scripts::remove_style(key))
223 }
224
225 pub fn navigate(&self, url: &str) -> Result<(), String> {
227 let url = Url::parse(url).map_err(|error| error.to_string())?;
228 self.webview
229 .navigate(url)
230 .map_err(|error| error.to_string())
231 }
232
233 pub fn reload(&self) -> tauri::Result<()> {
235 self.webview.reload()
236 }
237
238 #[cfg(any(debug_assertions, feature = "devtools"))]
240 pub fn open_devtools(&self) {
241 self.webview.open_devtools();
242 }
243
244 #[cfg(not(any(debug_assertions, feature = "devtools")))]
246 pub fn open_devtools(&self) {}
247
248 pub fn set_rect(&self, x: f64, y: f64, width: f64, height: f64) -> tauri::Result<()> {
250 self.webview.set_bounds(Rect {
251 position: LogicalPosition::new(x, y).into(),
252 size: LogicalSize::new(width, height).into(),
253 })
254 }
255
256 pub fn set_visible(&self, visible: bool) -> tauri::Result<()> {
258 if visible {
259 self.webview.show()
260 } else {
261 self.webview.hide()
262 }
263 }
264
265 pub fn close(&self) -> tauri::Result<()> {
267 self.webview.close()
268 }
269}