1#:
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#![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#[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 pub fn new() -> Box<Self> {
111 unreachable!()
112 }
113 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
129unsafe 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
140mod 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 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 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, 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, 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, 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 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 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#[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 #[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 #[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 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 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 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 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 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
1334extern "stdcall" {
1336 fn SHCreateMemStream(p_init: *const u8, cb_init: UINT) -> *mut *mut IStreamVTable;
1337}
1338
1339impl Stream {
1340 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 inner: unsafe { ComRc::from_raw(ppv) },
1347 }
1348 }
1349
1350 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) => (0, x.try_into().unwrap()),
1402 io::SeekFrom::Current(x) => (1, x),
1403 io::SeekFrom::End(x) => (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#[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
1488pub 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}