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    pub fn get_settings2(&self) -> Result<Settings2> {
1088        let inner = self
1089            .inner
1090            .get_interface::<dyn ICoreWebView2Settings2>()
1091            .ok_or_else(|| Error::new(E_NOINTERFACE))?;
1092        Ok(Settings2 { inner })
1093    }
1094}
1095
1096impl Settings2 {
1097    get_string!(get_user_agent);
1098    put_string!(put_user_agent);
1099}
1100
1101impl ContentLoadingEventArgs {
1102    get_bool!(get_is_error_page);
1103    get!(get_navigation_id, u64);
1104}
1105
1106impl WebMessageReceivedEventArgs {
1107    get_string!(get_source);
1108    get_string!(try_get_web_message_as_string);
1109    get_string!(get_web_message_as_json);
1110}
1111
1112impl HttpHeadersCollectionIterator {
1113    pub fn get_current_header(&self) -> Result<(String, String)> {
1114        let mut name = MaybeUninit::<LPWSTR>::uninit();
1115        let mut value = MaybeUninit::<LPWSTR>::uninit();
1116        unsafe {
1117            check_hresult(
1118                self.inner
1119                    .get_current_header(name.as_mut_ptr(), value.as_mut_ptr()),
1120            )?;
1121            let name = name.assume_init();
1122            let value = value.assume_init();
1123            let name1 = WideCStr::from_ptr_str(name)
1124                .to_string()
1125                .map_err(|_| Error::new(E_FAIL));
1126            let value1 = WideCStr::from_ptr_str(value)
1127                .to_string()
1128                .map_err(|_| Error::new(E_FAIL));
1129
1130            CoTaskMemFree(name as _);
1131            CoTaskMemFree(value as _);
1132
1133            Ok((name1?, value1?))
1134        }
1135    }
1136    get_bool!(get_has_current_header);
1137    get_bool!(move_next);
1138}
1139
1140impl Iterator for HttpHeadersCollectionIterator {
1141    type Item = (String, String);
1142
1143    fn next(&mut self) -> Option<(String, String)> {
1144        if self.get_has_current_header() != Ok(true) {
1145            return None;
1146        }
1147        let v = self.get_current_header().ok();
1148        let _ = self.move_next();
1149        v
1150    }
1151}
1152
1153impl HttpRequestHeaders {
1154    pub fn get_header(&self, name: &str) -> Result<String> {
1155        let name = WideCString::from_str(name)?;
1156        let mut value = MaybeUninit::<LPWSTR>::uninit();
1157        unsafe {
1158            check_hresult(self.inner.get_header(name.as_ptr(), value.as_mut_ptr()))?;
1159            let value = value.assume_init();
1160            let value1 = WideCStr::from_ptr_str(value)
1161                .to_string()
1162                .map_err(|_| Error::new(E_FAIL));
1163
1164            CoTaskMemFree(value as _);
1165
1166            value1
1167        }
1168    }
1169    pub fn get_headers(&self, name: &str) -> Result<HttpHeadersCollectionIterator> {
1170        let name = WideCString::from_str(name)?;
1171        let mut iterator: *mut *mut ICoreWebView2HttpHeadersCollectionIteratorVTable =
1172            ptr::null_mut();
1173        check_hresult(unsafe { self.inner.get_headers(name.as_ptr(), &mut iterator) })?;
1174        Ok(HttpHeadersCollectionIterator {
1175            inner: unsafe { add_ref_to_rc(iterator) },
1176        })
1177    }
1178    pub fn contains(&self, name: &str) -> Result<bool> {
1179        let name = WideCString::from_str(name)?;
1180        let mut result = MaybeUninit::<BOOL>::uninit();
1181        check_hresult(unsafe { self.inner.contains(name.as_ptr(), result.as_mut_ptr()) })?;
1182        Ok(unsafe { result.assume_init() } != 0)
1183    }
1184    pub fn set_header(&self, name: &str, value: &str) -> Result<()> {
1185        let name = WideCString::from_str(name)?;
1186        let value = WideCString::from_str(value)?;
1187        check_hresult(unsafe { self.inner.set_header(name.as_ptr(), value.as_ptr()) })
1188    }
1189    put_string!(remove_header);
1190    get_interface!(get_iterator, HttpHeadersCollectionIterator);
1191}
1192
1193impl HttpResponseHeaders {
1194    pub fn get_header(&self, name: &str) -> Result<String> {
1195        let name = WideCString::from_str(name)?;
1196        let mut value = MaybeUninit::<LPWSTR>::uninit();
1197        unsafe {
1198            check_hresult(self.inner.get_header(name.as_ptr(), value.as_mut_ptr()))?;
1199            let value = value.assume_init();
1200            let value1 = WideCStr::from_ptr_str(value)
1201                .to_string()
1202                .map_err(|_| Error::new(E_FAIL));
1203
1204            CoTaskMemFree(value as _);
1205
1206            value1
1207        }
1208    }
1209    pub fn contains(&self, name: &str) -> Result<bool> {
1210        let name = WideCString::from_str(name)?;
1211        let mut result = MaybeUninit::<BOOL>::uninit();
1212        check_hresult(unsafe { self.inner.contains(name.as_ptr(), result.as_mut_ptr()) })?;
1213        Ok(unsafe { result.assume_init() } != 0)
1214    }
1215    pub fn append_header(&self, name: &str, value: &str) -> Result<()> {
1216        let name = WideCString::from_str(name)?;
1217        let value = WideCString::from_str(value)?;
1218        check_hresult(unsafe { self.inner.append_header(name.as_ptr(), value.as_ptr()) })
1219    }
1220    pub fn get_headers(&self, name: &str) -> Result<HttpHeadersCollectionIterator> {
1221        let name = WideCString::from_str(name)?;
1222        let mut iterator: *mut *mut ICoreWebView2HttpHeadersCollectionIteratorVTable =
1223            ptr::null_mut();
1224        check_hresult(unsafe { self.inner.get_headers(name.as_ptr(), &mut iterator) })?;
1225        Ok(HttpHeadersCollectionIterator {
1226            inner: unsafe { add_ref_to_rc(iterator) },
1227        })
1228    }
1229    get_interface!(get_iterator, HttpHeadersCollectionIterator);
1230}
1231
1232impl Deferral {
1233    call!(complete);
1234}
1235
1236impl WebResourceRequest {
1237    get_string!(get_uri);
1238    put_string!(put_uri);
1239    get_string!(get_method);
1240    put_string!(put_method);
1241    get_interface!(get_content, Stream);
1242    put_interface!(put_content, Stream);
1243    get_interface!(get_headers, HttpRequestHeaders);
1244}
1245
1246impl WebResourceResponse {
1247    get_interface!(get_content, Stream);
1248    put_interface!(put_content, Stream);
1249    get_interface!(get_headers, HttpResponseHeaders);
1250    get!(get_status_code, i32);
1251    put!(put_status_code, status_code: i32);
1252    get_string!(get_reason_phrase);
1253    put_string!(put_reason_phrase);
1254}
1255
1256impl WebResourceRequestedEventArgs {
1257    get_interface!(get_request, WebResourceRequest);
1258    get_interface!(get_response, WebResourceResponse);
1259    put_interface!(put_response, WebResourceResponse);
1260    get_interface!(get_deferral, Deferral);
1261    get!(get_resource_context, WebResourceContext);
1262}
1263
1264impl NavigationCompletedEventArgs {
1265    get_bool!(get_is_success);
1266    get!(get_web_error_status, WebErrorStatus);
1267    get!(get_navigation_id, u64);
1268}
1269
1270impl NavigationStartingEventArgs {
1271    get_string!(get_uri);
1272    get_bool!(get_is_user_initiated);
1273    get_bool!(get_is_redirected);
1274    get_interface!(get_request_headers, HttpRequestHeaders);
1275    get_bool!(get_cancel);
1276    put_bool!(put_cancel);
1277    get!(get_navigation_id, u64);
1278}
1279
1280impl SourceChangedEventArgs {
1281    get_bool!(get_is_new_document);
1282}
1283
1284impl ScriptDialogOpeningEventArgs {
1285    get_string!(get_uri);
1286    get!(get_kind, ScriptDialogKind);
1287    get_string!(get_message);
1288    call!(accept);
1289    get_string!(get_default_text);
1290    get_string!(get_result_text);
1291    put_string!(put_result_text);
1292    get_interface!(get_deferral, Deferral);
1293}
1294
1295impl PermissionRequestedEventArgs {
1296    get_string!(get_uri);
1297    get!(get_permission_kind, PermissionKind);
1298    get_bool!(get_is_user_initiated);
1299    get!(get_state, PermissionState);
1300    put!(put_state, state: PermissionState);
1301    get_interface!(get_deferral, Deferral);
1302}
1303
1304impl ProcessFailedEventArgs {
1305    get!(get_process_failed_kind, ProcessFailedKind);
1306}
1307
1308impl NewWindowRequestedEventArgs {
1309    get_string!(get_uri);
1310    put_interface!(put_new_window, WebView);
1311    get_interface!(get_new_window, WebView);
1312    put_bool!(put_handled);
1313    get_bool!(get_handled);
1314    get_bool!(get_is_user_initiated);
1315    get_interface!(get_deferral, Deferral);
1316    get_interface!(get_window_features, WindowFeatures);
1317}
1318
1319impl WindowFeatures {
1320    get_bool!(get_has_position);
1321    get_bool!(get_has_size);
1322    get!(get_left, u32);
1323    get!(get_top, u32);
1324    get!(get_height, u32);
1325    get!(get_width, u32);
1326    get_bool!(get_should_display_menu_bar);
1327    get_bool!(get_should_display_status);
1328    get_bool!(get_should_display_toolbar);
1329    get_bool!(get_should_display_scroll_bars);
1330}
1331
1332impl MoveFocusRequestedEventArgs {
1333    get!(get_reason, MoveFocusReason);
1334    get_bool!(get_handled);
1335    put_bool!(put_handled);
1336}
1337
1338impl AcceleratorKeyPressedEventArgs {
1339    get!(get_key_event_kind, KeyEventKind);
1340    get!(get_virtual_key, u32);
1341    get!(get_key_event_lparam, i32);
1342    get!(get_physical_key_status, PhysicalKeyStatus);
1343    get_bool!(get_handled);
1344    put_bool!(put_handled);
1345}
1346
1347// Missing in winapi APIs. But present in its import libraries.
1348extern "stdcall" {
1349    fn SHCreateMemStream(p_init: *const u8, cb_init: UINT) -> *mut *mut IStreamVTable;
1350}
1351
1352impl Stream {
1353    /// Create a stream from a byte buffer. (`SHCreateMemStream`)
1354    pub fn from_bytes(buf: &[u8]) -> Self {
1355        let ppv = unsafe { SHCreateMemStream(buf.as_ptr(), buf.len() as _) };
1356        assert!(!ppv.is_null());
1357        Self {
1358            // Do not need to add ref for this pointer.
1359            inner: unsafe { ComRc::from_raw(ppv) },
1360        }
1361    }
1362
1363    /// Create a `Stream` from an owning raw pointer to an `IStream`.
1364    ///
1365    /// # Safety
1366    ///
1367    /// See `ComRc::from_raw`.
1368    pub unsafe fn from_raw(ppv: *mut *mut IStreamVTable) -> Self {
1369        Self {
1370            inner: ComRc::from_raw(ppv),
1371        }
1372    }
1373}
1374
1375impl io::Read for Stream {
1376    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
1377        let mut read_bytes = MaybeUninit::uninit();
1378        check_hresult(unsafe {
1379            self.inner.read(
1380                buf.as_mut_ptr() as *mut _,
1381                buf.len() as _,
1382                read_bytes.as_mut_ptr(),
1383            )
1384        })
1385        .map_err(|e| e.into_io_error())?;
1386        Ok(unsafe { read_bytes.assume_init() } as _)
1387    }
1388}
1389
1390impl io::Write for Stream {
1391    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
1392        let mut written_bytes = MaybeUninit::uninit();
1393        check_hresult(unsafe {
1394            self.inner.write(
1395                buf.as_ptr() as *mut _,
1396                buf.len() as _,
1397                written_bytes.as_mut_ptr(),
1398            )
1399        })
1400        .map_err(|e| e.into_io_error())?;
1401        Ok(unsafe { written_bytes.assume_init() } as _)
1402    }
1403
1404    fn flush(&mut self) -> io::Result<()> {
1405        Ok(())
1406    }
1407}
1408
1409impl io::Seek for Stream {
1410    fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
1411        use std::convert::TryInto;
1412
1413        let (origin, amount) = match pos {
1414            io::SeekFrom::Start(x) => (/* STREAM_SEEK_SET */ 0, x.try_into().unwrap()),
1415            io::SeekFrom::Current(x) => (/* STREAM_SEEK_CUR */ 1, x),
1416            io::SeekFrom::End(x) => (/* STREAM_SEEK_END */ 2, x),
1417        };
1418
1419        let mut new_pos = MaybeUninit::<u64>::uninit();
1420
1421        check_hresult(unsafe {
1422            self.inner.seek(
1423                mem::transmute(amount),
1424                origin,
1425                new_pos.as_mut_ptr() as *mut _,
1426            )
1427        })
1428        .map_err(|e| e.into_io_error())?;
1429        Ok(unsafe { new_pos.assume_init() })
1430    }
1431}
1432
1433#[doc(inline)]
1434pub use webview2_sys::{
1435    CapturePreviewImageFormat, EventRegistrationToken, KeyEventKind, MoveFocusReason,
1436    PermissionKind, PermissionState, PhysicalKeyStatus, ProcessFailedKind, ScriptDialogKind,
1437    WebErrorStatus, WebResourceContext,
1438};
1439
1440/// WebView2 Error.
1441///
1442/// Actually it's just an `HRESULT`.
1443#[derive(Eq, PartialEq)]
1444pub struct Error {
1445    hresult: HRESULT,
1446}
1447
1448pub type Result<T> = std::result::Result<T, Error>;
1449
1450impl fmt::Display for Error {
1451    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1452        write!(f, "webview2 error, HRESULT {:#X}", self.hresult as u32)
1453    }
1454}
1455
1456impl fmt::Debug for Error {
1457    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1458        write!(f, "webview2 error, HRESULT {:#X}", self.hresult as u32)
1459    }
1460}
1461
1462impl std::error::Error for Error {}
1463
1464impl From<NulError<u16>> for Error {
1465    fn from(_: NulError<u16>) -> Error {
1466        Error {
1467            hresult: E_INVALIDARG,
1468        }
1469    }
1470}
1471
1472impl From<io::Error> for Error {
1473    fn from(e: io::Error) -> Error {
1474        match e.raw_os_error() {
1475            Some(e) => Error::new(HRESULT_FROM_WIN32(e as u32)),
1476            _ => Error::new(E_FAIL),
1477        }
1478    }
1479}
1480
1481impl Error {
1482    pub fn new(hresult: HRESULT) -> Self {
1483        Self { hresult }
1484    }
1485
1486    fn into_io_error(self) -> io::Error {
1487        if (self.hresult & (0xffff_0000_u32 as i32))
1488            == MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, 0)
1489        {
1490            io::Error::from_raw_os_error(HRESULT_CODE(self.hresult))
1491        } else {
1492            io::Error::new(io::ErrorKind::Other, self)
1493        }
1494    }
1495
1496    pub fn hresult(&self) -> HRESULT {
1497        self.hresult
1498    }
1499}
1500
1501/// Check a `HRESULT`, if it is `SUCCEEDED`, return `Ok(())`. Otherwide return
1502/// an error containing the `HRESULT`.
1503pub fn check_hresult(hresult: HRESULT) -> Result<()> {
1504    if SUCCEEDED(hresult) {
1505        Ok(())
1506    } else {
1507        Err(Error { hresult })
1508    }
1509}
1510
1511fn to_hresult<T>(r: Result<T>) -> HRESULT {
1512    match r {
1513        Ok(_) => S_OK,
1514        Err(Error { hresult }) => hresult,
1515    }
1516}
1517
1518#[cfg(test)]
1519mod tests {
1520    use super::*;
1521    use std::io::{Read, Seek, Write};
1522
1523    #[test]
1524    fn test_stream() {
1525        let mut stream = Stream::from_bytes(b"hello,");
1526        stream.seek(io::SeekFrom::End(0)).unwrap();
1527        stream.write_all(b" world").unwrap();
1528
1529        let mut buf = Vec::new();
1530        stream.seek(io::SeekFrom::Start(0)).unwrap();
1531        stream.read_to_end(&mut buf).unwrap();
1532        assert_eq!(buf, b"hello, world");
1533    }
1534
1535    #[test]
1536    fn test_cmp_version() {
1537        assert_eq!(
1538            compare_browser_versions("84.0.498.0 canary", "84.0.498.0 canary").unwrap(),
1539            std::cmp::Ordering::Equal,
1540        );
1541        assert_eq!(
1542            compare_browser_versions("84.0.430.0 canary", "84.0.498.0 canary").unwrap(),
1543            std::cmp::Ordering::Less,
1544        );
1545        assert_eq!(
1546            compare_browser_versions("84.0.498.0", "84.0.440.0").unwrap(),
1547            std::cmp::Ordering::Greater,
1548        );
1549    }
1550}