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::{frames::FrameSlot, 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 pub offscreen: bool,
34}
35
36impl Default for WebviewState {
37 fn default() -> Self {
38 Self {
39 url: String::new(),
40 title: String::new(),
41 loading: false,
42 waiting_for_response: false,
43 can_go_back: false,
44 can_go_forward: false,
45 history_index: 0,
46 crashed: false,
47 audio_muted: false,
48 audible: false,
49 user_agent: String::new(),
50 zoom_factor: 1.0,
51 zoom_level: 0.0,
52 web_contents_id: 0,
53 devtools_focused: false,
54 last_finished_url: String::new(),
55 offscreen: false,
56 }
57 }
58}
59
60#[derive(Clone, Default)]
62pub(crate) struct SharedState(Arc<Mutex<WebviewState>>);
63
64impl SharedState {
65 pub(crate) fn update(&self, update: impl FnOnce(&mut WebviewState)) {
66 let mut state = self.0.lock().unwrap_or_else(|error| error.into_inner());
67 update(&mut state);
68 }
69
70 pub(crate) fn snapshot(&self) -> WebviewState {
71 self.0
72 .lock()
73 .unwrap_or_else(|error| error.into_inner())
74 .clone()
75 }
76}
77
78pub struct Registry<R: Runtime> {
80 webviews: Mutex<HashMap<String, Webview<R>>>,
81 states: Mutex<HashMap<String, SharedState>>,
82 frames: Mutex<HashMap<String, FrameSlot>>,
83 pending: Mutex<HashSet<String>>,
84 css_sequence: Arc<AtomicU64>,
85 contents_sequence: Arc<AtomicI32>,
86 config: Config,
87}
88
89impl<R: Runtime> Registry<R> {
90 pub(crate) fn new(config: Config) -> Self {
91 Self {
92 webviews: Mutex::new(HashMap::new()),
93 states: Mutex::new(HashMap::new()),
94 frames: Mutex::new(HashMap::new()),
95 pending: Mutex::new(HashSet::new()),
96 css_sequence: Arc::new(AtomicU64::new(0)),
97 contents_sequence: Arc::new(AtomicI32::new(0)),
98 config,
99 }
100 }
101
102 pub(crate) fn config(&self) -> &Config {
103 &self.config
104 }
105
106 pub(crate) fn state(&self, label: &str) -> SharedState {
107 self.states
108 .lock()
109 .unwrap_or_else(|error| error.into_inner())
110 .entry(label.to_string())
111 .or_default()
112 .clone()
113 }
114
115 pub(crate) fn remove_state(&self, label: &str) -> Option<SharedState> {
116 self.states
117 .lock()
118 .unwrap_or_else(|error| error.into_inner())
119 .remove(label)
120 }
121
122 pub(crate) fn next_contents_id(&self) -> i32 {
123 self.contents_sequence.fetch_add(1, Ordering::Relaxed) + 1
124 }
125
126 pub(crate) fn frames(&self, label: &str) -> FrameSlot {
128 self.frames
129 .lock()
130 .unwrap_or_else(|error| error.into_inner())
131 .entry(label.to_string())
132 .or_default()
133 .clone()
134 }
135
136 pub(crate) fn remove_frames(&self, label: &str) {
137 self.frames
138 .lock()
139 .unwrap_or_else(|error| error.into_inner())
140 .remove(label);
141 }
142
143 pub(crate) fn frame_packet(&self, label: &str, full: bool) -> Vec<u8> {
146 let frames = self
147 .frames
148 .lock()
149 .unwrap_or_else(|error| error.into_inner())
150 .get(label)
151 .cloned();
152 frames.map(|frames| frames.packet(full)).unwrap_or_default()
153 }
154
155 pub(crate) fn mark_child(&self, label: &str) {
157 self.pending
158 .lock()
159 .unwrap_or_else(|error| error.into_inner())
160 .insert(label.to_string());
161 }
162
163 pub(crate) fn unmark_child(&self, label: &str) {
164 self.pending
165 .lock()
166 .unwrap_or_else(|error| error.into_inner())
167 .remove(label);
168 }
169
170 pub fn is_child(&self, label: &str) -> bool {
172 self.pending
173 .lock()
174 .unwrap_or_else(|error| error.into_inner())
175 .contains(label)
176 || self
177 .webviews
178 .lock()
179 .unwrap_or_else(|error| error.into_inner())
180 .contains_key(label)
181 }
182
183 pub(crate) fn insert(&self, label: &str, webview: Webview<R>) {
184 self.webviews
185 .lock()
186 .unwrap_or_else(|error| error.into_inner())
187 .insert(label.to_string(), webview);
188 }
189
190 pub(crate) fn remove(&self, label: &str) -> Option<Webview<R>> {
191 self.webviews
192 .lock()
193 .unwrap_or_else(|error| error.into_inner())
194 .remove(label)
195 }
196
197 pub fn get(&self, label: &str) -> Option<CefWebview<R>> {
199 self.webviews
200 .lock()
201 .unwrap_or_else(|error| error.into_inner())
202 .get(label)
203 .cloned()
204 .map(|webview| CefWebview {
205 webview,
206 css_sequence: Arc::clone(&self.css_sequence),
207 })
208 }
209
210 pub fn labels(&self) -> Vec<String> {
212 self.webviews
213 .lock()
214 .unwrap_or_else(|error| error.into_inner())
215 .keys()
216 .cloned()
217 .collect()
218 }
219}
220
221#[derive(Clone)]
223pub struct CefWebview<R: Runtime> {
224 pub(crate) webview: Webview<R>,
225 css_sequence: Arc<AtomicU64>,
226}
227
228impl<R: Runtime> CefWebview<R> {
229 pub fn label(&self) -> &str {
231 self.webview.label()
232 }
233
234 pub fn inner(&self) -> &Webview<R> {
236 &self.webview
237 }
238
239 pub fn eval(&self, js: impl Into<String>) -> tauri::Result<()> {
241 self.webview.eval(js)
242 }
243
244 pub fn insert_css(&self, css: &str) -> tauri::Result<String> {
246 let key = format!(
247 "cwv-css-{}",
248 self.css_sequence.fetch_add(1, Ordering::Relaxed)
249 );
250 self.webview.eval(scripts::insert_style(&key, css))?;
251 Ok(key)
252 }
253
254 pub fn remove_inserted_css(&self, key: &str) -> tauri::Result<()> {
256 self.webview.eval(scripts::remove_style(key))
257 }
258
259 pub fn navigate(&self, url: &str) -> Result<(), String> {
261 let url = Url::parse(url).map_err(|error| error.to_string())?;
262 self.webview
263 .navigate(url)
264 .map_err(|error| error.to_string())
265 }
266
267 pub fn reload(&self) -> tauri::Result<()> {
269 self.webview.reload()
270 }
271
272 #[cfg(any(debug_assertions, feature = "devtools"))]
274 pub fn open_devtools(&self) {
275 self.webview.open_devtools();
276 }
277
278 #[cfg(not(any(debug_assertions, feature = "devtools")))]
280 pub fn open_devtools(&self) {}
281
282 pub fn set_rect(&self, x: f64, y: f64, width: f64, height: f64) -> tauri::Result<()> {
284 self.webview.set_bounds(Rect {
285 position: LogicalPosition::new(x, y).into(),
286 size: LogicalSize::new(width, height).into(),
287 })
288 }
289
290 pub fn set_visible(&self, visible: bool) -> tauri::Result<()> {
292 if visible {
293 self.webview.show()
294 } else {
295 self.webview.hide()
296 }
297 }
298
299 pub fn close(&self) -> tauri::Result<()> {
301 self.webview.close()
302 }
303}