1use super::*;
2use crate::handler::subscription;
3
4#[derive(Clone, Copy, Debug, PartialEq, Eq)]
7pub enum MemoryUsageTargetLevel {
8 Normal,
10 Low,
12}
13
14impl MemoryUsageTargetLevel {
15 fn from_raw(value: COREWEBVIEW2_MEMORY_USAGE_TARGET_LEVEL) -> Self {
16 match value {
17 1 => Self::Low,
18 _ => Self::Normal,
19 }
20 }
21
22 fn to_raw(self) -> COREWEBVIEW2_MEMORY_USAGE_TARGET_LEVEL {
23 match self {
24 Self::Normal => 0,
25 Self::Low => 1,
26 }
27 }
28}
29
30#[derive(Clone, Debug)]
34pub struct NavigationRequest {
35 uri: String,
36 method: String,
37 headers: Vec<(String, String)>,
38 body: Vec<u8>,
39}
40
41impl NavigationRequest {
42 pub fn new(uri: &str) -> Self {
44 Self {
45 uri: uri.to_string(),
46 method: "GET".to_string(),
47 headers: Vec::new(),
48 body: Vec::new(),
49 }
50 }
51
52 pub fn method(mut self, method: &str) -> Self {
54 self.method = method.to_string();
55 self
56 }
57
58 pub fn header(mut self, name: &str, value: &str) -> Self {
60 self.headers.push((name.to_string(), value.to_string()));
61 self
62 }
63
64 pub fn body(mut self, body: impl Into<Vec<u8>>) -> Self {
66 self.body = body.into();
67 self
68 }
69}
70
71#[derive(Clone, Copy, Debug, PartialEq, Eq)]
74pub enum HostResourceAccessKind {
75 Deny,
77 Allow,
79 DenyCors,
82}
83
84impl HostResourceAccessKind {
85 fn to_raw(self) -> COREWEBVIEW2_HOST_RESOURCE_ACCESS_KIND {
86 match self {
87 Self::Deny => 0,
88 Self::Allow => 1,
89 Self::DenyCors => 2,
90 }
91 }
92}
93
94#[derive(Clone)]
97pub struct WebView(pub(crate) ICoreWebView2);
98
99impl WebView {
100 #[cfg(feature = "reactor")]
104 pub(crate) fn from_core(core: ICoreWebView2) -> Self {
105 Self(core)
106 }
107
108 pub fn navigate(&self, uri: &str) -> Result<()> {
110 let uri = HSTRING::from(uri);
111 unsafe { self.0.Navigate(&uri) }.ok()
112 }
113
114 pub fn navigate_to_string(&self, html: &str) -> Result<()> {
116 let html = HSTRING::from(html);
117 unsafe { self.0.NavigateToString(&html) }.ok()
118 }
119
120 pub fn navigate_with_request(&self, request: &NavigationRequest) -> Result<()> {
125 let source: ICoreWebView2_2 = self.0.cast()?;
126 let environment: ICoreWebView2Environment2 = unsafe { source.Environment()? }.cast()?;
127
128 let uri = HSTRING::from(&request.uri);
129 let method = HSTRING::from(&request.method);
130 let mut headers = String::new();
131 for (name, value) in &request.headers {
132 headers.push_str(name);
133 headers.push_str(": ");
134 headers.push_str(value);
135 headers.push_str("\r\n");
136 }
137 let headers = HSTRING::from(&headers);
138 let stream = if request.body.is_empty() {
139 None
140 } else {
141 unsafe { SHCreateMemStream(request.body.as_ptr(), request.body.len() as u32) }
142 };
143
144 unsafe {
145 let request =
146 environment.CreateWebResourceRequest(&uri, &method, stream.as_ref(), &headers)?;
147 source.NavigateWithWebResourceRequest(&request).ok()
148 }
149 }
150
151 pub fn reload(&self) -> Result<()> {
153 unsafe { self.0.Reload() }.ok()
154 }
155
156 pub fn open_dev_tools_window(&self) -> Result<()> {
159 unsafe { self.0.OpenDevToolsWindow() }.ok()
160 }
161
162 pub fn stop(&self) -> Result<()> {
164 unsafe { self.0.Stop() }.ok()
165 }
166
167 pub fn go_back(&self) -> Result<()> {
169 unsafe { self.0.GoBack() }.ok()
170 }
171
172 pub fn go_forward(&self) -> Result<()> {
174 unsafe { self.0.GoForward() }.ok()
175 }
176
177 pub fn source(&self) -> String {
179 unsafe { string::take_result(self.0.Source()) }
180 }
181
182 pub fn document_title(&self) -> String {
184 unsafe { string::take_result(self.0.DocumentTitle()) }
185 }
186
187 pub fn set_virtual_host_name_to_folder_mapping(
191 &self,
192 host_name: &str,
193 folder_path: &str,
194 access_kind: HostResourceAccessKind,
195 ) -> Result<()> {
196 let source: ICoreWebView2_3 = self.0.cast()?;
197 let host_name = HSTRING::from(host_name);
198 let folder_path = HSTRING::from(folder_path);
199 unsafe {
200 source
201 .SetVirtualHostNameToFolderMapping(&host_name, &folder_path, access_kind.to_raw())
202 .ok()
203 }
204 }
205
206 pub fn clear_virtual_host_name_to_folder_mapping(&self, host_name: &str) -> Result<()> {
209 let source: ICoreWebView2_3 = self.0.cast()?;
210 let host_name = HSTRING::from(host_name);
211 unsafe { source.ClearVirtualHostNameToFolderMapping(&host_name) }.ok()
212 }
213
214 pub fn cookie_manager(&self) -> Result<CookieManager> {
217 let source: ICoreWebView2_2 = self.0.cast()?;
218 Ok(CookieManager(unsafe { source.CookieManager()? }))
219 }
220
221 pub fn profile(&self) -> Result<Profile> {
224 let source: ICoreWebView2_13 = self.0.cast()?;
225 Ok(Profile(unsafe { source.Profile()? }))
226 }
227
228 pub fn contains_fullscreen_element(&self) -> bool {
231 unsafe { self.0.ContainsFullScreenElement() }.is_ok_and(|value| value.as_bool())
232 }
233
234 pub fn memory_usage_target_level(&self) -> Result<MemoryUsageTargetLevel> {
236 let source: ICoreWebView2_19 = self.0.cast()?;
237 Ok(MemoryUsageTargetLevel::from_raw(unsafe {
238 source.MemoryUsageTargetLevel()?
239 }))
240 }
241
242 pub fn set_memory_usage_target_level(&self, level: MemoryUsageTargetLevel) -> Result<()> {
246 let source: ICoreWebView2_19 = self.0.cast()?;
247 unsafe { source.SetMemoryUsageTargetLevel(level.to_raw()) }.ok()
248 }
249
250 pub fn settings(&self) -> Result<Settings> {
253 unsafe { Ok(Settings(self.0.Settings()?)) }
254 }
255
256 pub fn execute_script<F: FnOnce(Result<String>) + 'static>(
259 &self,
260 javascript: &str,
261 handler: F,
262 ) -> Result<()> {
263 let javascript = HSTRING::from(javascript);
264 let handler = handler::ExecuteScriptCompleted::create(handler);
265 unsafe { self.0.ExecuteScript(&javascript, &handler) }.ok()
266 }
267
268 pub fn call_dev_tools_protocol_method<F: FnOnce(Result<String>) + 'static>(
272 &self,
273 method: &str,
274 params_json: &str,
275 handler: F,
276 ) -> Result<()> {
277 let method = HSTRING::from(method);
278 let params = HSTRING::from(params_json);
279 let handler = handler::CallDevToolsProtocolMethodCompleted::create(handler);
280 unsafe {
281 self.0
282 .CallDevToolsProtocolMethod(&method, ¶ms, &handler)
283 }
284 .ok()
285 }
286
287 pub fn on_dev_tools_protocol_event<F>(
291 &self,
292 event_name: &str,
293 handler: F,
294 ) -> Result<EventRegistration>
295 where
296 F: FnMut(DevToolsProtocolEventReceivedArgs) + 'static,
297 {
298 let event_name = HSTRING::from(event_name);
299 let receiver = unsafe { self.0.GetDevToolsProtocolEventReceiver(&event_name)? };
300 let handler = handler::DevToolsProtocolEventReceived::create(handler);
301 let token = unsafe { receiver.add_DevToolsProtocolEventReceived(&handler)? };
302 Ok(EventRegistration::new(move || {
303 let _ = unsafe { receiver.remove_DevToolsProtocolEventReceived(token) };
304 }))
305 }
306
307 pub fn add_script_to_execute_on_document_created(&self, javascript: &str) -> Result<ScriptId> {
312 let javascript = HSTRING::from(javascript);
313 let slot = pump::slot();
314 let handler = handler::AddScriptCompleted::create(pump::slot_handler(&slot));
315 unsafe {
316 self.0
317 .AddScriptToExecuteOnDocumentCreated(&javascript, &handler)
318 .ok()?;
319 }
320 Ok(ScriptId(pump::wait(&slot)?))
321 }
322
323 pub fn remove_script_to_execute_on_document_created(&self, id: &ScriptId) -> Result<()> {
325 let id = HSTRING::from(&id.0);
326 unsafe { self.0.RemoveScriptToExecuteOnDocumentCreated(&id) }.ok()
327 }
328
329 subscription! {
330 on_navigation_starting(NavigationStartingArgs) =>
334 NavigationStarting, add_NavigationStarting / remove_NavigationStarting
335 }
336
337 subscription! {
338 on_navigation_completed(NavigationCompletedArgs) =>
340 NavigationCompleted, add_NavigationCompleted / remove_NavigationCompleted
341 }
342
343 subscription! {
344 on_process_failed(ProcessFailedArgs) =>
348 ProcessFailed, add_ProcessFailed / remove_ProcessFailed
349 }
350
351 pub fn on_contains_fullscreen_element_changed<F: FnMut(bool) + 'static>(
353 &self,
354 handler: F,
355 ) -> Result<EventRegistration> {
356 let handler = handler::ContainsFullScreenElementChanged::create(handler);
357 let token = unsafe { self.0.add_ContainsFullScreenElementChanged(&handler)? };
358 let source = self.0.clone();
359 Ok(EventRegistration::new(move || {
360 let _ = unsafe { source.remove_ContainsFullScreenElementChanged(token) };
361 }))
362 }
363
364 pub fn post_web_message_as_json(&self, json: &str) -> Result<()> {
368 let json = HSTRING::from(json);
369 unsafe { self.0.PostWebMessageAsJson(&json) }.ok()
370 }
371
372 pub fn post_web_message_as_string(&self, message: &str) -> Result<()> {
376 let message = HSTRING::from(message);
377 unsafe { self.0.PostWebMessageAsString(&message) }.ok()
378 }
379
380 subscription! {
381 on_web_message_received(WebMessageReceivedArgs) =>
384 WebMessageReceived, add_WebMessageReceived / remove_WebMessageReceived
385 }
386
387 subscription! {
388 on_content_loading(ContentLoadingArgs) =>
391 ContentLoading, add_ContentLoading / remove_ContentLoading
392 }
393
394 subscription! {
395 on_document_title_changed(String) =>
398 DocumentTitleChanged, add_DocumentTitleChanged / remove_DocumentTitleChanged
399 }
400
401 pub fn on_window_close_requested<F: FnMut() + 'static>(
405 &self,
406 handler: F,
407 ) -> Result<EventRegistration> {
408 let handler = handler::WindowCloseRequested::create(handler);
409 let token = unsafe { self.0.add_WindowCloseRequested(&handler)? };
410 let source = self.0.clone();
411 Ok(EventRegistration::new(move || {
412 let _ = unsafe { source.remove_WindowCloseRequested(token) };
413 }))
414 }
415
416 subscription! {
417 on_new_window_requested(NewWindowRequestedArgs) =>
422 NewWindowRequested, add_NewWindowRequested / remove_NewWindowRequested
423 }
424
425 subscription! {
426 on_permission_requested(PermissionRequestedArgs) =>
432 PermissionRequested, add_PermissionRequested / remove_PermissionRequested
433 }
434
435 pub fn on_download_starting<F: FnMut(DownloadStartingArgs) + 'static>(
439 &self,
440 handler: F,
441 ) -> Result<EventRegistration> {
442 let source: ICoreWebView2_4 = self.0.cast()?;
443 let handler = handler::DownloadStarting::create(handler);
444 let token = unsafe { source.add_DownloadStarting(&handler)? };
445 Ok(EventRegistration::new(move || {
446 let _ = unsafe { source.remove_DownloadStarting(token) };
447 }))
448 }
449
450 pub fn on_web_resource_requested<F>(
452 &self,
453 uri_filter: &str,
454 handler: F,
455 ) -> Result<EventRegistration>
456 where
457 F: FnMut(WebResourceRequest) -> Option<WebResourceResponse> + 'static,
458 {
459 let environment = unsafe { self.0.cast::<ICoreWebView2_2>()?.Environment()? };
460 let filter = HSTRING::from(uri_filter);
461 unsafe { protocol::add_requested_filter(&self.0, &filter)? };
462 let handler = protocol::WebResourceRequested::create(environment, handler);
463 let token = match unsafe { self.0.add_WebResourceRequested(&handler) } {
464 Ok(token) => token,
465 Err(err) => {
466 unsafe {
467 protocol::remove_requested_filter(&self.0, &filter);
468 };
469 return Err(err);
470 }
471 };
472 let source = self.0.clone();
473 Ok(EventRegistration::new(move || {
474 let _ = unsafe { source.remove_WebResourceRequested(token) };
475 unsafe {
476 protocol::remove_requested_filter(&source, &filter);
477 };
478 }))
479 }
480}