windows_webview/
reactor.rs1use super::*;
2use std::cell::RefCell;
3use std::rc::Rc;
4use windows_reactor::{
5 Callback, Component, ComponentContext, ElementRef, IntegrationError, IntoPayloadCallback, View,
6 ViewContext, WebView2,
7};
8
9pub fn webview(on_ready: impl IntoPayloadCallback<WebView>) -> View {
14 let on_ready = on_ready.into_payload_callback();
15 webview_result(move |result| match result {
16 Ok(webview) => {
17 _ = on_ready.call(webview);
18 }
19 Err(IntegrationError::Native(code)) => {
20 panic!("windows-webview Reactor integration failed: HRESULT({code:#010X})");
21 }
22 Err(IntegrationError::Unavailable) => {}
23 })
24}
25
26pub fn webview_result(
28 on_ready: impl IntoPayloadCallback<std::result::Result<WebView, IntegrationError>>,
29) -> View {
30 View::component::<WebViewHost>(on_ready.into_payload_callback())
31}
32
33struct WebViewHost {
34 control: ElementRef<WebView2>,
35 on_ready: Rc<RefCell<Callback<std::result::Result<WebView, IntegrationError>>>>,
36}
37
38impl Component for WebViewHost {
39 type Input = Callback<std::result::Result<WebView, IntegrationError>>;
40 type Message = ();
41
42 fn create(input: &Self::Input, _context: &ComponentContext<Self>) -> Self {
43 Self {
44 control: ElementRef::new(),
45 on_ready: Rc::new(RefCell::new(input.clone())),
46 }
47 }
48
49 fn input_changed(&mut self, input: &Self::Input, _context: &ComponentContext<Self>) {
50 *self.on_ready.borrow_mut() = input.clone();
51 }
52
53 fn view(&self, _input: &Self::Input, context: &mut ViewContext<Self>) -> View {
54 let control = self.control.clone();
55 let on_ready = Rc::clone(&self.on_ready);
56 context.use_effect("initialize", (), move || {
57 let callback = Rc::clone(&on_ready);
58 let accepted = control.request_core_web_view2(move |result| {
59 let result = result.and_then(|core| bridge(&core));
60 let callback = callback.borrow().clone();
61 _ = callback.call(result);
62 });
63 if !accepted {
64 let callback = on_ready.borrow().clone();
65 _ = callback.call(Err(IntegrationError::Unavailable));
66 }
67 None
68 });
69 WebView2::new().element_ref(&self.control).into()
70 }
71}
72
73fn bridge(core: &IUnknown) -> std::result::Result<WebView, IntegrationError> {
74 let interop: ICoreWebView2Interop2 = core.cast().map_err(integration_error)?;
75 let com_core: ICoreWebView2 =
76 unsafe { interop.GetComICoreWebView2() }.map_err(integration_error)?;
77 Ok(WebView::from_core(com_core))
78}
79
80fn integration_error(error: Error) -> IntegrationError {
81 IntegrationError::Native(error.code().0)
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87
88 #[test]
89 fn bridge_errors_preserve_the_hresult() {
90 let code = HRESULT(0x8000_4005_u32 as i32);
91 assert_eq!(
92 integration_error(Error::from_hresult(code)),
93 IntegrationError::Native(code.0)
94 );
95 }
96}