mirror of
https://github.com/tauri-apps/tauri.git
synced 2026-04-01 10:01:07 +02:00
refactor(tauri): Webview traits (#1183)
This commit is contained in:
committed by
GitHub
parent
555d667f45
commit
b9ce7b94c4
5
.changes/webview-traits.md
Normal file
5
.changes/webview-traits.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"tauri": minor
|
||||
---
|
||||
|
||||
The Tauri integration with Webview was refactored to use traits, which allows custom implementations by developers and simplifies changes on the webview implementation.
|
||||
@@ -6,7 +6,7 @@
|
||||
mod cmd;
|
||||
|
||||
fn main() {
|
||||
tauri::AppBuilder::new()
|
||||
tauri::AppBuilder::<tauri::flavors::Official>::new()
|
||||
.invoke_handler(|_webview, arg| async move {
|
||||
use cmd::Cmd::*;
|
||||
match serde_json::from_str(&arg) {
|
||||
|
||||
@@ -20,7 +20,7 @@ features = [ "all-api" ]
|
||||
[dependencies]
|
||||
serde_json = "1.0"
|
||||
serde = { version = "1.0", features = [ "derive" ] }
|
||||
webview_official = "0.1.1"
|
||||
webview_official = "0.2.0"
|
||||
tauri_includedir = "0.6.0"
|
||||
phf = "0.8.0"
|
||||
base64 = "0.13.0"
|
||||
|
||||
@@ -1883,9 +1883,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "webview-official-sys"
|
||||
version = "0.1.1"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ea5c146a1d2e1b41bf9318fb17bb4b4cf809fd1b8d537318473b27aec178a61"
|
||||
checksum = "c4aec5fdf5bc938ba5fe47d23b8e02d6beaee395a91e16f0b2eec984a9a9e1d2"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"pkg-config",
|
||||
@@ -1893,9 +1893,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "webview_official"
|
||||
version = "0.1.1"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef1d99ead69b8f362550a88e161590acfe53e7698edfaecc98be15738a23d191"
|
||||
checksum = "b38277d3fa288b13db39eeb153f9b8ee3d8e3181648ade05264c1ba99e774999"
|
||||
dependencies = [
|
||||
"webview-official-sys",
|
||||
]
|
||||
|
||||
@@ -13,7 +13,7 @@ struct Reply {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
tauri::AppBuilder::new()
|
||||
tauri::AppBuilder::<tauri::flavors::Official>::new()
|
||||
.setup(|webview, _source| async move {
|
||||
let mut webview = webview.clone();
|
||||
tauri::event::listen(String::from("js-event"), move |msg| {
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
use crate::Webview;
|
||||
use futures::future::BoxFuture;
|
||||
use webview_official::WebviewMut;
|
||||
|
||||
mod runner;
|
||||
|
||||
type InvokeHandler =
|
||||
dyn Fn(WebviewMut, String) -> BoxFuture<'static, Result<(), String>> + Send + Sync;
|
||||
type Setup = dyn Fn(WebviewMut, String) -> BoxFuture<'static, ()> + Send + Sync;
|
||||
type InvokeHandler<W> = dyn Fn(W, String) -> BoxFuture<'static, Result<(), String>> + Send + Sync;
|
||||
type Setup<W> = dyn Fn(W, String) -> BoxFuture<'static, ()> + Send + Sync;
|
||||
|
||||
/// The application runner.
|
||||
pub struct App {
|
||||
pub struct App<W: Webview> {
|
||||
/// The JS message handler.
|
||||
invoke_handler: Option<Box<InvokeHandler>>,
|
||||
invoke_handler: Option<Box<InvokeHandler<W>>>,
|
||||
/// The setup callback, invoked when the webview is ready.
|
||||
setup: Option<Box<Setup>>,
|
||||
setup: Option<Box<Setup<W>>>,
|
||||
/// The HTML of the splashscreen to render.
|
||||
splashscreen_html: Option<String>,
|
||||
}
|
||||
|
||||
impl App {
|
||||
impl<W: Webview + 'static> App<W> {
|
||||
/// Runs the app until it finishes.
|
||||
pub fn run(self) {
|
||||
runner::run(self).expect("Failed to build webview");
|
||||
@@ -28,7 +27,7 @@ impl App {
|
||||
/// The message is considered consumed if the handler exists and returns an Ok Result.
|
||||
pub(crate) async fn run_invoke_handler(
|
||||
&self,
|
||||
webview: &mut WebviewMut,
|
||||
webview: &mut W,
|
||||
arg: &str,
|
||||
) -> Result<bool, String> {
|
||||
if let Some(ref invoke_handler) = self.invoke_handler {
|
||||
@@ -40,7 +39,7 @@ impl App {
|
||||
}
|
||||
|
||||
/// Runs the setup callback if defined.
|
||||
pub(crate) async fn run_setup(&self, webview: &mut WebviewMut, source: String) {
|
||||
pub(crate) async fn run_setup(&self, webview: &mut W, source: String) {
|
||||
if let Some(ref setup) = self.setup {
|
||||
let fut = setup(webview.clone(), source);
|
||||
fut.await;
|
||||
@@ -55,16 +54,16 @@ impl App {
|
||||
|
||||
/// The App builder.
|
||||
#[derive(Default)]
|
||||
pub struct AppBuilder {
|
||||
pub struct AppBuilder<W: Webview> {
|
||||
/// The JS message handler.
|
||||
invoke_handler: Option<Box<InvokeHandler>>,
|
||||
invoke_handler: Option<Box<InvokeHandler<W>>>,
|
||||
/// The setup callback, invoked when the webview is ready.
|
||||
setup: Option<Box<Setup>>,
|
||||
setup: Option<Box<Setup<W>>>,
|
||||
/// The HTML of the splashscreen to render.
|
||||
splashscreen_html: Option<String>,
|
||||
}
|
||||
|
||||
impl AppBuilder {
|
||||
impl<W: Webview + 'static> AppBuilder<W> {
|
||||
/// Creates a new App builder.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
@@ -77,7 +76,7 @@ impl AppBuilder {
|
||||
/// Defines the JS message handler callback.
|
||||
pub fn invoke_handler<
|
||||
T: futures::Future<Output = Result<(), String>> + Send + Sync + 'static,
|
||||
F: Fn(WebviewMut, String) -> T + Send + Sync + 'static,
|
||||
F: Fn(W, String) -> T + Send + Sync + 'static,
|
||||
>(
|
||||
mut self,
|
||||
invoke_handler: F,
|
||||
@@ -91,7 +90,7 @@ impl AppBuilder {
|
||||
/// Defines the setup callback.
|
||||
pub fn setup<
|
||||
T: futures::Future<Output = ()> + Send + Sync + 'static,
|
||||
F: Fn(WebviewMut, String) -> T + Send + Sync + 'static,
|
||||
F: Fn(W, String) -> T + Send + Sync + 'static,
|
||||
>(
|
||||
mut self,
|
||||
setup: F,
|
||||
@@ -109,13 +108,16 @@ impl AppBuilder {
|
||||
}
|
||||
|
||||
/// Adds a plugin to the runtime.
|
||||
pub fn plugin(self, plugin: impl crate::plugin::Plugin + Send + Sync + Sync + 'static) -> Self {
|
||||
crate::async_runtime::block_on(crate::plugin::register(plugin));
|
||||
pub fn plugin(
|
||||
self,
|
||||
plugin: impl crate::plugin::Plugin<W> + Send + Sync + Sync + 'static,
|
||||
) -> Self {
|
||||
crate::async_runtime::block_on(crate::plugin::register(W::plugin_store(), plugin));
|
||||
self
|
||||
}
|
||||
|
||||
/// Builds the App.
|
||||
pub fn build(self) -> App {
|
||||
pub fn build(self) -> App<W> {
|
||||
App {
|
||||
invoke_handler: self.invoke_handler,
|
||||
setup: self.setup,
|
||||
|
||||
@@ -6,7 +6,7 @@ use std::{
|
||||
},
|
||||
};
|
||||
|
||||
use webview_official::{SizeHint, Webview, WebviewBuilder};
|
||||
use crate::{SizeHint, Webview, WebviewBuilder};
|
||||
|
||||
use super::App;
|
||||
#[cfg(embedded_server)]
|
||||
@@ -20,7 +20,7 @@ enum Content<T> {
|
||||
}
|
||||
|
||||
/// Main entry point for running the Webview
|
||||
pub(crate) fn run(application: App) -> crate::Result<()> {
|
||||
pub(crate) fn run<W: Webview + 'static>(application: App<W>) -> crate::Result<()> {
|
||||
// setup the content using the config struct depending on the compile target
|
||||
let main_content = setup_content()?;
|
||||
|
||||
@@ -48,8 +48,10 @@ pub(crate) fn run(application: App) -> crate::Result<()> {
|
||||
// build the webview
|
||||
let mut webview = build_webview(application, main_content, splashscreen_content)?;
|
||||
|
||||
let mut webview_ = webview.as_mut();
|
||||
crate::async_runtime::spawn(async move { crate::plugin::created(&mut webview_).await });
|
||||
let mut webview_ = webview.clone();
|
||||
crate::async_runtime::spawn(async move {
|
||||
crate::plugin::created(W::plugin_store(), &mut webview_).await
|
||||
});
|
||||
|
||||
// spawn the embedded server on our server url
|
||||
#[cfg(embedded_server)]
|
||||
@@ -238,11 +240,11 @@ pub fn init() -> String {
|
||||
}
|
||||
|
||||
// build the webview struct
|
||||
fn build_webview<'a>(
|
||||
application: App,
|
||||
fn build_webview<W: Webview + 'static>(
|
||||
application: App<W>,
|
||||
content: Content<String>,
|
||||
splashscreen_content: Option<Content<String>>,
|
||||
) -> crate::Result<Webview<'a>> {
|
||||
) -> crate::Result<W> {
|
||||
let config = get()?;
|
||||
let debug = cfg!(debug_assertions);
|
||||
// get properties from config struct
|
||||
@@ -254,7 +256,7 @@ fn build_webview<'a>(
|
||||
SizeHint::FIXED
|
||||
};
|
||||
// let fullscreen = config.tauri.window.fullscreen;
|
||||
let title = config.tauri.window.title.clone().into_boxed_str();
|
||||
let title = config.tauri.window.title.clone();
|
||||
|
||||
let has_splashscreen = splashscreen_content.is_some();
|
||||
let initialized_splashscreen = Arc::new(AtomicBool::new(false));
|
||||
@@ -281,18 +283,18 @@ fn build_webview<'a>(
|
||||
{plugin_init}
|
||||
"#,
|
||||
event_init = init(),
|
||||
plugin_init = crate::async_runtime::block_on(crate::plugin::init_script())
|
||||
plugin_init = crate::async_runtime::block_on(crate::plugin::init_script(W::plugin_store()))
|
||||
);
|
||||
|
||||
let mut webview = WebviewBuilder::new()
|
||||
.init(Box::leak(init.into_boxed_str()))
|
||||
.title(Box::leak(title))
|
||||
let mut webview = W::Builder::new()
|
||||
.init(&init)
|
||||
.title(&title)
|
||||
.width(width as usize)
|
||||
.height(height as usize)
|
||||
.resize(resizable)
|
||||
.resizable(resizable)
|
||||
.debug(debug)
|
||||
.url(Box::leak(url.into_boxed_str()))
|
||||
.build();
|
||||
.url(&url)
|
||||
.finish();
|
||||
// TODO waiting for webview window API
|
||||
// webview.set_fullscreen(fullscreen);
|
||||
|
||||
@@ -304,7 +306,7 @@ fn build_webview<'a>(
|
||||
webview.dispatch(move |_webview| _webview.eval(&contents));
|
||||
}
|
||||
|
||||
let w = webview.as_mut();
|
||||
let w = webview.clone();
|
||||
let application = Arc::new(application);
|
||||
|
||||
webview.bind("__TAURI_INVOKE_HANDLER__", move |_, arg| {
|
||||
@@ -326,13 +328,12 @@ fn build_webview<'a>(
|
||||
};
|
||||
application.run_setup(&mut w, source.to_string()).await;
|
||||
if source == "window-1" {
|
||||
crate::plugin::ready(&mut w).await;
|
||||
crate::plugin::ready(W::plugin_store(), &mut w).await;
|
||||
}
|
||||
} else if arg == r#"{"cmd":"closeSplashscreen"}"# {
|
||||
w.dispatch(move |w| {
|
||||
w.eval(&format!(r#"window.location.href = "{}""#, content_url));
|
||||
})
|
||||
.unwrap();
|
||||
});
|
||||
} else {
|
||||
let mut endpoint_handle = crate::endpoints::handle(&mut w, &arg)
|
||||
.await
|
||||
@@ -354,7 +355,7 @@ fn build_webview<'a>(
|
||||
}
|
||||
if let Err(ref app_handle_error) = endpoint_handle {
|
||||
if app_handle_error.contains("unknown variant") {
|
||||
let error = match crate::plugin::extend_api(&mut w, &arg).await {
|
||||
let error = match crate::plugin::extend_api(W::plugin_store(), &mut w, &arg).await {
|
||||
Ok(handled) => {
|
||||
if handled {
|
||||
String::from("")
|
||||
|
||||
@@ -16,10 +16,10 @@ mod http;
|
||||
#[cfg(notification)]
|
||||
mod notification;
|
||||
|
||||
use webview_official::WebviewMut;
|
||||
use crate::Webview;
|
||||
|
||||
#[allow(unused_variables)]
|
||||
pub(crate) async fn handle(webview: &mut WebviewMut, arg: &str) -> crate::Result<()> {
|
||||
pub(crate) async fn handle<W: Webview + 'static>(webview: &mut W, arg: &str) -> crate::Result<()> {
|
||||
use cmd::Cmd::*;
|
||||
match serde_json::from_str(arg) {
|
||||
Err(e) => Err(e.into()),
|
||||
@@ -154,7 +154,7 @@ pub(crate) async fn handle(webview: &mut WebviewMut, arg: &str) -> crate::Result
|
||||
#[cfg(set_title)]
|
||||
webview.dispatch(move |w| {
|
||||
w.set_title(&title);
|
||||
})?;
|
||||
});
|
||||
#[cfg(not(set_title))]
|
||||
throw_allowlist_error(webview, "title");
|
||||
}
|
||||
@@ -192,7 +192,7 @@ pub(crate) async fn handle(webview: &mut WebviewMut, arg: &str) -> crate::Result
|
||||
let js_string = event::listen_fn(event, handler, once)?;
|
||||
webview.dispatch(move |w| {
|
||||
w.eval(&js_string);
|
||||
})?;
|
||||
});
|
||||
}
|
||||
#[cfg(not(event))]
|
||||
throw_allowlist_error(webview, "event");
|
||||
@@ -323,7 +323,7 @@ pub(crate) async fn handle(webview: &mut WebviewMut, arg: &str) -> crate::Result
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn api_error(webview: &mut WebviewMut, error_fn: String, message: &str) {
|
||||
fn api_error<W: Webview>(webview: &mut W, error_fn: String, message: &str) {
|
||||
let reject_code = tauri_api::rpc::format_callback(error_fn, message);
|
||||
let _ = webview.dispatch(move |w| {
|
||||
w.eval(&reject_code);
|
||||
@@ -331,7 +331,7 @@ fn api_error(webview: &mut WebviewMut, error_fn: String, message: &str) {
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn allowlist_error(webview: &mut WebviewMut, error_fn: String, allowlist_key: &str) {
|
||||
fn allowlist_error<W: Webview>(webview: &mut W, error_fn: String, allowlist_key: &str) {
|
||||
api_error(
|
||||
webview,
|
||||
error_fn,
|
||||
@@ -343,7 +343,7 @@ fn allowlist_error(webview: &mut WebviewMut, error_fn: String, allowlist_key: &s
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn throw_allowlist_error(webview: &mut WebviewMut, allowlist_key: &str) {
|
||||
fn throw_allowlist_error<W: Webview>(webview: &mut W, allowlist_key: &str) {
|
||||
let reject_code = format!(
|
||||
r#"throw new Error("'{}' not on the allowlist")"#,
|
||||
allowlist_key
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use crate::Webview;
|
||||
use std::path::PathBuf;
|
||||
use webview_official::WebviewMut;
|
||||
|
||||
#[allow(clippy::option_env_unwrap)]
|
||||
pub async fn load(
|
||||
webview: &mut WebviewMut,
|
||||
pub async fn load<W: Webview + 'static>(
|
||||
webview: &mut W,
|
||||
asset: String,
|
||||
asset_type: String,
|
||||
callback: String,
|
||||
@@ -90,7 +90,7 @@ pub async fn load(
|
||||
} else {
|
||||
webview_ref.eval(asset_str);
|
||||
}
|
||||
})?;
|
||||
});
|
||||
Ok("Asset loaded successfully".to_string())
|
||||
}
|
||||
},
|
||||
|
||||
@@ -3,8 +3,8 @@ use crate::api::dialog::{
|
||||
ask as ask_dialog, message as message_dialog, pick_folder, save_file, select, select_multiple,
|
||||
DialogSelection, Response,
|
||||
};
|
||||
use crate::Webview;
|
||||
use serde_json::Value as JsonValue;
|
||||
use webview_official::WebviewMut;
|
||||
|
||||
/// maps a dialog response to a JS value to eval
|
||||
#[cfg(any(open_dialog, save_dialog))]
|
||||
@@ -18,8 +18,8 @@ fn map_response(response: Response) -> JsonValue {
|
||||
|
||||
/// Shows an open dialog.
|
||||
#[cfg(open_dialog)]
|
||||
pub fn open(
|
||||
webview: &mut WebviewMut,
|
||||
pub fn open<W: Webview>(
|
||||
webview: &mut W,
|
||||
options: OpenDialogOptions,
|
||||
callback: String,
|
||||
error: String,
|
||||
@@ -44,8 +44,8 @@ pub fn open(
|
||||
|
||||
/// Shows a save dialog.
|
||||
#[cfg(save_dialog)]
|
||||
pub fn save(
|
||||
webview: &mut WebviewMut,
|
||||
pub fn save<W: Webview>(
|
||||
webview: &mut W,
|
||||
options: SaveDialogOptions,
|
||||
callback: String,
|
||||
error: String,
|
||||
@@ -65,8 +65,8 @@ pub fn message(title: String, message: String) {
|
||||
}
|
||||
|
||||
/// Shows a dialog with a yes/no question.
|
||||
pub fn ask(
|
||||
webview: &mut WebviewMut,
|
||||
pub fn ask<W: Webview>(
|
||||
webview: &mut W,
|
||||
title: String,
|
||||
message: String,
|
||||
callback: String,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use webview_official::WebviewMut;
|
||||
use crate::Webview;
|
||||
|
||||
use tauri_api::dir;
|
||||
use tauri_api::file;
|
||||
@@ -13,8 +13,8 @@ use super::cmd::{DirOperationOptions, FileOperationOptions};
|
||||
|
||||
/// Reads a directory.
|
||||
#[cfg(read_dir)]
|
||||
pub async fn read_dir(
|
||||
webview: &mut WebviewMut,
|
||||
pub async fn read_dir<W: Webview>(
|
||||
webview: &mut W,
|
||||
path: PathBuf,
|
||||
options: Option<DirOperationOptions>,
|
||||
callback: String,
|
||||
@@ -38,8 +38,8 @@ pub async fn read_dir(
|
||||
|
||||
/// Copies a file.
|
||||
#[cfg(copy_file)]
|
||||
pub async fn copy_file(
|
||||
webview: &mut WebviewMut,
|
||||
pub async fn copy_file<W: Webview>(
|
||||
webview: &mut W,
|
||||
source: PathBuf,
|
||||
destination: PathBuf,
|
||||
options: Option<FileOperationOptions>,
|
||||
@@ -66,8 +66,8 @@ pub async fn copy_file(
|
||||
|
||||
/// Creates a directory.
|
||||
#[cfg(create_dir)]
|
||||
pub async fn create_dir(
|
||||
webview: &mut WebviewMut,
|
||||
pub async fn create_dir<W: Webview>(
|
||||
webview: &mut W,
|
||||
path: PathBuf,
|
||||
options: Option<DirOperationOptions>,
|
||||
callback: String,
|
||||
@@ -98,8 +98,8 @@ pub async fn create_dir(
|
||||
|
||||
/// Removes a directory.
|
||||
#[cfg(remove_dir)]
|
||||
pub async fn remove_dir(
|
||||
webview: &mut WebviewMut,
|
||||
pub async fn remove_dir<W: Webview>(
|
||||
webview: &mut W,
|
||||
path: PathBuf,
|
||||
options: Option<DirOperationOptions>,
|
||||
callback: String,
|
||||
@@ -130,8 +130,8 @@ pub async fn remove_dir(
|
||||
|
||||
/// Removes a file
|
||||
#[cfg(remove_file)]
|
||||
pub async fn remove_file(
|
||||
webview: &mut WebviewMut,
|
||||
pub async fn remove_file<W: Webview>(
|
||||
webview: &mut W,
|
||||
path: PathBuf,
|
||||
options: Option<FileOperationOptions>,
|
||||
callback: String,
|
||||
@@ -151,8 +151,8 @@ pub async fn remove_file(
|
||||
|
||||
/// Renames a file.
|
||||
#[cfg(rename_file)]
|
||||
pub async fn rename_file(
|
||||
webview: &mut WebviewMut,
|
||||
pub async fn rename_file<W: Webview>(
|
||||
webview: &mut W,
|
||||
old_path: PathBuf,
|
||||
new_path: PathBuf,
|
||||
options: Option<FileOperationOptions>,
|
||||
@@ -179,8 +179,8 @@ pub async fn rename_file(
|
||||
|
||||
/// Writes a text file.
|
||||
#[cfg(write_file)]
|
||||
pub async fn write_file(
|
||||
webview: &mut WebviewMut,
|
||||
pub async fn write_file<W: Webview>(
|
||||
webview: &mut W,
|
||||
path: PathBuf,
|
||||
contents: String,
|
||||
options: Option<FileOperationOptions>,
|
||||
@@ -202,8 +202,8 @@ pub async fn write_file(
|
||||
|
||||
/// Writes a binary file.
|
||||
#[cfg(write_binary_file)]
|
||||
pub async fn write_binary_file(
|
||||
webview: &mut WebviewMut,
|
||||
pub async fn write_binary_file<W: Webview>(
|
||||
webview: &mut W,
|
||||
path: PathBuf,
|
||||
contents: String,
|
||||
options: Option<FileOperationOptions>,
|
||||
@@ -229,8 +229,8 @@ pub async fn write_binary_file(
|
||||
|
||||
/// Reads a text file.
|
||||
#[cfg(read_text_file)]
|
||||
pub async fn read_text_file(
|
||||
webview: &mut WebviewMut,
|
||||
pub async fn read_text_file<W: Webview>(
|
||||
webview: &mut W,
|
||||
path: PathBuf,
|
||||
options: Option<FileOperationOptions>,
|
||||
callback: String,
|
||||
@@ -247,8 +247,8 @@ pub async fn read_text_file(
|
||||
|
||||
/// Reads a binary file.
|
||||
#[cfg(read_binary_file)]
|
||||
pub async fn read_binary_file(
|
||||
webview: &mut WebviewMut,
|
||||
pub async fn read_binary_file<W: Webview>(
|
||||
webview: &mut W,
|
||||
path: PathBuf,
|
||||
options: Option<FileOperationOptions>,
|
||||
callback: String,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use crate::Webview;
|
||||
use tauri_api::http::{make_request as request, HttpRequestOptions};
|
||||
use webview_official::WebviewMut;
|
||||
|
||||
/// Makes an HTTP request and resolves the response to the webview
|
||||
pub async fn make_request(
|
||||
webview: &mut WebviewMut,
|
||||
pub async fn make_request<W: Webview>(
|
||||
webview: &mut W,
|
||||
options: HttpRequestOptions,
|
||||
callback: String,
|
||||
error: String,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use super::cmd::NotificationOptions;
|
||||
use crate::Webview;
|
||||
use serde_json::Value as JsonValue;
|
||||
use webview_official::WebviewMut;
|
||||
|
||||
pub async fn send(
|
||||
webview: &mut WebviewMut,
|
||||
pub async fn send<W: Webview>(
|
||||
webview: &mut W,
|
||||
options: NotificationOptions,
|
||||
callback: String,
|
||||
error: String,
|
||||
@@ -27,7 +27,7 @@ pub async fn send(
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn is_permission_granted(webview: &mut WebviewMut, callback: String, error: String) {
|
||||
pub async fn is_permission_granted<W: Webview>(webview: &mut W, callback: String, error: String) {
|
||||
crate::execute_promise(
|
||||
webview,
|
||||
async move {
|
||||
@@ -44,8 +44,8 @@ pub async fn is_permission_granted(webview: &mut WebviewMut, callback: String, e
|
||||
.await;
|
||||
}
|
||||
|
||||
pub fn request_permission(
|
||||
webview: &mut WebviewMut,
|
||||
pub fn request_permission<W: Webview>(
|
||||
webview: &mut W,
|
||||
callback: String,
|
||||
error: String,
|
||||
) -> crate::Result<()> {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#![cfg(path_api)]
|
||||
use crate::Webview;
|
||||
use tauri_api::path;
|
||||
use tauri_api::path::BaseDirectory;
|
||||
use webview_official::WebviewMut;
|
||||
|
||||
pub async fn resolve_path(
|
||||
webview: &mut WebviewMut,
|
||||
pub async fn resolve_path<W: Webview>(
|
||||
webview: &mut W,
|
||||
path: String,
|
||||
directory: Option<BaseDirectory>,
|
||||
callback: String,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use webview_official::WebviewMut;
|
||||
use crate::Webview;
|
||||
|
||||
/// Validates a salt.
|
||||
pub fn validate(
|
||||
webview: &mut WebviewMut,
|
||||
pub fn validate<W: Webview>(
|
||||
webview: &mut W,
|
||||
salt: String,
|
||||
callback: String,
|
||||
error: String,
|
||||
@@ -15,6 +15,6 @@ pub fn validate(
|
||||
let callback_string = crate::api::rpc::format_callback_result(response, callback, error)?;
|
||||
webview.dispatch(move |w| {
|
||||
w.eval(callback_string.as_str());
|
||||
})?;
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -2,11 +2,11 @@ use std::boxed::Box;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use crate::Webview;
|
||||
use lazy_static::lazy_static;
|
||||
use once_cell::sync::Lazy;
|
||||
use serde::Serialize;
|
||||
use serde_json::Value as JsonValue;
|
||||
use webview_official::WebviewMut;
|
||||
|
||||
/// An event handler.
|
||||
struct EventHandler {
|
||||
@@ -57,8 +57,8 @@ pub fn listen<F: FnMut(Option<String>) + Send + 'static>(id: impl Into<String>,
|
||||
}
|
||||
|
||||
/// Emits an event to JS.
|
||||
pub fn emit<S: Serialize>(
|
||||
webview: &mut WebviewMut,
|
||||
pub fn emit<W: Webview, S: Serialize>(
|
||||
webview: &mut W,
|
||||
event: impl AsRef<str> + Send + 'static,
|
||||
payload: Option<S>,
|
||||
) -> crate::Result<()> {
|
||||
@@ -78,7 +78,7 @@ pub fn emit<S: Serialize>(
|
||||
js_payload,
|
||||
salt
|
||||
))
|
||||
})?;
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -29,6 +29,8 @@ mod endpoints;
|
||||
pub mod plugin;
|
||||
/// The salt helpers.
|
||||
mod salt;
|
||||
/// Webview interface.
|
||||
mod webview;
|
||||
|
||||
pub(crate) mod async_runtime;
|
||||
|
||||
@@ -36,7 +38,12 @@ pub(crate) mod async_runtime;
|
||||
pub use anyhow::Result;
|
||||
pub use app::*;
|
||||
pub use tauri_api as api;
|
||||
pub use webview_official::{Webview, WebviewMut};
|
||||
pub use webview::*;
|
||||
|
||||
/// The Tauri webview implementations.
|
||||
pub mod flavors {
|
||||
pub use webview_official::Webview as Official;
|
||||
}
|
||||
|
||||
use std::process::Stdio;
|
||||
|
||||
@@ -46,10 +53,11 @@ use serde::Serialize;
|
||||
/// Synchronously executes the given task
|
||||
/// and evaluates its Result to the JS promise described by the `callback` and `error` function names.
|
||||
pub fn execute_promise_sync<
|
||||
W: Webview,
|
||||
R: Serialize,
|
||||
F: futures::Future<Output = Result<R>> + Send + 'static,
|
||||
>(
|
||||
webview: &mut WebviewMut,
|
||||
webview: &mut W,
|
||||
task: F,
|
||||
callback: String,
|
||||
error: String,
|
||||
@@ -57,7 +65,7 @@ pub fn execute_promise_sync<
|
||||
async_runtime::block_on(async move {
|
||||
let callback_string =
|
||||
format_callback_result(task.await.map_err(|err| err.to_string()), callback, error)?;
|
||||
webview.dispatch(move |w| w.eval(callback_string.as_str()))?;
|
||||
webview.dispatch(move |w| w.eval(callback_string.as_str()));
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
@@ -68,10 +76,11 @@ pub fn execute_promise_sync<
|
||||
/// If the Result `is_ok()`, the callback will be the `success_callback` function name and the argument will be the Ok value.
|
||||
/// If the Result `is_err()`, the callback will be the `error_callback` function name and the argument will be the Err value.
|
||||
pub async fn execute_promise<
|
||||
W: Webview,
|
||||
R: Serialize,
|
||||
F: futures::Future<Output = Result<R>> + Send + 'static,
|
||||
>(
|
||||
webview: &mut WebviewMut,
|
||||
webview: &mut W,
|
||||
task: F,
|
||||
success_callback: String,
|
||||
error_callback: String,
|
||||
@@ -84,14 +93,12 @@ pub async fn execute_promise<
|
||||
Ok(callback_string) => callback_string,
|
||||
Err(e) => format_callback(error_callback, e.to_string()),
|
||||
};
|
||||
webview
|
||||
.dispatch(move |webview_ref| webview_ref.eval(callback_string.as_str()))
|
||||
.expect("Failed to dispatch promise callback");
|
||||
webview.dispatch(move |webview_ref| webview_ref.eval(callback_string.as_str()));
|
||||
}
|
||||
|
||||
/// Calls the given command and evaluates its output to the JS promise described by the `callback` and `error` function names.
|
||||
pub async fn call(
|
||||
webview: &mut WebviewMut,
|
||||
pub async fn call<W: Webview>(
|
||||
webview: &mut W,
|
||||
command: String,
|
||||
args: Vec<String>,
|
||||
callback: String,
|
||||
@@ -107,7 +114,7 @@ pub async fn call(
|
||||
}
|
||||
|
||||
/// Closes the splashscreen.
|
||||
pub fn close_splashscreen(webview: &mut Webview<'_>) -> crate::Result<()> {
|
||||
pub fn close_splashscreen<W: Webview>(webview: &mut W) -> crate::Result<()> {
|
||||
// send a signal to the runner so it knows that it should redirect to the main app content
|
||||
webview.eval(r#"window.__TAURI_INVOKE_HANDLER__({ cmd: "closeSplashscreen" })"#);
|
||||
|
||||
|
||||
@@ -1,49 +1,47 @@
|
||||
use crate::async_runtime::Mutex;
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use webview_official::WebviewMut;
|
||||
use crate::Webview;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
/// The plugin interface.
|
||||
#[async_trait::async_trait]
|
||||
pub trait Plugin: Sync {
|
||||
pub trait Plugin<W: Webview + 'static>: Sync {
|
||||
/// The JS script to evaluate on init.
|
||||
async fn init_script(&self) -> Option<String> {
|
||||
None
|
||||
}
|
||||
/// Callback invoked when the webview is created.
|
||||
#[allow(unused_variables)]
|
||||
async fn created(&self, webview: WebviewMut) {}
|
||||
async fn created(&self, webview: W) {}
|
||||
|
||||
/// Callback invoked when the webview is ready.
|
||||
#[allow(unused_variables)]
|
||||
async fn ready(&self, webview: WebviewMut) {}
|
||||
async fn ready(&self, webview: W) {}
|
||||
|
||||
/// Add invoke_handler API extension commands.
|
||||
#[allow(unused_variables)]
|
||||
async fn extend_api(&self, webview: WebviewMut, payload: &str) -> Result<bool, String> {
|
||||
async fn extend_api(&self, webview: W, payload: &str) -> Result<bool, String> {
|
||||
Err("unknown variant".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
type PluginStore = Arc<Mutex<Vec<Box<dyn Plugin + Sync + Send>>>>;
|
||||
|
||||
fn plugins() -> &'static PluginStore {
|
||||
static PLUGINS: Lazy<PluginStore> = Lazy::new(Default::default);
|
||||
&PLUGINS
|
||||
}
|
||||
/// Plugin collection type.
|
||||
pub type PluginStore<W> = Arc<Mutex<Vec<Box<dyn Plugin<W> + Sync + Send>>>>;
|
||||
|
||||
/// Registers a plugin.
|
||||
pub async fn register(plugin: impl Plugin + Sync + Send + 'static) {
|
||||
let mut plugins = plugins().lock().await;
|
||||
pub async fn register<W: Webview + 'static>(
|
||||
store: &PluginStore<W>,
|
||||
plugin: impl Plugin<W> + Sync + Send + 'static,
|
||||
) {
|
||||
let mut plugins = store.lock().await;
|
||||
plugins.push(Box::new(plugin));
|
||||
}
|
||||
|
||||
pub(crate) async fn init_script() -> String {
|
||||
pub(crate) async fn init_script<W: Webview + 'static>(store: &PluginStore<W>) -> String {
|
||||
let mut init = String::new();
|
||||
|
||||
let plugins = plugins().lock().await;
|
||||
let plugins = store.lock().await;
|
||||
for plugin in plugins.iter() {
|
||||
if let Some(init_script) = plugin.init_script().await {
|
||||
init.push_str(&format!("(function () {{ {} }})();", init_script));
|
||||
@@ -53,22 +51,26 @@ pub(crate) async fn init_script() -> String {
|
||||
init
|
||||
}
|
||||
|
||||
pub(crate) async fn created(webview: &mut WebviewMut) {
|
||||
let plugins = plugins().lock().await;
|
||||
pub(crate) async fn created<W: Webview + 'static>(store: &PluginStore<W>, webview: &mut W) {
|
||||
let plugins = store.lock().await;
|
||||
for plugin in plugins.iter() {
|
||||
plugin.created(webview.clone()).await;
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn ready(webview: &mut WebviewMut) {
|
||||
let plugins = plugins().lock().await;
|
||||
pub(crate) async fn ready<W: Webview + 'static>(store: &PluginStore<W>, webview: &mut W) {
|
||||
let plugins = store.lock().await;
|
||||
for plugin in plugins.iter() {
|
||||
plugin.ready(webview.clone()).await;
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn extend_api(webview: &mut WebviewMut, arg: &str) -> Result<bool, String> {
|
||||
let plugins = plugins().lock().await;
|
||||
pub(crate) async fn extend_api<W: Webview + 'static>(
|
||||
store: &PluginStore<W>,
|
||||
webview: &mut W,
|
||||
arg: &str,
|
||||
) -> Result<bool, String> {
|
||||
let plugins = store.lock().await;
|
||||
for ext in plugins.iter() {
|
||||
match ext.extend_api(webview.clone(), arg).await {
|
||||
Ok(handled) => {
|
||||
|
||||
83
tauri/src/webview.rs
Normal file
83
tauri/src/webview.rs
Normal file
@@ -0,0 +1,83 @@
|
||||
pub(crate) mod official;
|
||||
|
||||
/// Size hints.
|
||||
pub enum SizeHint {
|
||||
/// None
|
||||
NONE = 0,
|
||||
/// Min
|
||||
MIN = 1,
|
||||
/// Max
|
||||
MAX = 2,
|
||||
/// Fixed
|
||||
FIXED = 3,
|
||||
}
|
||||
|
||||
impl Default for SizeHint {
|
||||
fn default() -> Self {
|
||||
Self::NONE
|
||||
}
|
||||
}
|
||||
|
||||
pub use crate::plugin::PluginStore;
|
||||
|
||||
/// The webview builder.
|
||||
pub trait WebviewBuilder: Sized {
|
||||
/// The webview object that this builder creates.
|
||||
type WebviewObject: Webview<Builder = Self>;
|
||||
|
||||
/// Initializes a new instance of the builder.
|
||||
fn new() -> Self;
|
||||
/// Sets the debug flag.
|
||||
fn debug(self, debug: bool) -> Self;
|
||||
/// Sets the window title.
|
||||
fn title(self, title: &str) -> Self;
|
||||
/// Sets the initial url.
|
||||
fn url(self, url: &str) -> Self;
|
||||
/// Sets the init script.
|
||||
fn init(self, init: &str) -> Self;
|
||||
/// Sets the window width.
|
||||
fn width(self, width: usize) -> Self;
|
||||
/// Sets the window height.
|
||||
fn height(self, height: usize) -> Self;
|
||||
/// Whether the window is resizable or not.
|
||||
fn resizable(self, resizable: SizeHint) -> Self;
|
||||
/// Builds the webview instance.
|
||||
fn finish(self) -> Self::WebviewObject;
|
||||
}
|
||||
|
||||
/// Webview core API.
|
||||
pub trait Webview: Clone + Send + Sync + Sized {
|
||||
/// The builder type.
|
||||
type Builder: WebviewBuilder<WebviewObject = Self>;
|
||||
|
||||
/// Returns the static plugin collection.
|
||||
fn plugin_store() -> &'static PluginStore<Self>;
|
||||
|
||||
/// Adds an init JS code.
|
||||
fn init(&mut self, js: &str);
|
||||
|
||||
/// Sets the window title.
|
||||
fn set_title(&mut self, title: &str);
|
||||
|
||||
/// Sets the window size.
|
||||
fn set_size(&mut self, width: i32, height: i32, hint: SizeHint);
|
||||
|
||||
/// terminate the webview.
|
||||
fn terminate(&mut self);
|
||||
|
||||
/// eval a string as JS code.
|
||||
fn eval(&mut self, js: &str);
|
||||
|
||||
/// Dispatches a closure to run on the main thread.
|
||||
fn dispatch<F>(&mut self, f: F)
|
||||
where
|
||||
F: FnOnce(&mut Self) + Send + 'static;
|
||||
|
||||
/// Binds a new API on the webview.
|
||||
fn bind<F>(&mut self, name: &str, f: F)
|
||||
where
|
||||
F: FnMut(&str, &str);
|
||||
|
||||
/// Run the webview event loop.
|
||||
fn run(&mut self);
|
||||
}
|
||||
143
tauri/src/webview/official.rs
Normal file
143
tauri/src/webview/official.rs
Normal file
@@ -0,0 +1,143 @@
|
||||
use super::{PluginStore, SizeHint, Webview, WebviewBuilder};
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct WebviewOfficialBuilder {
|
||||
title: Option<String>,
|
||||
url: Option<String>,
|
||||
init: Option<String>,
|
||||
eval: Option<String>,
|
||||
size: (usize, usize, SizeHint),
|
||||
debug: bool,
|
||||
}
|
||||
|
||||
impl WebviewBuilder for WebviewOfficialBuilder {
|
||||
type WebviewObject = webview_official::Webview;
|
||||
|
||||
fn new() -> Self {
|
||||
WebviewOfficialBuilder::default()
|
||||
}
|
||||
|
||||
fn debug(mut self, debug: bool) -> Self {
|
||||
self.debug = debug;
|
||||
self
|
||||
}
|
||||
|
||||
fn title(mut self, title: &str) -> Self {
|
||||
self.title = Some(title.to_string());
|
||||
self
|
||||
}
|
||||
|
||||
fn url(mut self, url: &str) -> Self {
|
||||
self.url = Some(url.to_string());
|
||||
self
|
||||
}
|
||||
|
||||
fn init(mut self, init: &str) -> Self {
|
||||
self.init = Some(init.to_string());
|
||||
self
|
||||
}
|
||||
|
||||
fn width(mut self, width: usize) -> Self {
|
||||
self.size.0 = width;
|
||||
self
|
||||
}
|
||||
|
||||
fn height(mut self, height: usize) -> Self {
|
||||
self.size.1 = height;
|
||||
self
|
||||
}
|
||||
|
||||
fn resizable(mut self, hint: SizeHint) -> Self {
|
||||
self.size.2 = hint;
|
||||
self
|
||||
}
|
||||
|
||||
fn finish(self) -> Self::WebviewObject {
|
||||
let mut w = webview_official::Webview::create(self.debug, None);
|
||||
if let Some(title) = self.title {
|
||||
w.set_title(&title);
|
||||
}
|
||||
|
||||
if let Some(init) = self.init {
|
||||
w.init(&init);
|
||||
}
|
||||
|
||||
if let Some(url) = self.url {
|
||||
w.navigate(&url);
|
||||
}
|
||||
|
||||
if let Some(eval) = self.eval {
|
||||
w.eval(&eval);
|
||||
}
|
||||
|
||||
w.set_size(
|
||||
self.size.0 as i32,
|
||||
self.size.1 as i32,
|
||||
match self.size.2 {
|
||||
SizeHint::NONE => webview_official::SizeHint::NONE,
|
||||
SizeHint::MIN => webview_official::SizeHint::MIN,
|
||||
SizeHint::MAX => webview_official::SizeHint::MAX,
|
||||
SizeHint::FIXED => webview_official::SizeHint::FIXED,
|
||||
},
|
||||
);
|
||||
|
||||
w
|
||||
}
|
||||
}
|
||||
|
||||
impl Webview for webview_official::Webview {
|
||||
type Builder = WebviewOfficialBuilder;
|
||||
|
||||
fn plugin_store() -> &'static PluginStore<Self> {
|
||||
static PLUGINS: Lazy<PluginStore<webview_official::Webview>> = Lazy::new(Default::default);
|
||||
&PLUGINS
|
||||
}
|
||||
|
||||
fn init(&mut self, js: &str) {
|
||||
self.init(js);
|
||||
}
|
||||
|
||||
fn set_title(&mut self, title: &str) {
|
||||
self.set_title(title);
|
||||
}
|
||||
|
||||
fn set_size(&mut self, width: i32, height: i32, hint: SizeHint) {
|
||||
self.set_size(
|
||||
width,
|
||||
height,
|
||||
match hint {
|
||||
SizeHint::NONE => webview_official::SizeHint::NONE,
|
||||
SizeHint::MIN => webview_official::SizeHint::MIN,
|
||||
SizeHint::MAX => webview_official::SizeHint::MAX,
|
||||
SizeHint::FIXED => webview_official::SizeHint::FIXED,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn terminate(&mut self) {
|
||||
self.terminate();
|
||||
}
|
||||
|
||||
fn eval(&mut self, js: &str) {
|
||||
self.eval(js);
|
||||
}
|
||||
|
||||
fn dispatch<F>(&mut self, f: F)
|
||||
where
|
||||
F: FnOnce(&mut Self) + Send + 'static,
|
||||
{
|
||||
self.dispatch(f);
|
||||
}
|
||||
|
||||
fn bind<F>(&mut self, name: &str, f: F)
|
||||
where
|
||||
F: FnMut(&str, &str),
|
||||
{
|
||||
self.bind(name, f);
|
||||
}
|
||||
|
||||
fn run(&mut self) {
|
||||
self.run();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user