diff --git a/.changes/tauri-webview.md b/.changes/tauri-webview.md new file mode 100644 index 000000000..6391507bc --- /dev/null +++ b/.changes/tauri-webview.md @@ -0,0 +1,8 @@ +--- +"tauri": minor +--- + +Moving the webview implementation to [webview](https://github.com/webview/webview), with the [official Rust binding](https://github.com/webview/webview_rust). +This is a breaking change. +IE support has been dropped, so the `edge` object on `tauri.conf.json > tauri` no longer exists and you need to remove it. +`webview.handle()` has been replaced with `webview.as_mut()`. diff --git a/cli/tauri.js/api-src/tauri.ts b/cli/tauri.js/api-src/tauri.ts index 5bdb89ea1..687c94995 100644 --- a/cli/tauri.js/api-src/tauri.ts +++ b/cli/tauri.js/api-src/tauri.ts @@ -1,6 +1,6 @@ declare global { - interface External { - invoke: (command: string) => void + interface Window { + __TAURI_INVOKE_HANDLER__: (command: string) => void } } @@ -21,7 +21,7 @@ function uid(): string { * @param args */ function invoke(args: any): void { - window.external.invoke(typeof args === 'object' ? JSON.stringify(args) : args) + window.__TAURI_INVOKE_HANDLER__(args) } function transformCallback(callback?: (response: any) => void, once = false): string { diff --git a/cli/tauri.js/src/api/info.ts b/cli/tauri.js/src/api/info.ts index 82a32c74b..65e95f07d 100644 --- a/cli/tauri.js/src/api/info.ts +++ b/cli/tauri.js/src/api/info.ts @@ -192,10 +192,6 @@ async function printAppInfo(tauriDir: string): Promise { key: ' CSP', value: config.tauri.security ? config.tauri.security.csp : 'unset' }) - printInfo({ - key: ' Windows', - value: config.tauri.edge?.active ? 'Edge' : 'MSHTML' - }) printInfo({ key: ' distDir', value: config.build diff --git a/cli/tauri.js/src/helpers/tauri-config.ts b/cli/tauri.js/src/helpers/tauri-config.ts index f4fdede2c..5a03e9e3f 100644 --- a/cli/tauri.js/src/helpers/tauri-config.ts +++ b/cli/tauri.js/src/helpers/tauri-config.ts @@ -54,9 +54,6 @@ const getTauriConfig = (cfg: Partial): TauriConfig => { security: { csp: "default-src blob: data: filesystem: ws: http: https: 'unsafe-eval' 'unsafe-inline'" }, - edge: { - active: true - }, inliner: { active: true } diff --git a/cli/tauri.js/src/runner.ts b/cli/tauri.js/src/runner.ts index 04a7d4ab9..cd40a685d 100644 --- a/cli/tauri.js/src/runner.ts +++ b/cli/tauri.js/src/runner.ts @@ -117,7 +117,7 @@ class Runner { self.__parseHtml(cfg, indexDir, false) .then(({ html }) => { const headers: { [key: string]: string } = {} - if(proxyRes.headers['content-type']) { + if (proxyRes.headers['content-type']) { headers['content-type'] = proxyRes.headers['content-type'] } else { const charsetMatch = /charset="(\S+)"/g.exec(bodyStr) @@ -527,10 +527,6 @@ class Runner { tomlFeatures.push(...whitelist.map(toKebabCase)) } - if (cfg.tauri.edge.active) { - tomlFeatures.push('edge') - } - if (cfg.tauri.cli) { tomlFeatures.push('cli') } diff --git a/cli/tauri.js/src/template/defaultConfig.ts b/cli/tauri.js/src/template/defaultConfig.ts index 3c898b020..a2a08998d 100644 --- a/cli/tauri.js/src/template/defaultConfig.ts +++ b/cli/tauri.js/src/template/defaultConfig.ts @@ -45,9 +45,6 @@ export default { security: { csp: "default-src blob: data: filesystem: ws: http: https: 'unsafe-eval' 'unsafe-inline'" }, - edge: { - active: true - }, inliner: { active: true } diff --git a/cli/tauri.js/src/types/config.schema.json b/cli/tauri.js/src/types/config.schema.json index e4d452fad..f22bb129d 100644 --- a/cli/tauri.js/src/types/config.schema.json +++ b/cli/tauri.js/src/types/config.schema.json @@ -391,17 +391,6 @@ "$ref": "#/definitions/CliConfig", "description": "app's CLI definition" }, - "edge": { - "additionalProperties": false, - "defaultProperties": [ - ], - "properties": { - "active": { - "type": "boolean" - } - }, - "type": "object" - }, "embeddedServer": { "additionalProperties": false, "defaultProperties": [ @@ -496,7 +485,6 @@ }, "required": [ "bundle", - "edge", "embeddedServer", "inliner", "security", diff --git a/cli/tauri.js/src/types/config.ts b/cli/tauri.js/src/types/config.ts index 68ae3ac5d..1e2843de7 100644 --- a/cli/tauri.js/src/types/config.ts +++ b/cli/tauri.js/src/types/config.ts @@ -285,9 +285,6 @@ export interface TauriConfig { security: { csp?: string } - edge: { - active?: boolean - } inliner: { active?: boolean } diff --git a/cli/tauri.js/src/types/config.validator.ts b/cli/tauri.js/src/types/config.validator.ts index bd2e443df..fdff57d3e 100644 --- a/cli/tauri.js/src/types/config.validator.ts +++ b/cli/tauri.js/src/types/config.validator.ts @@ -401,17 +401,6 @@ export const TauriConfigSchema = { "$ref": "#/definitions/CliConfig", "description": "app's CLI definition" }, - "edge": { - "additionalProperties": false, - "defaultProperties": [ - ], - "properties": { - "active": { - "type": "boolean" - } - }, - "type": "object" - }, "embeddedServer": { "additionalProperties": false, "defaultProperties": [ @@ -506,7 +495,6 @@ export const TauriConfigSchema = { }, "required": [ "bundle", - "edge", "embeddedServer", "inliner", "security", diff --git a/cli/tauri.js/templates/tauri.js b/cli/tauri.js/templates/tauri.js index 53b5a023d..272570ae6 100644 --- a/cli/tauri.js/templates/tauri.js +++ b/cli/tauri.js/templates/tauri.js @@ -6,31 +6,29 @@ if (!String.prototype.startsWith) { } } -// makes the window.external.invoke API available after window.location.href changes -switch (navigator.platform) { - case "Macintosh": - case "MacPPC": - case "MacIntel": - case "Mac68K": - window.external = this - invoke = function (x) { - webkit.messageHandlers.invoke.postMessage(x); - } - break; - case "Windows": - case "WinCE": - case "Win32": - case "Win64": - break; - default: - window.external = this - invoke = function (x) { - window.webkit.messageHandlers.external.postMessage(x); - } - break; -} - (function () { + function webviewBind (name) { + var RPC = window._rpc = (window._rpc || { nextSeq: 1 }); + window[name] = function () { + var seq = RPC.nextSeq++; + var promise = new Promise(function (resolve, reject) { + RPC[seq] = { + resolve: resolve, + reject: reject, + }; + }); + window.external.invoke(JSON.stringify({ + id: seq, + method: name, + params: Array.prototype.slice.call(arguments), + })); + return promise; + } + } + if (!window.__TAURI_INVOKE_HANDLER__) { + webviewBind('__TAURI_INVOKE_HANDLER__') + } + function s4() { return Math.floor((1 + Math.random()) * 0x10000) .toString(16) @@ -89,9 +87,6 @@ switch (navigator.platform) { if (!window.__TAURI__) { window.__TAURI__ = {} } - window.__TAURI__.invoke = function invoke(args) { - window.external.invoke(JSON.stringify(args)) - } window.__TAURI__.transformCallback = function transformCallback(callback) { var once = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false @@ -109,10 +104,10 @@ switch (navigator.platform) { } window.__TAURI__.promisified = function promisified(args) { - var _this = this; + var _this = this return new Promise(function (resolve, reject) { - _this.invoke(_objectSpread({ + window.__TAURI_INVOKE_HANDLER__(_objectSpread({ callback: _this.transformCallback(resolve), error: _this.transformCallback(reject) }, args)) @@ -127,19 +122,6 @@ switch (navigator.platform) { }) } - // init tauri API - try { - window.__TAURI__.invoke({ - cmd: 'init' - }) - } catch (e) { - window.addEventListener('DOMContentLoaded', function () { - window.__TAURI__.invoke({ - cmd: 'init' - }) - }, true) - } - document.addEventListener('error', function (e) { var target = e.target while (target != null) { @@ -161,7 +143,7 @@ switch (navigator.platform) { while (target != null) { if (target.matches ? target.matches('a') : target.msMatchesSelector('a')) { if (target.href && target.href.startsWith('http') && target.target === '_blank') { - window.__TAURI__.invoke({ + window.__TAURI_INVOKE_HANDLER__({ cmd: 'open', uri: target.href }) diff --git a/tauri/Cargo.toml b/tauri/Cargo.toml index 06a50ba74..dc9ef92ac 100644 --- a/tauri/Cargo.toml +++ b/tauri/Cargo.toml @@ -20,8 +20,7 @@ features = [ "all-api" ] [dependencies] serde_json = "1.0" serde = { version = "1.0", features = [ "derive" ] } -tauri-webview-sys = "0.5.0" -tauri-web-view = "0.6.2" +webview_rust = { version = "0.1", git = "https://github.com/webview/webview_rust.git", branch = "dev" } tauri_includedir = "0.6.0" phf = "0.8.0" base64 = "0.12.3" @@ -35,6 +34,7 @@ thiserror = "1.0.20" envmnt = "0.8.3" once_cell = "1.4.0" tauri-api = { version = "0.7", path = "../tauri-api" } +urlencoding = "1.1.1" [target."cfg(target_os = \"windows\")".dependencies] runas = "0.2" @@ -46,12 +46,11 @@ cfg_aliases = "0.1.0" [dev-dependencies] proptest = "0.10.0" serde_json = "1.0" -tauri = { path = ".", features = [ "all-api", "edge" ] } +tauri = { path = ".", features = [ "all-api" ] } serde = { version = "1.0", features = [ "derive" ] } [features] cli = [ "tauri-api/cli" ] -edge = [ "tauri-web-view/edge" ] embedded-server = [ "tiny_http" ] no-server = [ ] all-api = [ "tauri-api/notification" ] diff --git a/tauri/examples/communication/dist/communication.js b/tauri/examples/communication/dist/communication.js index 50386fe13..fd70b3a2e 100644 --- a/tauri/examples/communication/dist/communication.js +++ b/tauri/examples/communication/dist/communication.js @@ -1,5 +1,5 @@ document.getElementById('log').addEventListener('click', function () { - window.__TAURI__.invoke({ + window.__TAURI__.tauri.invoke({ cmd: 'logOperation', event: 'tauri-click', payload: 'this payload is optional because we used Option in Rust' @@ -7,7 +7,7 @@ document.getElementById('log').addEventListener('click', function () { }) document.getElementById('request').addEventListener('click', function () { - window.__TAURI__.promisified({ + window.__TAURI__.tauri.promisified({ cmd: 'performRequest', endpoint: 'dummy endpoint arg', body: { diff --git a/tauri/examples/communication/dist/index.tauri.html b/tauri/examples/communication/dist/index.tauri.html index 54ef99f00..31c619db2 100644 --- a/tauri/examples/communication/dist/index.tauri.html +++ b/tauri/examples/communication/dist/index.tauri.html @@ -1,4 +1,4 @@ -


\ No newline at end of file + + + + + +
+ +
+ +
+
+
+ + +
+ + + + + +
+ + +
+
+
+
+ + +
+
+ + + +
+
+ + +
+ + +
+
+ + +
+ + + +
+
+
+ +
+ + +
+
+ + +
+ +
+ + +
+ +
+ +
+
+
+
+ + +
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/tauri/examples/communication/src-tauri/Cargo.toml b/tauri/examples/communication/src-tauri/Cargo.toml index cea77ba1c..e5d50c7b8 100644 --- a/tauri/examples/communication/src-tauri/Cargo.toml +++ b/tauri/examples/communication/src-tauri/Cargo.toml @@ -24,7 +24,7 @@ icon = [ [dependencies] serde_json = "1.0" serde = { version = "1.0", features = [ "derive" ] } -tauri = { path = "../../..", features = [ "all-api", "edge", "cli" ] } +tauri = { path = "../../..", features = [ "all-api", "cli" ] } [target."cfg(windows)".build-dependencies] winres = "0.1" diff --git a/tauri/examples/communication/src-tauri/src/main.rs b/tauri/examples/communication/src-tauri/src/main.rs index 68fc59af6..5434e2414 100644 --- a/tauri/examples/communication/src-tauri/src/main.rs +++ b/tauri/examples/communication/src-tauri/src/main.rs @@ -15,14 +15,14 @@ struct Reply { fn main() { tauri::AppBuilder::new() .setup(|webview, _source| { - let handle = webview.handle(); + let mut webview = webview.as_mut(); tauri::event::listen(String::from("js-event"), move |msg| { println!("got js-event with message '{:?}'", msg); let reply = Reply { data: "something else".to_string(), }; - tauri::event::emit(&handle, String::from("rust-event"), Some(reply)) + tauri::event::emit(&mut webview, String::from("rust-event"), Some(reply)) .expect("failed to emit"); }); }) diff --git a/tauri/examples/communication/src-tauri/tauri.conf.json b/tauri/examples/communication/src-tauri/tauri.conf.json index 59a4a81e8..4715a3cd3 100644 --- a/tauri/examples/communication/src-tauri/tauri.conf.json +++ b/tauri/examples/communication/src-tauri/tauri.conf.json @@ -55,9 +55,6 @@ "security": { "csp": "default-src blob: data: filesystem: ws: http: https: 'unsafe-eval' 'unsafe-inline'" }, - "edge": { - "active": true - }, "inliner": { "active": true } diff --git a/tauri/src/app.rs b/tauri/src/app.rs index d9adf3e0a..d8cc16223 100644 --- a/tauri/src/app.rs +++ b/tauri/src/app.rs @@ -1,9 +1,9 @@ -use web_view::WebView; +use webview_rust_sys::Webview; mod runner; -type InvokeHandler = Box, &str) -> Result<(), String>>; -type Setup = Box, String)>; +type InvokeHandler = Box Result<(), String>>; +type Setup = Box; /// The application runner. pub struct App { @@ -26,7 +26,7 @@ impl App { /// The message is considered consumed if the handler exists and returns an Ok Result. pub(crate) fn run_invoke_handler( &mut self, - webview: &mut WebView<'_, ()>, + webview: &mut Webview, arg: &str, ) -> Result { if let Some(ref mut invoke_handler) = self.invoke_handler { @@ -37,7 +37,7 @@ impl App { } /// Runs the setup callback if defined. - pub(crate) fn run_setup(&mut self, webview: &mut WebView<'_, ()>, source: String) { + pub(crate) fn run_setup(&mut self, webview: &mut Webview, source: String) { if let Some(ref mut setup) = self.setup { setup(webview, source); } @@ -71,7 +71,7 @@ impl AppBuilder { } /// Defines the JS message handler callback. - pub fn invoke_handler, &str) -> Result<(), String> + 'static>( + pub fn invoke_handler Result<(), String> + 'static>( mut self, invoke_handler: F, ) -> Self { @@ -80,7 +80,7 @@ impl AppBuilder { } /// Defines the setup callback. - pub fn setup, String) + 'static>(mut self, setup: F) -> Self { + pub fn setup(mut self, setup: F) -> Self { self.setup = Some(Box::new(setup)); self } diff --git a/tauri/src/app/runner.rs b/tauri/src/app/runner.rs index 21b07a5ab..f27d037d9 100644 --- a/tauri/src/app/runner.rs +++ b/tauri/src/app/runner.rs @@ -7,13 +7,19 @@ use std::{ thread::spawn, }; -use web_view::{builder, Content, WebView}; +use webview_rust_sys::{SizeHint, Webview, WebviewBuilder}; use super::App; #[cfg(embedded_server)] use crate::api::tcp::{get_available_port, port_is_available}; use tauri_api::config::get; +#[allow(dead_code)] +enum Content { + Html(T), + Url(T), +} + /// Main entry point for running the Webview pub(crate) fn run(application: &mut App) -> crate::Result<()> { // setup the content using the config struct depending on the compile target @@ -56,7 +62,7 @@ pub(crate) fn run(application: &mut App) -> crate::Result<()> { spawn_updater()?; // run the webview - webview.run()?; + webview.run(); Ok(()) } @@ -99,7 +105,10 @@ fn setup_content() -> crate::Result> { dev_dir ); } - Ok(Content::Html(read_to_string(dev_path)?)) + Ok(Content::Html(format!( + "data:text/html,{}", + urlencoding::encode(&read_to_string(dev_path)?) + ))) } } @@ -121,7 +130,10 @@ fn setup_content() -> crate::Result> { #[cfg(no_server)] fn setup_content() -> crate::Result> { let html = include_str!(concat!(env!("OUT_DIR"), "/index.tauri.html")); - Ok(Content::Html(html.to_string())) + Ok(Content::Html(format!( + "data:text/html,{}", + urlencoding::encode(html) + ))) } // get the port for the embedded server @@ -182,12 +194,49 @@ fn spawn_updater() -> crate::Result<()> { Ok(()) } +pub fn init() -> String { + #[cfg(not(event))] + return String::from(""); + #[cfg(event)] + return format!( + " + window['{queue}'] = []; + window['{fn}'] = function (payload, salt, ignoreQueue) {{ + const listeners = (window['{listeners}'] && window['{listeners}'][payload.type]) || [] + if (!ignoreQueue && listeners.length === 0) {{ + window['{queue}'].push({{ + payload: payload, + salt: salt + }}) + }} + + if (listeners.length > 0) {{ + window.__TAURI__.promisified({{ + cmd: 'validateSalt', + salt: salt + }}).then(function () {{ + for (let i = listeners.length - 1; i >= 0; i--) {{ + const listener = listeners[i] + if (listener.once) + listeners.splice(i, 1) + listener.handler(payload) + }} + }}) + }} + }} + ", + fn = crate::event::emit_function_name(), + queue = crate::event::event_queue_object_name(), + listeners = crate::event::event_listeners_object_name() + ); +} + // build the webview struct fn build_webview( application: &mut App, content: Content, splashscreen_content: Option>, -) -> crate::Result> { +) -> crate::Result { let config = get()?; let content_clone = match content { Content::Html(ref html) => Content::Html(html.clone()), @@ -197,107 +246,123 @@ fn build_webview( // get properties from config struct let width = config.tauri.window.width; let height = config.tauri.window.height; - let resizable = config.tauri.window.resizable; - let fullscreen = config.tauri.window.fullscreen; + let resizable = if config.tauri.window.resizable { + SizeHint::NONE + } else { + SizeHint::FIXED + }; + // let fullscreen = config.tauri.window.fullscreen; let title = config.tauri.window.title.clone().into_boxed_str(); let has_splashscreen = splashscreen_content.is_some(); let mut initialized_splashscreen = false; + let url = match splashscreen_content { + Some(Content::Html(s)) => s, + _ => match content { + Content::Html(s) => s, + Content::Url(s) => s, + }, + }; - let mut webview = builder() + let mut webview = WebviewBuilder::new() + .init(&format!( + r#" + {event_init} + if (window.__TAURI_INVOKE_HANDLER__) {{ + window.__TAURI_INVOKE_HANDLER__({{ cmd: "__initialized" }}) + }} else {{ + window.addEventListener('DOMContentLoaded', function () {{ + window.__TAURI_INVOKE_HANDLER__({{ cmd: "__initialized" }}) + }}) + }} + "#, + event_init = init() + )) .title(Box::leak(title)) - .size(width, height) - .resizable(resizable) + .width(width as usize) + .height(height as usize) + .resize(resizable) .debug(debug) - .user_data(()) - .invoke_handler(move |webview, arg| { - if arg == r#"{"cmd":"__initialized"}"# { - let source = if has_splashscreen && !initialized_splashscreen { - initialized_splashscreen = true; - "splashscreen" - } else { - "window-1" - }; - application.run_setup(webview, source.to_string()); - if source == "window-1" { - let handle = webview.handle(); - handle - .dispatch(|webview| { - crate::plugin::ready(webview); - Ok(()) - }) - .expect("failed to invoke ready hook"); - } - } else if arg == r#"{"cmd":"closeSplashscreen"}"# { - let content_href = match content_clone { - Content::Html(ref html) => html, - Content::Url(ref url) => url, - }; - webview.eval(&format!(r#"window.location.href = "{}""#, content_href))?; - } else { - let endpoint_handle = crate::endpoints::handle(webview, arg) - .map_err(|tauri_handle_error| { - let tauri_handle_error_str = tauri_handle_error.to_string(); - if tauri_handle_error_str.contains("unknown variant") { - match application.run_invoke_handler(webview, arg) { - Ok(handled) => { - if handled { - String::from("") - } else { - tauri_handle_error_str - } - } - Err(e) => e, - } - } else { - tauri_handle_error_str - } - }) - .map_err(|app_handle_error| { - if app_handle_error.contains("unknown variant") { - match crate::plugin::extend_api(webview, arg) { - Ok(handled) => { - if handled { - String::from("") - } else { - app_handle_error - } - } - Err(e) => e, - } - } else { - app_handle_error - } - }) - .map_err(|e| e.replace("'", "\\'")); - if let Err(handler_error_message) = endpoint_handle { - if handler_error_message != "" { - webview.eval(&get_api_error_message(arg, handler_error_message))?; - } - } - } - - Ok(()) - }) - .content(if splashscreen_content.is_some() { - splashscreen_content.expect("failed to get splashscreen content") - } else { - content - }) - .build()?; - - webview.set_fullscreen(fullscreen); + .url(&url) + .build(); + // TODO waiting for webview window API + // webview.set_fullscreen(fullscreen); if has_splashscreen { let env_var = envmnt::get_or("TAURI_DIR", "../dist"); let path = Path::new(&env_var); let contents = fs::read_to_string(path.join("/tauri.js"))?; // inject the tauri.js entry point - webview - .handle() - .dispatch(move |_webview| _webview.eval(&contents))?; + webview.dispatch(move |_webview| _webview.eval(&contents)); } + let mut w = webview.clone(); + webview.bind("__TAURI_INVOKE_HANDLER__", move |_, arg| { + // transform `[payload]` to `payload` + let arg = arg.chars().skip(1).take(arg.len() - 2).collect::(); + if arg == r#"{"cmd":"__initialized"}"# { + let source = if has_splashscreen && !initialized_splashscreen { + initialized_splashscreen = true; + "splashscreen" + } else { + "window-1" + }; + application.run_setup(&mut w, source.to_string()); + if source == "window-1" { + w.dispatch(|w| { + crate::plugin::ready(w); + }); + } + } else if arg == r#"{"cmd":"closeSplashscreen"}"# { + let content_href = match content_clone { + Content::Html(ref html) => html, + Content::Url(ref url) => url, + }; + w.eval(&format!(r#"window.location.href = "{}""#, content_href)); + } else { + let endpoint_handle = crate::endpoints::handle(&mut w, &arg) + .map_err(|tauri_handle_error| { + let tauri_handle_error_str = tauri_handle_error.to_string(); + if tauri_handle_error_str.contains("unknown variant") { + match application.run_invoke_handler(&mut w, &arg) { + Ok(handled) => { + if handled { + String::from("") + } else { + tauri_handle_error_str + } + } + Err(e) => e, + } + } else { + tauri_handle_error_str + } + }) + .map_err(|app_handle_error| { + if app_handle_error.contains("unknown variant") { + match crate::plugin::extend_api(&mut w, &arg) { + Ok(handled) => { + if handled { + String::from("") + } else { + app_handle_error + } + } + Err(e) => e, + } + } else { + app_handle_error + } + }) + .map_err(|e| e.replace("'", "\\'")); + if let Err(handler_error_message) = endpoint_handle { + if handler_error_message != "" { + w.eval(&get_api_error_message(&arg, handler_error_message)); + } + } + } + }); + Ok(webview) } @@ -312,9 +377,9 @@ fn get_api_error_message(arg: &str, handler_error_message: String) -> String { #[cfg(test)] mod test { + use super::Content; use proptest::prelude::*; use std::env; - use web_view::Content; #[cfg(not(feature = "embedded-server"))] use std::{fs::read_to_string, path::Path}; @@ -351,7 +416,12 @@ mod test { }; assert_eq!( s, - read_to_string(Path::new(&dist_dir).join("index.tauri.html")).unwrap() + format!( + "data:text/html,{}", + urlencoding::encode( + &read_to_string(Path::new(&dist_dir).join("index.tauri.html")).unwrap() + ) + ) ); } _ => panic!("setup content failed"), @@ -367,7 +437,10 @@ mod test { let dev_path = Path::new(dev_dir).join("index.tauri.html"); assert_eq!( s, - read_to_string(dev_path).expect("failed to read dev path") + format!( + "data:text/html,{}", + urlencoding::encode(&read_to_string(dev_path).expect("failed to read dev path")) + ) ); } _ => panic!("setup content failed"), diff --git a/tauri/src/endpoints.rs b/tauri/src/endpoints.rs index b85db4e72..52b65a91a 100644 --- a/tauri/src/endpoints.rs +++ b/tauri/src/endpoints.rs @@ -1,11 +1,8 @@ mod cmd; #[allow(unused_imports)] mod file_system; -mod init; mod salt; -use init::init; - #[cfg(assets)] mod asset; #[cfg(open)] @@ -19,24 +16,15 @@ mod http; #[cfg(notification)] mod notification; -use web_view::WebView; +use webview_rust_sys::Webview; #[allow(unused_variables)] -pub(crate) fn handle(webview: &mut WebView<'_, T>, arg: &str) -> crate::Result<()> { +pub(crate) fn handle(webview: &mut Webview, arg: &str) -> crate::Result<()> { use cmd::Cmd::*; match serde_json::from_str(arg) { Err(e) => Err(e.into()), Ok(command) => { match command { - Init {} => { - let event_init = init()?; - webview.eval(&format!( - r#"{event_init} - window.external.invoke('{{"cmd":"__initialized"}}') - "#, - event_init = event_init - ))?; - } ReadTextFile { path, options, @@ -153,7 +141,7 @@ pub(crate) fn handle(webview: &mut WebView<'_, T>, arg: &str) -> cra } SetTitle { title } => { #[cfg(set_title)] - webview.set_title(&title)?; + webview.set_title(&title); #[cfg(not(set_title))] throw_whitelist_error(webview, "title"); } @@ -189,7 +177,7 @@ pub(crate) fn handle(webview: &mut WebView<'_, T>, arg: &str) -> cra #[cfg(event)] { let js_string = event::listen_fn(event, handler, once)?; - webview.eval(&js_string)?; + webview.eval(&js_string); } #[cfg(not(event))] throw_whitelist_error(webview, "event"); @@ -286,19 +274,13 @@ pub(crate) fn handle(webview: &mut WebView<'_, T>, arg: &str) -> cra } #[allow(dead_code)] -fn api_error(webview: &mut WebView<'_, T>, error_fn: String, message: &str) { +fn api_error(webview: &mut Webview, error_fn: String, message: &str) { let reject_code = tauri_api::rpc::format_callback(error_fn, message); - webview - .eval(&reject_code) - .expect("failed to eval api error") + webview.eval(&reject_code) } #[allow(dead_code)] -fn whitelist_error( - webview: &mut WebView<'_, T>, - error_fn: String, - whitelist_key: &str, -) { +fn whitelist_error(webview: &mut Webview, error_fn: String, whitelist_key: &str) { api_error( webview, error_fn, @@ -310,35 +292,15 @@ fn whitelist_error( } #[allow(dead_code)] -fn throw_whitelist_error(webview: &mut WebView<'_, T>, whitelist_key: &str) { +fn throw_whitelist_error(webview: &mut Webview, whitelist_key: &str) { let reject_code = format!(r#"throw new Error("'{}' not whitelisted")"#, whitelist_key); - webview - .eval(&reject_code) - .expect("failed to eval whitelist error") + webview.eval(&reject_code) } #[cfg(test)] mod test { use proptest::prelude::*; - #[test] - // test to see if check init produces a string or not. - fn check_init() { - if cfg!(not(event)) { - let res = super::init(); - match res { - Ok(s) => assert_eq!(s, ""), - Err(e) => panic!("init Err {:?}", e.to_string()), - } - } else if cfg!(event) { - let res = super::init(); - match res { - Ok(s) => assert!(s.contains("window.__TAURI__.promisified")), - Err(e) => panic!("init Err {:?}", e.to_string()), - } - } - } - // check the listen_fn for various usecases. proptest! { #[cfg(event)] diff --git a/tauri/src/endpoints/asset.rs b/tauri/src/endpoints/asset.rs index b910a10e8..c853144dc 100644 --- a/tauri/src/endpoints/asset.rs +++ b/tauri/src/endpoints/asset.rs @@ -1,14 +1,14 @@ use std::path::PathBuf; -use web_view::WebView; +use webview_rust_sys::Webview; -pub fn load( - webview: &mut WebView<'_, T>, +pub fn load( + webview: &mut Webview, asset: String, asset_type: String, callback: String, error: String, ) { - let handle = webview.handle(); + let mut webview_mut = webview.as_mut(); crate::execute_promise( webview, move || { @@ -58,19 +58,30 @@ pub fn load( base64::encode(&read_asset.expect("Failed to read asset type").into_owned()) )) } else { - handle - .dispatch(move |_webview| { - let asset_bytes = &read_asset.expect("Failed to read asset type").into_owned(); - let asset_str = - &std::str::from_utf8(asset_bytes).expect("failed to convert asset bytes to u8 slice"); - if asset_type == "stylesheet" { - _webview.inject_css(asset_str) - } else { - _webview.eval(asset_str) - } - }) - .map_err(|err| err.into()) - .map(|_| "Asset loaded successfully".to_string()) + let asset_bytes = read_asset.expect("Failed to read asset type"); + webview_mut.dispatch(move |webview_ref| { + let asset_str = + std::str::from_utf8(&asset_bytes).expect("failed to convert asset bytes to u8 slice"); + if asset_type == "stylesheet" { + webview_ref.eval(&format!( + r#" + (function () {{ + var css = document.createElement('style') + css.type = 'text/css' + if (css.styleSheet) + css.styleSheet.cssText = {css} + else + css.appendChild(document.createTextNode({css})) + document.getElementsByTagName("head")[0].appendChild(css); + }})() + "#, + css = asset_str + )); + } else { + webview_ref.eval(asset_str); + } + })?; + Ok("Asset loaded successfully".to_string()) } }, callback, diff --git a/tauri/src/endpoints/cmd.rs b/tauri/src/endpoints/cmd.rs index 5a874d201..551f70735 100644 --- a/tauri/src/endpoints/cmd.rs +++ b/tauri/src/endpoints/cmd.rs @@ -64,8 +64,6 @@ pub struct NotificationOptions { #[derive(Deserialize)] #[serde(tag = "cmd", rename_all = "camelCase")] pub enum Cmd { - /// The init command - Init {}, /// The read text file API. ReadTextFile { path: PathBuf, diff --git a/tauri/src/endpoints/dialog.rs b/tauri/src/endpoints/dialog.rs index cd1cf46cf..d14764ec9 100644 --- a/tauri/src/endpoints/dialog.rs +++ b/tauri/src/endpoints/dialog.rs @@ -1,7 +1,7 @@ use super::cmd::{OpenDialogOptions, SaveDialogOptions}; use crate::api::dialog::{pick_folder, save_file, select, select_multiple, Response}; use serde_json::Value as JsonValue; -use web_view::WebView; +use webview_rust_sys::Webview; /// maps a dialog response to a JS value to eval fn map_response(response: Response) -> JsonValue { @@ -14,8 +14,8 @@ fn map_response(response: Response) -> JsonValue { /// Shows an open dialog. #[cfg(open_dialog)] -pub fn open( - webview: &mut WebView<'_, T>, +pub fn open( + webview: &mut Webview, options: OpenDialogOptions, callback: String, error: String, @@ -40,8 +40,8 @@ pub fn open( /// Shows a save dialog. #[cfg(save_dialog)] -pub fn save( - webview: &mut WebView<'_, T>, +pub fn save( + webview: &mut Webview, options: SaveDialogOptions, callback: String, error: String, diff --git a/tauri/src/endpoints/file_system.rs b/tauri/src/endpoints/file_system.rs index ff06739b1..f6ba59521 100644 --- a/tauri/src/endpoints/file_system.rs +++ b/tauri/src/endpoints/file_system.rs @@ -1,4 +1,4 @@ -use web_view::WebView; +use webview_rust_sys::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 fn read_dir( - webview: &mut WebView<'_, T>, +pub fn read_dir( + webview: &mut Webview, path: PathBuf, options: Option, callback: String, @@ -37,8 +37,8 @@ pub fn read_dir( /// Copies a file. #[cfg(copy_file)] -pub fn copy_file( - webview: &mut WebView<'_, T>, +pub fn copy_file( + webview: &mut Webview, source: PathBuf, destination: PathBuf, options: Option, @@ -64,8 +64,8 @@ pub fn copy_file( /// Creates a directory. #[cfg(create_dir)] -pub fn create_dir( - webview: &mut WebView<'_, T>, +pub fn create_dir( + webview: &mut Webview, path: PathBuf, options: Option, callback: String, @@ -95,8 +95,8 @@ pub fn create_dir( /// Removes a directory. #[cfg(remove_dir)] -pub fn remove_dir( - webview: &mut WebView<'_, T>, +pub fn remove_dir( + webview: &mut Webview, path: PathBuf, options: Option, callback: String, @@ -126,8 +126,8 @@ pub fn remove_dir( /// Removes a file #[cfg(remove_file)] -pub fn remove_file( - webview: &mut WebView<'_, T>, +pub fn remove_file( + webview: &mut Webview, path: PathBuf, options: Option, callback: String, @@ -146,8 +146,8 @@ pub fn remove_file( /// Renames a file. #[cfg(rename_file)] -pub fn rename_file( - webview: &mut WebView<'_, T>, +pub fn rename_file( + webview: &mut Webview, old_path: PathBuf, new_path: PathBuf, options: Option, @@ -173,8 +173,8 @@ pub fn rename_file( /// Writes a text file. #[cfg(write_file)] -pub fn write_file( - webview: &mut WebView<'_, T>, +pub fn write_file( + webview: &mut Webview, path: PathBuf, contents: String, options: Option, @@ -195,8 +195,8 @@ pub fn write_file( /// Writes a binary file. #[cfg(write_binary_file)] -pub fn write_binary_file( - webview: &mut WebView<'_, T>, +pub fn write_binary_file( + webview: &mut Webview, path: PathBuf, contents: String, options: Option, @@ -221,8 +221,8 @@ pub fn write_binary_file( /// Reads a text file. #[cfg(read_text_file)] -pub fn read_text_file( - webview: &mut WebView<'_, T>, +pub fn read_text_file( + webview: &mut Webview, path: PathBuf, options: Option, callback: String, @@ -238,8 +238,8 @@ pub fn read_text_file( /// Reads a binary file. #[cfg(read_binary_file)] -pub fn read_binary_file( - webview: &mut WebView<'_, T>, +pub fn read_binary_file( + webview: &mut Webview, path: PathBuf, options: Option, callback: String, diff --git a/tauri/src/endpoints/http.rs b/tauri/src/endpoints/http.rs index e10133762..cc99e673f 100644 --- a/tauri/src/endpoints/http.rs +++ b/tauri/src/endpoints/http.rs @@ -1,9 +1,9 @@ use tauri_api::http::{make_request as request, HttpRequestOptions}; -use web_view::WebView; +use webview_rust_sys::Webview; /// Makes an HTTP request and resolves the response to the webview -pub fn make_request( - webview: &mut WebView<'_, T>, +pub fn make_request( + webview: &mut Webview, options: HttpRequestOptions, callback: String, error: String, diff --git a/tauri/src/endpoints/init.rs b/tauri/src/endpoints/init.rs deleted file mode 100644 index 644aecc86..000000000 --- a/tauri/src/endpoints/init.rs +++ /dev/null @@ -1,36 +0,0 @@ -pub fn init() -> crate::Result { - #[cfg(not(event))] - return Ok(String::from("")); - #[cfg(event)] - return Ok(format!( - " - window['{queue}'] = []; - window['{fn}'] = function (payload, salt, ignoreQueue) {{ - const listeners = (window['{listeners}'] && window['{listeners}'][payload.type]) || [] - if (!ignoreQueue && listeners.length === 0) {{ - window['{queue}'].push({{ - payload: payload, - salt: salt - }}) - }} - - if (listeners.length > 0) {{ - window.__TAURI__.promisified({{ - cmd: 'validateSalt', - salt: salt - }}).then(function () {{ - for (let i = listeners.length - 1; i >= 0; i--) {{ - const listener = listeners[i] - if (listener.once) - listeners.splice(i, 1) - listener.handler(payload) - }} - }}) - }} - }} - ", - fn = crate::event::emit_function_name(), - queue = crate::event::event_queue_object_name(), - listeners = crate::event::event_listeners_object_name() - )); -} diff --git a/tauri/src/endpoints/notification.rs b/tauri/src/endpoints/notification.rs index 2b53250cd..57ad2a15d 100644 --- a/tauri/src/endpoints/notification.rs +++ b/tauri/src/endpoints/notification.rs @@ -1,13 +1,8 @@ use super::cmd::NotificationOptions; use serde_json::Value as JsonValue; -use web_view::WebView; +use webview_rust_sys::Webview; -pub fn send( - webview: &mut WebView<'_, T>, - options: NotificationOptions, - callback: String, - error: String, -) { +pub fn send(webview: &mut Webview, options: NotificationOptions, callback: String, error: String) { crate::execute_promise( webview, move || { @@ -26,11 +21,7 @@ pub fn send( ); } -pub fn is_permission_granted( - webview: &mut WebView<'_, T>, - callback: String, - error: String, -) { +pub fn is_permission_granted(webview: &mut Webview, callback: String, error: String) { crate::execute_promise( webview, move || { @@ -46,8 +37,8 @@ pub fn is_permission_granted( ); } -pub fn request_permission( - webview: &mut WebView<'_, T>, +pub fn request_permission( + webview: &mut Webview, callback: String, error: String, ) -> crate::Result<()> { diff --git a/tauri/src/endpoints/salt.rs b/tauri/src/endpoints/salt.rs index 8d3101021..ca5b2be0d 100644 --- a/tauri/src/endpoints/salt.rs +++ b/tauri/src/endpoints/salt.rs @@ -1,8 +1,8 @@ -use web_view::WebView; +use webview_rust_sys::Webview; /// Validates a salt. -pub fn validate( - webview: &mut WebView<'_, T>, +pub fn validate( + webview: &mut Webview, salt: String, callback: String, error: String, @@ -13,6 +13,6 @@ pub fn validate( Err("Invalid salt") }; let callback_string = crate::api::rpc::format_callback_result(response, callback, error)?; - webview.eval(callback_string.as_str())?; + webview.eval(callback_string.as_str()); Ok(()) } diff --git a/tauri/src/event.rs b/tauri/src/event.rs index 00c669dfb..43418b346 100644 --- a/tauri/src/event.rs +++ b/tauri/src/event.rs @@ -6,7 +6,7 @@ use lazy_static::lazy_static; use once_cell::sync::Lazy; use serde::Serialize; use serde_json::Value as JsonValue; -use web_view::Handle; +use webview_rust_sys::WebviewMut; /// An event handler. struct EventHandler { @@ -57,8 +57,8 @@ pub fn listen) + Send + 'static>(id: impl Into, } /// Emits an event to JS. -pub fn emit( - webview_handle: &Handle, +pub fn emit( + webview: &mut WebviewMut, event: impl AsRef + Send + 'static, payload: Option, ) -> crate::Result<()> { @@ -70,17 +70,15 @@ pub fn emit( JsonValue::Null }; - webview_handle - .dispatch(move |_webview| { - _webview.eval(&format!( - "window['{}']({{type: '{}', payload: {}}}, '{}')", - emit_function_name(), - event.as_ref(), - js_payload, - salt - )) - }) - .expect("Failed to dispatch JS from emit"); + webview.dispatch(move |webview_ref| { + webview_ref.eval(&format!( + "window['{}']({{type: '{}', payload: {}}}, '{}')", + emit_function_name(), + event.as_ref(), + js_payload, + salt + )) + })?; Ok(()) } diff --git a/tauri/src/lib.rs b/tauri/src/lib.rs index 8583bd033..eeb36d435 100644 --- a/tauri/src/lib.rs +++ b/tauri/src/lib.rs @@ -39,8 +39,7 @@ mod salt; pub use anyhow::Result; pub use app::*; pub use tauri_api as api; -pub use web_view::Handle; -pub use web_view::WebView; +pub use webview_rust_sys::{Webview, WebviewMut}; use std::process::Stdio; @@ -61,20 +60,15 @@ pub fn spawn () + Send + 'static>(task: F) { /// 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< - T: 'static, - R: Serialize, - F: FnOnce() -> crate::Result + Send + 'static, ->( - webview: &mut WebView<'_, T>, +pub fn execute_promise_sync crate::Result + Send + 'static>( + webview: &mut Webview, task: F, callback: String, error: String, ) -> crate::Result<()> { - let handle = webview.handle(); let callback_string = format_callback_result(task().map_err(|err| err.to_string()), callback, error)?; - handle.dispatch(move |_webview| _webview.eval(callback_string.as_str()))?; + webview.dispatch(move |w| w.eval(callback_string.as_str())); Ok(()) } @@ -83,17 +77,13 @@ 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 fn execute_promise< - T: 'static, - R: Serialize, - F: FnOnce() -> crate::Result + Send + 'static, ->( - webview: &mut WebView<'_, T>, +pub fn execute_promise crate::Result + Send + 'static>( + webview: &mut Webview, task: F, success_callback: String, error_callback: String, ) { - let handle = webview.handle(); + let mut webview = webview.as_mut(); POOL.with(|thread| { thread.execute(move || { let callback_string = match format_callback_result( @@ -104,16 +94,16 @@ pub fn execute_promise< Ok(callback_string) => callback_string, Err(e) => format_callback(error_callback, e.to_string()), }; - handle - .dispatch(move |_webview| _webview.eval(callback_string.as_str())) - .expect("Failed to dispatch promise callback") + webview + .dispatch(move |webview_ref| webview_ref.eval(callback_string.as_str())) + .expect("Failed to dispatch promise callback"); }); }); } /// Calls the given command and evaluates its output to the JS promise described by the `callback` and `error` function names. -pub fn call( - webview: &mut WebView<'_, T>, +pub fn call( + webview: &mut Webview, command: String, args: Vec, callback: String, @@ -128,11 +118,10 @@ pub fn call( } /// Closes the splashscreen. -pub fn close_splashscreen(webview_handle: &Handle) -> crate::Result<()> { - webview_handle.dispatch(|webview| { - // send a signal to the runner so it knows that it should redirect to the main app content - webview.eval(r#"window.external.invoke(JSON.stringify({ cmd: "closeSplashscreen" }))"#) - })?; +pub fn close_splashscreen(webview: &mut Webview) -> 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" })"#); + Ok(()) } diff --git a/tauri/src/plugin.rs b/tauri/src/plugin.rs index 9d8b0259f..51f99b15e 100644 --- a/tauri/src/plugin.rs +++ b/tauri/src/plugin.rs @@ -1,19 +1,19 @@ use std::sync::{Arc, Mutex}; -use web_view::WebView; +use webview_rust_sys::Webview; /// The plugin interface. pub trait Plugin { /// Callback invoked when the webview is created. #[allow(unused_variables)] - fn created(&self, webview: &mut WebView<'_, ()>) {} + fn created(&self, webview: &mut Webview) {} /// Callback invoked when the webview is ready. #[allow(unused_variables)] - fn ready(&self, webview: &mut WebView<'_, ()>) {} + fn ready(&self, webview: &mut Webview) {} /// Add invoke_handler API extension commands. #[allow(unused_variables)] - fn extend_api(&self, webview: &mut WebView<'_, ()>, payload: &str) -> Result { + fn extend_api(&self, webview: &mut Webview, payload: &str) -> Result { Err("unknown variant".to_string()) } } @@ -37,19 +37,19 @@ fn run_plugin)>(mut callback: T) { }); } -pub(crate) fn created(webview: &mut WebView<'_, ()>) { +pub(crate) fn created(webview: &mut Webview) { run_plugin(|ext| { ext.created(webview); }); } -pub(crate) fn ready(webview: &mut WebView<'_, ()>) { +pub(crate) fn ready(webview: &mut Webview) { run_plugin(|ext| { ext.ready(webview); }); } -pub(crate) fn extend_api(webview: &mut WebView<'_, ()>, arg: &str) -> Result { +pub(crate) fn extend_api(webview: &mut Webview, arg: &str) -> Result { PLUGINS.with(|plugins| { let exts = plugins.lock().unwrap(); for ext in exts.iter() { diff --git a/tauri/test/fixture/src-tauri/tauri.conf.json b/tauri/test/fixture/src-tauri/tauri.conf.json index 70d92aa59..b3e5e0399 100644 --- a/tauri/test/fixture/src-tauri/tauri.conf.json +++ b/tauri/test/fixture/src-tauri/tauri.conf.json @@ -19,7 +19,9 @@ }, "security": { "csp": "default-src blob: data: filesystem: ws: http: https: 'unsafe-eval' 'unsafe-inline'" + }, + "inliner": { + "active": true } - }, - "edge": true + } }