windows-webview 0.100.0

Windows WebView2 library
Documentation
use super::*;
use std::cell::{Cell, RefCell};
use windows_core::implement_decl;

pub(crate) struct EnvironmentCompleted(Cell<Option<Box<dyn FnOnce(Result<Environment>)>>>);

implement_decl! {
    impl EnvironmentCompleted as pub(crate) EnvironmentCompleted_Impl:
        [ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler]
}

impl EnvironmentCompleted {
    pub(crate) fn create<F: FnOnce(Result<Environment>) + 'static>(
        handler: F,
    ) -> ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler {
        Self(Cell::new(Some(Box::new(handler)))).into()
    }
}

impl ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler_Impl for EnvironmentCompleted_Impl {
    fn Invoke(&self, errorcode: HRESULT, result: Ref<ICoreWebView2Environment>) -> Result<()> {
        let outcome = errorcode
            .ok()
            .and_then(|()| Ok(Environment(result.ok()?.clone())));

        if let Some(handler) = self.0.take() {
            handler(outcome);
        }

        Ok(())
    }
}

pub(crate) struct ControllerCompleted(Cell<Option<Box<dyn FnOnce(Result<Controller>)>>>);

implement_decl! {
    impl ControllerCompleted as pub(crate) ControllerCompleted_Impl:
        [ICoreWebView2CreateCoreWebView2ControllerCompletedHandler]
}

impl ControllerCompleted {
    pub(crate) fn create<F: FnOnce(Result<Controller>) + 'static>(
        handler: F,
    ) -> ICoreWebView2CreateCoreWebView2ControllerCompletedHandler {
        Self(Cell::new(Some(Box::new(handler)))).into()
    }
}

impl ICoreWebView2CreateCoreWebView2ControllerCompletedHandler_Impl for ControllerCompleted_Impl {
    fn Invoke(&self, errorcode: HRESULT, result: Ref<ICoreWebView2Controller>) -> Result<()> {
        let outcome = errorcode
            .ok()
            .and_then(|()| Ok(Controller(result.ok()?.clone())));

        if let Some(handler) = self.0.take() {
            handler(outcome);
        }

        Ok(())
    }
}

pub(crate) struct ExecuteScriptCompleted(Cell<Option<Box<dyn FnOnce(Result<String>)>>>);

implement_decl! {
    impl ExecuteScriptCompleted as pub(crate) ExecuteScriptCompleted_Impl:
        [ICoreWebView2ExecuteScriptCompletedHandler]
}

impl ExecuteScriptCompleted {
    pub(crate) fn create<F: FnOnce(Result<String>) + 'static>(
        handler: F,
    ) -> ICoreWebView2ExecuteScriptCompletedHandler {
        Self(Cell::new(Some(Box::new(handler)))).into()
    }
}

impl ICoreWebView2ExecuteScriptCompletedHandler_Impl for ExecuteScriptCompleted_Impl {
    fn Invoke(&self, errorcode: HRESULT, result: &PCWSTR) -> Result<()> {
        let outcome = errorcode
            .ok()
            .map(|()| unsafe { result.to_string().unwrap_or_default() });

        if let Some(handler) = self.0.take() {
            handler(outcome);
        }

        Ok(())
    }
}

pub(crate) struct AddScriptCompleted(Cell<Option<Box<dyn FnOnce(Result<String>)>>>);

implement_decl! {
    impl AddScriptCompleted as pub(crate) AddScriptCompleted_Impl:
        [ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler]
}

impl AddScriptCompleted {
    pub(crate) fn create<F: FnOnce(Result<String>) + 'static>(
        handler: F,
    ) -> ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler {
        Self(Cell::new(Some(Box::new(handler)))).into()
    }
}

impl ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler_Impl
    for AddScriptCompleted_Impl
{
    fn Invoke(&self, errorcode: HRESULT, id: &PCWSTR) -> Result<()> {
        let outcome = errorcode
            .ok()
            .map(|()| unsafe { id.to_string().unwrap_or_default() });

        if let Some(handler) = self.0.take() {
            handler(outcome);
        }

        Ok(())
    }
}

pub(crate) struct GetCookiesCompleted(Cell<Option<Box<dyn FnOnce(Result<Vec<Cookie>>)>>>);

implement_decl! {
    impl GetCookiesCompleted as pub(crate) GetCookiesCompleted_Impl:
        [ICoreWebView2GetCookiesCompletedHandler]
}

impl GetCookiesCompleted {
    pub(crate) fn create<F: FnOnce(Result<Vec<Cookie>>) + 'static>(
        handler: F,
    ) -> ICoreWebView2GetCookiesCompletedHandler {
        Self(Cell::new(Some(Box::new(handler)))).into()
    }
}

impl ICoreWebView2GetCookiesCompletedHandler_Impl for GetCookiesCompleted_Impl {
    fn Invoke(&self, errorcode: HRESULT, result: Ref<ICoreWebView2CookieList>) -> Result<()> {
        let outcome = errorcode.ok().and_then(|()| cookie::collect(result.ok()?));

        if let Some(handler) = self.0.take() {
            handler(outcome);
        }

        Ok(())
    }
}

pub(crate) struct ClearBrowsingDataCompleted(Cell<Option<Box<dyn FnOnce(Result<()>)>>>);

implement_decl! {
    impl ClearBrowsingDataCompleted as pub(crate) ClearBrowsingDataCompleted_Impl:
        [ICoreWebView2ClearBrowsingDataCompletedHandler]
}

impl ClearBrowsingDataCompleted {
    pub(crate) fn create<F: FnOnce(Result<()>) + 'static>(
        handler: F,
    ) -> ICoreWebView2ClearBrowsingDataCompletedHandler {
        Self(Cell::new(Some(Box::new(handler)))).into()
    }
}

impl ICoreWebView2ClearBrowsingDataCompletedHandler_Impl for ClearBrowsingDataCompleted_Impl {
    fn Invoke(&self, errorcode: HRESULT) -> Result<()> {
        if let Some(handler) = self.0.take() {
            handler(errorcode.ok());
        }

        Ok(())
    }
}

pub(crate) struct CallDevToolsProtocolMethodCompleted(
    Cell<Option<Box<dyn FnOnce(Result<String>)>>>,
);

implement_decl! {
    impl CallDevToolsProtocolMethodCompleted as pub(crate) CallDevToolsProtocolMethodCompleted_Impl:
        [ICoreWebView2CallDevToolsProtocolMethodCompletedHandler]
}

impl CallDevToolsProtocolMethodCompleted {
    pub(crate) fn create<F: FnOnce(Result<String>) + 'static>(
        handler: F,
    ) -> ICoreWebView2CallDevToolsProtocolMethodCompletedHandler {
        Self(Cell::new(Some(Box::new(handler)))).into()
    }
}

impl ICoreWebView2CallDevToolsProtocolMethodCompletedHandler_Impl
    for CallDevToolsProtocolMethodCompleted_Impl
{
    fn Invoke(&self, errorcode: HRESULT, result: &PCWSTR) -> Result<()> {
        let outcome = errorcode
            .ok()
            .map(|()| unsafe { result.to_string().unwrap_or_default() });

        if let Some(handler) = self.0.take() {
            handler(outcome);
        }

        Ok(())
    }
}

/// Defines adapters from WebView2 event COM interfaces to Rust closures.
macro_rules! event_handler {
    ($name:ident / $impl:ident, $handler:ident / $handler_trait:ident, $args:ident => $wrapper:ident) => {
        event_handler!($name / $impl, $handler / $handler_trait, ICoreWebView2, $args => $wrapper);
    };
    ($name:ident / $impl:ident, $handler:ident / $handler_trait:ident, $sender:ident, $args:ident => $wrapper:ident) => {
        pub(crate) struct $name(RefCell<Box<dyn FnMut($wrapper)>>);

        implement_decl! {
            impl $name as pub(crate) $impl: [$handler]
        }

        impl $name {
            pub(crate) fn create<F: FnMut($wrapper) + 'static>(handler: F) -> $handler {
                Self(RefCell::new(Box::new(handler))).into()
            }
        }

        impl $handler_trait for $impl {
            fn Invoke(&self, _sender: Ref<$sender>, args: Ref<$args>) -> Result<()> {
                let args = $wrapper(args.ok()?.clone());
                (*self.0.borrow_mut())(args);
                Ok(())
            }
        }
    };
    ($name:ident / $impl:ident, $handler:ident / $handler_trait:ident, sender $sender:ident => $wrapper:ident) => {
        pub(crate) struct $name(RefCell<Box<dyn FnMut($wrapper)>>);

        implement_decl! {
            impl $name as pub(crate) $impl: [$handler]
        }

        impl $name {
            pub(crate) fn create<F: FnMut($wrapper) + 'static>(handler: F) -> $handler {
                Self(RefCell::new(Box::new(handler))).into()
            }
        }

        impl $handler_trait for $impl {
            fn Invoke(&self, sender: Ref<$sender>, _args: Ref<IUnknown>) -> Result<()> {
                let value = $wrapper(sender.ok()?.clone());
                (*self.0.borrow_mut())(value);
                Ok(())
            }
        }
    };
}

event_handler!(NavigationCompleted / NavigationCompleted_Impl, ICoreWebView2NavigationCompletedEventHandler / ICoreWebView2NavigationCompletedEventHandler_Impl, ICoreWebView2NavigationCompletedEventArgs => NavigationCompletedArgs);
event_handler!(NavigationStarting / NavigationStarting_Impl, ICoreWebView2NavigationStartingEventHandler / ICoreWebView2NavigationStartingEventHandler_Impl, ICoreWebView2NavigationStartingEventArgs => NavigationStartingArgs);
event_handler!(WebMessageReceived / WebMessageReceived_Impl, ICoreWebView2WebMessageReceivedEventHandler / ICoreWebView2WebMessageReceivedEventHandler_Impl, ICoreWebView2WebMessageReceivedEventArgs => WebMessageReceivedArgs);
event_handler!(ContentLoading / ContentLoading_Impl, ICoreWebView2ContentLoadingEventHandler / ICoreWebView2ContentLoadingEventHandler_Impl, ICoreWebView2ContentLoadingEventArgs => ContentLoadingArgs);
event_handler!(NewWindowRequested / NewWindowRequested_Impl, ICoreWebView2NewWindowRequestedEventHandler / ICoreWebView2NewWindowRequestedEventHandler_Impl, ICoreWebView2NewWindowRequestedEventArgs => NewWindowRequestedArgs);
event_handler!(PermissionRequested / PermissionRequested_Impl, ICoreWebView2PermissionRequestedEventHandler / ICoreWebView2PermissionRequestedEventHandler_Impl, ICoreWebView2PermissionRequestedEventArgs => PermissionRequestedArgs);
event_handler!(DownloadStarting / DownloadStarting_Impl, ICoreWebView2DownloadStartingEventHandler / ICoreWebView2DownloadStartingEventHandler_Impl, ICoreWebView2DownloadStartingEventArgs => DownloadStartingArgs);
event_handler!(DownloadStateChanged / DownloadStateChanged_Impl, ICoreWebView2StateChangedEventHandler / ICoreWebView2StateChangedEventHandler_Impl, sender ICoreWebView2DownloadOperation => DownloadOperation);
event_handler!(BytesReceivedChanged / BytesReceivedChanged_Impl, ICoreWebView2BytesReceivedChangedEventHandler / ICoreWebView2BytesReceivedChangedEventHandler_Impl, sender ICoreWebView2DownloadOperation => DownloadOperation);
event_handler!(ProcessFailed / ProcessFailed_Impl, ICoreWebView2ProcessFailedEventHandler / ICoreWebView2ProcessFailedEventHandler_Impl, ICoreWebView2ProcessFailedEventArgs => ProcessFailedArgs);
event_handler!(DevToolsProtocolEventReceived / DevToolsProtocolEventReceived_Impl, ICoreWebView2DevToolsProtocolEventReceivedEventHandler / ICoreWebView2DevToolsProtocolEventReceivedEventHandler_Impl, ICoreWebView2DevToolsProtocolEventReceivedEventArgs => DevToolsProtocolEventReceivedArgs);
event_handler!(MoveFocusRequested / MoveFocusRequested_Impl, ICoreWebView2MoveFocusRequestedEventHandler / ICoreWebView2MoveFocusRequestedEventHandler_Impl, ICoreWebView2Controller, ICoreWebView2MoveFocusRequestedEventArgs => MoveFocusRequestedArgs);
event_handler!(AcceleratorKeyPressed / AcceleratorKeyPressed_Impl, ICoreWebView2AcceleratorKeyPressedEventHandler / ICoreWebView2AcceleratorKeyPressedEventHandler_Impl, ICoreWebView2Controller, ICoreWebView2AcceleratorKeyPressedEventArgs => AcceleratorKeyPressedArgs);

pub(crate) struct FocusChanged(RefCell<Box<dyn FnMut()>>);

implement_decl! {
    impl FocusChanged as pub(crate) FocusChanged_Impl:
        [ICoreWebView2FocusChangedEventHandler]
}

impl FocusChanged {
    pub(crate) fn create<F: FnMut() + 'static>(
        handler: F,
    ) -> ICoreWebView2FocusChangedEventHandler {
        Self(RefCell::new(Box::new(handler))).into()
    }
}

impl ICoreWebView2FocusChangedEventHandler_Impl for FocusChanged_Impl {
    fn Invoke(&self, _sender: Ref<ICoreWebView2Controller>, _args: Ref<IUnknown>) -> Result<()> {
        (*self.0.borrow_mut())();
        Ok(())
    }
}

pub(crate) struct DocumentTitleChanged(RefCell<Box<dyn FnMut(String)>>);

implement_decl! {
    impl DocumentTitleChanged as pub(crate) DocumentTitleChanged_Impl:
        [ICoreWebView2DocumentTitleChangedEventHandler]
}

impl DocumentTitleChanged {
    pub(crate) fn create<F: FnMut(String) + 'static>(
        handler: F,
    ) -> ICoreWebView2DocumentTitleChangedEventHandler {
        Self(RefCell::new(Box::new(handler))).into()
    }
}

impl ICoreWebView2DocumentTitleChangedEventHandler_Impl for DocumentTitleChanged_Impl {
    fn Invoke(&self, sender: Ref<ICoreWebView2>, _args: Ref<IUnknown>) -> Result<()> {
        let title =
            unsafe { string::take_result(sender.ok().and_then(|sender| sender.DocumentTitle())) };
        (*self.0.borrow_mut())(title);
        Ok(())
    }
}

pub(crate) struct ContainsFullScreenElementChanged(RefCell<Box<dyn FnMut(bool)>>);

implement_decl! {
    impl ContainsFullScreenElementChanged as pub(crate) ContainsFullScreenElementChanged_Impl:
        [ICoreWebView2ContainsFullScreenElementChangedEventHandler]
}

impl ContainsFullScreenElementChanged {
    pub(crate) fn create<F: FnMut(bool) + 'static>(
        handler: F,
    ) -> ICoreWebView2ContainsFullScreenElementChangedEventHandler {
        Self(RefCell::new(Box::new(handler))).into()
    }
}

impl ICoreWebView2ContainsFullScreenElementChangedEventHandler_Impl
    for ContainsFullScreenElementChanged_Impl
{
    fn Invoke(&self, sender: Ref<ICoreWebView2>, _args: Ref<IUnknown>) -> Result<()> {
        let contains = sender
            .ok()
            .and_then(|sender| unsafe { sender.ContainsFullScreenElement() })
            .is_ok_and(|value| value.as_bool());
        (*self.0.borrow_mut())(contains);
        Ok(())
    }
}

pub(crate) struct WindowCloseRequested(RefCell<Box<dyn FnMut()>>);

implement_decl! {
    impl WindowCloseRequested as pub(crate) WindowCloseRequested_Impl:
        [ICoreWebView2WindowCloseRequestedEventHandler]
}

impl WindowCloseRequested {
    pub(crate) fn create<F: FnMut() + 'static>(
        handler: F,
    ) -> ICoreWebView2WindowCloseRequestedEventHandler {
        Self(RefCell::new(Box::new(handler))).into()
    }
}

impl ICoreWebView2WindowCloseRequestedEventHandler_Impl for WindowCloseRequested_Impl {
    fn Invoke(&self, _sender: Ref<ICoreWebView2>, _args: Ref<IUnknown>) -> Result<()> {
        (*self.0.borrow_mut())();
        Ok(())
    }
}

/// Defines a subscription method that returns an RAII removal guard.
macro_rules! subscription {
    (
        $(#[$doc:meta])*
        $method:ident($arg:ty) => $handler:ident, $add:ident / $remove:ident
    ) => {
        $(#[$doc])*
        pub fn $method<F: FnMut($arg) + 'static>(&self, handler: F) -> Result<EventRegistration> {
            let handler = crate::handler::$handler::create(handler);
            let token = unsafe { self.0.$add(&handler)? };
            let source = self.0.clone();
            Ok(EventRegistration::new(move || {
                let _ = unsafe { source.$remove(token) };
            }))
        }
    };
}

pub(crate) use subscription;