Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat] Expose AddBrowserExtension for Windows #11259

Closed
Revxrsal opened this issue Oct 7, 2024 · 4 comments
Closed

[feat] Expose AddBrowserExtension for Windows #11259

Revxrsal opened this issue Oct 7, 2024 · 4 comments

Comments

@Revxrsal
Copy link

Revxrsal commented Oct 7, 2024

Describe the problem

I can see in #10909 that it is possible now to enable browser extensions on Windows. Unfortunately, we can hardly take advantage of this feature as you currently don't have any way to actually add extensions :P

Describe the solution you'd like

Add a function that invokes ICoreWebviewProfile7#AddBrowserExtension.

A possible approach is to add an extensions function in WebviewWindowBuilder, as shown below:

let window = tauri::WebviewWindowBuilder::new(...)
    .title(...)
    .browser_extensions_enabled(true)
    .extensions(
        /* directory, possibly a Path or PathBuf */, 
        /* list of extension IDs, maybe a Vec<String> */
    );

Since this is only possible on Windows, it could either:

  • Be put behind a feature flag
  • Be callable on all platforms, but only affect Windows

The second approach is likely better, as it is consistent with Tauri's overall design and ensures forward compatibility in case other platforms allow extensions in the future.

Alternatives considered

Modifying the underlying Wry window. Looks like this is not possible.

Additional context

No response

@FabianLars
Copy link
Member

You can use with_webview for now https://docs.rs/tauri/latest/tauri/webview/struct.WebviewWindow.html#method.with_webview

AreBrowserExtensionsEnabled got an actual method because it needs to be set when the inner webview is created.

@Revxrsal
Copy link
Author

Revxrsal commented Oct 8, 2024

I've spent some decent three hours trying to get this right 😂 Had to learn a bit about the Windows API and how it's used in Rust. I also got sidetracked by the whole COM thing.

Anyway, here's the final code, in case anybody needs it:

[target.'cfg(target_os = "windows")'.dependencies]
windows-core = "0.58.0"
webview2-com-sys = "0.33.0"

use std::path::Path;
use tauri::webview::PlatformWebview;

/// Represents an Error that occurs when adding a browser extension
#[derive(Debug)]
pub enum Error {
    Unsupported,
    #[cfg(target_os = "windows")]
    WindowsError(windows_core::Error),
}

/// Defines extensions for [PlatformWebview] to easily add extensions
/// to web-views
pub trait BrowserExtensionsExt {
    fn add_extension<P: AsRef<Path>>(&self, extension_folder: P) -> Result<(), Error>;
}

impl BrowserExtensionsExt for PlatformWebview {
    #[cfg(not(target_os = "windows"))]
    fn add_extension<P: AsRef<Path>>(&self, extension_folder: P) -> Result<(), Error> {
        Err(Error::Unsupported)
    }

    #[cfg(target_os = "windows")]
    fn add_extension<P: AsRef<Path>>(&self, extension_folder: P) -> Result<(), Error> {
        windows_webview::add_extension(self, extension_folder.as_ref()).map_err(|e| Error::WindowsError(e))
    }
}

#[cfg(target_os = "windows")]
mod windows_webview {
    use std::ffi::OsStr;
    use std::os::windows::ffi::OsStrExt;
    use std::path::Path;
    use tauri::webview::PlatformWebview;
    use webview2_com_sys::Microsoft::Web::WebView2::Win32::ICoreWebView2BrowserExtension;
    use webview2_com_sys::Microsoft::Web::WebView2::Win32::ICoreWebView2ProfileAddBrowserExtensionCompletedHandler;
    use webview2_com_sys::Microsoft::Web::WebView2::Win32::ICoreWebView2ProfileAddBrowserExtensionCompletedHandler_Impl;
    use webview2_com_sys::Microsoft::Web::WebView2::Win32::{ICoreWebView2Profile7, ICoreWebView2_13};
    use windows_core::{Interface, Result, PCWSTR};
    use windows_core::{implement, HRESULT};

    #[implement(ICoreWebView2ProfileAddBrowserExtensionCompletedHandler)]
    pub struct AddBrowserExtensionCallback;

    impl ICoreWebView2ProfileAddBrowserExtensionCompletedHandler_Impl for AddBrowserExtensionCallback_Impl {
        #[allow(non_snake_case)]
        fn Invoke(&self, error_code: HRESULT, _extension: Option<&ICoreWebView2BrowserExtension>) -> windows_core::Result<()> {
            error_code.ok()
        }
    }

    impl AddBrowserExtensionCallback {
        pub fn new() -> Self {
            Self
        }
    }

    pub(crate) fn add_extension(webview: &PlatformWebview, path: &Path) -> Result<()> {
        unsafe {
            let web_view2 = webview.controller().CoreWebView2()?;
            let profile = web_view2.cast::<ICoreWebView2_13>()?.Profile()?;
            let profile: ICoreWebView2Profile7 = profile.cast()?;
            add_extension_to_profile(profile, path.as_os_str())?;
        }
        Ok(())
    }

    fn add_extension_to_profile(profile: ICoreWebView2Profile7, extension_folder: &OsStr) -> Result<()> {
        unsafe {
            let str: Vec<u16> = extension_folder.encode_wide()
                .chain(std::iter::once(0))
                .collect();
            let path_ptr = PCWSTR(str.as_ptr());

            let handler = AddBrowserExtensionCallback::new();
            let h = ICoreWebView2ProfileAddBrowserExtensionCompletedHandler::from(handler);
            profile.AddBrowserExtension(path_ptr, Some(&h))?;
        }
        Ok(())
    }
}

Which can be used as follows:

    let window = tauri::WebviewWindowBuilder::new(...)
        .title(website)
        .browser_extensions_enabled(true)
        .build().unwrap();

    window.with_webview(|webview| {
        let r = webview.add_extension(r#"<extension path here>"#);
        if let Err(why) = r {
            println!("Failed to add extension: {why:?}");
        }
    }).unwrap();

(Excuse my unwrap() use)

@SpikeHD
Copy link
Contributor

SpikeHD commented Nov 6, 2024

Once tauri-apps/wry#1399 is merged (which contains my with_extension_path implementation), I will be making a PR to tauri to expose that to the Tauri API 👍

@amrbashir
Copy link
Member

added in #11628

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants