webview2/
lib.rs

1#![doc = r###"
2Rust bindings for
3[WebView2](https://docs.microsoft.com/en-us/microsoft-edge/hosting/webview2):
4
5> The Microsoft Edge WebView2 control enables you to embed web technologies
6(HTML, CSS, and JavaScript) in your native applications. The WebView2 control
7uses Microsoft Edge (Chromium) as the rendering engine to display the web
8content in native applications. With WebView2, you may embed web code in
9different parts of your native application, or build the entire native
10application within a single WebView. For information on how to start building a
11WebView2 application, see [Get
12Started](https://docs.microsoft.com/en-us/microsoft-edge/webview2/#getting-started).
13
14# API
15
16The `webview2` crate contains high-level, idiomatic wrappers for the raw COM
17APIs, which can be found in the `webview2-sys` crate.
18
19The API mapping should be quite straightforward.
20
21The `CreateCoreWebView2EnvironmentWithDetails` function does not have a direct
22equivalent. It is replaced with a nicer `EnvironmentBuilder` API. The
23`GetAvailableCoreWebView2BrowserVersionString` and `CompareBrowserVersions`
24functions are also exposed through the builder.
25
26# Runtime
27
28The Edge browser from beta, dev or canary channels (>= 86.0.622.0) or the
29[Evergreen WebView2
30Runtime](https://docs.microsoft.com/en-us/microsoft-edge/webview2/concepts/distribution#understand-the-webview2-runtime-and-installer-preview)
31need to be installed for this to actually work. Or the
32[`build`](struct.EnvironmentBuilder.html#method.build) method will return an
33error.
34
35# WebView2Loader
36
37A binary library `WebView2Loader` from the WebView2 SDK need to be used, either
38the DLL `WebView2Loader.dll` or the static library `WebView2LoaderStatic.lib`.
39This brings some complexities:
40
41* When using the **gnu** toolchain, the static library does not seem to work so
42  the `WebView2Loader.dll` DLL is used. You need to make sure that the DLL can
43  be loaded at runtime, e.g. by **putting it alongside the built exe files**.
44
45* When using the **msvc** toolchain, the static library is used. Make sure you
46  have the **v142** toolset (or **visual studio 2019**), because the static
47  library seem to be built with visual studio 2019 and could not be correctly
48  linked by earlier versions of the visual studio. See [C++ binary compatibility
49  between Visual Studio 2015, 2017, and
50  2019](https://docs.microsoft.com/en-us/cpp/porting/binary-compat-2015-2017?view=vs-2019).
51
52# Examples
53
54See the `examples` directory, especially the heavily commented `win32` example.
55"###]
56#![cfg(windows)]
57// Caused by the `com_interface` macro.
58#![allow(clippy::cmp_null)]
59#![allow(clippy::type_complexity)]
60#![allow(clippy::upper_case_acronyms)]
61#![allow(non_camel_case_types)]
62
63use com::{interfaces::IUnknown, ComInterface, ComPtr, ComRc};
64use std::cell::{Cell, RefCell};
65use std::fmt;
66use std::io;
67use std::mem::{self, MaybeUninit};
68use std::path::Path;
69use std::ptr;
70use webview2_sys::*;
71use widestring::{NulError, WideCStr, WideCString};
72use winapi::shared::minwindef::*;
73use winapi::shared::ntdef::*;
74use winapi::shared::windef::*;
75use winapi::shared::winerror::{
76    E_FAIL, E_INVALIDARG, E_NOINTERFACE, FACILITY_WIN32, HRESULT_CODE, HRESULT_FROM_WIN32,
77    MAKE_HRESULT, SEVERITY_ERROR, SUCCEEDED, S_OK,
78};
79use winapi::um::combaseapi::{CoTaskMemAlloc, CoTaskMemFree};
80
81static DEFAULT_TARGET_COMPATIBLE_BROWSER_VERSION: &str = "89.0.765";
82
83/// Returns a pointer that implements the COM callback interface with the specified closure.
84/// Inspired by C++ Microsoft::WRT::Callback.
85#[macro_export]
86macro_rules! callback {
87    ($name:ident, move | $($arg:ident : $arg_type:ty),* $(,)?| -> $ret_type:ty { $($body:tt)* }) => {{
88        #[com::co_class(implements($name))]
89        struct Impl {
90            cb: Box<dyn Fn($($arg_type),*) -> $ret_type>,
91        }
92
93        impl $name for Impl {
94            unsafe fn invoke(&self, $($arg : $arg_type),*) -> $ret_type {
95                let r = std::panic::catch_unwind(std::panic::AssertUnwindSafe(move || {
96                    (self.cb)($($arg),*)
97                }));
98                match r {
99                    Ok(r) => r,
100                    Err(_) => {
101                        eprintln!("webview2: panic in callback function. Aborting because it's UB to unwind across FFI boundaries.");
102                        std::process::abort()
103                    }
104                }
105            }
106        }
107
108        impl Impl {
109            // It is never used.
110            pub fn new() -> Box<Self> {
111                unreachable!()
112            }
113            // Returns an owning ComPtr. Suitable for passing over FFI.
114            // The receiver is responsible for releasing it.
115            pub fn new_ptr(cb: impl Fn($($arg_type),*) -> $ret_type + 'static) -> com::ComPtr<dyn $name> {
116                let e = Self::allocate(Box::new(cb));
117                unsafe {
118                    use com::interfaces::IUnknown;
119                    e.add_ref();
120                    com::ComPtr::<dyn $name>::new(Box::into_raw(e) as _)
121                }
122            }
123        }
124
125        Impl::new_ptr(move |$($arg : $arg_type),*| -> $ret_type { $($body)* })
126    }}
127}
128
129// Call `AddRef` and convert to `ComRc`.
130unsafe fn add_ref_to_rc<T: ComInterface + ?Sized>(
131    ptr: *mut *mut <T as ComInterface>::VTable,
132) -> ComRc<T> {
133    let ptr = ComPtr::new(ptr);
134    ptr.add_ref();
135    ptr.upgrade()
136}
137
138include!("interfaces.rs");
139
140// Put it in a module so that the `EnvironmentOptionsImplClassFactory` struct
141// does not leak into our public API.
142mod environment_options {
143    use super::*;
144
145    #[com::co_class(implements(ICoreWebView2EnvironmentOptions))]
146    pub struct EnvironmentOptionsImpl {
147        additional_browser_arguments: RefCell<Option<WideCString>>,
148        language: RefCell<Option<WideCString>>,
149        target_compatible_browser_version: RefCell<Option<WideCString>>,
150        allow_single_sign_on_using_osprimary_account: Cell<bool>,
151    }
152
153    impl EnvironmentOptionsImpl {
154        fn new() -> Box<Self> {
155            unreachable!()
156        }
157
158        pub fn from_builder(
159            builder: &EnvironmentBuilder,
160        ) -> Result<*mut *mut ICoreWebView2EnvironmentOptionsVTable> {
161            let additional_browser_arguments = if let Some(v) = builder.additional_browser_arguments
162            {
163                Some(WideCString::from_str(v)?)
164            } else {
165                None
166            };
167            let language = if let Some(v) = builder.language {
168                Some(WideCString::from_str(v)?)
169            } else {
170                None
171            };
172            // Strangely, `CreateCoreWebView2EnvironmentWithDetails` will fail
173            // with 0x80070057 (`E_INVALIDARG`) if the
174            // `TargetCompatibleBrowserVersion` property is `NULL`.
175            let version = builder
176                .target_compatible_browser_version
177                .unwrap_or(DEFAULT_TARGET_COMPATIBLE_BROWSER_VERSION);
178            let version = Some(WideCString::from_str(version)?);
179
180            let allow_single_sign_on_using_osprimary_account =
181                builder.allow_single_sign_on_using_osprimary_account;
182
183            let instance = Self::allocate(
184                additional_browser_arguments.into(),
185                language.into(),
186                version.into(),
187                allow_single_sign_on_using_osprimary_account.into(),
188            );
189            unsafe {
190                instance.add_ref();
191            }
192            Ok(Box::into_raw(instance) as _)
193        }
194    }
195
196    fn clone_wide_cstr_with_co_task_mem_alloc(s: &WideCStr) -> LPWSTR {
197        let len = s.len() + 1;
198        unsafe {
199            let s1 = CoTaskMemAlloc(len * 2) as *mut u16;
200            assert!(!s1.is_null());
201            ptr::copy_nonoverlapping(s.as_ptr(), s1, len);
202            s1
203        }
204    }
205
206    impl ICoreWebView2EnvironmentOptions for EnvironmentOptionsImpl {
207        unsafe fn get_additional_browser_arguments(
208            &self,
209            /* out, retval */ value: *mut LPWSTR,
210        ) -> HRESULT {
211            if let Some(v) = self.additional_browser_arguments.borrow().as_ref() {
212                value.write(clone_wide_cstr_with_co_task_mem_alloc(&v));
213            } else {
214                value.write(ptr::null_mut());
215            }
216            S_OK
217        }
218
219        unsafe fn put_additional_browser_arguments(&self, /* in */ value: LPCWSTR) -> HRESULT {
220            *self.additional_browser_arguments.borrow_mut() =
221                Some(WideCString::from_ptr_str(value));
222            S_OK
223        }
224
225        unsafe fn get_language(&self, /* out, retval */ value: *mut LPWSTR) -> HRESULT {
226            if let Some(v) = self.language.borrow().as_ref() {
227                value.write(clone_wide_cstr_with_co_task_mem_alloc(&v));
228            } else {
229                value.write(ptr::null_mut());
230            }
231            S_OK
232        }
233
234        unsafe fn put_language(&self, /* in */ value: LPCWSTR) -> HRESULT {
235            *self.language.borrow_mut() = Some(WideCString::from_ptr_str(value));
236            S_OK
237        }
238
239        unsafe fn get_target_compatible_browser_version(
240            &self,
241            /* out, retval */ value: *mut LPWSTR,
242        ) -> HRESULT {
243            if let Some(v) = self.target_compatible_browser_version.borrow().as_ref() {
244                value.write(clone_wide_cstr_with_co_task_mem_alloc(&v));
245            } else {
246                value.write(ptr::null_mut());
247            }
248            S_OK
249        }
250
251        unsafe fn put_target_compatible_browser_version(
252            &self,
253            /* in */ value: LPCWSTR,
254        ) -> HRESULT {
255            *self.target_compatible_browser_version.borrow_mut() =
256                Some(WideCString::from_ptr_str(value));
257            S_OK
258        }
259
260        unsafe fn get_allow_single_sign_on_using_osprimary_account(&self, value: *mut i32) -> i32 {
261            value.write(if self.allow_single_sign_on_using_osprimary_account.get() {
262                1
263            } else {
264                0
265            });
266            S_OK
267        }
268
269        unsafe fn put_allow_single_sign_on_using_osprimary_account(&self, value: i32) -> i32 {
270            self.allow_single_sign_on_using_osprimary_account
271                .set(value != 0);
272            S_OK
273        }
274    }
275}
276
277pub fn get_available_browser_version_string(
278    browser_executable_folder: Option<&Path>,
279) -> Result<String> {
280    let browser_executable_folder = if let Some(p) = browser_executable_folder {
281        Some(WideCString::from_os_str(p)?)
282    } else {
283        None
284    };
285
286    let mut result = MaybeUninit::<LPWSTR>::uninit();
287
288    check_hresult(unsafe {
289        GetAvailableCoreWebView2BrowserVersionString(
290            browser_executable_folder
291                .as_ref()
292                .map_or(ptr::null(), |x| x.as_ptr()),
293            result.as_mut_ptr(),
294        )
295    })?;
296    let result = unsafe { result.assume_init() };
297    let result1 = unsafe { WideCStr::from_ptr_str(result) }
298        .to_string()
299        .map_err(|_| Error::new(E_FAIL));
300    unsafe { CoTaskMemFree(result as _) };
301    result1
302}
303
304pub fn compare_browser_versions(version1: &str, version2: &str) -> Result<std::cmp::Ordering> {
305    let version1 = WideCString::from_str(version1)?;
306    let version2 = WideCString::from_str(version2)?;
307    let mut result = MaybeUninit::<i32>::uninit();
308
309    check_hresult(unsafe {
310        CompareBrowserVersions(version1.as_ptr(), version2.as_ptr(), result.as_mut_ptr())
311    })?;
312    let result = unsafe { result.assume_init() };
313
314    Ok(result.cmp(&0))
315}
316
317/// A builder for calling the `CreateCoreWebView2EnvironmentWithOptions`
318/// function.
319///
320/// Use [Environment::builder()](./struct.Environment.html#method.builder) to create one.
321#[derive(Default)]
322pub struct EnvironmentBuilder<'a> {
323    browser_executable_folder: Option<&'a Path>,
324    user_data_folder: Option<&'a Path>,
325    additional_browser_arguments: Option<&'a str>,
326    language: Option<&'a str>,
327    target_compatible_browser_version: Option<&'a str>,
328    allow_single_sign_on_using_osprimary_account: bool,
329}
330
331impl<'a> EnvironmentBuilder<'a> {
332    // Hidden. Prefer `Environment::builder()`.
333    #[doc(hidden)]
334    #[inline]
335    pub fn new() -> Self {
336        Self::default()
337    }
338
339    #[inline]
340    pub fn with_browser_executable_folder(self, browser_executable_folder: &'a Path) -> Self {
341        Self {
342            browser_executable_folder: Some(browser_executable_folder),
343            ..self
344        }
345    }
346
347    #[inline]
348    pub fn with_user_data_folder(self, user_data_folder: &'a Path) -> Self {
349        Self {
350            user_data_folder: Some(user_data_folder),
351            ..self
352        }
353    }
354
355    #[inline]
356    pub fn with_additional_browser_arguments(self, additional_browser_arguments: &'a str) -> Self {
357        Self {
358            additional_browser_arguments: Some(additional_browser_arguments),
359            ..self
360        }
361    }
362
363    #[inline]
364    pub fn with_language(mut self, language: &'a str) -> Self {
365        self.language = Some(language);
366        self
367    }
368
369    #[inline]
370    pub fn with_target_compatible_browser_version(mut self, version: &'a str) -> Self {
371        self.target_compatible_browser_version = Some(version);
372        self
373    }
374
375    #[inline]
376    pub fn with_allow_single_sign_on_using_osprimary_account(mut self, allow: bool) -> Self {
377        self.allow_single_sign_on_using_osprimary_account = allow;
378        self
379    }
380
381    /// Get available browser version string (within the
382    /// browser_executable_folder if it is specified.)
383    #[inline]
384    pub fn get_available_browser_version_string(&self) -> Result<String> {
385        get_available_browser_version_string(self.browser_executable_folder)
386    }
387
388    #[deprecated = "use webview2::compare_browser_versions instead"]
389    #[inline]
390    pub fn compare_browser_versions(
391        &self,
392        version1: &str,
393        version2: &str,
394    ) -> Result<std::cmp::Ordering> {
395        compare_browser_versions(version1, version2)
396    }
397
398    #[inline]
399    pub fn build(
400        &self,
401        completed: impl FnOnce(Result<Environment>) -> Result<()> + 'static,
402    ) -> Result<()> {
403        let browser_executable_folder = if let Some(p) = self.browser_executable_folder {
404            Some(WideCString::from_os_str(p)?)
405        } else {
406            None
407        };
408        let user_data_folder = if let Some(p) = self.user_data_folder {
409            Some(WideCString::from_os_str(p)?)
410        } else {
411            None
412        };
413        let options = environment_options::EnvironmentOptionsImpl::from_builder(&self)?;
414
415        let completed = Cell::new(Some(completed));
416        let completed = callback!(
417            ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler,
418            move |result: HRESULT,
419                  created_environment: *mut *mut ICoreWebView2EnvironmentVTable|
420                  -> HRESULT {
421                let result = check_hresult(result).map(move |_| Environment {
422                    inner: unsafe { add_ref_to_rc(created_environment) },
423                });
424                if let Some(completed) = completed.take() {
425                    to_hresult(completed(result))
426                } else {
427                    S_OK
428                }
429            }
430        );
431
432        check_hresult(unsafe {
433            CreateCoreWebView2EnvironmentWithOptions(
434                browser_executable_folder
435                    .as_ref()
436                    .map(|p| p.as_ptr())
437                    .unwrap_or(ptr::null()),
438                user_data_folder
439                    .as_ref()
440                    .map(|p| p.as_ptr())
441                    .unwrap_or(ptr::null()),
442                options,
443                completed.as_raw(),
444            )
445        })
446    }
447}
448
449macro_rules! get {
450    ($get_method:ident, $T: ident) => {
451        pub fn $get_method(&self) -> Result<$T> {
452            let mut value = MaybeUninit::<$T>::uninit();
453            check_hresult(unsafe { self.inner.$get_method(value.as_mut_ptr()) })?;
454            Ok(unsafe { value.assume_init() })
455        }
456    };
457}
458
459macro_rules! put {
460    ($put_method:ident, $arg_name:ident : $T:ident) => {
461        pub fn $put_method(&self, $arg_name: $T) -> Result<()> {
462            check_hresult(unsafe { self.inner.$put_method($arg_name) })
463        }
464    };
465}
466
467macro_rules! get_interface {
468    ($get_method:ident, $T: ident) => {
469        pub fn $get_method(&self) -> Result<$T> {
470            let mut ppv = ptr::null_mut();
471            check_hresult(unsafe { self.inner.$get_method(&mut ppv) })?;
472            if ppv.is_null() {
473                Err(Error::new(E_FAIL))
474            } else {
475                Ok(unsafe {
476                    $T {
477                        inner: add_ref_to_rc(ppv),
478                    }
479                })
480            }
481        }
482    };
483}
484
485macro_rules! put_interface {
486    ($put_method:ident, $T: ident) => {
487        pub fn $put_method(&self, i: $T) -> Result<()> {
488            check_hresult(unsafe {
489                // Convert to `ComPtr` so that it is not automatically released.
490                self.inner.$put_method(ComPtr::from(i.inner).as_raw())
491            })
492        }
493    };
494}
495
496macro_rules! get_bool {
497    ($get_method:ident) => {
498        pub fn $get_method(&self) -> Result<bool> {
499            let mut enabled = MaybeUninit::<BOOL>::uninit();
500            check_hresult(unsafe { self.inner.$get_method(enabled.as_mut_ptr()) })?;
501            Ok(unsafe { enabled.assume_init() } != 0)
502        }
503    };
504}
505
506macro_rules! put_bool {
507    ($put_method:ident) => {
508        pub fn $put_method(&self, enabled: bool) -> Result<()> {
509            let enabled = if enabled { 1 } else { 0 };
510            check_hresult(unsafe { self.inner.$put_method(enabled) })
511        }
512    };
513}
514
515macro_rules! get_string {
516    ($get_string_method:ident) => {
517        pub fn $get_string_method(&self) -> Result<String> {
518            let mut result: LPWSTR = ptr::null_mut();
519            check_hresult(unsafe { self.inner.$get_string_method(&mut result) })?;
520            let result1 = unsafe { WideCStr::from_ptr_str(result) };
521            let result1 = result1.to_string().map_err(|_| Error { hresult: E_FAIL });
522            unsafe {
523                CoTaskMemFree(result as _);
524            }
525            result1
526        }
527    };
528}
529
530macro_rules! put_string {
531    ($put_string_method:ident) => {
532        pub fn $put_string_method(&self, message_string: &str) -> Result<()> {
533            let message = WideCString::from_str(message_string)?;
534            check_hresult(unsafe { self.inner.$put_string_method(message.as_ptr()) })
535        }
536    };
537}
538
539macro_rules! call {
540    ($method:ident) => {
541        pub fn $method(&self) -> Result<()> {
542            check_hresult(unsafe { self.inner.$method() })
543        }
544    };
545}
546
547macro_rules! add_event_handler_controller {
548    ($method:ident, $arg_type:ident) => {
549        pub fn $method(
550            &self,
551            event_handler: impl Fn(Controller) -> Result<()> + 'static,
552        ) -> Result<EventRegistrationToken> {
553            let mut token = MaybeUninit::<EventRegistrationToken>::uninit();
554
555            let event_handler = callback!(
556                $arg_type,
557                move |sender: *mut *mut ICoreWebView2ControllerVTable,
558                      _args: *mut *mut com::interfaces::iunknown::IUnknownVTable|
559                      -> HRESULT {
560                    let sender = Controller {
561                        inner: unsafe { add_ref_to_rc(sender) },
562                    };
563                    to_hresult(event_handler(sender))
564                }
565            );
566
567            check_hresult(unsafe {
568                self.inner
569                    .$method(event_handler.as_raw(), token.as_mut_ptr())
570            })?;
571            Ok(unsafe { token.assume_init() })
572        }
573    };
574}
575
576macro_rules! add_event_handler_view {
577    ($method:ident, $arg_type:ident) => {
578        pub fn $method(
579            &self,
580            event_handler: impl Fn(WebView) -> Result<()> + 'static,
581        ) -> Result<EventRegistrationToken> {
582            let mut token = MaybeUninit::<EventRegistrationToken>::uninit();
583
584            let event_handler = callback!(
585                $arg_type,
586                move |sender: *mut *mut ICoreWebView2VTable,
587                      _args: *mut *mut com::interfaces::iunknown::IUnknownVTable|
588                      -> HRESULT {
589                    let sender = WebView {
590                        inner: unsafe { add_ref_to_rc(sender) },
591                    };
592                    to_hresult(event_handler(sender))
593                }
594            );
595
596            check_hresult(unsafe {
597                self.inner
598                    .$method(event_handler.as_raw(), token.as_mut_ptr())
599            })?;
600            Ok(unsafe { token.assume_init() })
601        }
602    };
603}
604
605macro_rules! add_event_handler {
606    ($method:ident, $arg_type:ident, $arg_args:ident, $arg_args_type:ident) => {
607        pub fn $method(
608            &self,
609            handler: impl Fn(WebView, $arg_args) -> Result<()> + 'static,
610        ) -> Result<EventRegistrationToken> {
611            let mut token = MaybeUninit::<EventRegistrationToken>::uninit();
612
613            let handler = callback!($arg_type, move |sender: *mut *mut ICoreWebView2VTable,
614                                                     args: *mut *mut $arg_args_type|
615                  -> HRESULT {
616                let sender = WebView {
617                    inner: unsafe { add_ref_to_rc(sender) },
618                };
619                let args = $arg_args {
620                    inner: unsafe { add_ref_to_rc(args) },
621                };
622                to_hresult(handler(sender, args))
623            });
624
625            check_hresult(unsafe { self.inner.$method(handler.as_raw(), token.as_mut_ptr()) })?;
626            Ok(unsafe { token.assume_init() })
627        }
628    };
629}
630
631macro_rules! remove_event_handler {
632    ($method:ident) => {
633        pub fn $method(&self, token: EventRegistrationToken) -> Result<()> {
634            check_hresult(unsafe { self.inner.$method(token) })
635        }
636    };
637}
638
639impl Environment {
640    pub fn builder<'a>() -> EnvironmentBuilder<'a> {
641        EnvironmentBuilder::new()
642    }
643
644    pub fn create_controller(
645        &self,
646        parent_window: HWND,
647        completed: impl FnOnce(Result<Controller>) -> Result<()> + 'static,
648    ) -> Result<()> {
649        let completed = Cell::new(Some(completed));
650        let completed = callback!(
651            ICoreWebView2CreateCoreWebView2ControllerCompletedHandler,
652            move |result: HRESULT,
653                  created_host: *mut *mut ICoreWebView2ControllerVTable|
654                  -> HRESULT {
655                let result = check_hresult(result).map(|_| Controller {
656                    inner: unsafe { add_ref_to_rc(created_host) },
657                });
658                if let Some(completed) = completed.take() {
659                    to_hresult(completed(result))
660                } else {
661                    S_OK
662                }
663            }
664        );
665        check_hresult(unsafe {
666            self.inner
667                .create_core_web_view2_controller(parent_window, completed.as_raw())
668        })
669    }
670    pub fn create_web_resource_response(
671        &self,
672        content: Stream,
673        status_code: i32,
674        reason_phrase: &str,
675        headers: &str,
676    ) -> Result<WebResourceResponse> {
677        let content = ComPtr::from(content.into_inner());
678        let reason_phrase = WideCString::from_str(reason_phrase)?;
679        let headers = WideCString::from_str(headers)?;
680        let mut response =
681            MaybeUninit::<*mut *mut ICoreWebView2WebResourceResponseVTable>::uninit();
682        check_hresult(unsafe {
683            self.inner.create_web_resource_response(
684                content.as_raw(),
685                status_code,
686                reason_phrase.as_ptr(),
687                headers.as_ptr(),
688                response.as_mut_ptr(),
689            )
690        })?;
691        Ok(WebResourceResponse::from(unsafe {
692            ComRc::from_raw(response.assume_init())
693        }))
694    }
695    get_string!(get_browser_version_string);
696    pub fn add_new_browser_version_available(
697        &self,
698        event_handler: impl Fn(Environment) -> Result<()> + 'static,
699    ) -> Result<EventRegistrationToken> {
700        let mut token = MaybeUninit::<EventRegistrationToken>::uninit();
701
702        let event_handler = callback!(
703            ICoreWebView2NewBrowserVersionAvailableEventHandler,
704            move |sender: *mut *mut ICoreWebView2EnvironmentVTable,
705                  _args: *mut *mut com::interfaces::iunknown::IUnknownVTable|
706                  -> HRESULT {
707                let sender = Environment {
708                    inner: unsafe { add_ref_to_rc(sender) },
709                };
710                to_hresult(event_handler(sender))
711            }
712        );
713
714        check_hresult(unsafe {
715            self.inner
716                .add_new_browser_version_available(event_handler.as_raw(), token.as_mut_ptr())
717        })?;
718        Ok(unsafe { token.assume_init() })
719    }
720    remove_event_handler!(remove_new_browser_version_available);
721}
722
723impl Controller {
724    get_bool!(get_is_visible);
725    put_bool!(put_is_visible);
726    get!(get_bounds, RECT);
727    put!(put_bounds, bounds: RECT);
728    get!(get_zoom_factor, f64);
729    put!(put_zoom_factor, zoom_factor: f64);
730    add_event_handler_controller!(
731        add_zoom_factor_changed,
732        ICoreWebView2ZoomFactorChangedEventHandler
733    );
734    remove_event_handler!(remove_zoom_factor_changed);
735    pub fn set_bounds_and_zoom_factor(&self, bounds: RECT, zoom_factor: f64) -> Result<()> {
736        check_hresult(unsafe { self.inner.set_bounds_and_zoom_factor(bounds, zoom_factor) })
737    }
738    pub fn move_focus(&self, reason: MoveFocusReason) -> Result<()> {
739        check_hresult(unsafe { self.inner.move_focus(reason) })
740    }
741    pub fn add_move_focus_requested(
742        &self,
743        handler: impl Fn(Controller, MoveFocusRequestedEventArgs) -> Result<()> + 'static,
744    ) -> Result<EventRegistrationToken> {
745        let mut token = MaybeUninit::<EventRegistrationToken>::uninit();
746
747        let handler = callback!(
748            ICoreWebView2MoveFocusRequestedEventHandler,
749            move |sender: *mut *mut ICoreWebView2ControllerVTable,
750                  args: *mut *mut ICoreWebView2MoveFocusRequestedEventArgsVTable|
751                  -> HRESULT {
752                let sender = Controller {
753                    inner: unsafe { add_ref_to_rc(sender) },
754                };
755                let args = MoveFocusRequestedEventArgs {
756                    inner: unsafe { add_ref_to_rc(args) },
757                };
758                to_hresult(handler(sender, args))
759            }
760        );
761
762        check_hresult(unsafe {
763            self.inner
764                .add_move_focus_requested(handler.as_raw(), token.as_mut_ptr())
765        })?;
766        Ok(unsafe { token.assume_init() })
767    }
768    remove_event_handler!(remove_move_focus_requested);
769    add_event_handler_controller!(add_got_focus, ICoreWebView2FocusChangedEventHandler);
770    remove_event_handler!(remove_got_focus);
771    add_event_handler_controller!(add_lost_focus, ICoreWebView2FocusChangedEventHandler);
772    remove_event_handler!(remove_lost_focus);
773    pub fn add_accelerator_key_pressed(
774        &self,
775        handler: impl Fn(Controller, AcceleratorKeyPressedEventArgs) -> Result<()> + 'static,
776    ) -> Result<EventRegistrationToken> {
777        let mut token = MaybeUninit::<EventRegistrationToken>::uninit();
778
779        let handler = callback!(
780            ICoreWebView2AcceleratorKeyPressedEventHandler,
781            move |sender: *mut *mut ICoreWebView2ControllerVTable,
782                  args: *mut *mut ICoreWebView2AcceleratorKeyPressedEventArgsVTable|
783                  -> HRESULT {
784                let sender = Controller {
785                    inner: unsafe { add_ref_to_rc(sender) },
786                };
787                let args = AcceleratorKeyPressedEventArgs {
788                    inner: unsafe { add_ref_to_rc(args) },
789                };
790                to_hresult(handler(sender, args))
791            }
792        );
793
794        check_hresult(unsafe {
795            self.inner
796                .add_accelerator_key_pressed(handler.as_raw(), token.as_mut_ptr())
797        })?;
798        Ok(unsafe { token.assume_init() })
799    }
800    remove_event_handler!(remove_accelerator_key_pressed);
801    get!(get_parent_window, HWND);
802    put!(put_parent_window, top_level_window: HWND);
803    call!(notify_parent_window_position_changed);
804    call!(close);
805    pub fn get_webview(&self) -> Result<WebView> {
806        let mut ppv: *mut *mut ICoreWebView2VTable = ptr::null_mut();
807        check_hresult(unsafe { self.inner.get_core_web_view2(&mut ppv) })?;
808        Ok(WebView {
809            inner: unsafe { add_ref_to_rc(ppv) },
810        })
811    }
812    pub fn get_controller2(&self) -> Result<Controller2> {
813        let inner = self
814            .inner
815            .get_interface::<dyn ICoreWebView2Controller2>()
816            .ok_or_else(|| Error::new(E_NOINTERFACE))?;
817        Ok(Controller2 { inner })
818    }
819}
820
821impl Controller2 {
822    get!(get_default_background_color, Color);
823    put!(put_default_background_color, color: Color);
824}
825
826impl WebView {
827    pub fn get_settings(&self) -> Result<Settings> {
828        let mut ppv: *mut *mut ICoreWebView2SettingsVTable = ptr::null_mut();
829        check_hresult(unsafe { self.inner.get_settings(&mut ppv) })?;
830        Ok(Settings {
831            inner: unsafe { add_ref_to_rc(ppv) },
832        })
833    }
834    get_string!(get_source);
835    put_string!(navigate);
836    put_string!(navigate_to_string);
837    add_event_handler!(
838        add_navigation_starting,
839        ICoreWebView2NavigationStartingEventHandler,
840        NavigationStartingEventArgs,
841        ICoreWebView2NavigationStartingEventArgsVTable
842    );
843    remove_event_handler!(remove_navigation_starting);
844    add_event_handler!(
845        add_content_loading,
846        ICoreWebView2ContentLoadingEventHandler,
847        ContentLoadingEventArgs,
848        ICoreWebView2ContentLoadingEventArgsVTable
849    );
850    remove_event_handler!(remove_content_loading);
851    add_event_handler!(
852        add_source_changed,
853        ICoreWebView2SourceChangedEventHandler,
854        SourceChangedEventArgs,
855        ICoreWebView2SourceChangedEventArgsVTable
856    );
857    remove_event_handler!(remove_source_changed);
858    add_event_handler_view!(add_history_changed, ICoreWebView2HistoryChangedEventHandler);
859    remove_event_handler!(remove_history_changed);
860    add_event_handler!(
861        add_navigation_completed,
862        ICoreWebView2NavigationCompletedEventHandler,
863        NavigationCompletedEventArgs,
864        ICoreWebView2NavigationCompletedEventArgsVTable
865    );
866    remove_event_handler!(remove_navigation_completed);
867    add_event_handler!(
868        add_frame_navigation_starting,
869        ICoreWebView2NavigationStartingEventHandler,
870        NavigationStartingEventArgs,
871        ICoreWebView2NavigationStartingEventArgsVTable
872    );
873    remove_event_handler!(remove_frame_navigation_starting);
874    add_event_handler!(
875        add_script_dialog_opening,
876        ICoreWebView2ScriptDialogOpeningEventHandler,
877        ScriptDialogOpeningEventArgs,
878        ICoreWebView2ScriptDialogOpeningEventArgsVTable
879    );
880    remove_event_handler!(remove_script_dialog_opening);
881    add_event_handler!(
882        add_permission_requested,
883        ICoreWebView2PermissionRequestedEventHandler,
884        PermissionRequestedEventArgs,
885        ICoreWebView2PermissionRequestedEventArgsVTable
886    );
887    remove_event_handler!(remove_permission_requested);
888    add_event_handler!(
889        add_process_failed,
890        ICoreWebView2ProcessFailedEventHandler,
891        ProcessFailedEventArgs,
892        ICoreWebView2ProcessFailedEventArgsVTable
893    );
894    remove_event_handler!(remove_process_failed);
895    // Don't take an `Option<impl FnOnce>`:
896    // https://users.rust-lang.org/t/solved-how-to-pass-none-to-a-function-when-an-option-closure-is-expected/10956/8
897    pub fn add_script_to_execute_on_document_created(
898        &self,
899        script: &str,
900        callback: impl FnOnce(String) -> Result<()> + 'static,
901    ) -> Result<()> {
902        let script = WideCString::from_str(script)?;
903        let callback = Cell::new(Some(callback));
904        let callback = callback!(
905            ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler,
906            move |error_code: HRESULT, id: LPCWSTR| -> HRESULT {
907                to_hresult(check_hresult(error_code).and_then(|_| {
908                    let id = unsafe { WideCStr::from_ptr_str(id) }
909                        .to_string()
910                        .map_err(|_| Error::new(E_FAIL))?;
911                    if let Some(callback) = callback.take() {
912                        callback(id)
913                    } else {
914                        Ok(())
915                    }
916                }))
917            }
918        );
919        check_hresult(unsafe {
920            self.inner
921                .add_script_to_execute_on_document_created(script.as_ptr(), callback.as_raw())
922        })
923    }
924    pub fn remove_script_to_execute_on_document_created(&self, id: &str) -> Result<()> {
925        let id = WideCString::from_str(id)?;
926        check_hresult(unsafe {
927            self.inner
928                .remove_script_to_execute_on_document_created(id.as_ptr())
929        })
930    }
931    pub fn execute_script(
932        &self,
933        script: &str,
934        callback: impl FnOnce(String) -> Result<()> + 'static,
935    ) -> Result<()> {
936        let script = WideCString::from_str(script)?;
937        let callback = Cell::new(Some(callback));
938        let callback = callback!(
939            ICoreWebView2ExecuteScriptCompletedHandler,
940            move |error_code: HRESULT, result_object_as_json: LPCWSTR| -> HRESULT {
941                to_hresult(check_hresult(error_code).and_then(|_| {
942                    let result_object_as_json_string =
943                        unsafe { WideCStr::from_ptr_str(result_object_as_json) }
944                            .to_string()
945                            .map_err(|_| Error::new(E_FAIL))?;
946                    if let Some(callback) = callback.take() {
947                        callback(result_object_as_json_string)
948                    } else {
949                        Ok(())
950                    }
951                }))
952            }
953        );
954        check_hresult(unsafe {
955            self.inner
956                .execute_script(script.as_ptr(), callback.as_raw())
957        })
958    }
959    add_event_handler_view!(
960        add_document_title_changed,
961        ICoreWebView2DocumentTitleChangedEventHandler
962    );
963    remove_event_handler!(remove_document_title_changed);
964    pub fn capture_preview(
965        &self,
966        image_format: CapturePreviewImageFormat,
967        image_stream: Stream,
968        handler: impl FnOnce(Result<()>) -> Result<()> + 'static,
969    ) -> Result<()> {
970        let handler = Cell::new(Some(handler));
971        let handler = callback!(
972            ICoreWebView2CapturePreviewCompletedHandler,
973            move |result: HRESULT| -> HRESULT {
974                if let Some(handler) = handler.take() {
975                    to_hresult(handler(check_hresult(result)))
976                } else {
977                    S_OK
978                }
979            }
980        );
981        let image_stream = ComPtr::from(image_stream.inner);
982
983        check_hresult(unsafe {
984            self.inner
985                .capture_preview(image_format, image_stream.as_raw(), handler.as_raw())
986        })
987    }
988    call!(reload);
989    put_string!(post_web_message_as_json);
990    put_string!(post_web_message_as_string);
991    add_event_handler!(
992        add_web_message_received,
993        ICoreWebView2WebMessageReceivedEventHandler,
994        WebMessageReceivedEventArgs,
995        ICoreWebView2WebMessageReceivedEventArgsVTable
996    );
997    remove_event_handler!(remove_web_message_received);
998    // TODO: call_dev_tools_protocol_method
999    get!(get_browser_process_id, u32);
1000    get_bool!(get_can_go_back);
1001    get_bool!(get_can_go_forward);
1002    call!(go_back);
1003    call!(go_forward);
1004    // TODO: get_dev_tools_protocol_event_receiver
1005    call!(stop);
1006    add_event_handler!(
1007        add_new_window_requested,
1008        ICoreWebView2NewWindowRequestedEventHandler,
1009        NewWindowRequestedEventArgs,
1010        ICoreWebView2NewWindowRequestedEventArgsVTable
1011    );
1012    remove_event_handler!(remove_new_window_requested);
1013    get_string!(get_document_title);
1014    // TODO: add_host_object_to_script ??
1015    // TODO: remove_host_object_to_script ??
1016    call!(open_dev_tools_window);
1017    add_event_handler_view!(
1018        add_contains_full_screen_element_changed,
1019        ICoreWebView2ContainsFullScreenElementChangedEventHandler
1020    );
1021    remove_event_handler!(remove_contains_full_screen_element_changed);
1022    get_bool!(get_contains_full_screen_element);
1023    add_event_handler!(
1024        add_web_resource_requested,
1025        ICoreWebView2WebResourceRequestedEventHandler,
1026        WebResourceRequestedEventArgs,
1027        ICoreWebView2WebResourceRequestedEventArgsVTable
1028    );
1029    remove_event_handler!(remove_web_resource_requested);
1030    pub fn add_web_resource_requested_filter(
1031        &self,
1032        uri: &str,
1033        resource_context: WebResourceContext,
1034    ) -> Result<()> {
1035        let uri = WideCString::from_str(uri)?;
1036        check_hresult(unsafe {
1037            self.inner
1038                .add_web_resource_requested_filter(uri.as_ptr(), resource_context)
1039        })
1040    }
1041    pub fn remove_web_resource_requested_filter(
1042        &self,
1043        uri: &str,
1044        resource_context: WebResourceContext,
1045    ) -> Result<()> {
1046        let uri = WideCString::from_str(uri)?;
1047        check_hresult(unsafe {
1048            self.inner
1049                .remove_web_resource_requested_filter(uri.as_ptr(), resource_context)
1050        })
1051    }
1052    add_event_handler_view!(
1053        add_window_close_requested,
1054        ICoreWebView2WindowCloseRequestedEventHandler
1055    );
1056    remove_event_handler!(remove_window_close_requested);
1057}
1058
1059impl Settings {
1060    get_bool!(get_is_script_enabled);
1061    put_bool!(put_is_script_enabled);
1062
1063    get_bool!(get_is_web_message_enabled);
1064    put_bool!(put_is_web_message_enabled);
1065
1066    get_bool!(get_are_default_script_dialogs_enabled);
1067    put_bool!(put_are_default_script_dialogs_enabled);
1068
1069    get_bool!(get_is_status_bar_enabled);
1070    put_bool!(put_is_status_bar_enabled);
1071
1072    get_bool!(get_are_dev_tools_enabled);
1073    put_bool!(put_are_dev_tools_enabled);
1074
1075    get_bool!(get_are_default_context_menus_enabled);
1076    put_bool!(put_are_default_context_menus_enabled);
1077
1078    get_bool!(get_are_host_objects_allowed);
1079    put_bool!(put_are_host_objects_allowed);
1080
1081    get_bool!(get_is_zoom_control_enabled);
1082    put_bool!(put_is_zoom_control_enabled);
1083
1084    get_bool!(get_is_built_in_error_page_enabled);
1085    put_bool!(put_is_built_in_error_page_enabled);
1086}
1087
1088impl ContentLoadingEventArgs {
1089    get_bool!(get_is_error_page);
1090    get!(get_navigation_id, u64);
1091}
1092
1093impl WebMessageReceivedEventArgs {
1094    get_string!(get_source);
1095    get_string!(try_get_web_message_as_string);
1096    get_string!(get_web_message_as_json);
1097}
1098
1099impl HttpHeadersCollectionIterator {
1100    pub fn get_current_header(&self) -> Result<(String, String)> {
1101        let mut name = MaybeUninit::<LPWSTR>::uninit();
1102        let mut value = MaybeUninit::<LPWSTR>::uninit();
1103        unsafe {
1104            check_hresult(
1105                self.inner
1106                    .get_current_header(name.as_mut_ptr(), value.as_mut_ptr()),
1107            )?;
1108            let name = name.assume_init();
1109            let value = value.assume_init();
1110            let name1 = WideCStr::from_ptr_str(name)
1111                .to_string()
1112                .map_err(|_| Error::new(E_FAIL));
1113            let value1 = WideCStr::from_ptr_str(value)
1114                .to_string()
1115                .map_err(|_| Error::new(E_FAIL));
1116
1117            CoTaskMemFree(name as _);
1118            CoTaskMemFree(value as _);
1119
1120            Ok((name1?, value1?))
1121        }
1122    }
1123    get_bool!(get_has_current_header);
1124    get_bool!(move_next);
1125}
1126
1127impl Iterator for HttpHeadersCollectionIterator {
1128    type Item = (String, String);
1129
1130    fn next(&mut self) -> Option<(String, String)> {
1131        if self.get_has_current_header() != Ok(true) {
1132            return None;
1133        }
1134        let v = self.get_current_header().ok();
1135        let _ = self.move_next();
1136        v
1137    }
1138}
1139
1140impl HttpRequestHeaders {
1141    pub fn get_header(&self, name: &str) -> Result<String> {
1142        let name = WideCString::from_str(name)?;
1143        let mut value = MaybeUninit::<LPWSTR>::uninit();
1144        unsafe {
1145            check_hresult(self.inner.get_header(name.as_ptr(), value.as_mut_ptr()))?;
1146            let value = value.assume_init();
1147            let value1 = WideCStr::from_ptr_str(value)
1148                .to_string()
1149                .map_err(|_| Error::new(E_FAIL));
1150
1151            CoTaskMemFree(value as _);
1152
1153            value1
1154        }
1155    }
1156    pub fn get_headers(&self, name: &str) -> Result<HttpHeadersCollectionIterator> {
1157        let name = WideCString::from_str(name)?;
1158        let mut iterator: *mut *mut ICoreWebView2HttpHeadersCollectionIteratorVTable =
1159            ptr::null_mut();
1160        check_hresult(unsafe { self.inner.get_headers(name.as_ptr(), &mut iterator) })?;
1161        Ok(HttpHeadersCollectionIterator {
1162            inner: unsafe { add_ref_to_rc(iterator) },
1163        })
1164    }
1165    pub fn contains(&self, name: &str) -> Result<bool> {
1166        let name = WideCString::from_str(name)?;
1167        let mut result = MaybeUninit::<BOOL>::uninit();
1168        check_hresult(unsafe { self.inner.contains(name.as_ptr(), result.as_mut_ptr()) })?;
1169        Ok(unsafe { result.assume_init() } != 0)
1170    }
1171    pub fn set_header(&self, name: &str, value: &str) -> Result<()> {
1172        let name = WideCString::from_str(name)?;
1173        let value = WideCString::from_str(value)?;
1174        check_hresult(unsafe { self.inner.set_header(name.as_ptr(), value.as_ptr()) })
1175    }
1176    put_string!(remove_header);
1177    get_interface!(get_iterator, HttpHeadersCollectionIterator);
1178}
1179
1180impl HttpResponseHeaders {
1181    pub fn get_header(&self, name: &str) -> Result<String> {
1182        let name = WideCString::from_str(name)?;
1183        let mut value = MaybeUninit::<LPWSTR>::uninit();
1184        unsafe {
1185            check_hresult(self.inner.get_header(name.as_ptr(), value.as_mut_ptr()))?;
1186            let value = value.assume_init();
1187            let value1 = WideCStr::from_ptr_str(value)
1188                .to_string()
1189                .map_err(|_| Error::new(E_FAIL));
1190
1191            CoTaskMemFree(value as _);
1192
1193            value1
1194        }
1195    }
1196    pub fn contains(&self, name: &str) -> Result<bool> {
1197        let name = WideCString::from_str(name)?;
1198        let mut result = MaybeUninit::<BOOL>::uninit();
1199        check_hresult(unsafe { self.inner.contains(name.as_ptr(), result.as_mut_ptr()) })?;
1200        Ok(unsafe { result.assume_init() } != 0)
1201    }
1202    pub fn append_header(&self, name: &str, value: &str) -> Result<()> {
1203        let name = WideCString::from_str(name)?;
1204        let value = WideCString::from_str(value)?;
1205        check_hresult(unsafe { self.inner.append_header(name.as_ptr(), value.as_ptr()) })
1206    }
1207    pub fn get_headers(&self, name: &str) -> Result<HttpHeadersCollectionIterator> {
1208        let name = WideCString::from_str(name)?;
1209        let mut iterator: *mut *mut ICoreWebView2HttpHeadersCollectionIteratorVTable =
1210            ptr::null_mut();
1211        check_hresult(unsafe { self.inner.get_headers(name.as_ptr(), &mut iterator) })?;
1212        Ok(HttpHeadersCollectionIterator {
1213            inner: unsafe { add_ref_to_rc(iterator) },
1214        })
1215    }
1216    get_interface!(get_iterator, HttpHeadersCollectionIterator);
1217}
1218
1219impl Deferral {
1220    call!(complete);
1221}
1222
1223impl WebResourceRequest {
1224    get_string!(get_uri);
1225    put_string!(put_uri);
1226    get_string!(get_method);
1227    put_string!(put_method);
1228    get_interface!(get_content, Stream);
1229    put_interface!(put_content, Stream);
1230    get_interface!(get_headers, HttpRequestHeaders);
1231}
1232
1233impl WebResourceResponse {
1234    get_interface!(get_content, Stream);
1235    put_interface!(put_content, Stream);
1236    get_interface!(get_headers, HttpResponseHeaders);
1237    get!(get_status_code, i32);
1238    put!(put_status_code, status_code: i32);
1239    get_string!(get_reason_phrase);
1240    put_string!(put_reason_phrase);
1241}
1242
1243impl WebResourceRequestedEventArgs {
1244    get_interface!(get_request, WebResourceRequest);
1245    get_interface!(get_response, WebResourceResponse);
1246    put_interface!(put_response, WebResourceResponse);
1247    get_interface!(get_deferral, Deferral);
1248    get!(get_resource_context, WebResourceContext);
1249}
1250
1251impl NavigationCompletedEventArgs {
1252    get_bool!(get_is_success);
1253    get!(get_web_error_status, WebErrorStatus);
1254    get!(get_navigation_id, u64);
1255}
1256
1257impl NavigationStartingEventArgs {
1258    get_string!(get_uri);
1259    get_bool!(get_is_user_initiated);
1260    get_bool!(get_is_redirected);
1261    get_interface!(get_request_headers, HttpRequestHeaders);
1262    get_bool!(get_cancel);
1263    put_bool!(put_cancel);
1264    get!(get_navigation_id, u64);
1265}
1266
1267impl SourceChangedEventArgs {
1268    get_bool!(get_is_new_document);
1269}
1270
1271impl ScriptDialogOpeningEventArgs {
1272    get_string!(get_uri);
1273    get!(get_kind, ScriptDialogKind);
1274    get_string!(get_message);
1275    call!(accept);
1276    get_string!(get_default_text);
1277    get_string!(get_result_text);
1278    put_string!(put_result_text);
1279    get_interface!(get_deferral, Deferral);
1280}
1281
1282impl PermissionRequestedEventArgs {
1283    get_string!(get_uri);
1284    get!(get_permission_kind, PermissionKind);
1285    get_bool!(get_is_user_initiated);
1286    get!(get_state, PermissionState);
1287    put!(put_state, state: PermissionState);
1288    get_interface!(get_deferral, Deferral);
1289}
1290
1291impl ProcessFailedEventArgs {
1292    get!(get_process_failed_kind, ProcessFailedKind);
1293}
1294
1295impl NewWindowRequestedEventArgs {
1296    get_string!(get_uri);
1297    put_interface!(put_new_window, WebView);
1298    get_interface!(get_new_window, WebView);
1299    put_bool!(put_handled);
1300    get_bool!(get_handled);
1301    get_bool!(get_is_user_initiated);
1302    get_interface!(get_deferral, Deferral);
1303    get_interface!(get_window_features, WindowFeatures);
1304}
1305
1306impl WindowFeatures {
1307    get_bool!(get_has_position);
1308    get_bool!(get_has_size);
1309    get!(get_left, u32);
1310    get!(get_top, u32);
1311    get!(get_height, u32);
1312    get!(get_width, u32);
1313    get_bool!(get_should_display_menu_bar);
1314    get_bool!(get_should_display_status);
1315    get_bool!(get_should_display_toolbar);
1316    get_bool!(get_should_display_scroll_bars);
1317}
1318
1319impl MoveFocusRequestedEventArgs {
1320    get!(get_reason, MoveFocusReason);
1321    get_bool!(get_handled);
1322    put_bool!(put_handled);
1323}
1324
1325impl AcceleratorKeyPressedEventArgs {
1326    get!(get_key_event_kind, KeyEventKind);
1327    get!(get_virtual_key, u32);
1328    get!(get_key_event_lparam, i32);
1329    get!(get_physical_key_status, PhysicalKeyStatus);
1330    get_bool!(get_handled);
1331    put_bool!(put_handled);
1332}
1333
1334// Missing in winapi APIs. But present in its import libraries.
1335extern "stdcall" {
1336    fn SHCreateMemStream(p_init: *const u8, cb_init: UINT) -> *mut *mut IStreamVTable;
1337}
1338
1339impl Stream {
1340    /// Create a stream from a byte buffer. (`SHCreateMemStream`)
1341    pub fn from_bytes(buf: &[u8]) -> Self {
1342        let ppv = unsafe { SHCreateMemStream(buf.as_ptr(), buf.len() as _) };
1343        assert!(!ppv.is_null());
1344        Self {
1345            // Do not need to add ref for this pointer.
1346            inner: unsafe { ComRc::from_raw(ppv) },
1347        }
1348    }
1349
1350    /// Create a `Stream` from an owning raw pointer to an `IStream`.
1351    ///
1352    /// # Safety
1353    ///
1354    /// See `ComRc::from_raw`.
1355    pub unsafe fn from_raw(ppv: *mut *mut IStreamVTable) -> Self {
1356        Self {
1357            inner: ComRc::from_raw(ppv),
1358        }
1359    }
1360}
1361
1362impl io::Read for Stream {
1363    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
1364        let mut read_bytes = MaybeUninit::uninit();
1365        check_hresult(unsafe {
1366            self.inner.read(
1367                buf.as_mut_ptr() as *mut _,
1368                buf.len() as _,
1369                read_bytes.as_mut_ptr(),
1370            )
1371        })
1372        .map_err(|e| e.into_io_error())?;
1373        Ok(unsafe { read_bytes.assume_init() } as _)
1374    }
1375}
1376
1377impl io::Write for Stream {
1378    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
1379        let mut written_bytes = MaybeUninit::uninit();
1380        check_hresult(unsafe {
1381            self.inner.write(
1382                buf.as_ptr() as *mut _,
1383                buf.len() as _,
1384                written_bytes.as_mut_ptr(),
1385            )
1386        })
1387        .map_err(|e| e.into_io_error())?;
1388        Ok(unsafe { written_bytes.assume_init() } as _)
1389    }
1390
1391    fn flush(&mut self) -> io::Result<()> {
1392        Ok(())
1393    }
1394}
1395
1396impl io::Seek for Stream {
1397    fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
1398        use std::convert::TryInto;
1399
1400        let (origin, amount) = match pos {
1401            io::SeekFrom::Start(x) => (/* STREAM_SEEK_SET */ 0, x.try_into().unwrap()),
1402            io::SeekFrom::Current(x) => (/* STREAM_SEEK_CUR */ 1, x),
1403            io::SeekFrom::End(x) => (/* STREAM_SEEK_END */ 2, x),
1404        };
1405
1406        let mut new_pos = MaybeUninit::<u64>::uninit();
1407
1408        check_hresult(unsafe {
1409            self.inner.seek(
1410                mem::transmute(amount),
1411                origin,
1412                new_pos.as_mut_ptr() as *mut _,
1413            )
1414        })
1415        .map_err(|e| e.into_io_error())?;
1416        Ok(unsafe { new_pos.assume_init() })
1417    }
1418}
1419
1420#[doc(inline)]
1421pub use webview2_sys::{
1422    CapturePreviewImageFormat, EventRegistrationToken, KeyEventKind, MoveFocusReason,
1423    PermissionKind, PermissionState, PhysicalKeyStatus, ProcessFailedKind, ScriptDialogKind,
1424    WebErrorStatus, WebResourceContext,
1425};
1426
1427/// WebView2 Error.
1428///
1429/// Actually it's just an `HRESULT`.
1430#[derive(Eq, PartialEq)]
1431pub struct Error {
1432    hresult: HRESULT,
1433}
1434
1435pub type Result<T> = std::result::Result<T, Error>;
1436
1437impl fmt::Display for Error {
1438    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1439        write!(f, "webview2 error, HRESULT {:#X}", self.hresult as u32)
1440    }
1441}
1442
1443impl fmt::Debug for Error {
1444    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1445        write!(f, "webview2 error, HRESULT {:#X}", self.hresult as u32)
1446    }
1447}
1448
1449impl std::error::Error for Error {}
1450
1451impl From<NulError<u16>> for Error {
1452    fn from(_: NulError<u16>) -> Error {
1453        Error {
1454            hresult: E_INVALIDARG,
1455        }
1456    }
1457}
1458
1459impl From<io::Error> for Error {
1460    fn from(e: io::Error) -> Error {
1461        match e.raw_os_error() {
1462            Some(e) => Error::new(HRESULT_FROM_WIN32(e as u32)),
1463            _ => Error::new(E_FAIL),
1464        }
1465    }
1466}
1467
1468impl Error {
1469    pub fn new(hresult: HRESULT) -> Self {
1470        Self { hresult }
1471    }
1472
1473    fn into_io_error(self) -> io::Error {
1474        if (self.hresult & (0xffff_0000_u32 as i32))
1475            == MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, 0)
1476        {
1477            io::Error::from_raw_os_error(HRESULT_CODE(self.hresult))
1478        } else {
1479            io::Error::new(io::ErrorKind::Other, self)
1480        }
1481    }
1482
1483    pub fn hresult(&self) -> HRESULT {
1484        self.hresult
1485    }
1486}
1487
1488/// Check a `HRESULT`, if it is `SUCCEEDED`, return `Ok(())`. Otherwide return
1489/// an error containing the `HRESULT`.
1490pub fn check_hresult(hresult: HRESULT) -> Result<()> {
1491    if SUCCEEDED(hresult) {
1492        Ok(())
1493    } else {
1494        Err(Error { hresult })
1495    }
1496}
1497
1498fn to_hresult<T>(r: Result<T>) -> HRESULT {
1499    match r {
1500        Ok(_) => S_OK,
1501        Err(Error { hresult }) => hresult,
1502    }
1503}
1504
1505#[cfg(test)]
1506mod tests {
1507    use super::*;
1508    use std::io::{Read, Seek, Write};
1509
1510    #[test]
1511    fn test_stream() {
1512        let mut stream = Stream::from_bytes(b"hello,");
1513        stream.seek(io::SeekFrom::End(0)).unwrap();
1514        stream.write_all(b" world").unwrap();
1515
1516        let mut buf = Vec::new();
1517        stream.seek(io::SeekFrom::Start(0)).unwrap();
1518        stream.read_to_end(&mut buf).unwrap();
1519        assert_eq!(buf, b"hello, world");
1520    }
1521
1522    #[test]
1523    fn test_cmp_version() {
1524        assert_eq!(
1525            compare_browser_versions("84.0.498.0 canary", "84.0.498.0 canary").unwrap(),
1526            std::cmp::Ordering::Equal,
1527        );
1528        assert_eq!(
1529            compare_browser_versions("84.0.430.0 canary", "84.0.498.0 canary").unwrap(),
1530            std::cmp::Ordering::Less,
1531        );
1532        assert_eq!(
1533            compare_browser_versions("84.0.498.0", "84.0.440.0").unwrap(),
1534            std::cmp::Ordering::Greater,
1535        );
1536    }
1537}