Isolation Pattern (#43)

Co-authored-by: Ngo Iok Ui (Wu Yu Wei) <wusyong9104@gmail.com>
Co-authored-by: Lucas Fernandes Nogueira <lucas@tauri.studio>
This commit is contained in:
chip 2022-01-17 10:46:14 -03:00 committed by Lucas Nogueira
parent c077f44927
commit d5d6d2abc1
No known key found for this signature in database
GPG Key ID: 2714B66BCFB01F7F
110 changed files with 6116 additions and 857 deletions

View File

@ -0,0 +1,7 @@
---
"tauri": patch
"tauri-utils": patch
"tauri-codegen": patch
---
Added the `isolation` pattern.

5
.changes/rust-1.57.md Normal file
View File

@ -0,0 +1,5 @@
---
"tauri": patch
---
The minimum Rust version is now 1.57.

View File

@ -14,7 +14,8 @@ exclude = [
"examples/api/src-tauri", "examples/api/src-tauri",
"examples/updater/src-tauri", "examples/updater/src-tauri",
"examples/resources/src-tauri", "examples/resources/src-tauri",
"examples/sidecar/src-tauri" "examples/sidecar/src-tauri",
"examples/isolation/src-tauri"
] ]
# default to small, optimized workspace release binaries # default to small, optimized workspace release binaries

View File

@ -8,7 +8,7 @@ homepage = "https://tauri.studio"
repository = "https://github.com/tauri-apps/tauri/tree/dev/core/tauri-build" repository = "https://github.com/tauri-apps/tauri/tree/dev/core/tauri-build"
description = "build time code to pair with https://crates.io/crates/tauri" description = "build time code to pair with https://crates.io/crates/tauri"
edition = "2021" edition = "2021"
rust-version = "1.56" rust-version = "1.57"
exclude = [ ".license_template", "CHANGELOG.md", "/target" ] exclude = [ ".license_template", "CHANGELOG.md", "/target" ]
readme = "README.md" readme = "README.md"
@ -29,3 +29,4 @@ winres = "0.1"
[features] [features]
codegen = [ "tauri-codegen", "quote" ] codegen = [ "tauri-codegen", "quote" ]
isolation = ["tauri-codegen/isolation", "tauri-utils/isolation"]

View File

@ -42,6 +42,7 @@ impl CodegenContext {
/// This defaults to a file called `tauri.conf.json` inside of the current working directory of /// This defaults to a file called `tauri.conf.json` inside of the current working directory of
/// the package compiling; does not need to be set manually if that config file is in the same /// the package compiling; does not need to be set manually if that config file is in the same
/// directory as your `Cargo.toml`. /// directory as your `Cargo.toml`.
#[must_use]
pub fn config_path(mut self, config_path: impl Into<PathBuf>) -> Self { pub fn config_path(mut self, config_path: impl Into<PathBuf>) -> Self {
self.config_path = config_path.into(); self.config_path = config_path.into();
self self
@ -58,6 +59,7 @@ impl CodegenContext {
/// Defaults to `tauri-build-context.rs`. /// Defaults to `tauri-build-context.rs`.
/// ///
/// [`tauri::include_codegen_context!`]: https://docs.rs/tauri/0.12/tauri/macro.include_codegen_context.html /// [`tauri::include_codegen_context!`]: https://docs.rs/tauri/0.12/tauri/macro.include_codegen_context.html
#[must_use]
pub fn out_file(mut self, filename: PathBuf) -> Self { pub fn out_file(mut self, filename: PathBuf) -> Self {
self.out_file = filename; self.out_file = filename;
self self
@ -65,6 +67,7 @@ impl CodegenContext {
/// Run the codegen in a `dev` context, meaning that Tauri is using a dev server or local file for development purposes, /// Run the codegen in a `dev` context, meaning that Tauri is using a dev server or local file for development purposes,
/// usually with the `tauri dev` CLI command. /// usually with the `tauri dev` CLI command.
#[must_use]
pub fn dev(mut self) -> Self { pub fn dev(mut self) -> Self {
self.dev = true; self.dev = true;
self self

View File

@ -43,6 +43,7 @@ impl WindowsAttributes {
/// Sets the icon to use on the window. Currently only used on Windows. /// Sets the icon to use on the window. Currently only used on Windows.
/// It must be in `ico` format. Defaults to `icons/icon.ico`. /// It must be in `ico` format. Defaults to `icons/icon.ico`.
#[must_use]
pub fn window_icon_path<P: AsRef<Path>>(mut self, window_icon_path: P) -> Self { pub fn window_icon_path<P: AsRef<Path>>(mut self, window_icon_path: P) -> Self {
self.window_icon_path = window_icon_path.as_ref().into(); self.window_icon_path = window_icon_path.as_ref().into();
self self
@ -50,6 +51,7 @@ impl WindowsAttributes {
/// Sets the sdk dir for windows. Currently only used on Windows. This must be a vaild UTF-8 /// Sets the sdk dir for windows. Currently only used on Windows. This must be a vaild UTF-8
/// path. Defaults to whatever the `winres` crate determines is best. /// path. Defaults to whatever the `winres` crate determines is best.
#[must_use]
pub fn sdk_dir<P: AsRef<Path>>(mut self, sdk_dir: P) -> Self { pub fn sdk_dir<P: AsRef<Path>>(mut self, sdk_dir: P) -> Self {
self.sdk_dir = Some(sdk_dir.as_ref().into()); self.sdk_dir = Some(sdk_dir.as_ref().into());
self self
@ -70,6 +72,7 @@ impl Attributes {
} }
/// Sets the icon to use on the window. Currently only used on Windows. /// Sets the icon to use on the window. Currently only used on Windows.
#[must_use]
pub fn windows_attributes(mut self, windows_attributes: WindowsAttributes) -> Self { pub fn windows_attributes(mut self, windows_attributes: WindowsAttributes) -> Self {
self.windows_attributes = windows_attributes; self.windows_attributes = windows_attributes;
self self

View File

@ -8,7 +8,7 @@ homepage = "https://tauri.studio"
repository = "https://github.com/tauri-apps/tauri/tree/dev/core/tauri-codegen" repository = "https://github.com/tauri-apps/tauri/tree/dev/core/tauri-codegen"
description = "code generation meant to be consumed inside of `tauri` through `tauri-build` or `tauri-macros`" description = "code generation meant to be consumed inside of `tauri` through `tauri-build` or `tauri-macros`"
edition = "2021" edition = "2021"
rust-version = "1.56" rust-version = "1.57"
exclude = [ ".license_template", "CHANGELOG.md", "/target" ] exclude = [ ".license_template", "CHANGELOG.md", "/target" ]
readme = "README.md" readme = "README.md"
@ -25,7 +25,9 @@ thiserror = "1"
walkdir = "2" walkdir = "2"
zstd = { version = "0.9", optional = true } zstd = { version = "0.9", optional = true }
regex = "1" regex = "1"
uuid = { version = "0.8", features = [ "v4" ] }
[features] [features]
default = [ "compression" ] default = [ "compression" ]
compression = [ "zstd", "tauri-utils/compression" ] compression = [ "zstd", "tauri-utils/compression" ]
isolation = ["tauri-utils/isolation"]

View File

@ -2,11 +2,18 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
use crate::embedded_assets::{AssetOptions, EmbeddedAssets, EmbeddedAssetsError}; use std::ffi::OsStr;
use std::path::{Path, PathBuf};
use proc_macro2::TokenStream; use proc_macro2::TokenStream;
use quote::quote; use quote::quote;
use std::path::{Path, PathBuf}; use sha2::{Digest, Sha256};
use tauri_utils::config::{AppUrl, Config, WindowUrl};
use tauri_utils::assets::AssetKey;
use tauri_utils::config::{AppUrl, Config, PatternKind, WindowUrl};
use tauri_utils::html::{inject_nonce_token, parse as parse_html, NodeRef, PatternObject};
use crate::embedded_assets::{AssetOptions, CspHashes, EmbeddedAssets, EmbeddedAssetsError};
/// Necessary data needed by [`context_codegen`] to generate code for a Tauri application context. /// Necessary data needed by [`context_codegen`] to generate code for a Tauri application context.
pub struct ContextData { pub struct ContextData {
@ -16,6 +23,81 @@ pub struct ContextData {
pub root: TokenStream, pub root: TokenStream,
} }
fn load_csp(document: &mut NodeRef, key: &AssetKey, csp_hashes: &mut CspHashes) {
#[cfg(target_os = "linux")]
::tauri_utils::html::inject_csp_token(document);
inject_nonce_token(document);
if let Ok(inline_script_elements) = document.select("script:not(empty)") {
let mut scripts = Vec::new();
for inline_script_el in inline_script_elements {
let script = inline_script_el.as_node().text_contents();
let mut hasher = Sha256::new();
hasher.update(&script);
let hash = hasher.finalize();
scripts.push(format!("'sha256-{}'", base64::encode(&hash)));
}
csp_hashes
.inline_scripts
.entry(key.clone().into())
.or_default()
.append(&mut scripts);
}
}
fn map_core_assets(
options: &AssetOptions,
) -> impl Fn(&AssetKey, &Path, &mut Vec<u8>, &mut CspHashes) -> Result<(), EmbeddedAssetsError> {
#[allow(unused_variables)]
let pattern = PatternObject::from(&options.pattern);
let csp = options.csp;
move |key, path, input, csp_hashes| {
if path.extension() == Some(OsStr::new("html")) {
let mut document = parse_html(String::from_utf8_lossy(input).into_owned());
if csp {
load_csp(&mut document, key, csp_hashes);
#[cfg(feature = "isolation")]
if let PatternObject::Isolation { .. } = &pattern {
// create the csp for the isolation iframe styling now, to make the runtime less complex
let mut hasher = Sha256::new();
hasher.update(tauri_utils::pattern::isolation::IFRAME_STYLE);
let hash = hasher.finalize();
csp_hashes
.styles
.push(format!("'sha256-{}'", base64::encode(&hash)));
}
}
*input = document.to_string().as_bytes().to_vec();
}
Ok(())
}
}
#[cfg(feature = "isolation")]
fn map_isolation(
_options: &AssetOptions,
dir: PathBuf,
) -> impl Fn(&AssetKey, &Path, &mut Vec<u8>, &mut CspHashes) -> Result<(), EmbeddedAssetsError> {
move |_key, path, input, _csp_hashes| {
if path.extension() == Some(OsStr::new("html")) {
let mut isolation_html =
tauri_utils::html::parse(String::from_utf8_lossy(input).into_owned());
// this is appended, so no need to reverse order it
tauri_utils::html::inject_codegen_isolation_script(&mut isolation_html);
// temporary workaround for windows not loading assets
tauri_utils::html::inline_isolation(&mut isolation_html, &dir);
*input = isolation_html.to_string().as_bytes().to_vec()
}
Ok(())
}
}
/// Build a `tauri::Context` for including in application code. /// Build a `tauri::Context` for including in application code.
pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsError> { pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsError> {
let ContextData { let ContextData {
@ -25,7 +107,8 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
root, root,
} = data; } = data;
let mut options = AssetOptions::new(); let mut options = AssetOptions::new(config.tauri.pattern.clone())
.freeze_prototype(config.tauri.security.freeze_prototype);
let csp = if dev { let csp = if dev {
config config
.tauri .tauri
@ -64,7 +147,7 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
path path
) )
} }
EmbeddedAssets::new(assets_path, options)? EmbeddedAssets::new(assets_path, map_core_assets(&options))?
} }
_ => unimplemented!(), _ => unimplemented!(),
}, },
@ -73,7 +156,7 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
.iter() .iter()
.map(|p| config_parent.join(p)) .map(|p| config_parent.join(p))
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
options, map_core_assets(&options),
)?, )?,
_ => unimplemented!(), _ => unimplemented!(),
}; };
@ -180,7 +263,31 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
#[cfg(not(target_os = "macos"))] #[cfg(not(target_os = "macos"))]
let info_plist = quote!(()); let info_plist = quote!(());
// double braces are purposeful to force the code into a block expression let pattern = match &options.pattern {
PatternKind::Brownfield => quote!(#root::Pattern::Brownfield(std::marker::PhantomData)),
#[cfg(feature = "isolation")]
PatternKind::Isolation { dir } => {
let dir = config_parent.join(dir);
if !dir.exists() {
panic!(
"The isolation dir configuration is set to `{:?}` but this path doesn't exist",
dir
)
}
let key = uuid::Uuid::new_v4().to_string();
let assets = EmbeddedAssets::new(dir.clone(), map_isolation(&options, dir))?;
let schema = options.isolation_schema;
quote!(#root::Pattern::Isolation {
assets: ::std::sync::Arc::new(#assets),
schema: #schema.into(),
key: #key.into(),
crypto_keys: std::boxed::Box::new(::tauri::utils::pattern::isolation::Keys::new().expect("unable to generate cryptographically secure keys for Tauri \"Isolation\" Pattern")),
})
}
};
Ok(quote!(#root::Context::new( Ok(quote!(#root::Context::new(
#config, #config,
::std::sync::Arc::new(#assets), ::std::sync::Arc::new(#assets),
@ -188,6 +295,7 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
#system_tray_icon, #system_tray_icon,
#package_info, #package_info,
#info_plist, #info_plist,
#pattern
))) )))
} }

View File

@ -4,18 +4,14 @@
use proc_macro2::TokenStream; use proc_macro2::TokenStream;
use quote::{quote, ToTokens, TokenStreamExt}; use quote::{quote, ToTokens, TokenStreamExt};
use regex::RegexSet;
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
use std::{ use std::{
collections::HashMap, collections::HashMap,
ffi::OsStr,
fs::File, fs::File,
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
use tauri_utils::{ use tauri_utils::assets::AssetKey;
assets::AssetKey, use tauri_utils::config::PatternKind;
html::{inject_invoke_key_token, inject_nonce_token, parse as parse_html},
};
use thiserror::Error; use thiserror::Error;
use walkdir::{DirEntry, WalkDir}; use walkdir::{DirEntry, WalkDir};
@ -143,12 +139,14 @@ impl RawEmbeddedAssets {
} }
/// Holds all hashes that we will apply on the CSP tag/header. /// Holds all hashes that we will apply on the CSP tag/header.
#[derive(Default)] #[derive(Debug, Default)]
struct CspHashes { pub struct CspHashes {
/// Scripts that are part of the asset collection (JS or MJS files). /// Scripts that are part of the asset collection (JS or MJS files).
scripts: Vec<String>, pub(crate) scripts: Vec<String>,
/// Inline scripts (`<script>code</script>`). Maps a HTML path to a list of hashes. /// Inline scripts (`<script>code</script>`). Maps a HTML path to a list of hashes.
inline_scripts: HashMap<String, Vec<String>>, pub(crate) inline_scripts: HashMap<String, Vec<String>>,
/// A list of hashes of the contents of all `style` elements.
pub(crate) styles: Vec<String>,
} }
impl CspHashes { impl CspHashes {
@ -181,20 +179,38 @@ impl CspHashes {
/// Options used to embed assets. /// Options used to embed assets.
#[derive(Default)] #[derive(Default)]
pub struct AssetOptions { pub struct AssetOptions {
csp: bool, pub(crate) csp: bool,
pub(crate) pattern: PatternKind,
pub(crate) freeze_prototype: bool,
#[cfg(feature = "isolation")]
pub(crate) isolation_schema: String,
} }
impl AssetOptions { impl AssetOptions {
/// Creates the default asset options. /// Creates the default asset options.
pub fn new() -> Self { pub fn new(pattern: PatternKind) -> Self {
Self::default() Self {
csp: false,
pattern,
freeze_prototype: true,
#[cfg(feature = "isolation")]
isolation_schema: format!("isolation-{}", uuid::Uuid::new_v4()),
}
} }
/// Instruct the asset handler to inject the CSP token to HTML files. /// Instruct the asset handler to inject the CSP token to HTML files.
#[must_use]
pub fn with_csp(mut self) -> Self { pub fn with_csp(mut self) -> Self {
self.csp = true; self.csp = true;
self self
} }
/// Instruct the asset handler to include a script to freeze the `Object.prototype` on all HTML files.
#[must_use]
pub fn freeze_prototype(mut self, freeze: bool) -> Self {
self.freeze_prototype = freeze;
self
}
} }
impl EmbeddedAssets { impl EmbeddedAssets {
@ -203,18 +219,27 @@ impl EmbeddedAssets {
/// [`Assets`]: tauri_utils::assets::Assets /// [`Assets`]: tauri_utils::assets::Assets
pub fn new( pub fn new(
input: impl Into<EmbeddedAssetsInput>, input: impl Into<EmbeddedAssetsInput>,
options: AssetOptions, map: impl Fn(&AssetKey, &Path, &mut Vec<u8>, &mut CspHashes) -> Result<(), EmbeddedAssetsError>,
) -> Result<Self, EmbeddedAssetsError> { ) -> Result<Self, EmbeddedAssetsError> {
// we need to pre-compute all files now, so that we can inject data from all files into a few // we need to pre-compute all files now, so that we can inject data from all files into a few
let RawEmbeddedAssets { let RawEmbeddedAssets { paths, csp_hashes } = RawEmbeddedAssets::new(input.into())?;
paths,
mut csp_hashes,
} = RawEmbeddedAssets::new(input.into())?;
let assets = paths struct CompressState {
.into_iter() csp_hashes: CspHashes,
.map(|(prefix, entry)| Self::compress_file(&prefix, entry.path(), &options, &mut csp_hashes)) assets: HashMap<AssetKey, (PathBuf, PathBuf)>,
.collect::<Result<_, _>>()?; }
let CompressState { assets, csp_hashes } = paths.into_iter().try_fold(
CompressState {
csp_hashes,
assets: HashMap::new(),
},
move |mut state, (prefix, entry)| {
let (key, asset) = Self::compress_file(&prefix, entry.path(), &map, &mut state.csp_hashes)?;
state.assets.insert(key, asset);
Ok(state)
},
)?;
Ok(Self { assets, csp_hashes }) Ok(Self { assets, csp_hashes })
} }
@ -234,7 +259,7 @@ impl EmbeddedAssets {
fn compress_file( fn compress_file(
prefix: &Path, prefix: &Path,
path: &Path, path: &Path,
options: &AssetOptions, map: &impl Fn(&AssetKey, &Path, &mut Vec<u8>, &mut CspHashes) -> Result<(), EmbeddedAssetsError>,
csp_hashes: &mut CspHashes, csp_hashes: &mut CspHashes,
) -> Result<Asset, EmbeddedAssetsError> { ) -> Result<Asset, EmbeddedAssetsError> {
let mut input = std::fs::read(path).map_err(|error| EmbeddedAssetsError::AssetRead { let mut input = std::fs::read(path).map_err(|error| EmbeddedAssetsError::AssetRead {
@ -251,75 +276,8 @@ impl EmbeddedAssets {
path: path.to_owned(), path: path.to_owned(),
})?; })?;
if path.extension() == Some(OsStr::new("html")) { // perform any caller-requested input manipulation
let mut document = parse_html(String::from_utf8_lossy(&input).into_owned()); map(&key, path, &mut input, csp_hashes)?;
if options.csp {
#[cfg(target_os = "linux")]
::tauri_utils::html::inject_csp_token(&mut document);
inject_nonce_token(&mut document);
if let Ok(inline_script_elements) = document.select("script:not(empty)") {
let mut scripts = Vec::new();
for inline_script_el in inline_script_elements {
let script = inline_script_el.as_node().text_contents();
let mut hasher = Sha256::new();
hasher.update(&script);
let hash = hasher.finalize();
scripts.push(format!("'sha256-{}'", base64::encode(&hash)));
}
csp_hashes
.inline_scripts
.insert(key.clone().into(), scripts);
}
}
inject_invoke_key_token(&mut document);
input = document.to_string().as_bytes().to_vec();
} else {
let is_javascript = ["js", "cjs", "mjs"]
.iter()
.any(|e| path.extension() == Some(OsStr::new(e)));
if is_javascript {
let js = String::from_utf8_lossy(&input).into_owned();
input = if RegexSet::new(&[
// import keywords
"import\\{",
"import \\{",
"import\\*",
"import \\*",
"import (\"|');?$",
"import\\(",
"import (.|\n)+ from (\"|')([A-Za-z/\\.@-]+)(\"|')",
// export keywords
"export\\{",
"export \\{",
"export\\*",
"export \\*",
"export (default|class|let|const|function|async)",
])
.unwrap()
.is_match(&js)
{
format!(
r#"
const __TAURI_INVOKE_KEY__ = __TAURI__INVOKE_KEY_TOKEN__;
{}
"#,
js
)
.as_bytes()
.to_vec()
} else {
format!(
r#"(function () {{
const __TAURI_INVOKE_KEY__ = __TAURI__INVOKE_KEY_TOKEN__;
{}
}})()"#,
js
)
.as_bytes()
.to_vec()
};
}
}
// we must canonicalize the base of our paths to allow long paths on windows // we must canonicalize the base of our paths to allow long paths on windows
let out_dir = std::env::var("OUT_DIR") let out_dir = std::env::var("OUT_DIR")
@ -404,6 +362,11 @@ impl ToTokens for EmbeddedAssets {
global_hashes.append_all(quote!(CspHash::Script(#hash),)); global_hashes.append_all(quote!(CspHash::Script(#hash),));
} }
for style_hash in &self.csp_hashes.styles {
let hash = style_hash.as_str();
global_hashes.append_all(quote!(CspHash::Style(#hash),));
}
let mut html_hashes = TokenStream::new(); let mut html_hashes = TokenStream::new();
for (path, hashes) in &self.csp_hashes.inline_scripts { for (path, hashes) in &self.csp_hashes.inline_scripts {
let key = path.as_str(); let key = path.as_str();

View File

@ -8,7 +8,7 @@ homepage = "https://tauri.studio"
repository = "https://github.com/tauri-apps/tauri" repository = "https://github.com/tauri-apps/tauri"
description = "Macros for the tauri crate." description = "Macros for the tauri crate."
edition = "2021" edition = "2021"
rust-version = "1.56" rust-version = "1.57"
exclude = [ ".license_template", "CHANGELOG.md", "/target" ] exclude = [ ".license_template", "CHANGELOG.md", "/target" ]
readme = "README.md" readme = "README.md"
@ -25,3 +25,4 @@ tauri-codegen = { version = "1.0.0-beta.4", default-features = false, path = "..
[features] [features]
custom-protocol = [ ] custom-protocol = [ ]
compression = [ "tauri-codegen/compression" ] compression = [ "tauri-codegen/compression" ]
isolation = ["tauri-codegen/isolation"]

View File

@ -8,7 +8,7 @@ homepage = "https://tauri.studio"
repository = "https://github.com/tauri-apps/tauri" repository = "https://github.com/tauri-apps/tauri"
description = "Wry bindings to the Tauri runtime" description = "Wry bindings to the Tauri runtime"
edition = "2021" edition = "2021"
rust-version = "1.56" rust-version = "1.57"
exclude = [ ".license_template", "CHANGELOG.md", "/target" ] exclude = [ ".license_template", "CHANGELOG.md", "/target" ]
readme = "README.md" readme = "README.md"

View File

@ -8,7 +8,7 @@ homepage = "https://tauri.studio"
repository = "https://github.com/tauri-apps/tauri" repository = "https://github.com/tauri-apps/tauri"
description = "Runtime for Tauri applications" description = "Runtime for Tauri applications"
edition = "2021" edition = "2021"
rust-version = "1.56" rust-version = "1.57"
exclude = [ ".license_template", "CHANGELOG.md", "/target" ] exclude = [ ".license_template", "CHANGELOG.md", "/target" ]
readme = "README.md" readme = "README.md"

View File

@ -168,7 +168,8 @@ impl Builder {
} }
/// Set the HTTP mimetype for this response. /// Set the HTTP mimetype for this response.
pub fn mimetype(self, mimetype: &str) -> Builder { #[must_use]
pub fn mimetype(self, mimetype: &str) -> Self {
self.and_then(move |mut head| { self.and_then(move |mut head| {
head.mimetype = Some(mimetype.to_string()); head.mimetype = Some(mimetype.to_string());
Ok(head) Ok(head)
@ -176,7 +177,8 @@ impl Builder {
} }
/// Set the HTTP status for this response. /// Set the HTTP status for this response.
pub fn status<T>(self, status: T) -> Builder #[must_use]
pub fn status<T>(self, status: T) -> Self
where where
StatusCode: TryFrom<T>, StatusCode: TryFrom<T>,
<StatusCode as TryFrom<T>>::Error: Into<crate::Error>, <StatusCode as TryFrom<T>>::Error: Into<crate::Error>,
@ -193,7 +195,8 @@ impl Builder {
/// will be returned from `Builder::build`. /// will be returned from `Builder::build`.
/// ///
/// By default this is HTTP/1.1 /// By default this is HTTP/1.1
pub fn version(self, version: Version) -> Builder { #[must_use]
pub fn version(self, version: Version) -> Self {
self.and_then(move |mut head| { self.and_then(move |mut head| {
head.version = version; head.version = version;
Ok(head) Ok(head)
@ -205,7 +208,8 @@ impl Builder {
/// This function will append the provided key/value as a header to the /// This function will append the provided key/value as a header to the
/// internal `HeaderMap` being constructed. Essentially this is equivalent /// internal `HeaderMap` being constructed. Essentially this is equivalent
/// to calling `HeaderMap::append`. /// to calling `HeaderMap::append`.
pub fn header<K, V>(self, key: K, value: V) -> Builder #[must_use]
pub fn header<K, V>(self, key: K, value: V) -> Self
where where
HeaderName: TryFrom<K>, HeaderName: TryFrom<K>,
<HeaderName as TryFrom<K>>::Error: Into<crate::Error>, <HeaderName as TryFrom<K>>::Error: Into<crate::Error>,

View File

@ -57,6 +57,7 @@ impl SystemTray {
} }
/// Sets the tray icon. Must be a [`Icon::File`] on Linux and a [`Icon::Raw`] on Windows and macOS. /// Sets the tray icon. Must be a [`Icon::File`] on Linux and a [`Icon::Raw`] on Windows and macOS.
#[must_use]
pub fn with_icon(mut self, icon: Icon) -> Self { pub fn with_icon(mut self, icon: Icon) -> Self {
self.icon.replace(icon); self.icon.replace(icon);
self self
@ -64,12 +65,14 @@ impl SystemTray {
/// Sets the tray icon as template. /// Sets the tray icon as template.
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
#[must_use]
pub fn with_icon_as_template(mut self, is_template: bool) -> Self { pub fn with_icon_as_template(mut self, is_template: bool) -> Self {
self.icon_as_template = is_template; self.icon_as_template = is_template;
self self
} }
/// Sets the menu to show when the system tray is right clicked. /// Sets the menu to show when the system tray is right clicked.
#[must_use]
pub fn with_menu(mut self, menu: menu::SystemTrayMenu) -> Self { pub fn with_menu(mut self, menu: menu::SystemTrayMenu) -> Self {
self.menu.replace(menu); self.menu.replace(menu);
self self

View File

@ -206,18 +206,21 @@ impl Menu {
} }
/// Adds the custom menu item to the menu. /// Adds the custom menu item to the menu.
#[must_use]
pub fn add_item(mut self, item: CustomMenuItem) -> Self { pub fn add_item(mut self, item: CustomMenuItem) -> Self {
self.items.push(MenuEntry::CustomItem(item)); self.items.push(MenuEntry::CustomItem(item));
self self
} }
/// Adds a native item to the menu. /// Adds a native item to the menu.
#[must_use]
pub fn add_native_item(mut self, item: MenuItem) -> Self { pub fn add_native_item(mut self, item: MenuItem) -> Self {
self.items.push(MenuEntry::NativeItem(item)); self.items.push(MenuEntry::NativeItem(item));
self self
} }
/// Adds an entry with submenu. /// Adds an entry with submenu.
#[must_use]
pub fn add_submenu(mut self, submenu: Submenu) -> Self { pub fn add_submenu(mut self, submenu: Submenu) -> Self {
self.items.push(MenuEntry::Submenu(submenu)); self.items.push(MenuEntry::Submenu(submenu));
self self
@ -255,6 +258,7 @@ impl CustomMenuItem {
} }
/// Assign a keyboard shortcut to the menu action. /// Assign a keyboard shortcut to the menu action.
#[must_use]
pub fn accelerator<T: Into<String>>(mut self, accelerator: T) -> Self { pub fn accelerator<T: Into<String>>(mut self, accelerator: T) -> Self {
self.keyboard_accelerator.replace(accelerator.into()); self.keyboard_accelerator.replace(accelerator.into());
self self
@ -262,6 +266,7 @@ impl CustomMenuItem {
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
#[cfg_attr(doc_cfg, doc(cfg(target_os = "macos")))] #[cfg_attr(doc_cfg, doc(cfg(target_os = "macos")))]
#[must_use]
/// A native image do render on the menu item. /// A native image do render on the menu item.
pub fn native_image(mut self, image: NativeImage) -> Self { pub fn native_image(mut self, image: NativeImage) -> Self {
self.native_image.replace(image); self.native_image.replace(image);
@ -269,12 +274,14 @@ impl CustomMenuItem {
} }
/// Mark the item as disabled. /// Mark the item as disabled.
#[must_use]
pub fn disabled(mut self) -> Self { pub fn disabled(mut self) -> Self {
self.enabled = false; self.enabled = false;
self self
} }
/// Mark the item as selected. /// Mark the item as selected.
#[must_use]
pub fn selected(mut self) -> Self { pub fn selected(mut self) -> Self {
self.selected = true; self.selected = true;
self self
@ -320,18 +327,21 @@ impl SystemTrayMenu {
} }
/// Adds the custom menu item to the system tray menu. /// Adds the custom menu item to the system tray menu.
#[must_use]
pub fn add_item(mut self, item: CustomMenuItem) -> Self { pub fn add_item(mut self, item: CustomMenuItem) -> Self {
self.items.push(SystemTrayMenuEntry::CustomItem(item)); self.items.push(SystemTrayMenuEntry::CustomItem(item));
self self
} }
/// Adds a native item to the system tray menu. /// Adds a native item to the system tray menu.
#[must_use]
pub fn add_native_item(mut self, item: SystemTrayMenuItem) -> Self { pub fn add_native_item(mut self, item: SystemTrayMenuItem) -> Self {
self.items.push(SystemTrayMenuEntry::NativeItem(item)); self.items.push(SystemTrayMenuEntry::NativeItem(item));
self self
} }
/// Adds an entry with submenu. /// Adds an entry with submenu.
#[must_use]
pub fn add_submenu(mut self, submenu: SystemTraySubmenu) -> Self { pub fn add_submenu(mut self, submenu: SystemTraySubmenu) -> Self {
self.items.push(SystemTrayMenuEntry::Submenu(submenu)); self.items.push(SystemTrayMenuEntry::Submenu(submenu));
self self

View File

@ -36,18 +36,21 @@ impl WebviewAttributes {
} }
/// Sets the init script. /// Sets the init script.
#[must_use]
pub fn initialization_script(mut self, script: &str) -> Self { pub fn initialization_script(mut self, script: &str) -> Self {
self.initialization_scripts.push(script.to_string()); self.initialization_scripts.push(script.to_string());
self self
} }
/// Data directory for the webview. /// Data directory for the webview.
#[must_use]
pub fn data_directory(mut self, data_directory: PathBuf) -> Self { pub fn data_directory(mut self, data_directory: PathBuf) -> Self {
self.data_directory.replace(data_directory); self.data_directory.replace(data_directory);
self self
} }
/// Disables the file drop handler. This is required to use drag and drop APIs on the front end on Windows. /// Disables the file drop handler. This is required to use drag and drop APIs on the front end on Windows.
#[must_use]
pub fn disable_file_drop_handler(mut self) -> Self { pub fn disable_file_drop_handler(mut self) -> Self {
self.file_drop_handler_enabled = false; self.file_drop_handler_enabled = false;
self self
@ -57,6 +60,7 @@ impl WebviewAttributes {
/// ///
/// **macOS** doesn't provide such method and is always enabled by default, /// **macOS** doesn't provide such method and is always enabled by default,
/// but you still need to add menu item accelerators to use shortcuts. /// but you still need to add menu item accelerators to use shortcuts.
#[must_use]
pub fn enable_clipboard_access(mut self) -> Self { pub fn enable_clipboard_access(mut self) -> Self {
self.clipboard = true; self.clipboard = true;
self self
@ -80,39 +84,51 @@ pub trait WindowBuilder: WindowBuilderBase {
fn with_config(config: WindowConfig) -> Self; fn with_config(config: WindowConfig) -> Self;
/// Sets the menu for the window. /// Sets the menu for the window.
#[must_use]
fn menu(self, menu: Menu) -> Self; fn menu(self, menu: Menu) -> Self;
/// Show window in the center of the screen. /// Show window in the center of the screen.
#[must_use]
fn center(self) -> Self; fn center(self) -> Self;
/// The initial position of the window's. /// The initial position of the window's.
#[must_use]
fn position(self, x: f64, y: f64) -> Self; fn position(self, x: f64, y: f64) -> Self;
/// Window size. /// Window size.
#[must_use]
fn inner_size(self, min_width: f64, min_height: f64) -> Self; fn inner_size(self, min_width: f64, min_height: f64) -> Self;
/// Window min inner size. /// Window min inner size.
#[must_use]
fn min_inner_size(self, min_width: f64, min_height: f64) -> Self; fn min_inner_size(self, min_width: f64, min_height: f64) -> Self;
/// Window max inner size. /// Window max inner size.
#[must_use]
fn max_inner_size(self, max_width: f64, max_height: f64) -> Self; fn max_inner_size(self, max_width: f64, max_height: f64) -> Self;
/// Whether the window is resizable or not. /// Whether the window is resizable or not.
#[must_use]
fn resizable(self, resizable: bool) -> Self; fn resizable(self, resizable: bool) -> Self;
/// The title of the window in the title bar. /// The title of the window in the title bar.
#[must_use]
fn title<S: Into<String>>(self, title: S) -> Self; fn title<S: Into<String>>(self, title: S) -> Self;
/// Whether to start the window in fullscreen or not. /// Whether to start the window in fullscreen or not.
#[must_use]
fn fullscreen(self, fullscreen: bool) -> Self; fn fullscreen(self, fullscreen: bool) -> Self;
/// Whether the window will be initially hidden or focused. /// Whether the window will be initially hidden or focused.
#[must_use]
fn focus(self) -> Self; fn focus(self) -> Self;
/// Whether the window should be maximized upon creation. /// Whether the window should be maximized upon creation.
#[must_use]
fn maximized(self, maximized: bool) -> Self; fn maximized(self, maximized: bool) -> Self;
/// Whether the window should be immediately visible upon creation. /// Whether the window should be immediately visible upon creation.
#[must_use]
fn visible(self, visible: bool) -> Self; fn visible(self, visible: bool) -> Self;
/// Whether the the window should be transparent. If this is true, writing colors /// Whether the the window should be transparent. If this is true, writing colors
@ -122,18 +138,22 @@ pub trait WindowBuilder: WindowBuilderBase {
doc_cfg, doc_cfg,
doc(cfg(any(not(target_os = "macos"), feature = "macos-private-api"))) doc(cfg(any(not(target_os = "macos"), feature = "macos-private-api")))
)] )]
#[must_use]
fn transparent(self, transparent: bool) -> Self; fn transparent(self, transparent: bool) -> Self;
/// Whether the window should have borders and bars. /// Whether the window should have borders and bars.
#[must_use]
fn decorations(self, decorations: bool) -> Self; fn decorations(self, decorations: bool) -> Self;
/// Whether the window should always be on top of other windows. /// Whether the window should always be on top of other windows.
#[must_use]
fn always_on_top(self, always_on_top: bool) -> Self; fn always_on_top(self, always_on_top: bool) -> Self;
/// Sets the window icon. /// Sets the window icon.
fn icon(self, icon: Icon) -> crate::Result<Self>; fn icon(self, icon: Icon) -> crate::Result<Self>;
/// Sets whether or not the window icon should be added to the taskbar. /// Sets whether or not the window icon should be added to the taskbar.
#[must_use]
fn skip_taskbar(self, skip: bool) -> Self; fn skip_taskbar(self, skip: bool) -> Self;
/// Sets a parent to the window to be created. /// Sets a parent to the window to be created.
@ -142,6 +162,7 @@ pub trait WindowBuilder: WindowBuilderBase {
/// ///
/// For more information, see <https://docs.microsoft.com/en-us/windows/win32/winmsg/window-features#child-windows> /// For more information, see <https://docs.microsoft.com/en-us/windows/win32/winmsg/window-features#child-windows>
#[cfg(windows)] #[cfg(windows)]
#[must_use]
fn parent_window(self, parent: HWND) -> Self; fn parent_window(self, parent: HWND) -> Self;
/// Set an owner to the window to be created. /// Set an owner to the window to be created.
@ -153,6 +174,7 @@ pub trait WindowBuilder: WindowBuilderBase {
/// ///
/// For more information, see <https://docs.microsoft.com/en-us/windows/win32/winmsg/window-features#owned-windows> /// For more information, see <https://docs.microsoft.com/en-us/windows/win32/winmsg/window-features#owned-windows>
#[cfg(windows)] #[cfg(windows)]
#[must_use]
fn owner_window(self, owner: HWND) -> Self; fn owner_window(self, owner: HWND) -> Self;
/// Whether the icon was set or not. /// Whether the icon was set or not.

View File

@ -168,6 +168,7 @@ impl<R: Runtime> PendingWindow<R> {
} }
} }
#[must_use]
pub fn set_menu(mut self, menu: Menu) -> Self { pub fn set_menu(mut self, menu: Menu) -> Self {
let mut menu_ids = HashMap::new(); let mut menu_ids = HashMap::new();
get_menu_ids(&mut menu_ids, &menu); get_menu_ids(&mut menu_ids, &menu);

View File

@ -7,7 +7,7 @@ homepage = "https://tauri.studio"
repository = "https://github.com/tauri-apps/tauri" repository = "https://github.com/tauri-apps/tauri"
description = "Utilities for Tauri" description = "Utilities for Tauri"
edition = "2021" edition = "2021"
rust-version = "1.56" rust-version = "1.57"
exclude = [ ".license_template", "CHANGELOG.md", "/target" ] exclude = [ ".license_template", "CHANGELOG.md", "/target" ]
readme = "README.md" readme = "README.md"
@ -24,6 +24,13 @@ proc-macro2 = { version = "1.0", optional = true }
quote = { version = "1.0", optional = true } quote = { version = "1.0", optional = true }
schemars = { version = "0.8", features = ["url"], optional = true } schemars = { version = "0.8", features = ["url"], optional = true }
serde_with = "1.10" serde_with = "1.10"
sha2 = "0.9"
base64 = "0.13"
aes-gcm = { version = "0.9", optional = true }
ring = { version = "0.16", optional = true, features = ["std"] }
once_cell = { version = "1.8", optional = true }
serialize-to-javascript = { git = "https://github.com/chippers/serialize-to-javascript" }
[target."cfg(target_os = \"linux\")".dependencies] [target."cfg(target_os = \"linux\")".dependencies]
heck = "0.4" heck = "0.4"
@ -32,3 +39,4 @@ heck = "0.4"
build = [ "proc-macro2", "quote" ] build = [ "proc-macro2", "quote" ]
compression = [ "zstd" ] compression = [ "zstd" ]
schema = ["schemars"] schema = ["schemars"]
isolation = [ "aes-gcm", "ring", "once_cell" ]

View File

@ -81,6 +81,9 @@ impl<P: AsRef<Path>> From<P> for AssetKey {
pub enum CspHash<'a> { pub enum CspHash<'a> {
/// The `script-src` directive. /// The `script-src` directive.
Script(&'a str), Script(&'a str),
/// The `style-src` directive.
Style(&'a str),
} }
impl CspHash<'_> { impl CspHash<'_> {
@ -88,6 +91,7 @@ impl CspHash<'_> {
pub fn directive(&self) -> &'static str { pub fn directive(&self) -> &'static str {
match self { match self {
Self::Script(_) => "script-src", Self::Script(_) => "script-src",
Self::Style(_) => "style-src",
} }
} }
@ -95,6 +99,7 @@ impl CspHash<'_> {
pub fn hash(&self) -> &str { pub fn hash(&self) -> &str {
match self { match self {
Self::Script(hash) => hash, Self::Script(hash) => hash,
Self::Style(hash) => hash,
} }
} }
} }

View File

@ -36,8 +36,8 @@ pub enum WindowUrl {
App(PathBuf), App(PathBuf),
} }
impl std::fmt::Display for WindowUrl { impl fmt::Display for WindowUrl {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Self::External(url) => write!(f, "{}", url), Self::External(url) => write!(f, "{}", url),
Self::App(path) => write!(f, "{}", path.display()), Self::App(path) => write!(f, "{}", path.display()),
@ -542,7 +542,7 @@ fn default_file_drop_enabled() -> bool {
/// Security configuration. /// Security configuration.
#[skip_serializing_none] #[skip_serializing_none]
#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize)] #[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))] #[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)] #[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct SecurityConfig { pub struct SecurityConfig {
@ -557,6 +557,23 @@ pub struct SecurityConfig {
/// This is a really important part of the configuration since it helps you ensure your WebView is secured. /// This is a really important part of the configuration since it helps you ensure your WebView is secured.
/// See <https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP>. /// See <https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP>.
pub dev_csp: Option<String>, pub dev_csp: Option<String>,
/// Freeze the `Object.prototype` when using the custom protocol.
#[serde(default = "default_freeze_prototype")]
pub freeze_prototype: bool,
}
impl Default for SecurityConfig {
fn default() -> Self {
Self {
csp: None,
dev_csp: None,
freeze_prototype: default_freeze_prototype(),
}
}
}
fn default_freeze_prototype() -> bool {
true
} }
/// Defines an allowlist type. /// Defines an allowlist type.
@ -1292,12 +1309,37 @@ fn default_window_config() -> Vec<WindowConfig> {
vec![Default::default()] vec![Default::default()]
} }
/// The application pattern.
#[skip_serializing_none]
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
#[serde(rename_all = "lowercase", tag = "use", content = "options")]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
pub enum PatternKind {
/// Brownfield pattern.
Brownfield,
/// Isolation pattern. Recommended for security purposes.
#[cfg(feature = "isolation")]
Isolation {
/// The dir containing the index.html file that contains the secure isolation application.
dir: PathBuf,
},
}
impl Default for PatternKind {
fn default() -> Self {
Self::Brownfield
}
}
/// The Tauri configuration object. /// The Tauri configuration object.
#[skip_serializing_none] #[skip_serializing_none]
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)] #[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(JsonSchema))] #[cfg_attr(feature = "schema", derive(JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)] #[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct TauriConfig { pub struct TauriConfig {
/// The pattern to use.
#[serde(default)]
pub pattern: PatternKind,
/// The windows configuration. /// The windows configuration.
#[serde(default = "default_window_config")] #[serde(default = "default_window_config")]
pub windows: Vec<WindowConfig>, pub windows: Vec<WindowConfig>,
@ -1325,6 +1367,7 @@ pub struct TauriConfig {
impl Default for TauriConfig { impl Default for TauriConfig {
fn default() -> Self { fn default() -> Self {
Self { Self {
pattern: Default::default(),
windows: default_window_config(), windows: default_window_config(),
cli: None, cli: None,
bundle: BundleConfig::default(), bundle: BundleConfig::default(),
@ -1338,6 +1381,20 @@ impl Default for TauriConfig {
} }
impl TauriConfig { impl TauriConfig {
/// Returns all Cargo features.
#[allow(dead_code)]
pub fn all_features() -> Vec<&'static str> {
let mut features = AllowlistConfig::all_features();
features.extend(vec![
"cli",
"updater",
"system-tray",
"macos-private-api",
"isolation",
]);
features
}
/// Returns the enabled Cargo features. /// Returns the enabled Cargo features.
#[allow(dead_code)] #[allow(dead_code)]
pub fn features(&self) -> Vec<&str> { pub fn features(&self) -> Vec<&str> {
@ -1354,6 +1411,10 @@ impl TauriConfig {
if self.macos_private_api { if self.macos_private_api {
features.push("macos-private-api"); features.push("macos-private-api");
} }
#[cfg(feature = "isolation")]
if let PatternKind::Isolation { .. } = self.pattern {
features.push("isolation");
}
features.sort_unstable(); features.sort_unstable();
features features
} }
@ -1964,6 +2025,21 @@ mod build {
} }
} }
impl ToTokens for PatternKind {
fn to_tokens(&self, tokens: &mut TokenStream) {
let prefix = quote! { ::tauri::utils::config::PatternKind };
tokens.append_all(match self {
Self::Brownfield => quote! { #prefix::Brownfield },
#[cfg(feature = "isolation")]
Self::Isolation { dir } => {
let dir = path_buf_lit(dir);
quote! { #prefix::Isolation { dir: #dir } }
}
})
}
}
impl ToTokens for WindowsConfig { impl ToTokens for WindowsConfig {
fn to_tokens(&self, tokens: &mut TokenStream) { fn to_tokens(&self, tokens: &mut TokenStream) {
let webview_fixed_runtime_path = opt_lit( let webview_fixed_runtime_path = opt_lit(
@ -2082,8 +2158,9 @@ mod build {
fn to_tokens(&self, tokens: &mut TokenStream) { fn to_tokens(&self, tokens: &mut TokenStream) {
let csp = opt_str_lit(self.csp.as_ref()); let csp = opt_str_lit(self.csp.as_ref());
let dev_csp = opt_str_lit(self.dev_csp.as_ref()); let dev_csp = opt_str_lit(self.dev_csp.as_ref());
let freeze_prototype = self.freeze_prototype;
literal_struct!(tokens, SecurityConfig, csp, dev_csp); literal_struct!(tokens, SecurityConfig, csp, dev_csp, freeze_prototype);
} }
} }
@ -2151,6 +2228,7 @@ mod build {
impl ToTokens for TauriConfig { impl ToTokens for TauriConfig {
fn to_tokens(&self, tokens: &mut TokenStream) { fn to_tokens(&self, tokens: &mut TokenStream) {
let pattern = &self.pattern;
let windows = vec_lit(&self.windows, identity); let windows = vec_lit(&self.windows, identity);
let cli = opt_lit(self.cli.as_ref()); let cli = opt_lit(self.cli.as_ref());
let bundle = &self.bundle; let bundle = &self.bundle;
@ -2163,6 +2241,7 @@ mod build {
literal_struct!( literal_struct!(
tokens, tokens,
TauriConfig, TauriConfig,
pattern,
windows, windows,
cli, cli,
bundle, bundle,
@ -2234,6 +2313,7 @@ mod test {
// create a tauri config. // create a tauri config.
let tauri = TauriConfig { let tauri = TauriConfig {
pattern: Default::default(),
windows: vec![WindowConfig { windows: vec![WindowConfig {
label: "main".to_string(), label: "main".to_string(),
url: WindowUrl::default(), url: WindowUrl::default(),
@ -2283,6 +2363,7 @@ mod test {
security: SecurityConfig { security: SecurityConfig {
csp: None, csp: None,
dev_csp: None, dev_csp: None,
freeze_prototype: true,
}, },
allowlist: AllowlistConfig::default(), allowlist: AllowlistConfig::default(),
system_tray: None, system_tray: None,

View File

@ -4,8 +4,18 @@
//! The module to process HTML in Tauri. //! The module to process HTML in Tauri.
use std::path::{Path, PathBuf};
use html5ever::{interface::QualName, namespace_url, ns, tendril::TendrilSink, LocalName}; use html5ever::{interface::QualName, namespace_url, ns, tendril::TendrilSink, LocalName};
use kuchiki::{Attribute, ExpandedName, NodeRef}; pub use kuchiki::NodeRef;
use kuchiki::{Attribute, ExpandedName};
use serde::Serialize;
#[cfg(feature = "isolation")]
use serialize_to_javascript::DefaultTemplate;
use crate::config::PatternKind;
#[cfg(feature = "isolation")]
use crate::pattern::isolation::IsolationJavascriptCodegen;
/// The token used on the CSP tag content. /// The token used on the CSP tag content.
pub const CSP_TOKEN: &str = "__TAURI_CSP__"; pub const CSP_TOKEN: &str = "__TAURI_CSP__";
@ -13,14 +23,25 @@ pub const CSP_TOKEN: &str = "__TAURI_CSP__";
pub const SCRIPT_NONCE_TOKEN: &str = "__TAURI_SCRIPT_NONCE__"; pub const SCRIPT_NONCE_TOKEN: &str = "__TAURI_SCRIPT_NONCE__";
/// The token used for style nonces. /// The token used for style nonces.
pub const STYLE_NONCE_TOKEN: &str = "__TAURI_STYLE_NONCE__"; pub const STYLE_NONCE_TOKEN: &str = "__TAURI_STYLE_NONCE__";
/// The token used for the invoke key.
pub const INVOKE_KEY_TOKEN: &str = "__TAURI__INVOKE_KEY_TOKEN__";
/// Parses the given HTML string. /// Parses the given HTML string.
pub fn parse(html: String) -> NodeRef { pub fn parse(html: String) -> NodeRef {
kuchiki::parse_html().one(html) kuchiki::parse_html().one(html)
} }
fn with_head<F: FnOnce(&NodeRef)>(document: &mut NodeRef, f: F) {
if let Ok(ref node) = document.select_first("head") {
f(node.as_node())
} else {
let node = NodeRef::new_element(
QualName::new(None, ns!(html), LocalName::from("head")),
None,
);
f(&node);
document.prepend(node)
}
}
fn inject_nonce(document: &mut NodeRef, selector: &str, token: &str) { fn inject_nonce(document: &mut NodeRef, selector: &str, token: &str) {
if let Ok(scripts) = document.select(selector) { if let Ok(scripts) = document.select(selector) {
for target in scripts { for target in scripts {
@ -43,88 +64,11 @@ pub fn inject_nonce_token(document: &mut NodeRef) {
inject_nonce(document, "style", STYLE_NONCE_TOKEN); inject_nonce(document, "style", STYLE_NONCE_TOKEN);
} }
/// Injects the invoke key token to each script on the document.
///
/// The invoke key token is replaced at runtime with the actual invoke key value.
pub fn inject_invoke_key_token(document: &mut NodeRef) {
let mut targets = vec![];
if let Ok(scripts) = document.select("script") {
for target in scripts {
targets.push(target);
}
for target in targets {
let node = target.as_node();
let element = node.as_element().unwrap();
let attrs = element.attributes.borrow();
// if the script is external (has `src`), we won't inject the token
if attrs.get("src").is_some() {
continue;
}
let replacement_node = match attrs.get("type") {
Some("module") | Some("application/ecmascript") => {
let replacement_node = NodeRef::new_element(
QualName::new(None, ns!(html), "script".into()),
element
.attributes
.borrow()
.clone()
.map
.into_iter()
.collect::<Vec<_>>(),
);
let script = node.text_contents();
replacement_node.append(NodeRef::new_text(format!(
r#"
const __TAURI_INVOKE_KEY__ = {token};
{script}
"#,
token = INVOKE_KEY_TOKEN,
script = script
)));
replacement_node
}
Some("application/javascript") | None => {
let replacement_node = NodeRef::new_element(
QualName::new(None, ns!(html), "script".into()),
element
.attributes
.borrow()
.clone()
.map
.into_iter()
.collect::<Vec<_>>(),
);
let script = node.text_contents();
replacement_node.append(NodeRef::new_text(
script.replace("__TAURI_INVOKE_KEY__", INVOKE_KEY_TOKEN),
));
replacement_node
}
_ => {
continue;
}
};
node.insert_after(replacement_node);
node.detach();
}
}
}
/// Injects a content security policy to the HTML. /// Injects a content security policy to the HTML.
pub fn inject_csp(document: &mut NodeRef, csp: &str) { pub fn inject_csp(document: &mut NodeRef, csp: &str) {
if let Ok(ref head) = document.select_first("head") { with_head(document, |head| {
head.as_node().append(create_csp_meta_tag(csp));
} else {
let head = NodeRef::new_element(
QualName::new(None, ns!(html), LocalName::from("head")),
None,
);
head.append(create_csp_meta_tag(csp)); head.append(create_csp_meta_tag(csp));
document.prepend(head); });
}
} }
/// Injects a content security policy token to the HTML. /// Injects a content security policy token to the HTML.
@ -154,9 +98,101 @@ fn create_csp_meta_tag(csp: &str) -> NodeRef {
) )
} }
/// The shape of the JavaScript Pattern config
#[derive(Debug, Serialize)]
#[serde(rename_all = "lowercase", tag = "pattern")]
pub enum PatternObject {
/// Brownfield pattern.
Brownfield,
/// Isolation pattern. Recommended for security purposes.
Isolation {
/// Which `IsolationSide` this `PatternObject` is getting injected into
side: IsolationSide,
},
}
impl From<&PatternKind> for PatternObject {
fn from(pattern_kind: &PatternKind) -> Self {
match pattern_kind {
PatternKind::Brownfield => Self::Brownfield,
#[cfg(feature = "isolation")]
PatternKind::Isolation { .. } => Self::Isolation {
side: IsolationSide::default(),
},
}
}
}
/// Where the JavaScript is injected to
#[derive(Debug, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum IsolationSide {
/// Original frame, the Brownfield application
Original,
/// Secure frame, the isolation security application
Secure,
}
impl Default for IsolationSide {
fn default() -> Self {
Self::Original
}
}
/// Injects the Isolation JavaScript to a codegen time document.
///
/// Note: This function is not considered part of the stable API.
#[cfg(feature = "isolation")]
pub fn inject_codegen_isolation_script(document: &mut NodeRef) {
with_head(document, |head| {
let script = NodeRef::new_element(QualName::new(None, ns!(html), "script".into()), None);
script.append(NodeRef::new_text(
IsolationJavascriptCodegen {}
.render_default(&Default::default())
.expect("unable to render codegen isolation script template"),
));
head.prepend(script);
});
}
/// Temporary workaround for Windows not allowing requests
///
/// Note: this does not prevent path traversal due to the isolation application expectation that it
/// is secure.
pub fn inline_isolation(document: &mut NodeRef, dir: &Path) {
for script in document
.select("script[src]")
.expect("unable to parse document for scripts")
{
let src = {
let attributes = script.attributes.borrow();
attributes
.get(LocalName::from("src"))
.expect("script with src attribute has no src value")
.to_string()
};
let mut path = PathBuf::from(src);
if path.has_root() {
path = path
.strip_prefix("/")
.expect("Tauri \"Isolation\" Pattern only supports relative or absolute (`/`) paths.")
.into();
}
let file = std::fs::read_to_string(dir.join(path)).expect("unable to find isolation file");
script.as_node().append(NodeRef::new_text(file));
let mut attributes = script.attributes.borrow_mut();
attributes.remove(LocalName::from("src"));
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use kuchiki::traits::*; use kuchiki::traits::*;
#[test] #[test]
fn csp() { fn csp() {
let htmls = vec![ let htmls = vec![

View File

@ -10,6 +10,9 @@ pub mod config;
pub mod html; pub mod html;
pub mod platform; pub mod platform;
/// Application pattern.
pub mod pattern;
/// `tauri::App` package information. /// `tauri::App` package information.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct PackageInfo { pub struct PackageInfo {
@ -112,4 +115,7 @@ pub enum Error {
/// IO error /// IO error
#[error("{0}")] #[error("{0}")]
Io(#[from] std::io::Error), Io(#[from] std::io::Error),
/// Invalid pattern.
#[error("invalid pattern `{0}`. Expected either `brownfield` or `isolation`.")]
InvalidPattern(String),
} }

View File

@ -0,0 +1,115 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
/**
* IMPORTANT: See ipc.js for the main frame implementation.
* main frame -> isolation frame = isolation payload
* isolation frame -> main frame = isolation message
*/
;(async function () {
/**
* Sends the message to the isolation frame.
* @param {any} message
*/
function sendMessage(message) {
window.parent.postMessage(message, '*')
}
/**
* @type {Uint8Array} - Injected by Tauri during runtime
*/
const aesGcmKeyRaw = new Uint8Array(__TEMPLATE_runtime_aes_gcm_key__)
/**
* @type {CryptoKey}
*/
const aesGcmKey = await window.crypto.subtle.importKey(
'raw',
aesGcmKeyRaw,
'AES-GCM',
true,
['encrypt']
)
/**
* @param {object} data
* @return {Promise<{nonce: number[], payload: number[]}>}
*/
async function encrypt(data) {
let algorithm = Object.create(null)
algorithm.name = 'AES-GCM'
algorithm.iv = window.crypto.getRandomValues(new Uint8Array(12))
let encoder = new TextEncoder()
let payloadRaw = encoder.encode(JSON.stringify(data))
return window.crypto.subtle
.encrypt(algorithm, aesGcmKey, payloadRaw)
.then((payload) => {
let result = Object.create(null)
result.nonce = Array.from(new Uint8Array(algorithm.iv))
result.payload = Array.from(new Uint8Array(payload))
return result
})
}
/**
* Detect if a message event is a valid isolation payload.
*
* @param {MessageEvent<object>} event - a message event that is expected to be an isolation payload
* @return boolean
*/
function isIsolationPayload(event) {
return (
typeof event.data === 'object' &&
'callback' in event.data &&
'error' in event.data
)
}
/**
* Handle incoming payload events.
* @param {MessageEvent<any>} event
*/
async function payloadHandler(event) {
if (!isIsolationPayload(event)) {
return
}
let data = event.data
if (typeof window.__TAURI_ISOLATION_HOOK__ === 'function') {
// await even if it's not async so that we can support async ones
data = await window.__TAURI_ISOLATION_HOOK__(data)
}
const encrypted = await encrypt(data)
sendMessage(encrypted)
}
window.addEventListener('message', payloadHandler, false)
/**
* @type {number} - How many milliseconds to wait between ready checks
*/
const readyIntervalMs = 50
/**
* Wait until this Isolation context is ready to receive messages, and let the main frame know.
*/
function waitUntilReady() {
// consider either a function or an explicitly set null value as the ready signal
if (
typeof window.__TAURI_ISOLATION_HOOK__ === 'function' ||
window.__TAURI_ISOLATION_HOOK__ === null
) {
sendMessage('__TAURI_ISOLATION_READY__')
} else {
setTimeout(waitUntilReady, readyIntervalMs)
}
}
setTimeout(waitUntilReady, readyIntervalMs)
})()

View File

@ -0,0 +1,164 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use std::array::TryFromSliceError;
use std::borrow::Cow;
use std::fmt::{Debug, Formatter};
use std::string::FromUtf8Error;
use aes_gcm::aead::Aead;
use aes_gcm::{aead::NewAead, Aes256Gcm, Nonce};
use once_cell::sync::OnceCell;
use ring::error::Unspecified;
use ring::rand::SystemRandom;
use serialize_to_javascript::{default_template, Template};
/// Cryptographically secure pseudo-random number generator.
static RNG: OnceCell<SystemRandom> = OnceCell::new();
/// The style for the isolation iframe.
pub const IFRAME_STYLE: &str = "#__tauri_isolation__ { display: none !important }";
/// Errors that can occur during Isolation keys generation.
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
/// Something went wrong with the CSPRNG.
#[error("Unspecified CSPRNG error")]
Csprng,
/// Something went wrong with decryping an AES-GCM payload
#[error("AES-GCM")]
Aes,
/// Nonce was not 96 bits
#[error("Nonce: {0}")]
NonceSize(#[from] TryFromSliceError),
/// Payload was not valid utf8
#[error("{0}")]
Utf8(#[from] FromUtf8Error),
/// Invalid json format
#[error("{0}")]
Json(#[from] serde_json::Error),
}
impl From<Unspecified> for Error {
fn from(_: Unspecified) -> Self {
Self::Csprng
}
}
/// A formatted AES-GCM cipher instance along with the key used to initialize it.
#[derive(Clone)]
pub struct AesGcmPair {
raw: [u8; 32],
key: Aes256Gcm,
}
impl Debug for AesGcmPair {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "AesGcmPair(...)")
}
}
impl AesGcmPair {
fn new() -> Result<Self, Error> {
let rng = RNG.get_or_init(SystemRandom::new);
let raw: [u8; 32] = ring::rand::generate(rng)?.expose();
let key = aes_gcm::Key::from_slice(&raw);
Ok(Self {
raw,
key: Aes256Gcm::new(key),
})
}
/// The raw value used to create the AES-GCM key
pub fn raw(&self) -> &[u8; 32] {
&self.raw
}
/// The formatted AES-GCM key
pub fn key(&self) -> &Aes256Gcm {
&self.key
}
}
/// All cryptographic keys required for Isolation encryption
#[derive(Debug, Clone)]
pub struct Keys {
/// AES-GCM key
aes_gcm: AesGcmPair,
}
impl Keys {
/// Securely generate required keys for Isolation encryption.
pub fn new() -> Result<Self, Error> {
AesGcmPair::new()
.map(|aes_gcm| Self { aes_gcm })
.map_err(Into::into)
}
/// The AES-GCM data (and raw data).
pub fn aes_gcm(&self) -> &AesGcmPair {
&self.aes_gcm
}
/// Decrypts a message using the generated keys.
pub fn decrypt(&self, raw: RawIsolationPayload<'_>) -> Result<String, Error> {
let RawIsolationPayload { nonce, payload } = raw;
let nonce: [u8; 12] = nonce.as_ref().try_into()?;
let bytes = self
.aes_gcm
.key
.decrypt(Nonce::from_slice(&nonce), payload.as_ref())
.map_err(|_| self::Error::Aes)?;
String::from_utf8(bytes).map_err(Into::into)
}
}
/// Raw representation of
#[derive(Debug, serde::Deserialize)]
pub struct RawIsolationPayload<'a> {
nonce: Cow<'a, [u8]>,
payload: Cow<'a, [u8]>,
}
impl<'a> TryFrom<&'a str> for RawIsolationPayload<'a> {
type Error = Error;
fn try_from(value: &'a str) -> Result<Self, Self::Error> {
serde_json::from_str(value).map_err(Into::into)
}
}
/// The Isolation JavaScript template meant to be injected during codegen.
///
/// Note: This struct is not considered part of the stable API
#[derive(Template)]
#[default_template("isolation.js")]
pub struct IsolationJavascriptCodegen {
// this template intentionally does not include the runtime field
}
/// The Isolation JavaScript template meant to be injected during runtime.
///
/// Note: This struct is not considered part of the stable API
#[derive(Template)]
#[default_template("isolation.js")]
pub struct IsolationJavascriptRuntime<'a> {
/// The key used on the Rust backend and the Isolation Javascript
pub runtime_aes_gcm_key: &'a [u8; 32],
}
#[cfg(test)]
mod test {
#[test]
fn create_keys() -> Result<(), Box<dyn std::error::Error>> {
let _ = super::Keys::new()?;
Ok(())
}
}

View File

@ -0,0 +1,7 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
/// Handling the Tauri "Isolation" Pattern.
#[cfg(feature = "isolation")]
pub mod isolation;

View File

@ -3,7 +3,7 @@ authors = ["Tauri Programme within The Commons Conservancy"]
categories = ["gui", "web-programming"] categories = ["gui", "web-programming"]
description = "Make tiny, secure apps for all desktop platforms with Tauri" description = "Make tiny, secure apps for all desktop platforms with Tauri"
edition = "2021" edition = "2021"
rust-version = "1.56" rust-version = "1.57"
exclude = [ exclude = [
"/test", "/test",
"/.scripts", "/.scripts",
@ -77,6 +77,7 @@ epi = { git = "https://github.com/wusyong/egui", branch = "tao", optional = true
regex = "1.5" regex = "1.5"
glob = "0.3" glob = "0.3"
data-url = "0.1" data-url = "0.1"
serialize-to-javascript = { git = "https://github.com/chippers/serialize-to-javascript" }
[target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies] [target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies]
glib = "0.14" glib = "0.14"
@ -99,10 +100,14 @@ tauri = {path = "."}
tokio-test = "0.4.2" tokio-test = "0.4.2"
tokio = { version = "1.15", features = [ "full" ] } tokio = { version = "1.15", features = [ "full" ] }
[target."cfg(windows)".dev-dependencies]
webview2-com = "0.7.0"
[features] [features]
default = [ "wry", "compression" ] default = [ "wry", "compression" ]
compression = [ "tauri-macros/compression", "tauri-utils/compression" ] compression = [ "tauri-macros/compression", "tauri-utils/compression" ]
wry = ["tauri-runtime-wry"] wry = ["tauri-runtime-wry"]
isolation = ["tauri-utils/isolation", "tauri-macros/isolation"]
custom-protocol = ["tauri-macros/custom-protocol"] custom-protocol = ["tauri-macros/custom-protocol"]
updater = ["minisign-verify", "base64", "dialog-ask"] updater = ["minisign-verify", "base64", "dialog-ask"]
http-api = ["attohttpc"] http-api = ["attohttpc"]
@ -233,10 +238,12 @@ path = "../../examples/helloworld/src-tauri/src/main.rs"
[[example]] [[example]]
name = "multiwindow" name = "multiwindow"
path = "../../examples/multiwindow/src-tauri/src/main.rs" path = "../../examples/multiwindow/src-tauri/src/main.rs"
required-features = [ "window-create" ]
[[example]] [[example]]
name = "navigation" name = "navigation"
path = "../../examples/navigation/src-tauri/src/main.rs" path = "../../examples/navigation/src-tauri/src/main.rs"
required-features = [ "window-create" ]
[[example]] [[example]]
name = "splashscreen" name = "splashscreen"
@ -249,3 +256,8 @@ path = "../../examples/state/src-tauri/src/main.rs"
[[example]] [[example]]
name = "streaming" name = "streaming"
path = "../../examples/streaming/src-tauri/src/main.rs" path = "../../examples/streaming/src-tauri/src/main.rs"
[[example]]
name = "isolation"
path = "../../examples/isolation/src-tauri/src/main.rs"
required-features = [ "isolation" ]

File diff suppressed because one or more lines are too long

View File

@ -8,7 +8,9 @@
} }
if (!window.__TAURI__) { if (!window.__TAURI__) {
window.__TAURI__ = {} Object.defineProperty(window, '__TAURI__', {
value: {}
})
} }
window.__TAURI__.transformCallback = function transformCallback( window.__TAURI__.transformCallback = function transformCallback(
@ -33,7 +35,20 @@
return identifier return identifier
} }
window.__TAURI_INVOKE__ = function invoke(cmd, args = {}, key = null) { const ipcQueue = []
let isWaitingForIpc = false
function waitForIpc() {
if ('__TAURI_IPC__' in window) {
for (const action of ipcQueue) {
action()
}
} else {
setTimeout(waitForIpc, 50)
}
}
window.__TAURI_INVOKE__ = function invoke(cmd, args = {}) {
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
var callback = window.__TAURI__.transformCallback(function (r) { var callback = window.__TAURI__.transformCallback(function (r) {
resolve(r) resolve(r)
@ -52,25 +67,21 @@
return reject(new Error('Invalid argument type.')) return reject(new Error('Invalid argument type.'))
} }
if ( const action = () => {
document.readyState === 'complete' || window.__TAURI_IPC__({
document.readyState === 'interactive'
) {
window.__TAURI_POST_MESSAGE__(cmd, {
...args, ...args,
callback: callback, callback,
error: error, error: error
__invokeKey: key || __TAURI_INVOKE_KEY__
}) })
}
if (window.__TAURI_IPC__) {
action()
} else { } else {
window.addEventListener('DOMContentLoaded', function () { ipcQueue.push(action)
window.__TAURI_POST_MESSAGE__(cmd, { if (!isWaitingForIpc) {
...args, waitForIpc()
callback: callback, isWaitingForIpc = true
error: error, }
__invokeKey: key || __TAURI_INVOKE_KEY__
})
})
} }
}) })
} }
@ -88,17 +99,13 @@
target.href.startsWith('http') && target.href.startsWith('http') &&
target.target === '_blank' target.target === '_blank'
) { ) {
window.__TAURI_INVOKE__( window.__TAURI_INVOKE__('tauri', {
'tauri', __tauriModule: 'Shell',
{ message: {
__tauriModule: 'Shell', cmd: 'open',
message: { path: target.href
cmd: 'open', }
path: target.href })
}
},
_KEY_VALUE_
)
e.preventDefault() e.preventDefault()
} }
break break
@ -129,43 +136,35 @@
document.addEventListener('mousedown', (e) => { document.addEventListener('mousedown', (e) => {
if (e.target.hasAttribute('data-tauri-drag-region') && e.buttons === 1) { if (e.target.hasAttribute('data-tauri-drag-region') && e.buttons === 1) {
// start dragging if the element has a `tauri-drag-region` data attribute and maximize on double-clicking it // start dragging if the element has a `tauri-drag-region` data attribute and maximize on double-clicking it
window.__TAURI_INVOKE__( window.__TAURI_INVOKE__('tauri', {
'tauri', __tauriModule: 'Window',
{ message: {
__tauriModule: 'Window', cmd: 'manage',
message: { data: {
cmd: 'manage', cmd: {
data: { type: e.detail === 2 ? '__toggleMaximize' : 'startDragging'
cmd: {
type: e.detail === 2 ? '__toggleMaximize' : 'startDragging'
}
} }
} }
}, }
_KEY_VALUE_ })
)
} }
}) })
window.__TAURI_INVOKE__( window.__TAURI_INVOKE__('tauri', {
'tauri', __tauriModule: 'Event',
{ message: {
__tauriModule: 'Event', cmd: 'listen',
message: { event: 'tauri://window-created',
cmd: 'listen', handler: window.__TAURI__.transformCallback(function (event) {
event: 'tauri://window-created', if (event.payload) {
handler: window.__TAURI__.transformCallback(function (event) { var windowLabel = event.payload.label
if (event.payload) { window.__TAURI__.__windows.push({
var windowLabel = event.payload.label label: windowLabel
window.__TAURI__.__windows.push({ })
label: windowLabel }
}) })
} }
}) })
}
},
_KEY_VALUE_
)
let permissionSettable = false let permissionSettable = false
let permissionValue = 'default' let permissionValue = 'default'
@ -174,16 +173,12 @@
if (window.Notification.permission !== 'default') { if (window.Notification.permission !== 'default') {
return Promise.resolve(window.Notification.permission === 'granted') return Promise.resolve(window.Notification.permission === 'granted')
} }
return window.__TAURI_INVOKE__( return window.__TAURI_INVOKE__('tauri', {
'tauri', __tauriModule: 'Notification',
{ message: {
__tauriModule: 'Notification', cmd: 'isNotificationPermissionGranted'
message: { }
cmd: 'isNotificationPermissionGranted' })
}
},
_KEY_VALUE_
)
} }
function setNotificationPermission(value) { function setNotificationPermission(value) {
@ -194,16 +189,12 @@
function requestPermission() { function requestPermission() {
return window return window
.__TAURI_INVOKE__( .__TAURI_INVOKE__('tauri', {
'tauri', __tauriModule: 'Notification',
{ message: {
__tauriModule: 'Notification', cmd: 'requestNotificationPermission'
message: { }
cmd: 'requestNotificationPermission' })
}
},
_KEY_VALUE_
)
.then(function (permission) { .then(function (permission) {
setNotificationPermission(permission) setNotificationPermission(permission)
return permission return permission
@ -215,20 +206,18 @@
Object.freeze(options) Object.freeze(options)
} }
return window.__TAURI_INVOKE__( return window.__TAURI_INVOKE__('tauri', {
'tauri', { __tauriModule: 'Notification',
__tauriModule: 'Notification', message: {
message: { cmd: 'notification',
cmd: 'notification', options:
options: typeof options === 'string' ? typeof options === 'string'
{ ? {
title: options title: options
} : }
options : options
} }
}, })
_KEY_VALUE_
)
} }
window.Notification = function (title, options) { window.Notification = function (title, options) {
@ -264,51 +253,39 @@
}) })
window.alert = function (message) { window.alert = function (message) {
window.__TAURI_INVOKE__( window.__TAURI_INVOKE__('tauri', {
'tauri', __tauriModule: 'Dialog',
{ message: {
__tauriModule: 'Dialog', cmd: 'messageDialog',
message: { message: message
cmd: 'messageDialog', }
message: message })
}
},
_KEY_VALUE_
)
} }
window.confirm = function (message) { window.confirm = function (message) {
return window.__TAURI_INVOKE__( return window.__TAURI_INVOKE__('tauri', {
'tauri', __tauriModule: 'Dialog',
{ message: {
__tauriModule: 'Dialog', cmd: 'confirmDialog',
message: { message: message
cmd: 'confirmDialog', }
message: message })
}
},
_KEY_VALUE_
)
} }
// window.print works on Linux/Windows; need to use the API on macOS // window.print works on Linux/Windows; need to use the API on macOS
if (navigator.userAgent.includes('Mac')) { if (navigator.userAgent.includes('Mac')) {
window.print = function () { window.print = function () {
return window.__TAURI_INVOKE__( return window.__TAURI_INVOKE__('tauri', {
'tauri', __tauriModule: 'Window',
{ message: {
__tauriModule: 'Window', cmd: 'manage',
message: { data: {
cmd: 'manage', cmd: {
data: { type: 'print'
cmd: {
type: 'print'
}
} }
} }
}, }
_KEY_VALUE_ })
)
} }
} }
})() })()

View File

@ -0,0 +1,5 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
Object.freeze(Object.prototype)

View File

@ -0,0 +1,26 @@
;(function () {
if (window.location.origin.startsWith(__TEMPLATE_origin__)) {
__RAW_freeze_prototype__
__RAW_pattern_script__
__RAW_ipc_script__
;(function () {
__RAW_bundle_script__
})()
__RAW_core_script__
__RAW_event_initialization_script__
if (window.ipc) {
window.__TAURI_INVOKE__('__initialized', { url: window.location.href })
} else {
window.addEventListener('DOMContentLoaded', function () {
window.__TAURI_INVOKE__('__initialized', { url: window.location.href })
})
}
__RAW_plugin_initialization_script__
}
})()

153
core/tauri/scripts/ipc.js Normal file
View File

@ -0,0 +1,153 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
/**
* @typedef {{callback: string, error: string, data: *}} IsolationPayload - a valid isolation payload
*/
;
(function () {
/**
* @type {string}
*/
const pattern = window.__TAURI_PATTERN__.pattern
/**
* @type {string}
*/
const isolationOrigin = __TEMPLATE_isolation_origin__
/**
* @type {{queue: object[], ready: boolean, frame: HTMLElement | null}}
*/
const isolation = Object.create(null)
isolation.queue = []
isolation.ready = false
isolation.frame = null
/**
* Detects if a message event is a valid isolation message.
*
* @param {MessageEvent<object>} event - a message event that is expected to be an isolation message
* @return {boolean} - if the event was a valid isolation message
*/
function isIsolationMessage(event) {
return (
typeof event.data === 'object' &&
'nonce' in event.data &&
'payload' in event.data
)
}
/**
* Detects if data is able to transform into an isolation payload.
*
* @param {object} data - object that is expected to contain at least a callback and error identifier
* @return {boolean} - if the data is able to transform into an isolation payload
*/
function isIsolationPayload(data) {
return typeof data === 'object' && 'callback' in data && 'error' in data
}
/**
* Sends a properly formatted message to the isolation frame.
*
* @param {IsolationPayload} data - data that has been validated to be an isolation payload
*/
function sendIsolationMessage(data) {
// set the frame dom element if it's not been set before
if (!isolation.frame) {
const frame = document.querySelector('iframe#__tauri_isolation__')
if (frame.src.startsWith(isolationOrigin)) {
isolation.frame = frame
} else {
console.error(
'Tauri IPC found an isolation iframe, but it had the wrong origin'
)
}
}
// ensure we have the target to send the message to
if (!isolation.frame || !isolation.frame.contentWindow) {
console.error(
'Tauri "Isolation" Pattern could not find the Isolation iframe window'
)
return
}
isolation.frame.contentWindow.postMessage(
data,
'*' /* todo: set this to the secure origin */
)
}
Object.defineProperty(window, '__TAURI_IPC__', {
// todo: JSDoc this function
value: Object.freeze((message) => {
switch (pattern) {
case 'brownfield':
window.__TAURI_POST_MESSAGE__(message)
break
case 'isolation':
if (!isIsolationPayload(message)) {
console.error(
'Tauri "Isolation" Pattern found an invalid isolation message payload',
message
)
break
}
if (isolation.ready) {
sendIsolationMessage(message)
} else {
isolation.queue.push(message)
}
break
case 'error':
console.error(
'Tauri IPC found a Tauri Pattern, but it was an error. Check for other log messages to find the cause.'
)
break
default:
console.error(
'Tauri IPC did not find a Tauri Pattern that it understood.'
)
break
}
})
})
/**
* IMPORTANT: See isolation_secure.js for the isolation frame implementation.
* main frame -> isolation frame = isolation payload
* isolation frame -> main frame = isolation message
*/
if (pattern === 'isolation') {
window.addEventListener(
'message',
(event) => {
// watch for the isolation frame being ready and flush any queued messages
if (event.data === '__TAURI_ISOLATION_READY__') {
isolation.ready = true
for (const message of isolation.queue) {
sendIsolationMessage(message)
}
isolation.queue = []
return
}
if (isIsolationMessage(event)) {
window.__TAURI_POST_MESSAGE__(event.data)
}
},
false
)
}
})()

View File

@ -0,0 +1,15 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
window.addEventListener('DOMContentLoaded', () => {
let style = document.createElement('style')
style.textContent = __TEMPLATE_style__
document.head.append(style)
let iframe = document.createElement('iframe')
iframe.id = '__tauri_isolation__'
iframe.sandbox.add('allow-scripts')
iframe.src = __TEMPLATE_isolation_src__
document.body.append(iframe)
})

View File

@ -0,0 +1,21 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
;(function () {
function __tauriDeepFreeze(object) {
const props = Object.getOwnPropertyNames(object)
for (const prop of props) {
if (typeof object[name] === 'object') {
__tauriDeepFreeze(object[name])
}
}
return Object.freeze(object)
}
Object.defineProperty(window, '__TAURI_PATTERN__', {
value: __tauriDeepFreeze(__TEMPLATE_pattern__)
})
})()

View File

@ -47,24 +47,28 @@ impl FileDialogBuilder {
} }
/// Add file extension filter. Takes in the name of the filter, and list of extensions /// Add file extension filter. Takes in the name of the filter, and list of extensions
#[must_use]
pub fn add_filter(mut self, name: impl AsRef<str>, extensions: &[&str]) -> Self { pub fn add_filter(mut self, name: impl AsRef<str>, extensions: &[&str]) -> Self {
self.0 = self.0.add_filter(name.as_ref(), extensions); self.0 = self.0.add_filter(name.as_ref(), extensions);
self self
} }
/// Set starting directory of the dialog. /// Set starting directory of the dialog.
#[must_use]
pub fn set_directory<P: AsRef<Path>>(mut self, directory: P) -> Self { pub fn set_directory<P: AsRef<Path>>(mut self, directory: P) -> Self {
self.0 = self.0.set_directory(directory); self.0 = self.0.set_directory(directory);
self self
} }
/// Set starting file name of the dialog. /// Set starting file name of the dialog.
#[must_use]
pub fn set_file_name(mut self, file_name: &str) -> Self { pub fn set_file_name(mut self, file_name: &str) -> Self {
self.0 = self.0.set_file_name(file_name); self.0 = self.0.set_file_name(file_name);
self self
} }
/// Sets the parent window of the dialog. /// Sets the parent window of the dialog.
#[must_use]
pub fn set_parent<W: raw_window_handle::HasRawWindowHandle>(mut self, parent: &W) -> Self { pub fn set_parent<W: raw_window_handle::HasRawWindowHandle>(mut self, parent: &W) -> Self {
self.0 = self.0.set_parent(parent); self.0 = self.0.set_parent(parent);
self self

View File

@ -29,12 +29,14 @@ impl ClientBuilder {
} }
/// Sets the maximum number of redirections. /// Sets the maximum number of redirections.
#[must_use]
pub fn max_redirections(mut self, max_redirections: usize) -> Self { pub fn max_redirections(mut self, max_redirections: usize) -> Self {
self.max_redirections = Some(max_redirections); self.max_redirections = Some(max_redirections);
self self
} }
/// Sets the connection timeout. /// Sets the connection timeout.
#[must_use]
pub fn connect_timeout(mut self, connect_timeout: u64) -> Self { pub fn connect_timeout(mut self, connect_timeout: u64) -> Self {
self.connect_timeout = Some(connect_timeout); self.connect_timeout = Some(connect_timeout);
self self
@ -294,30 +296,35 @@ impl HttpRequestBuilder {
} }
/// Sets the request parameters. /// Sets the request parameters.
#[must_use]
pub fn query(mut self, query: HashMap<String, String>) -> Self { pub fn query(mut self, query: HashMap<String, String>) -> Self {
self.query = Some(query); self.query = Some(query);
self self
} }
/// Sets the request headers. /// Sets the request headers.
#[must_use]
pub fn headers(mut self, headers: HashMap<String, String>) -> Self { pub fn headers(mut self, headers: HashMap<String, String>) -> Self {
self.headers = Some(headers); self.headers = Some(headers);
self self
} }
/// Sets the request body. /// Sets the request body.
#[must_use]
pub fn body(mut self, body: Body) -> Self { pub fn body(mut self, body: Body) -> Self {
self.body = Some(body); self.body = Some(body);
self self
} }
/// Sets the general request timeout. /// Sets the general request timeout.
#[must_use]
pub fn timeout(mut self, timeout: u64) -> Self { pub fn timeout(mut self, timeout: u64) -> Self {
self.timeout = Some(timeout); self.timeout = Some(timeout);
self self
} }
/// Sets the type of the response. Interferes with the way we read the response. /// Sets the type of the response. Interferes with the way we read the response.
#[must_use]
pub fn response_type(mut self, response_type: ResponseType) -> Self { pub fn response_type(mut self, response_type: ResponseType) -> Self {
self.response_type = Some(response_type); self.response_type = Some(response_type);
self self

View File

@ -43,18 +43,21 @@ impl Notification {
} }
/// Sets the notification body. /// Sets the notification body.
#[must_use]
pub fn body(mut self, body: impl Into<String>) -> Self { pub fn body(mut self, body: impl Into<String>) -> Self {
self.body = Some(body.into()); self.body = Some(body.into());
self self
} }
/// Sets the notification title. /// Sets the notification title.
#[must_use]
pub fn title(mut self, title: impl Into<String>) -> Self { pub fn title(mut self, title: impl Into<String>) -> Self {
self.title = Some(title.into()); self.title = Some(title.into());
self self
} }
/// Sets the notification icon. /// Sets the notification icon.
#[must_use]
pub fn icon(mut self, icon: impl Into<String>) -> Self { pub fn icon(mut self, icon: impl Into<String>) -> Self {
self.icon = Some(icon.into()); self.icon = Some(icon.into());
self self

View File

@ -134,8 +134,7 @@ pub fn parse<P: AsRef<Path>>(
let mut p = PathBuf::new(); let mut p = PathBuf::new();
let mut components = path.as_ref().components(); let mut components = path.as_ref().components();
if let Some(Component::Normal(str)) = components.next() { if let Some(Component::Normal(str)) = components.next() {
if let Some(base_directory) = BaseDirectory::from_variable(&str.to_string_lossy().into_owned()) if let Some(base_directory) = BaseDirectory::from_variable(&str.to_string_lossy()) {
{
p.push(resolve_path( p.push(resolve_path(
config, config,
package_info, package_info,

View File

@ -192,6 +192,7 @@ impl Command {
} }
/// Appends arguments to the command. /// Appends arguments to the command.
#[must_use]
pub fn args<I, S>(mut self, args: I) -> Self pub fn args<I, S>(mut self, args: I) -> Self
where where
I: IntoIterator<Item = S>, I: IntoIterator<Item = S>,
@ -204,18 +205,21 @@ impl Command {
} }
/// Clears the entire environment map for the child process. /// Clears the entire environment map for the child process.
#[must_use]
pub fn env_clear(mut self) -> Self { pub fn env_clear(mut self) -> Self {
self.env_clear = true; self.env_clear = true;
self self
} }
/// Adds or updates multiple environment variable mappings. /// Adds or updates multiple environment variable mappings.
#[must_use]
pub fn envs(mut self, env: HashMap<String, String>) -> Self { pub fn envs(mut self, env: HashMap<String, String>) -> Self {
self.env = env; self.env = env;
self self
} }
/// Sets the working directory for the child process. /// Sets the working directory for the child process.
#[must_use]
pub fn current_dir(mut self, current_dir: PathBuf) -> Self { pub fn current_dir(mut self, current_dir: PathBuf) -> Self {
self.current_dir.replace(current_dir); self.current_dir.replace(current_dir);
self self

View File

@ -36,7 +36,6 @@ impl FromStr for Program {
type Err = super::Error; type Err = super::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
#[allow(clippy::match_str_case_mismatch)]
let p = match s.to_lowercase().as_str() { let p = match s.to_lowercase().as_str() {
"open" => Self::Open, "open" => Self::Open,
"start" => Self::Start, "start" => Self::Start,

View File

@ -676,7 +676,7 @@ impl<R: Runtime> Builder<R> {
invoke_handler: Box::new(|_| ()), invoke_handler: Box::new(|_| ()),
invoke_responder: Arc::new(window_invoke_responder), invoke_responder: Arc::new(window_invoke_responder),
invoke_initialization_script: invoke_initialization_script:
"Object.defineProperty(window, '__TAURI_POST_MESSAGE__', { value: (command, args) => window.ipc.notify(JSON.stringify({{ ...args, command }})) })".into(), "Object.defineProperty(window, '__TAURI_POST_MESSAGE__', { value: (message) => window.ipc.postMessage(JSON.stringify(message)) })".into(),
on_page_load: Box::new(|_, _| ()), on_page_load: Box::new(|_, _| ()),
pending_windows: Default::default(), pending_windows: Default::default(),
plugins: PluginStore::default(), plugins: PluginStore::default(),
@ -693,6 +693,7 @@ impl<R: Runtime> Builder<R> {
} }
/// Defines the JS message handler callback. /// Defines the JS message handler callback.
#[must_use]
pub fn invoke_handler<F>(mut self, invoke_handler: F) -> Self pub fn invoke_handler<F>(mut self, invoke_handler: F) -> Self
where where
F: Fn(Invoke<R>) + Send + Sync + 'static, F: Fn(Invoke<R>) + Send + Sync + 'static,
@ -706,7 +707,7 @@ impl<R: Runtime> Builder<R> {
/// The `responder` is a function that will be called when a command has been executed and must send a response to the JS layer. /// The `responder` is a function that will be called when a command has been executed and must send a response to the JS layer.
/// ///
/// The `initialization_script` is a script that initializes `window.__TAURI_POST_MESSAGE__`. /// The `initialization_script` is a script that initializes `window.__TAURI_POST_MESSAGE__`.
/// That function must take the `command: string` and `args: object` types and send a message to the backend. /// That function must take the `message: object` argument and send it to the backend.
pub fn invoke_system<F>(mut self, initialization_script: String, responder: F) -> Self pub fn invoke_system<F>(mut self, initialization_script: String, responder: F) -> Self
where where
F: Fn(Window<R>, InvokeResponse, CallbackFn, CallbackFn) + Send + Sync + 'static, F: Fn(Window<R>, InvokeResponse, CallbackFn, CallbackFn) + Send + Sync + 'static,
@ -717,6 +718,7 @@ impl<R: Runtime> Builder<R> {
} }
/// Defines the setup hook. /// Defines the setup hook.
#[must_use]
pub fn setup<F>(mut self, setup: F) -> Self pub fn setup<F>(mut self, setup: F) -> Self
where where
F: FnOnce(&mut App<R>) -> Result<(), Box<dyn std::error::Error + Send>> + Send + 'static, F: FnOnce(&mut App<R>) -> Result<(), Box<dyn std::error::Error + Send>> + Send + 'static,
@ -726,6 +728,7 @@ impl<R: Runtime> Builder<R> {
} }
/// Defines the page load hook. /// Defines the page load hook.
#[must_use]
pub fn on_page_load<F>(mut self, on_page_load: F) -> Self pub fn on_page_load<F>(mut self, on_page_load: F) -> Self
where where
F: Fn(Window<R>, PageLoadPayload) + Send + Sync + 'static, F: Fn(Window<R>, PageLoadPayload) + Send + Sync + 'static,
@ -735,6 +738,7 @@ impl<R: Runtime> Builder<R> {
} }
/// Adds a plugin to the runtime. /// Adds a plugin to the runtime.
#[must_use]
pub fn plugin<P: Plugin<R> + 'static>(mut self, plugin: P) -> Self { pub fn plugin<P: Plugin<R> + 'static>(mut self, plugin: P) -> Self {
self.plugins.register(plugin); self.plugins.register(plugin);
self self
@ -815,6 +819,7 @@ impl<R: Runtime> Builder<R> {
/// .expect("error while running tauri application"); /// .expect("error while running tauri application");
/// } /// }
/// ``` /// ```
#[must_use]
pub fn manage<T>(self, state: T) -> Self pub fn manage<T>(self, state: T) -> Self
where where
T: Send + Sync + 'static, T: Send + Sync + 'static,
@ -829,6 +834,7 @@ impl<R: Runtime> Builder<R> {
} }
/// Creates a new webview window. /// Creates a new webview window.
#[must_use]
pub fn create_window<F>(mut self, label: impl Into<String>, url: WindowUrl, setup: F) -> Self pub fn create_window<F>(mut self, label: impl Into<String>, url: WindowUrl, setup: F) -> Self
where where
F: FnOnce( F: FnOnce(
@ -854,18 +860,21 @@ impl<R: Runtime> Builder<R> {
/// Adds the icon configured on `tauri.conf.json` to the system tray with the specified menu items. /// Adds the icon configured on `tauri.conf.json` to the system tray with the specified menu items.
#[cfg(feature = "system-tray")] #[cfg(feature = "system-tray")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "system-tray")))] #[cfg_attr(doc_cfg, doc(cfg(feature = "system-tray")))]
#[must_use]
pub fn system_tray(mut self, system_tray: tray::SystemTray) -> Self { pub fn system_tray(mut self, system_tray: tray::SystemTray) -> Self {
self.system_tray.replace(system_tray); self.system_tray.replace(system_tray);
self self
} }
/// Sets the menu to use on all windows. /// Sets the menu to use on all windows.
#[must_use]
pub fn menu(mut self, menu: Menu) -> Self { pub fn menu(mut self, menu: Menu) -> Self {
self.menu.replace(menu); self.menu.replace(menu);
self self
} }
/// Registers a menu event handler for all windows. /// Registers a menu event handler for all windows.
#[must_use]
pub fn on_menu_event<F: Fn(WindowMenuEvent<R>) + Send + Sync + 'static>( pub fn on_menu_event<F: Fn(WindowMenuEvent<R>) + Send + Sync + 'static>(
mut self, mut self,
handler: F, handler: F,
@ -875,6 +884,7 @@ impl<R: Runtime> Builder<R> {
} }
/// Registers a window event handler for all windows. /// Registers a window event handler for all windows.
#[must_use]
pub fn on_window_event<F: Fn(GlobalWindowEvent<R>) + Send + Sync + 'static>( pub fn on_window_event<F: Fn(GlobalWindowEvent<R>) + Send + Sync + 'static>(
mut self, mut self,
handler: F, handler: F,
@ -886,6 +896,7 @@ impl<R: Runtime> Builder<R> {
/// Registers a system tray event handler. /// Registers a system tray event handler.
#[cfg(feature = "system-tray")] #[cfg(feature = "system-tray")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "system-tray")))] #[cfg_attr(doc_cfg, doc(cfg(feature = "system-tray")))]
#[must_use]
pub fn on_system_tray_event< pub fn on_system_tray_event<
F: Fn(&AppHandle<R>, tray::SystemTrayEvent) + Send + Sync + 'static, F: Fn(&AppHandle<R>, tray::SystemTrayEvent) + Send + Sync + 'static,
>( >(
@ -905,6 +916,7 @@ impl<R: Runtime> Builder<R> {
/// ///
/// * `uri_scheme` The URI scheme to register, such as `example`. /// * `uri_scheme` The URI scheme to register, such as `example`.
/// * `protocol` the protocol associated with the given URI scheme. It's a function that takes an URL such as `example://localhost/asset.css`. /// * `protocol` the protocol associated with the given URI scheme. It's a function that takes an URL such as `example://localhost/asset.css`.
#[must_use]
pub fn register_uri_scheme_protocol< pub fn register_uri_scheme_protocol<
N: Into<String>, N: Into<String>,
H: Fn(&AppHandle<R>, &HttpRequest) -> Result<HttpResponse, Box<dyn std::error::Error>> H: Fn(&AppHandle<R>, &HttpRequest) -> Result<HttpResponse, Box<dyn std::error::Error>>
@ -1245,16 +1257,16 @@ impl Default for Builder<crate::Wry> {
mod tests { mod tests {
#[test] #[test]
fn is_send_sync() { fn is_send_sync() {
crate::test::assert_send::<super::AppHandle>(); crate::test_utils::assert_send::<super::AppHandle>();
crate::test::assert_sync::<super::AppHandle>(); crate::test_utils::assert_sync::<super::AppHandle>();
#[cfg(feature = "wry")] #[cfg(feature = "wry")]
{ {
crate::test::assert_send::<super::AssetResolver<crate::Wry>>(); crate::test_utils::assert_send::<super::AssetResolver<crate::Wry>>();
crate::test::assert_sync::<super::AssetResolver<crate::Wry>>(); crate::test_utils::assert_sync::<super::AssetResolver<crate::Wry>>();
} }
crate::test::assert_send::<super::PathResolver>(); crate::test_utils::assert_send::<super::PathResolver>();
crate::test::assert_sync::<super::PathResolver>(); crate::test_utils::assert_sync::<super::PathResolver>();
} }
} }

View File

@ -279,6 +279,7 @@ where
runtime.spawn_blocking(func) runtime.spawn_blocking(func)
} }
#[allow(dead_code)]
pub(crate) fn safe_block_on<F>(task: F) -> F::Output pub(crate) fn safe_block_on<F>(task: F) -> F::Output
where where
F: Future + Send + 'static, F: Future + Send + 'static,

View File

@ -19,7 +19,7 @@ impl Cmd {
#[module_command_handler(cli, "CLI definition not set under tauri.conf.json > tauri > cli (https://tauri.studio/docs/api/config#tauri.cli)")] #[module_command_handler(cli, "CLI definition not set under tauri.conf.json > tauri > cli (https://tauri.studio/docs/api/config#tauri.cli)")]
fn cli_matches<R: Runtime>(context: InvokeContext<R>) -> crate::Result<InvokeResponse> { fn cli_matches<R: Runtime>(context: InvokeContext<R>) -> crate::Result<InvokeResponse> {
if let Some(cli) = &context.config.tauri.cli { if let Some(cli) = &context.config.tauri.cli {
crate::api::cli::get_matches(cli, context.package_info) crate::api::cli::get_matches(cli, &context.package_info)
.map(Into::into) .map(Into::into)
.map_err(Into::into) .map_err(Into::into)
} else { } else {

View File

@ -15,6 +15,7 @@ use serde::{
}; };
use tauri_macros::{module_command_handler, CommandModule}; use tauri_macros::{module_command_handler, CommandModule};
use std::fmt::{Debug, Formatter};
use std::{ use std::{
fs, fs,
fs::File, fs::File,
@ -334,8 +335,21 @@ fn resolve_path<R: Runtime>(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{BaseDirectory, DirOperationOptions, FileOperationOptions, SafePathBuf}; use super::{BaseDirectory, DirOperationOptions, FileOperationOptions, SafePathBuf};
use quickcheck::{Arbitrary, Gen}; use quickcheck::{Arbitrary, Gen};
use std::path::PathBuf;
impl Arbitrary for super::SafePathBuf {
fn arbitrary(g: &mut Gen) -> Self {
Self(PathBuf::arbitrary(g))
}
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
Box::new(self.0.shrink().map(SafePathBuf))
}
}
impl Arbitrary for BaseDirectory { impl Arbitrary for BaseDirectory {
fn arbitrary(g: &mut Gen) -> Self { fn arbitrary(g: &mut Gen) -> Self {
if bool::arbitrary(g) { if bool::arbitrary(g) {
@ -363,12 +377,6 @@ mod tests {
} }
} }
impl Arbitrary for SafePathBuf {
fn arbitrary(g: &mut Gen) -> Self {
SafePathBuf(std::path::PathBuf::arbitrary(g))
}
}
#[tauri_macros::module_command_test(fs_read_file, "fs > readFile")] #[tauri_macros::module_command_test(fs_read_file, "fs > readFile")]
#[quickcheck_macros::quickcheck] #[quickcheck_macros::quickcheck]
fn read_file(path: SafePathBuf, options: Option<FileOperationOptions>) { fn read_file(path: SafePathBuf, options: Option<FileOperationOptions>) {

View File

@ -96,6 +96,10 @@ pub enum Error {
/// Program not allowed by the scope. /// Program not allowed by the scope.
#[error("program not allowed on the configured shell scope: {0}")] #[error("program not allowed on the configured shell scope: {0}")]
ProgramNotAllowed(PathBuf), ProgramNotAllowed(PathBuf),
/// An error happened inside the isolation pattern.
#[cfg(feature = "isolation")]
#[error("isolation pattern error: {0}")]
IsolationPattern(#[from] tauri_utils::pattern::isolation::Error),
} }
impl From<serde_json::Error> for Error { impl From<serde_json::Error> for Error {

View File

@ -10,6 +10,7 @@ use crate::{
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::Value as JsonValue; use serde_json::Value as JsonValue;
use serialize_to_javascript::{default_template, Template};
use std::{future::Future, sync::Arc}; use std::{future::Future, sync::Arc};
use tauri_macros::default_runtime; use tauri_macros::default_runtime;
@ -28,6 +29,21 @@ pub type InvokeResponder<R> =
/// A closure that is run once every time a window is created and loaded. /// A closure that is run once every time a window is created and loaded.
pub type OnPageLoad<R> = dyn Fn(Window<R>, PageLoadPayload) + Send + Sync + 'static; pub type OnPageLoad<R> = dyn Fn(Window<R>, PageLoadPayload) + Send + Sync + 'static;
// todo: why is this derive broken but the output works manually?
#[derive(Template)]
#[default_template("../scripts/ipc.js")]
pub(crate) struct IpcJavascript<'a> {
pub(crate) isolation_origin: &'a str,
}
#[cfg(feature = "isolation")]
#[derive(Template)]
#[default_template("../scripts/isolation.js")]
pub(crate) struct IsolationJavascript<'a> {
pub(crate) isolation_src: &'a str,
pub(crate) style: &'a str,
}
/// The payload for the [`OnPageLoad`] hook. /// The payload for the [`OnPageLoad`] hook.
#[derive(Debug, Clone, Deserialize)] #[derive(Debug, Clone, Deserialize)]
pub struct PageLoadPayload { pub struct PageLoadPayload {
@ -45,7 +61,7 @@ impl PageLoadPayload {
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
pub struct InvokePayload { pub struct InvokePayload {
/// The invoke command. /// The invoke command.
pub command: String, pub cmd: String,
#[serde(rename = "__tauriModule")] #[serde(rename = "__tauriModule")]
#[doc(hidden)] #[doc(hidden)]
pub tauri_module: Option<String>, pub tauri_module: Option<String>,
@ -53,9 +69,6 @@ pub struct InvokePayload {
pub callback: CallbackFn, pub callback: CallbackFn,
/// The error callback. /// The error callback.
pub error: CallbackFn, pub error: CallbackFn,
/// The invoke key.
#[serde(rename = "__invokeKey")]
pub key: u32,
/// The payload of the message. /// The payload of the message.
#[serde(flatten)] #[serde(flatten)]
pub inner: JsonValue, pub inner: JsonValue,

View File

@ -11,6 +11,7 @@
//! The following are a list of [Cargo features](https://doc.rust-lang.org/stable/cargo/reference/manifest.html#the-features-section) that can be enabled or disabled: //! The following are a list of [Cargo features](https://doc.rust-lang.org/stable/cargo/reference/manifest.html#the-features-section) that can be enabled or disabled:
//! //!
//! - **wry** *(enabled by default)*: Enables the [wry](https://github.com/tauri-apps/wry) runtime. Only disable it if you want a custom runtime. //! - **wry** *(enabled by default)*: Enables the [wry](https://github.com/tauri-apps/wry) runtime. Only disable it if you want a custom runtime.
//! - **isolation**: Enables the isolation pattern. Enabled by default if the `tauri > pattern > use` config option is set to `isolation` on the `tauri.conf.json` file.
//! - **custom-protocol**: Feature managed by the Tauri CLI. When enabled, Tauri assumes a production environment instead of a development one. //! - **custom-protocol**: Feature managed by the Tauri CLI. When enabled, Tauri assumes a production environment instead of a development one.
//! - **updater**: Enables the application auto updater. Enabled by default if the `updater` config is defined on the `tauri.conf.json` file. //! - **updater**: Enables the application auto updater. Enabled by default if the `updater` config is defined on the `tauri.conf.json` file.
//! - **http-api**: Enables the [`api::http`] module. //! - **http-api**: Enables the [`api::http`] module.
@ -144,6 +145,7 @@ mod error;
mod event; mod event;
mod hooks; mod hooks;
mod manager; mod manager;
mod pattern;
pub mod plugin; pub mod plugin;
pub mod window; pub mod window;
pub use tauri_runtime as runtime; pub use tauri_runtime as runtime;
@ -255,6 +257,8 @@ macro_rules! tauri_build_context {
}; };
} }
pub use pattern::Pattern;
/// User supplied data required inside of a Tauri application. /// User supplied data required inside of a Tauri application.
/// ///
/// # Stability /// # Stability
@ -267,6 +271,7 @@ pub struct Context<A: Assets> {
pub(crate) system_tray_icon: Option<Icon>, pub(crate) system_tray_icon: Option<Icon>,
pub(crate) package_info: PackageInfo, pub(crate) package_info: PackageInfo,
pub(crate) _info_plist: (), pub(crate) _info_plist: (),
pub(crate) pattern: Pattern,
} }
impl<A: Assets> fmt::Debug for Context<A> { impl<A: Assets> fmt::Debug for Context<A> {
@ -276,6 +281,7 @@ impl<A: Assets> fmt::Debug for Context<A> {
.field("default_window_icon", &self.default_window_icon) .field("default_window_icon", &self.default_window_icon)
.field("system_tray_icon", &self.system_tray_icon) .field("system_tray_icon", &self.system_tray_icon)
.field("package_info", &self.package_info) .field("package_info", &self.package_info)
.field("pattern", &self.pattern)
.finish() .finish()
} }
} }
@ -341,6 +347,12 @@ impl<A: Assets> Context<A> {
&mut self.package_info &mut self.package_info
} }
/// The application pattern.
#[inline(always)]
pub fn pattern(&self) -> &Pattern {
&self.pattern
}
/// Create a new [`Context`] from the minimal required items. /// Create a new [`Context`] from the minimal required items.
#[inline(always)] #[inline(always)]
pub fn new( pub fn new(
@ -350,6 +362,7 @@ impl<A: Assets> Context<A> {
system_tray_icon: Option<Icon>, system_tray_icon: Option<Icon>,
package_info: PackageInfo, package_info: PackageInfo,
info_plist: (), info_plist: (),
pattern: Pattern,
) -> Self { ) -> Self {
Self { Self {
config, config,
@ -358,6 +371,7 @@ impl<A: Assets> Context<A> {
system_tray_icon, system_tray_icon,
package_info, package_info,
_info_plist: info_plist, _info_plist: info_plist,
pattern,
} }
} }
} }
@ -503,7 +517,7 @@ pub(crate) mod sealed {
pub mod test; pub mod test;
#[cfg(test)] #[cfg(test)]
mod tests { mod test_utils {
use proptest::prelude::*; use proptest::prelude::*;
pub fn assert_send<T: Send>() {} pub fn assert_send<T: Send>() {}

View File

@ -2,6 +2,35 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
use std::{
borrow::Cow,
collections::{HashMap, HashSet},
fmt,
fs::create_dir_all,
sync::{Arc, Mutex, MutexGuard},
};
use regex::{Captures, Regex};
use serde::Serialize;
use serde_json::Value as JsonValue;
use serialize_to_javascript::{default_template, DefaultTemplate, Template};
use url::Url;
use tauri_macros::default_runtime;
#[cfg(feature = "isolation")]
use tauri_utils::pattern::isolation::RawIsolationPayload;
use tauri_utils::{
assets::{AssetKey, CspHash},
html::{inject_csp, parse as parse_html, SCRIPT_NONCE_TOKEN, STYLE_NONCE_TOKEN},
};
#[cfg(target_os = "windows")]
use crate::api::path::{resolve_path, BaseDirectory};
use crate::app::{GlobalMenuEventListener, WindowMenuEvent};
use crate::hooks::IpcJavascript;
#[cfg(feature = "isolation")]
use crate::hooks::IsolationJavascript;
use crate::pattern::{format_real_schema, PatternJavascript};
use crate::{ use crate::{
app::{AppHandle, GlobalWindowEvent, GlobalWindowEventListener}, app::{AppHandle, GlobalWindowEvent, GlobalWindowEventListener},
event::{is_event_name_valid, Event, EventHandler, Listeners}, event::{is_event_name_valid, Event, EventHandler, Listeners},
@ -21,36 +50,14 @@ use crate::{
config::{AppUrl, Config, WindowUrl}, config::{AppUrl, Config, WindowUrl},
PackageInfo, PackageInfo,
}, },
Context, Invoke, StateManager, Window, Context, Invoke, Pattern, StateManager, Window,
}; };
#[cfg(any(target_os = "linux", target_os = "windows"))] #[cfg(any(target_os = "linux", target_os = "windows"))]
use crate::api::path::{resolve_path, BaseDirectory}; use crate::api::path::{resolve_path, BaseDirectory};
use crate::app::{GlobalMenuEventListener, WindowMenuEvent};
use crate::{runtime::menu::Menu, MenuEvent}; use crate::{runtime::menu::Menu, MenuEvent};
use regex::{Captures, Regex};
use serde::Serialize;
use serde_json::Value as JsonValue;
use std::{
borrow::Cow,
collections::{HashMap, HashSet},
fmt,
fs::create_dir_all,
sync::{Arc, Mutex, MutexGuard},
};
use tauri_macros::default_runtime;
use tauri_utils::{
assets::{AssetKey, CspHash},
html::{
inject_csp, parse as parse_html, CSP_TOKEN, INVOKE_KEY_TOKEN, SCRIPT_NONCE_TOKEN,
STYLE_NONCE_TOKEN,
},
};
use url::Url;
const WINDOW_RESIZED_EVENT: &str = "tauri://resize"; const WINDOW_RESIZED_EVENT: &str = "tauri://resize";
const WINDOW_MOVED_EVENT: &str = "tauri://move"; const WINDOW_MOVED_EVENT: &str = "tauri://move";
const WINDOW_CLOSE_REQUESTED_EVENT: &str = "tauri://close-requested"; const WINDOW_CLOSE_REQUESTED_EVENT: &str = "tauri://close-requested";
@ -67,6 +74,71 @@ struct CspHashStrings {
style: String, style: String,
} }
/// Sets the CSP value to the asset HTML if needed (on Linux).
/// Returns the CSP string for access on the response header (on Windows and macOS).
fn set_csp<R: Runtime>(
asset: &mut String,
assets: Arc<dyn Assets>,
asset_path: &AssetKey,
#[allow(unused_variables)] manager: &WindowManager<R>,
mut csp: String,
) -> String {
let hash_strings =
assets
.csp_hashes(asset_path)
.fold(CspHashStrings::default(), |mut acc, hash| {
match hash {
CspHash::Script(hash) => {
acc.script.push(' ');
acc.script.push_str(hash);
}
CspHash::Style(hash) => {
acc.style.push(' ');
acc.style.push_str(hash);
}
_csp_hash => {
#[cfg(debug_assertions)]
eprintln!("Unknown CspHash variant encountered: {:?}", _csp_hash)
}
}
acc
});
replace_csp_nonce(
asset,
SCRIPT_NONCE_TOKEN,
&mut csp,
"script-src",
hash_strings.script,
);
replace_csp_nonce(
asset,
STYLE_NONCE_TOKEN,
&mut csp,
"style-src",
hash_strings.style,
);
#[cfg(feature = "isolation")]
if let Pattern::Isolation { schema, .. } = &manager.inner.pattern {
let default_src = format!("default-src {}", format_real_schema(schema));
if csp.contains("default-src") {
csp = csp.replace("default-src", &default_src);
} else {
csp.push_str("; ");
csp.push_str(&default_src);
}
}
#[cfg(target_os = "linux")]
{
*asset = asset.replacen(tauri_utils::html::CSP_TOKEN, &csp, 1);
}
csp
}
fn replace_csp_nonce( fn replace_csp_nonce(
asset: &mut String, asset: &mut String,
token: &str, token: &str,
@ -141,6 +213,8 @@ pub struct InnerWindowManager<R: Runtime> {
invoke_responder: Arc<InvokeResponder<R>>, invoke_responder: Arc<InvokeResponder<R>>,
/// The script that initializes the invoke system. /// The script that initializes the invoke system.
invoke_initialization_script: String, invoke_initialization_script: String,
/// Application pattern.
pattern: Pattern,
} }
impl<R: Runtime> fmt::Debug for InnerWindowManager<R> { impl<R: Runtime> fmt::Debug for InnerWindowManager<R> {
@ -152,6 +226,7 @@ impl<R: Runtime> fmt::Debug for InnerWindowManager<R> {
.field("default_window_icon", &self.default_window_icon) .field("default_window_icon", &self.default_window_icon)
.field("package_info", &self.package_info) .field("package_info", &self.package_info)
.field("menu", &self.menu) .field("menu", &self.menu)
.field("pattern", &self.pattern)
.finish() .finish()
} }
} }
@ -181,14 +256,12 @@ pub struct CustomProtocol<R: Runtime> {
#[derive(Debug)] #[derive(Debug)]
pub struct WindowManager<R: Runtime> { pub struct WindowManager<R: Runtime> {
pub inner: Arc<InnerWindowManager<R>>, pub inner: Arc<InnerWindowManager<R>>,
invoke_keys: Arc<Mutex<Vec<u32>>>,
} }
impl<R: Runtime> Clone for WindowManager<R> { impl<R: Runtime> Clone for WindowManager<R> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self {
inner: self.inner.clone(), inner: self.inner.clone(),
invoke_keys: self.invoke_keys.clone(),
} }
} }
} }
@ -196,7 +269,7 @@ impl<R: Runtime> Clone for WindowManager<R> {
impl<R: Runtime> WindowManager<R> { impl<R: Runtime> WindowManager<R> {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub(crate) fn with_handlers( pub(crate) fn with_handlers(
context: Context<impl Assets>, #[allow(unused_mut)] mut context: Context<impl Assets>,
plugins: PluginStore<R>, plugins: PluginStore<R>,
invoke_handler: Box<InvokeHandler<R>>, invoke_handler: Box<InvokeHandler<R>>,
on_page_load: Box<OnPageLoad<R>>, on_page_load: Box<OnPageLoad<R>>,
@ -206,6 +279,12 @@ impl<R: Runtime> WindowManager<R> {
(menu, menu_event_listeners): (Option<Menu>, Vec<GlobalMenuEventListener<R>>), (menu, menu_event_listeners): (Option<Menu>, Vec<GlobalMenuEventListener<R>>),
(invoke_responder, invoke_initialization_script): (Arc<InvokeResponder<R>>, String), (invoke_responder, invoke_initialization_script): (Arc<InvokeResponder<R>>, String),
) -> Self { ) -> Self {
// generate a random isolation key at runtime
#[cfg(feature = "isolation")]
if let Pattern::Isolation { ref mut key, .. } = &mut context.pattern {
*key = uuid::Uuid::new_v4().to_string();
}
Self { Self {
inner: Arc::new(InnerWindowManager { inner: Arc::new(InnerWindowManager {
windows: Mutex::default(), windows: Mutex::default(),
@ -218,6 +297,7 @@ impl<R: Runtime> WindowManager<R> {
assets: context.assets, assets: context.assets,
default_window_icon: context.default_window_icon, default_window_icon: context.default_window_icon,
package_info: context.package_info, package_info: context.package_info,
pattern: context.pattern,
uri_scheme_protocols, uri_scheme_protocols,
menu, menu,
menu_event_listeners: Arc::new(menu_event_listeners), menu_event_listeners: Arc::new(menu_event_listeners),
@ -225,10 +305,13 @@ impl<R: Runtime> WindowManager<R> {
invoke_responder, invoke_responder,
invoke_initialization_script, invoke_initialization_script,
}), }),
invoke_keys: Default::default(),
} }
} }
pub(crate) fn pattern(&self) -> &Pattern {
&self.inner.pattern
}
/// Get a locked handle to the windows. /// Get a locked handle to the windows.
pub(crate) fn windows_lock(&self) -> MutexGuard<'_, HashMap<String, Window<R>>> { pub(crate) fn windows_lock(&self) -> MutexGuard<'_, HashMap<String, Window<R>>> {
self.inner.windows.lock().expect("poisoned window manager") self.inner.windows.lock().expect("poisoned window manager")
@ -268,10 +351,18 @@ impl<R: Runtime> WindowManager<R> {
} }
} }
fn generate_invoke_key(&self) -> u32 { /// Get the origin as it will be seen in the webview.
let key = rand::random(); fn get_browser_origin(&self) -> Cow<'_, str> {
self.invoke_keys.lock().unwrap().push(key); match self.base_path() {
key AppUrl::Url(WindowUrl::External(url)) => {
let mut url = url.to_string();
if url.ends_with('/') {
url.pop();
}
Cow::Owned(url)
}
_ => Cow::Owned(format_real_schema("tauri")),
}
} }
fn csp(&self) -> Option<String> { fn csp(&self) -> Option<String> {
@ -289,13 +380,6 @@ impl<R: Runtime> WindowManager<R> {
} }
} }
/// Checks whether the invoke key is valid or not.
///
/// An invoke key is valid if it was generated by this manager instance.
pub(crate) fn verify_invoke_key(&self, key: u32) -> bool {
self.invoke_keys.lock().unwrap().contains(&key)
}
fn prepare_pending_window( fn prepare_pending_window(
&self, &self,
mut pending: PendingWindow<R>, mut pending: PendingWindow<R>,
@ -311,39 +395,48 @@ impl<R: Runtime> WindowManager<R> {
.expect("poisoned plugin store") .expect("poisoned plugin store")
.initialization_script(); .initialization_script();
let mut webview_attributes = pending.webview_attributes; let pattern_init = PatternJavascript {
webview_attributes = pattern: self.pattern().into(),
webview_attributes.initialization_script(&self.inner.invoke_initialization_script);
if is_init_global {
webview_attributes = webview_attributes.initialization_script(&format!(
"(function () {{
const __TAURI_INVOKE_KEY__ = {key};
{bundle_script}
}})()",
key = self.generate_invoke_key(),
bundle_script = include_str!("../scripts/bundle.js"),
));
} }
.render_default(&Default::default())?;
let ipc_init = IpcJavascript {
isolation_origin: &match self.pattern() {
#[cfg(feature = "isolation")]
Pattern::Isolation { schema, .. } => crate::pattern::format_real_schema(schema),
_ => "".to_string(),
},
}
.render_default(&Default::default())?;
let mut webview_attributes = pending.webview_attributes;
webview_attributes = webview_attributes webview_attributes = webview_attributes
.initialization_script(&self.inner.invoke_initialization_script)
.initialization_script(&self.initialization_script(&ipc_init,&pattern_init,&plugin_init, is_init_global)?)
.initialization_script(&format!( .initialization_script(&format!(
r#" r#"
if (!window.__TAURI__) {{ if (!window.__TAURI__) {{
window.__TAURI__ = {{}} Object.defineProperty(window, '__TAURI__', {{
value: {{}}
}})
}} }}
window.__TAURI__.__windows = {window_labels_array}.map(function (label) {{ return {{ label: label }} }}); window.__TAURI__.__windows = {window_labels_array}.map(function (label) {{ return {{ label: label }} }});
window.__TAURI__.__currentWindow = {{ label: {current_window_label} }} window.__TAURI__.__currentWindow = {{ label: {current_window_label} }}
"#, "#,
window_labels_array = serde_json::to_string(pending_labels)?, window_labels_array = serde_json::to_string(pending_labels)?,
current_window_label = serde_json::to_string(&label)?, current_window_label = serde_json::to_string(&label)?,
))
.initialization_script(&self.initialization_script(&plugin_init));
#[cfg(dev)]
{
webview_attributes = webview_attributes.initialization_script(&format!(
"window.__TAURI_INVOKE_KEY__ = {}",
self.generate_invoke_key()
)); ));
#[cfg(feature = "isolation")]
if let Pattern::Isolation { schema, .. } = self.pattern() {
webview_attributes = webview_attributes.initialization_script(
&IsolationJavascript {
isolation_src: &crate::pattern::format_real_schema(schema),
style: tauri_utils::pattern::isolation::IFRAME_STYLE,
}
.render_default(&Default::default())?,
);
} }
pending.webview_attributes = webview_attributes; pending.webview_attributes = webview_attributes;
@ -492,14 +585,76 @@ impl<R: Runtime> WindowManager<R> {
}); });
} }
#[cfg(feature = "isolation")]
if let Pattern::Isolation {
assets,
schema,
key: _,
crypto_keys,
} = &self.inner.pattern
{
let assets = assets.clone();
let schema_ = schema.clone();
let url_base = format!("{}://localhost", schema_);
let aes_gcm_key = *crypto_keys.aes_gcm().raw();
pending.register_uri_scheme_protocol(schema, move |request| {
match request_to_path(request, &url_base).as_str() {
"index.html" => match assets.get(&"index.html".into()) {
Some(asset) => {
let asset = String::from_utf8_lossy(asset.as_ref());
let template = tauri_utils::pattern::isolation::IsolationJavascriptRuntime {
runtime_aes_gcm_key: &aes_gcm_key,
};
match template.render(asset.as_ref(), &Default::default()) {
Ok(asset) => HttpResponseBuilder::new()
.mimetype("text/html")
.body(asset.as_bytes().to_vec()),
Err(_) => HttpResponseBuilder::new()
.status(500)
.mimetype("text/plain")
.body(Vec::new()),
}
}
None => HttpResponseBuilder::new()
.status(404)
.mimetype("text/plain")
.body(Vec::new()),
},
_ => HttpResponseBuilder::new()
.status(404)
.mimetype("text/plain")
.body(Vec::new()),
}
});
}
Ok(pending) Ok(pending)
} }
fn prepare_ipc_handler(&self, app_handle: AppHandle<R>) -> WebviewIpcHandler<R> { fn prepare_ipc_handler(&self, app_handle: AppHandle<R>) -> WebviewIpcHandler<R> {
let manager = self.clone(); let manager = self.clone();
Box::new(move |window, request| { Box::new(move |window, #[allow(unused_mut)] mut request| {
let window = Window::new(manager.clone(), window, app_handle.clone()); let window = Window::new(manager.clone(), window, app_handle.clone());
#[cfg(feature = "isolation")]
if let Pattern::Isolation { crypto_keys, .. } = manager.pattern() {
match RawIsolationPayload::try_from(request.as_str())
.and_then(|raw| crypto_keys.decrypt(raw))
{
Ok(json) => request = json,
Err(e) => {
let error: crate::Error = e.into();
let _ = window.eval(&format!(
r#"console.error({})"#,
JsonValue::String(error.to_string())
));
return;
}
}
}
match serde_json::from_str::<InvokePayload>(&request) { match serde_json::from_str::<InvokePayload>(&request) {
Ok(message) => { Ok(message) => {
let _ = window.on_message(message); let _ = window.on_message(message);
@ -530,7 +685,6 @@ impl<R: Runtime> WindowManager<R> {
// skip leading `/` // skip leading `/`
path.chars().skip(1).collect::<String>() path.chars().skip(1).collect::<String>()
}; };
let is_javascript = path.ends_with(".js") || path.ends_with(".cjs") || path.ends_with(".mjs");
let is_html = path.ends_with(".html"); let is_html = path.ends_with(".html");
let mut asset_path = AssetKey::from(path.as_str()); let mut asset_path = AssetKey::from(path.as_str());
@ -558,48 +712,16 @@ impl<R: Runtime> WindowManager<R> {
match asset_response { match asset_response {
Ok(asset) => { Ok(asset) => {
let final_data = if is_javascript || is_html { let final_data = if is_html {
let mut asset = String::from_utf8_lossy(&asset).into_owned(); let mut asset = String::from_utf8_lossy(&asset).into_owned();
asset = asset.replacen(INVOKE_KEY_TOKEN, &self.generate_invoke_key().to_string(), 1); if let Some(csp) = self.csp() {
csp_header.replace(set_csp(
if is_html { &mut asset,
if let Some(mut csp) = self.csp() { self.inner.assets.clone(),
let hash_strings = self.inner.assets.csp_hashes(&asset_path).fold( &asset_path,
CspHashStrings::default(), &self,
|mut acc, hash| { csp,
match hash { ));
CspHash::Script(hash) => {
acc.script.push(' ');
acc.script.push_str(hash);
}
csp_hash => {
#[cfg(debug_assertions)]
eprintln!("Unknown CspHash variant encountered: {:?}", csp_hash)
}
}
acc
},
);
replace_csp_nonce(
&mut asset,
SCRIPT_NONCE_TOKEN,
&mut csp,
"script-src",
hash_strings.script,
);
replace_csp_nonce(
&mut asset,
STYLE_NONCE_TOKEN,
&mut csp,
"style-src",
hash_strings.style,
);
asset = asset.replace(CSP_TOKEN, &csp);
csp_header.replace(csp);
}
} }
asset.as_bytes().to_vec() asset.as_bytes().to_vec()
@ -659,26 +781,57 @@ impl<R: Runtime> WindowManager<R> {
}) })
} }
fn initialization_script(&self, plugin_initialization_script: &str) -> String { fn initialization_script(
let key = self.generate_invoke_key(); &self,
format!( ipc_script: &str,
r#" pattern_script: &str,
{core_script} plugin_initialization_script: &str,
{event_initialization_script} with_global_tauri: bool,
if (document.readyState === 'complete') {{ ) -> crate::Result<String> {
window.__TAURI_INVOKE__("__initialized", {{ url: window.location.href }}, {key}) #[derive(Template)]
}} else {{ #[default_template("../scripts/init.js")]
window.addEventListener('DOMContentLoaded', function () {{ struct InitJavascript<'a> {
window.__TAURI_INVOKE__("__initialized", {{ url: window.location.href }}, {key}) origin: Cow<'a, str>,
}}) #[raw]
}} pattern_script: &'a str,
{plugin_initialization_script} #[raw]
"#, ipc_script: &'a str,
key = key, #[raw]
core_script = include_str!("../scripts/core.js").replace("_KEY_VALUE_", &key.to_string()), bundle_script: &'a str,
event_initialization_script = self.event_initialization_script(), #[raw]
plugin_initialization_script = plugin_initialization_script core_script: &'a str,
) #[raw]
event_initialization_script: &'a str,
#[raw]
plugin_initialization_script: &'a str,
#[raw]
freeze_prototype: &'a str,
}
let bundle_script = if with_global_tauri {
include_str!("../scripts/bundle.js")
} else {
""
};
let freeze_prototype = if self.inner.config.tauri.security.freeze_prototype {
include_str!("../scripts/freeze_prototype.js")
} else {
""
};
InitJavascript {
origin: self.get_browser_origin(),
pattern_script,
ipc_script,
bundle_script,
core_script: include_str!("../scripts/core.js"),
event_initialization_script: &self.event_initialization_script(),
plugin_initialization_script,
freeze_prototype,
}
.render_default(&Default::default())
.map_err(Into::into)
} }
fn event_initialization_script(&self) -> String { fn event_initialization_script(&self) -> String {
@ -702,9 +855,10 @@ impl<R: Runtime> WindowManager<R> {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::WindowManager;
use crate::{generate_context, plugin::PluginStore, StateManager, Wry}; use crate::{generate_context, plugin::PluginStore, StateManager, Wry};
use super::WindowManager;
#[test] #[test]
fn check_get_url() { fn check_get_url() {
let context = generate_context!("test/fixture/src-tauri/tauri.conf.json", crate); let context = generate_context!("test/fixture/src-tauri/tauri.conf.json", crate);
@ -1029,3 +1183,31 @@ struct ScaleFactorChanged {
fn on_menu_event<R: Runtime>(window: &Window<R>, event: &MenuEvent) -> crate::Result<()> { fn on_menu_event<R: Runtime>(window: &Window<R>, event: &MenuEvent) -> crate::Result<()> {
window.emit_and_trigger(MENU_EVENT, event.menu_item_id.clone()) window.emit_and_trigger(MENU_EVENT, event.menu_item_id.clone())
} }
#[cfg(feature = "isolation")]
fn request_to_path(request: &tauri_runtime::http::Request, replace: &str) -> String {
let mut path = request
.uri()
.split(&['?', '#'][..])
// ignore query string
.next()
.unwrap()
.to_string()
.replace(replace, "");
if path.ends_with('/') {
path.pop();
}
let path = percent_encoding::percent_decode(path.as_bytes())
.decode_utf8_lossy()
.to_string();
if path.is_empty() {
// if the url has no path, we should load `index.html`
"index.html".to_string()
} else {
// skip leading `/`
path.chars().skip(1).collect()
}
}

94
core/tauri/src/pattern.rs Normal file
View File

@ -0,0 +1,94 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use std::marker::PhantomData;
#[cfg(feature = "isolation")]
use std::sync::Arc;
use serde::Serialize;
use serialize_to_javascript::{default_template, Template};
use tauri_utils::assets::{Assets, EmbeddedAssets};
/// An application pattern.
#[derive(Debug, Clone)]
pub enum Pattern<A: Assets = EmbeddedAssets> {
/// The brownfield pattern.
Brownfield(PhantomData<A>),
/// Isolation pattern. Recommended for security purposes.
#[cfg(feature = "isolation")]
Isolation {
/// The HTML served on `isolation://index.html`.
assets: Arc<A>,
/// The schema used for the isolation frames.
schema: String,
/// A random string used to ensure that the message went through the isolation frame.
///
/// This should be regenerated at runtime.
key: String,
/// Cryptographically secure keys
crypto_keys: Box<tauri_utils::pattern::isolation::Keys>,
},
}
/// The shape of the JavaScript Pattern config
#[derive(Debug, Serialize)]
#[serde(rename_all = "lowercase", tag = "pattern")]
pub(crate) enum PatternObject {
/// Brownfield pattern.
Brownfield,
/// Isolation pattern. Recommended for security purposes.
#[cfg(feature = "isolation")]
Isolation {
/// Which `IsolationSide` this `PatternObject` is getting injected into
side: IsolationSide,
},
}
impl From<&Pattern> for PatternObject {
fn from(pattern: &Pattern) -> Self {
match pattern {
Pattern::Brownfield(_) => Self::Brownfield,
#[cfg(feature = "isolation")]
Pattern::Isolation { .. } => Self::Isolation {
side: IsolationSide::default(),
},
}
}
}
/// Where the JavaScript is injected to
#[derive(Debug, Serialize)]
#[serde(rename_all = "lowercase")]
pub(crate) enum IsolationSide {
/// Original frame, the Brownfield application
Original,
/// Secure frame, the isolation security application
#[allow(dead_code)]
Secure,
}
impl Default for IsolationSide {
fn default() -> Self {
Self::Original
}
}
#[derive(Template)]
#[default_template("../scripts/pattern.js")]
pub(crate) struct PatternJavascript {
pub(crate) pattern: PatternObject,
}
#[allow(dead_code)]
pub(crate) fn format_real_schema(schema: &str) -> String {
if cfg!(windows) {
format!("https://{}.localhost", schema)
} else {
format!("{}://localhost", schema)
}
}

View File

@ -12,14 +12,17 @@ use tauri_runtime::{
dpi::{PhysicalPosition, PhysicalSize, Position, Size}, dpi::{PhysicalPosition, PhysicalSize, Position, Size},
DetachedWindow, MenuEvent, PendingWindow, WindowEvent, DetachedWindow, MenuEvent, PendingWindow, WindowEvent,
}, },
ActivationPolicy, ClipboardManager, Dispatch, GlobalShortcutManager, Icon, Result, RunEvent, ClipboardManager, Dispatch, GlobalShortcutManager, Icon, Result, RunEvent, Runtime,
RunIteration, Runtime, RuntimeHandle, UserAttentionType, RuntimeHandle, UserAttentionType,
}; };
#[cfg(feature = "system-tray")] #[cfg(feature = "system-tray")]
use tauri_runtime::{SystemTray, SystemTrayEvent}; use tauri_runtime::{SystemTray, SystemTrayEvent};
use tauri_utils::config::WindowConfig; use tauri_utils::config::WindowConfig;
use uuid::Uuid; use uuid::Uuid;
#[cfg(windows)]
use webview2_com::Windows::Win32::Foundation::HWND;
use std::{ use std::{
collections::HashMap, collections::HashMap,
fmt, fmt,
@ -60,6 +63,7 @@ impl RuntimeHandle for MockRuntimeHandle {
context: self.context.clone(), context: self.context.clone(),
}, },
menu_ids: Default::default(), menu_ids: Default::default(),
js_event_listeners: Default::default(),
}) })
} }
@ -534,6 +538,7 @@ impl Runtime for MockRuntime {
context: self.context.clone(), context: self.context.clone(),
}, },
menu_ids: Default::default(), menu_ids: Default::default(),
js_event_listeners: Default::default(),
}) })
} }
@ -551,10 +556,13 @@ impl Runtime for MockRuntime {
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
#[cfg_attr(doc_cfg, doc(cfg(target_os = "macos")))] #[cfg_attr(doc_cfg, doc(cfg(target_os = "macos")))]
fn set_activation_policy(&mut self, activation_policy: ActivationPolicy) {} fn set_activation_policy(&mut self, activation_policy: tauri_runtime::ActivationPolicy) {}
#[cfg(any(target_os = "windows", target_os = "macos"))] #[cfg(any(target_os = "windows", target_os = "macos"))]
fn run_iteration<F: Fn(RunEvent) + 'static>(&mut self, callback: F) -> RunIteration { fn run_iteration<F: Fn(RunEvent) + 'static>(
&mut self,
callback: F,
) -> tauri_runtime::RunIteration {
Default::default() Default::default()
} }

View File

@ -9,10 +9,10 @@ pub use mock_runtime::*;
use std::{borrow::Cow, sync::Arc}; use std::{borrow::Cow, sync::Arc};
use crate::Manager; use crate::{Manager, Pattern};
use tauri_utils::{ use tauri_utils::{
assets::{AssetKey, Assets, CspHash}, assets::{AssetKey, Assets, CspHash},
config::{CliConfig, Config, TauriConfig}, config::{CliConfig, Config, PatternKind, TauriConfig},
}; };
pub struct NoopAsset { pub struct NoopAsset {
@ -40,6 +40,7 @@ pub fn mock_context<A: Assets>(assets: A) -> crate::Context<A> {
config: Config { config: Config {
package: Default::default(), package: Default::default(),
tauri: TauriConfig { tauri: TauriConfig {
pattern: PatternKind::Brownfield,
windows: vec![Default::default()], windows: vec![Default::default()],
cli: Some(CliConfig { cli: Some(CliConfig {
description: None, description: None,
@ -65,8 +66,11 @@ pub fn mock_context<A: Assets>(assets: A) -> crate::Context<A> {
package_info: crate::PackageInfo { package_info: crate::PackageInfo {
name: "test".into(), name: "test".into(),
version: "0.1.0".into(), version: "0.1.0".into(),
authors: "Tauri".into(),
description: "Tauri test".into(),
}, },
_info_plist: (), _info_plist: (),
pattern: Pattern::Brownfield(std::marker::PhantomData),
} }
} }

View File

@ -11,8 +11,7 @@ use crate::api::{
use base64::decode; use base64::decode;
use http::StatusCode; use http::StatusCode;
use minisign_verify::{PublicKey, Signature}; use minisign_verify::{PublicKey, Signature};
use tauri_utils::platform::current_exe; use tauri_utils::{platform::current_exe, Env};
use tauri_utils::Env;
use std::{ use std::{
collections::HashMap, collections::HashMap,
@ -552,7 +551,7 @@ fn copy_files_and_run<R: Read + Seek>(
let mut extractor = Extract::from_cursor(archive_buffer, ArchiveFormat::Zip); let mut extractor = Extract::from_cursor(archive_buffer, ArchiveFormat::Zip);
// extract the msi // extract the msi
extractor.extract_into(&tmp_dir); extractor.extract_into(&tmp_dir)?;
let paths = read_dir(&tmp_dir)?; let paths = read_dir(&tmp_dir)?;
// This consumes the TempDir without deleting directory on the filesystem, // This consumes the TempDir without deleting directory on the filesystem,
@ -648,7 +647,7 @@ fn copy_files_and_run<R: Read + Seek>(archive_buffer: R, extract_path: &Path) ->
.tempdir()?; .tempdir()?;
// create backup of our current app // create backup of our current app
Move::from_source(extract_path).to_dest(&tmp_dir.path())?; Move::from_source(extract_path).to_dest(tmp_dir.path())?;
// extract all the files // extract all the files
for file in all_files { for file in all_files {
@ -666,7 +665,7 @@ fn copy_files_and_run<R: Read + Seek>(archive_buffer: R, extract_path: &Path) ->
std::fs::remove_file(file)?; std::fs::remove_file(file)?;
} }
} }
Move::from_source(&tmp_dir.path()).to_dest(extract_path)?; Move::from_source(tmp_dir.path()).to_dest(extract_path)?;
return Err(Error::Extract(err.to_string())); return Err(Error::Extract(err.to_string()));
} }
@ -776,7 +775,7 @@ where
mod test { mod test {
use super::*; use super::*;
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
use std::{env, fs::File}; use std::fs::File;
macro_rules! block { macro_rules! block {
($e:expr) => { ($e:expr) => {

View File

@ -226,7 +226,7 @@ impl<R: Runtime> Window<R> {
/// How to handle this window receiving an [`InvokeMessage`]. /// How to handle this window receiving an [`InvokeMessage`].
pub fn on_message(self, payload: InvokePayload) -> crate::Result<()> { pub fn on_message(self, payload: InvokePayload) -> crate::Result<()> {
let manager = self.manager.clone(); let manager = self.manager.clone();
match payload.command.as_str() { match payload.cmd.as_str() {
"__initialized" => { "__initialized" => {
let payload: PageLoadPayload = serde_json::from_value(payload.inner)?; let payload: PageLoadPayload = serde_json::from_value(payload.inner)?;
manager.run_on_page_load(self, payload); manager.run_on_page_load(self, payload);
@ -235,25 +235,19 @@ impl<R: Runtime> Window<R> {
let message = InvokeMessage::new( let message = InvokeMessage::new(
self.clone(), self.clone(),
manager.state(), manager.state(),
payload.command.to_string(), payload.cmd.to_string(),
payload.inner, payload.inner,
); );
let resolver = InvokeResolver::new(self, payload.callback, payload.error); let resolver = InvokeResolver::new(self, payload.callback, payload.error);
let invoke = Invoke { message, resolver }; let invoke = Invoke { message, resolver };
if manager.verify_invoke_key(payload.key) { if let Some(module) = &payload.tauri_module {
if let Some(module) = &payload.tauri_module { let module = module.to_string();
let module = module.to_string(); crate::endpoints::handle(module, invoke, manager.config(), manager.package_info());
crate::endpoints::handle(module, invoke, manager.config(), manager.package_info()); } else if payload.cmd.starts_with("plugin:") {
} else if payload.command.starts_with("plugin:") { manager.extend_api(invoke);
manager.extend_api(invoke);
} else {
manager.run_invoke_handler(invoke);
}
} else { } else {
panic!( manager.run_invoke_handler(invoke);
r#"The invoke key "{}" is invalid. This means that an external, possible malicious script is trying to access the system interface."#,
payload.key
);
} }
} }
} }
@ -718,7 +712,7 @@ impl<R: Runtime> Window<R> {
mod tests { mod tests {
#[test] #[test]
fn window_is_send_sync() { fn window_is_send_sync() {
crate::test::assert_send::<super::Window>(); crate::test_utils::assert_send::<super::Window>();
crate::test::assert_sync::<super::Window>(); crate::test_utils::assert_sync::<super::Window>();
} }
} }

View File

@ -107,7 +107,7 @@ fn create_symlink(original: &Path, link: PathBuf) -> io::Result<Symlink> {
Ok(()) => Ok(Symlink::Created(link)), Ok(()) => Ok(Symlink::Created(link)),
Err(e) => match e.raw_os_error() { Err(e) => match e.raw_os_error() {
Some(ERROR_PRIVILEGE_NOT_HELD) => Ok(Symlink::Privilege), Some(ERROR_PRIVILEGE_NOT_HELD) => Ok(Symlink::Privilege),
_ => Err(e.into()), _ => Err(e),
}, },
}; };
} }

View File

@ -34,9 +34,9 @@ dependencies = [
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.44" version = "1.0.51"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61604a8f862e1d5c3229fdd78f8b02c68dcf73a4c4b05fd636d12240aaa242c1" checksum = "8b26702f315f53b6071259e15dd9d64528213b44d61de1ec926eca7715d62203"
[[package]] [[package]]
name = "arrayref" name = "arrayref"
@ -103,9 +103,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]] [[package]]
name = "blake3" name = "blake3"
version = "1.1.0" version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2607a74355ce2e252d0c483b2d8a348e1bba36036e786ccc2dcd777213c86ffd" checksum = "526c210b4520e416420759af363083471656e819a75e831b8d2c9d5a584f2413"
dependencies = [ dependencies = [
"arrayref", "arrayref",
"arrayvec", "arrayvec",
@ -199,9 +199,9 @@ dependencies = [
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.71" version = "1.0.72"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd" checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
dependencies = [ dependencies = [
"jobserver", "jobserver",
] ]
@ -252,18 +252,6 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
[[package]]
name = "chrono"
version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
dependencies = [
"libc",
"num-integer",
"num-traits",
"winapi",
]
[[package]] [[package]]
name = "cocoa" name = "cocoa"
version = "0.24.0" version = "0.24.0"
@ -295,12 +283,6 @@ dependencies = [
"objc", "objc",
] ]
[[package]]
name = "const-sha1"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb58b6451e8c2a812ad979ed1d83378caa5e927eef2622017a45f251457c2c9d"
[[package]] [[package]]
name = "constant_time_eq" name = "constant_time_eq"
version = "0.1.5" version = "0.1.5"
@ -406,9 +388,9 @@ dependencies = [
[[package]] [[package]]
name = "crc32fast" name = "crc32fast"
version = "1.2.1" version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" checksum = "738c290dfaea84fc1ca15ad9c168d083b05a714e1efddd8edaab678dc28d2836"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if 1.0.0",
] ]
@ -465,7 +447,7 @@ checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a"
dependencies = [ dependencies = [
"cssparser-macros", "cssparser-macros",
"dtoa-short", "dtoa-short",
"itoa", "itoa 0.4.8",
"matches", "matches",
"phf 0.8.0", "phf 0.8.0",
"proc-macro2", "proc-macro2",
@ -484,6 +466,12 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "cty"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35"
[[package]] [[package]]
name = "darling" name = "darling"
version = "0.10.2" version = "0.10.2"
@ -496,12 +484,12 @@ dependencies = [
[[package]] [[package]]
name = "darling" name = "darling"
version = "0.13.0" version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "757c0ded2af11d8e739c4daea1ac623dd1624b06c844cf3f5a39f1bdbd99bb12" checksum = "d0d720b8683f8dd83c65155f0530560cba68cd2bf395f6513a483caee57ff7f4"
dependencies = [ dependencies = [
"darling_core 0.13.0", "darling_core 0.13.1",
"darling_macro 0.13.0", "darling_macro 0.13.1",
] ]
[[package]] [[package]]
@ -520,9 +508,9 @@ dependencies = [
[[package]] [[package]]
name = "darling_core" name = "darling_core"
version = "0.13.0" version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c34d8efb62d0c2d7f60ece80f75e5c63c1588ba68032740494b0b9a996466e3" checksum = "7a340f241d2ceed1deb47ae36c4144b2707ec7dd0b649f894cb39bb595986324"
dependencies = [ dependencies = [
"fnv", "fnv",
"ident_case", "ident_case",
@ -545,20 +533,20 @@ dependencies = [
[[package]] [[package]]
name = "darling_macro" name = "darling_macro"
version = "0.13.0" version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ade7bff147130fe5e6d39f089c6bd49ec0250f35d70b2eebf72afdfc919f15cc" checksum = "72c41b3b7352feb3211a0d743dc5700a4e3b60f51bd2b368892d1e0f9a95f44b"
dependencies = [ dependencies = [
"darling_core 0.13.0", "darling_core 0.13.1",
"quote", "quote",
"syn", "syn",
] ]
[[package]] [[package]]
name = "data-url" name = "data-url"
version = "0.1.0" version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d33fe99ccedd6e84bc035f1931bb2e6be79739d6242bd895e7311c886c50dc9c" checksum = "3a30bfce702bcfa94e906ef82421f2c0e61c076ad76030c16ee5d2e9a32fe193"
dependencies = [ dependencies = [
"matches", "matches",
] ]
@ -596,14 +584,14 @@ dependencies = [
[[package]] [[package]]
name = "derive_more" name = "derive_more"
version = "0.99.16" version = "0.99.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40eebddd2156ce1bb37b20bbe5151340a31828b1f2d22ba4141f3531710e38df" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
dependencies = [ dependencies = [
"convert_case", "convert_case",
"proc-macro2", "proc-macro2",
"quote", "quote",
"rustc_version", "rustc_version 0.4.0",
"syn", "syn",
] ]
@ -672,9 +660,9 @@ checksum = "53dd2e43a7d32952a6054141ee0d75183958620e84e5eab045de362dff13dc99"
[[package]] [[package]]
name = "fastrand" name = "fastrand"
version = "1.5.0" version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b394ed3d285a429378d3b384b9eb1285267e7df4b166df24b7a6939a04dc392e" checksum = "779d043b6a0b90cc4c0ed7ee380a6504394cee7efd7db050e3774eee387324b2"
dependencies = [ dependencies = [
"instant", "instant",
] ]
@ -686,7 +674,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e1c54951450cbd39f3dbcf1005ac413b49487dabf18a720ad2383eccfeffb92" checksum = "1e1c54951450cbd39f3dbcf1005ac413b49487dabf18a720ad2383eccfeffb92"
dependencies = [ dependencies = [
"memoffset", "memoffset",
"rustc_version", "rustc_version 0.3.3",
] ]
[[package]] [[package]]
@ -756,9 +744,9 @@ dependencies = [
[[package]] [[package]]
name = "futures" name = "futures"
version = "0.3.17" version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a12aa0eb539080d55c3f2d45a67c3b58b6b0773c1a3ca2dfec66d58c97fd66ca" checksum = "28560757fe2bb34e79f907794bb6b22ae8b0e5c669b638a1132f2592b19035b4"
dependencies = [ dependencies = [
"futures-channel", "futures-channel",
"futures-core", "futures-core",
@ -771,9 +759,9 @@ dependencies = [
[[package]] [[package]]
name = "futures-channel" name = "futures-channel"
version = "0.3.17" version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5da6ba8c3bb3c165d3c7319fc1cc8304facf1fb8db99c5de877183c08a273888" checksum = "ba3dda0b6588335f360afc675d0564c17a77a2bda81ca178a4b6081bd86c7f0b"
dependencies = [ dependencies = [
"futures-core", "futures-core",
"futures-sink", "futures-sink",
@ -781,15 +769,15 @@ dependencies = [
[[package]] [[package]]
name = "futures-core" name = "futures-core"
version = "0.3.17" version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88d1c26957f23603395cd326b0ffe64124b818f4449552f960d815cfba83a53d" checksum = "d0c8ff0461b82559810cdccfde3215c3f373807f5e5232b71479bff7bb2583d7"
[[package]] [[package]]
name = "futures-executor" name = "futures-executor"
version = "0.3.17" version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45025be030969d763025784f7f355043dc6bc74093e4ecc5000ca4dc50d8745c" checksum = "29d6d2ff5bb10fb95c85b8ce46538a2e5f5e7fdc755623a7d4529ab8a4ed9d2a"
dependencies = [ dependencies = [
"futures-core", "futures-core",
"futures-task", "futures-task",
@ -798,9 +786,9 @@ dependencies = [
[[package]] [[package]]
name = "futures-io" name = "futures-io"
version = "0.3.17" version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "522de2a0fe3e380f1bc577ba0474108faf3f6b18321dbf60b3b9c39a75073377" checksum = "b1f9d34af5a1aac6fb380f735fe510746c38067c5bf16c7fd250280503c971b2"
[[package]] [[package]]
name = "futures-lite" name = "futures-lite"
@ -819,12 +807,10 @@ dependencies = [
[[package]] [[package]]
name = "futures-macro" name = "futures-macro"
version = "0.3.17" version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18e4a4b95cea4b4ccbcf1c5675ca7c4ee4e9e75eb79944d07defde18068f79bb" checksum = "6dbd947adfffb0efc70599b3ddcf7b5597bb5fa9e245eb99f62b3a5f7bb8bd3c"
dependencies = [ dependencies = [
"autocfg",
"proc-macro-hack",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn",
@ -832,23 +818,22 @@ dependencies = [
[[package]] [[package]]
name = "futures-sink" name = "futures-sink"
version = "0.3.17" version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36ea153c13024fe480590b3e3d4cad89a0cfacecc24577b68f86c6ced9c2bc11" checksum = "e3055baccb68d74ff6480350f8d6eb8fcfa3aa11bdc1a1ae3afdd0514617d508"
[[package]] [[package]]
name = "futures-task" name = "futures-task"
version = "0.3.17" version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d3d00f4eddb73e498a54394f228cd55853bdf059259e8e7bc6e69d408892e99" checksum = "6ee7c6485c30167ce4dfb83ac568a849fe53274c831081476ee13e0dce1aad72"
[[package]] [[package]]
name = "futures-util" name = "futures-util"
version = "0.3.17" version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36568465210a3a6ee45e1f165136d68671471a501e632e9a98d96872222b5481" checksum = "d9b5cf40b47a271f77a8b1bec03ca09044d99d2372c0de244e66430761127164"
dependencies = [ dependencies = [
"autocfg",
"futures-channel", "futures-channel",
"futures-core", "futures-core",
"futures-io", "futures-io",
@ -858,8 +843,6 @@ dependencies = [
"memchr", "memchr",
"pin-project-lite", "pin-project-lite",
"pin-utils", "pin-utils",
"proc-macro-hack",
"proc-macro-nested",
"slab", "slab",
] ]
@ -1209,7 +1192,7 @@ checksum = "1323096b05d41827dadeaee54c9981958c0f94e670bc94ed80037d1a7b8b186b"
dependencies = [ dependencies = [
"bytes", "bytes",
"fnv", "fnv",
"itoa", "itoa 0.4.8",
] ]
[[package]] [[package]]
@ -1292,9 +1275,9 @@ dependencies = [
[[package]] [[package]]
name = "itertools" name = "itertools"
version = "0.10.1" version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf" checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3"
dependencies = [ dependencies = [
"either", "either",
] ]
@ -1306,10 +1289,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
[[package]] [[package]]
name = "javascriptcore-rs" name = "itoa"
version = "0.15.1" version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9644bfae308588f4e57616bd8402b982dbcd68d1934c0848e2869ffcc03b562" checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
[[package]]
name = "javascriptcore-rs"
version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e207780c1d1cd3c36056695e44010a19dd481574a2106cd2540edda4128a9794"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"glib", "glib",
@ -1318,9 +1307,9 @@ dependencies = [
[[package]] [[package]]
name = "javascriptcore-rs-sys" name = "javascriptcore-rs-sys"
version = "0.3.1" version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79a4f53f580ab519a92d142d6dbcff5fd59b8e9634f965bdeb892383d787165e" checksum = "2adf2de824b178d76c6017da59f4e7e95de49a766b584c59d47821a6c3dce9e2"
dependencies = [ dependencies = [
"glib-sys 0.14.0", "glib-sys 0.14.0",
"gobject-sys 0.14.0", "gobject-sys 0.14.0",
@ -1363,9 +1352,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.106" version = "0.2.112"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a60553f9a9e039a333b4e9b20573b9e9b9c0bb3a11e201ccc48ef4283456d673" checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
[[package]] [[package]]
name = "lock_api" name = "lock_api"
@ -1387,9 +1376,9 @@ dependencies = [
[[package]] [[package]]
name = "loom" name = "loom"
version = "0.5.2" version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2b9df80a3804094bf49bb29881d18f6f05048db72127e84e09c26fc7c2324f5" checksum = "edc5c7d328e32cc4954e8e01193d7f0ef5ab257b5090b70a964e099a36034309"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if 1.0.0",
"generator", "generator",
@ -1431,9 +1420,9 @@ dependencies = [
[[package]] [[package]]
name = "matchers" name = "matchers"
version = "0.0.1" version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
dependencies = [ dependencies = [
"regex-automata", "regex-automata",
] ]
@ -1452,9 +1441,9 @@ checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]] [[package]]
name = "memoffset" name = "memoffset"
version = "0.6.4" version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
dependencies = [ dependencies = [
"autocfg", "autocfg",
] ]
@ -1520,9 +1509,9 @@ dependencies = [
[[package]] [[package]]
name = "ndk-sys" name = "ndk-sys"
version = "0.2.1" version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c44922cb3dbb1c70b5e5f443d63b64363a898564d739ba5198e3a9138442868d" checksum = "e1bcdd74c20ad5d95aacd60ef9ba40fdf77f767051040541df557b7a9b2a2121"
[[package]] [[package]]
name = "new_debug_unreachable" name = "new_debug_unreachable"
@ -1568,9 +1557,9 @@ dependencies = [
[[package]] [[package]]
name = "num_cpus" name = "num_cpus"
version = "1.13.0" version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
dependencies = [ dependencies = [
"hermit-abi", "hermit-abi",
"libc", "libc",
@ -1578,9 +1567,9 @@ dependencies = [
[[package]] [[package]]
name = "num_enum" name = "num_enum"
version = "0.5.4" version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9bd055fb730c4f8f4f57d45d35cd6b3f0980535b056dc7ff119cee6a66ed6f" checksum = "085fe377a4b2805c0fbc09484415ec261174614b7f080b0e0d520456ac421a67"
dependencies = [ dependencies = [
"derivative", "derivative",
"num_enum_derive", "num_enum_derive",
@ -1588,9 +1577,9 @@ dependencies = [
[[package]] [[package]]
name = "num_enum_derive" name = "num_enum_derive"
version = "0.5.4" version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "486ea01961c4a818096de679a8b740b26d9033146ac5291b1c98557658f8cdd9" checksum = "5249369707a1e07b39f78d98c8f34e00aca7dcb053812fdbb5ad7be82c1bba38"
dependencies = [ dependencies = [
"proc-macro-crate 1.1.0", "proc-macro-crate 1.1.0",
"proc-macro2", "proc-macro2",
@ -1628,9 +1617,9 @@ dependencies = [
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.8.0" version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
[[package]] [[package]]
name = "opaque-debug" name = "opaque-debug"
@ -1722,9 +1711,9 @@ dependencies = [
[[package]] [[package]]
name = "phf" name = "phf"
version = "0.10.0" version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9fc3db1018c4b59d7d582a739436478b6035138b6aecbce989fc91c3e98409f" checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259"
dependencies = [ dependencies = [
"phf_macros 0.10.0", "phf_macros 0.10.0",
"phf_shared 0.10.0", "phf_shared 0.10.0",
@ -1821,9 +1810,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]] [[package]]
name = "pkg-config" name = "pkg-config"
version = "0.3.22" version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12295df4f294471248581bc09bef3c38a5e46f1e36d6a37353621a0c6c357e1f" checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe"
[[package]] [[package]]
name = "png" name = "png"
@ -1910,17 +1899,11 @@ version = "0.5.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
[[package]]
name = "proc-macro-nested"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.32" version = "1.0.34"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43" checksum = "2f84e92c0f7c9d58328b85a78557813e4bd845130db68d7184635344399423b1"
dependencies = [ dependencies = [
"unicode-xid", "unicode-xid",
] ]
@ -2027,11 +2010,21 @@ dependencies = [
[[package]] [[package]]
name = "raw-window-handle" name = "raw-window-handle"
version = "0.3.3" version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a441a7a6c80ad6473bd4b74ec1c9a4c951794285bf941c2126f607c72e48211" checksum = "e28f55143d0548dad60bb4fbdc835a3d7ac6acc3324506450c5fdd6e42903a76"
dependencies = [ dependencies = [
"libc", "libc",
"raw-window-handle 0.4.2",
]
[[package]]
name = "raw-window-handle"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fba75eee94a9d5273a68c9e1e105d9cffe1ef700532325788389e5a83e2522b7"
dependencies = [
"cty",
] ]
[[package]] [[package]]
@ -2130,16 +2123,25 @@ dependencies = [
] ]
[[package]] [[package]]
name = "rustversion" name = "rustc_version"
version = "1.0.5" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61b3909d758bb75c79f23d4736fac9433868679d3ad2ea7a61e3c25cfda9a088" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
dependencies = [
"semver 1.0.4",
]
[[package]]
name = "rustversion"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f"
[[package]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.5" version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
[[package]] [[package]]
name = "same-file" name = "same-file"
@ -2208,18 +2210,18 @@ dependencies = [
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.130" version = "1.0.132"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" checksum = "8b9875c23cf305cd1fd7eb77234cbb705f21ea6a72c637a5c6db5fe4b8e7f008"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.130" version = "1.0.132"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" checksum = "ecc0db5cb2556c0e558887d9bbdcf6ac4471e83ff66cf696e5419024d1606276"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -2228,11 +2230,11 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.68" version = "1.0.73"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f690853975602e1bfe1ccbf50504d67174e3bcf340f23b5ea9992e0587a52d8" checksum = "bcbd0344bc6533bc7ec56df11d42fb70f1b912351c0825ccb7211b59d8af7cf5"
dependencies = [ dependencies = [
"itoa", "itoa 1.0.1",
"ryu", "ryu",
"serde", "serde",
] ]
@ -2265,7 +2267,27 @@ version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12e47be9471c72889ebafb5e14d5ff930d89ae7a67bbdb5f8abb564f845a927e" checksum = "12e47be9471c72889ebafb5e14d5ff930d89ae7a67bbdb5f8abb564f845a927e"
dependencies = [ dependencies = [
"darling 0.13.0", "darling 0.13.1",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serialize-to-javascript"
version = "0.1.0"
source = "git+https://github.com/chippers/serialize-to-javascript#38d5026f371bfba4f5197ed143a6667a09375fbd"
dependencies = [
"serde",
"serde_json",
"serialize-to-javascript-impl",
]
[[package]]
name = "serialize-to-javascript-impl"
version = "0.1.0"
source = "git+https://github.com/chippers/serialize-to-javascript#38d5026f371bfba4f5197ed143a6667a09375fbd"
dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn",
@ -2427,9 +2449,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.81" version = "1.0.82"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966" checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -2485,7 +2507,7 @@ dependencies = [
[[package]] [[package]]
name = "tao" name = "tao"
version = "0.5.2" version = "0.5.2"
source = "git+https://github.com/tauri-apps/tao?branch=next#97cd0497ae827903f7d67aa1c991a77a397c9cb8" source = "git+https://github.com/tauri-apps/tao?branch=next#9f699a345788fbb08bc483a3f335ca4a94339676"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"cairo-rs", "cairo-rs",
@ -2512,20 +2534,19 @@ dependencies = [
"ndk-sys", "ndk-sys",
"objc", "objc",
"parking_lot", "parking_lot",
"raw-window-handle", "raw-window-handle 0.3.4",
"scopeguard", "scopeguard",
"serde", "serde",
"unicode-segmentation", "unicode-segmentation",
"webview2-com-sys",
"windows", "windows",
"x11-dl", "x11-dl",
] ]
[[package]] [[package]]
name = "tar" name = "tar"
version = "0.4.37" version = "0.4.38"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6f5515d3add52e0bbdcad7b83c388bb36ba7b754dda3b5f5bc2d38640cdba5c" checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6"
dependencies = [ dependencies = [
"filetime", "filetime",
"libc", "libc",
@ -2553,12 +2574,13 @@ dependencies = [
"once_cell", "once_cell",
"percent-encoding", "percent-encoding",
"rand 0.8.4", "rand 0.8.4",
"raw-window-handle", "raw-window-handle 0.3.4",
"regex", "regex",
"semver 1.0.4", "semver 1.0.4",
"serde", "serde",
"serde_json", "serde_json",
"serde_repr", "serde_repr",
"serialize-to-javascript",
"state", "state",
"tar", "tar",
"tauri-macros", "tauri-macros",
@ -2587,6 +2609,7 @@ dependencies = [
"sha2", "sha2",
"tauri-utils", "tauri-utils",
"thiserror", "thiserror",
"uuid",
"walkdir", "walkdir",
"zstd", "zstd",
] ]
@ -2615,7 +2638,7 @@ dependencies = [
"tauri-utils", "tauri-utils",
"thiserror", "thiserror",
"uuid", "uuid",
"webview2-com-sys", "webview2-com",
] ]
[[package]] [[package]]
@ -2630,6 +2653,7 @@ dependencies = [
"tauri-utils", "tauri-utils",
"uuid", "uuid",
"webview2-com", "webview2-com",
"windows",
"wry", "wry",
] ]
@ -2637,15 +2661,18 @@ dependencies = [
name = "tauri-utils" name = "tauri-utils"
version = "1.0.0-beta.3" version = "1.0.0-beta.3"
dependencies = [ dependencies = [
"base64",
"heck", "heck",
"html5ever", "html5ever",
"kuchiki", "kuchiki",
"phf 0.10.0", "phf 0.10.1",
"proc-macro2", "proc-macro2",
"quote", "quote",
"serde", "serde",
"serde_json", "serde_json",
"serde_with", "serde_with",
"serialize-to-javascript",
"sha2",
"thiserror", "thiserror",
"url", "url",
"zstd", "zstd",
@ -2723,9 +2750,9 @@ dependencies = [
[[package]] [[package]]
name = "tinyvec" name = "tinyvec"
version = "1.5.0" version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f83b2a3d4d9091d0abd7eba4dc2710b1718583bd4d8992e2190720ea38f391f7" checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2"
dependencies = [ dependencies = [
"tinyvec_macros", "tinyvec_macros",
] ]
@ -2738,11 +2765,10 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "1.13.0" version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "588b2d10a336da58d877567cd8fb8a14b463e2104910f8132cd054b4b96e29ee" checksum = "fbbf1c778ec206785635ce8ad57fe52b3009ae9e0c9f574a728f3049d3e55838"
dependencies = [ dependencies = [
"autocfg",
"bytes", "bytes",
"memchr", "memchr",
"num_cpus", "num_cpus",
@ -2801,36 +2827,22 @@ dependencies = [
"tracing-core", "tracing-core",
] ]
[[package]]
name = "tracing-serde"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb65ea441fbb84f9f6748fd496cf7f63ec9af5bca94dd86456978d055e8eb28b"
dependencies = [
"serde",
"tracing-core",
]
[[package]] [[package]]
name = "tracing-subscriber" name = "tracing-subscriber"
version = "0.2.25" version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71" checksum = "245da694cc7fc4729f3f418b304cb57789f1bed2a78c575407ab8a23f53cb4d3"
dependencies = [ dependencies = [
"ansi_term", "ansi_term",
"chrono",
"lazy_static", "lazy_static",
"matchers", "matchers",
"regex", "regex",
"serde",
"serde_json",
"sharded-slab", "sharded-slab",
"smallvec", "smallvec",
"thread_local", "thread_local",
"tracing", "tracing",
"tracing-core", "tracing-core",
"tracing-log", "tracing-log",
"tracing-serde",
] ]
[[package]] [[package]]
@ -2949,9 +2961,9 @@ checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
[[package]] [[package]]
name = "webkit2gtk" name = "webkit2gtk"
version = "0.15.2" version = "0.15.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ca90927b3d0625c84c7de59b70174cc6384237d0599f2b8e510a7ee10509d1d" checksum = "d5725e8ede878b7c00419066868085b83fb7d01eea904c1a0bd0159ad3ce0ba3"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"cairo-rs", "cairo-rs",
@ -2972,9 +2984,9 @@ dependencies = [
[[package]] [[package]]
name = "webkit2gtk-sys" name = "webkit2gtk-sys"
version = "0.15.1" version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b0e5e08218be3884e56aff0aedcb9e222b311be53214eb6019200caf9b52815" checksum = "b34314407e381b88a72610d0f4eeff1552cbf8ee5420dbe84749fa26aa11b4e3"
dependencies = [ dependencies = [
"atk-sys", "atk-sys",
"bitflags", "bitflags",
@ -2995,9 +3007,9 @@ dependencies = [
[[package]] [[package]]
name = "webview2-com" name = "webview2-com"
version = "0.4.0" version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2294dee38668da0d71019097dddc6cef525fde7aa4784243dd83f0752e08aa5" checksum = "abdc9ca7cebd96a1005d5ba1e9d70c61c0f6c276a41cddaeecb7842d436ab3bc"
dependencies = [ dependencies = [
"webview2-com-macros", "webview2-com-macros",
"webview2-com-sys", "webview2-com-sys",
@ -3006,9 +3018,9 @@ dependencies = [
[[package]] [[package]]
name = "webview2-com-macros" name = "webview2-com-macros"
version = "0.3.0" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2eba35fdbb8fbc8de7e7479532a356dbbf2754d8a6e9c9fbfa430896cbb1ca89" checksum = "07bca4b354035275764ea4ca8d6bfa74cc5b0e8126e7cd675ee327574b59e13d"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -3017,9 +3029,9 @@ dependencies = [
[[package]] [[package]]
name = "webview2-com-sys" name = "webview2-com-sys"
version = "0.4.0" version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14690dcb8b57c5238c4502cfc321f858fa1306edd4109e8e1d7ddee0c29b06a5" checksum = "73472d7f0e9038b58204cb3f582ee138a8c181719dc6825ea03371ad085c6058"
dependencies = [ dependencies = [
"regex", "regex",
"serde", "serde",
@ -3061,30 +3073,53 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]] [[package]]
name = "windows" name = "windows"
version = "0.19.0" version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef84dd25f4c69a271b1bba394532bf400523b43169de21dfc715e8f8e491053d" checksum = "e46c474738425c090573ecf5472d54ee5f78132e6195d0bbfcc2aabc0ed29f37"
dependencies = [ dependencies = [
"const-sha1", "windows_aarch64_msvc",
"windows_gen", "windows_gen",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_macros", "windows_macros",
"windows_reader",
"windows_x86_64_gnu",
"windows_x86_64_msvc",
] ]
[[package]] [[package]]
name = "windows_gen" name = "windows_aarch64_msvc"
version = "0.19.0" version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac7bb21b8ff5e801232b72a6ff554b4cc0cef9ed9238188c3ca78fe3968a7e5d" checksum = "3022d174000fcaeb6f95933fb04171ea0e21b9289ac57fe4400bfa148e41df79"
[[package]]
name = "windows_gen"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54e0f0e40e950724f92de0f714817c7030a88161738b9b1c58d62c817246fe1c"
dependencies = [ dependencies = [
"windows_quote", "windows_quote",
"windows_reader", "windows_reader",
] ]
[[package]] [[package]]
name = "windows_macros" name = "windows_i686_gnu"
version = "0.19.0" version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5566b8c51118769e4a9094a688bf1233a3f36aacbfc78f3b15817fe0b6e0442f" checksum = "03b1584eebf06654708eab4301152032c13c1e47f4a754ffc93c733f10993e85"
[[package]]
name = "windows_i686_msvc"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f49df16591e9ad429997ec57d462b0cc45168f639d03489e8c2e933ea9c389d7"
[[package]]
name = "windows_macros"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6103bcf1a7396d66f6f08a2d67d8a2ab34efaf4b1d7567301af2c002507c8c3b"
dependencies = [ dependencies = [
"syn", "syn",
"windows_gen", "windows_gen",
@ -3094,20 +3129,32 @@ dependencies = [
[[package]] [[package]]
name = "windows_quote" name = "windows_quote"
version = "0.19.0" version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4af8236a9493c38855f95cdd11b38b342512a5df4ee7473cffa828b5ebb0e39c" checksum = "e414df8d5dd2013f2317fdc414d3ad035effcb7aef1f16bf508ac5743154835a"
[[package]] [[package]]
name = "windows_reader" name = "windows_reader"
version = "0.19.0" version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c8d5cf83fb08083438c5c46723e6206b2970da57ce314f80b57724439aaacab" checksum = "8132c9fb77903d852ea20053af816bd15c088a6e8d283b8283e80353347bb6b9"
[[package]]
name = "windows_x86_64_gnu"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cb06177184100374f97d5e7261ee0b6adefa8ee32e38f87518ca22b519bb80e"
[[package]]
name = "windows_x86_64_msvc"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3c27bcbb33ddbed3569e36c14775c99f72b97c72ce49f81d128637fb48a061f"
[[package]] [[package]]
name = "wry" name = "wry"
version = "0.12.2" version = "0.12.2"
source = "git+ssh://git@github.com/tauri-sec/wry?branch=next#0f9eb9b1b9288b5be443bced0e8fa58a2479c491" source = "git+ssh://git@github.com/tauri-sec/wry?branch=next#c60bb8f49e971237a1e9ed73fad2cec38dc7c9db"
dependencies = [ dependencies = [
"cocoa", "cocoa",
"core-graphics 0.22.3", "core-graphics 0.22.3",

View File

@ -72,7 +72,7 @@ to the Cargo Manifest (`Cargo.toml`) so that Cargo knows to pull in our dependen
name = "hello-tauri-webdriver" name = "hello-tauri-webdriver"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
rust-version = "1.56" rust-version = "1.57"
# Needed to set up some things for Tauri at build time # Needed to set up some things for Tauri at build time
[build-dependencies] [build-dependencies]

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -5,8 +5,8 @@
<link rel="stylesheet" href="/global.css" /> <link rel="stylesheet" href="/global.css" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Svelte + Vite App</title> <title>Svelte + Vite App</title>
<script type="module" crossorigin src="/assets/index.ab727ab4.js"></script> <script type="module" crossorigin src="/assets/index.4b6f33f7.js"></script>
<link rel="modulepreload" href="/assets/vendor.32016365.js"> <link rel="modulepreload" href="/assets/vendor.3a672f03.js">
<link rel="stylesheet" href="/assets/index.b706bb41.css"> <link rel="stylesheet" href="/assets/index.b706bb41.css">
</head> </head>

View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Isolation Secure Script</title>
</head>
<body>
<script src="index.js"></script>
</body>
</html>

View File

@ -0,0 +1,4 @@
window.__TAURI_ISOLATION_HOOK__= (payload) => {
console.log('hook', payload)
return payload
}

View File

@ -14,6 +14,41 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
[[package]]
name = "aead"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877"
dependencies = [
"generic-array",
]
[[package]]
name = "aes"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8"
dependencies = [
"cfg-if 1.0.0",
"cipher",
"cpufeatures",
"opaque-debug",
]
[[package]]
name = "aes-gcm"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df5f85a83a7d8b0442b6aa7b504b8212c1733da07b98aae43d4bc21b2cb3cdf6"
dependencies = [
"aead",
"aes",
"cipher",
"ctr",
"ghash",
"subtle",
]
[[package]] [[package]]
name = "aho-corasick" name = "aho-corasick"
version = "0.7.18" version = "0.7.18"
@ -295,9 +330,9 @@ dependencies = [
[[package]] [[package]]
name = "cargo_toml" name = "cargo_toml"
version = "0.10.1" version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6d613611c914a7db07f28526941ce1e956d2f977b0c5e2014fbfa42230d420f" checksum = "363c7cfaa15f101415c4ac9e68706ca4a2277773932828b33f96e59d28c68e62"
dependencies = [ dependencies = [
"serde", "serde",
"serde_derive", "serde_derive",
@ -372,6 +407,15 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "cipher"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7"
dependencies = [
"generic-array",
]
[[package]] [[package]]
name = "clap" name = "clap"
version = "3.0.4" version = "3.0.4"
@ -418,16 +462,6 @@ dependencies = [
"objc", "objc",
] ]
[[package]]
name = "combine"
version = "4.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2b2f5d0ee456f3928812dfc8c6d9a1d592b98678f6d56db9b0cd2b7bc6c8db5"
dependencies = [
"bytes",
"memchr",
]
[[package]] [[package]]
name = "concurrent-queue" name = "concurrent-queue"
version = "1.2.2" version = "1.2.2"
@ -629,6 +663,15 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "ctr"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea"
dependencies = [
"cipher",
]
[[package]] [[package]]
name = "cty" name = "cty"
version = "0.2.2" version = "0.2.2"
@ -705,6 +748,15 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "data-url"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a30bfce702bcfa94e906ef82421f2c0e61c076ad76030c16ee5d2e9a32fe193"
dependencies = [
"matches",
]
[[package]] [[package]]
name = "deflate" name = "deflate"
version = "0.7.20" version = "0.7.20"
@ -1156,6 +1208,16 @@ dependencies = [
"wasi 0.10.2+wasi-snapshot-preview1", "wasi 0.10.2+wasi-snapshot-preview1",
] ]
[[package]]
name = "ghash"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99"
dependencies = [
"opaque-debug",
"polyval",
]
[[package]] [[package]]
name = "gio" name = "gio"
version = "0.14.8" version = "0.14.8"
@ -1527,15 +1589,6 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "kstring"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b310ccceade8121d7d77fee406160e457c2f4e7c7982d589da3499bc7ea4526"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "kuchiki" name = "kuchiki"
version = "0.8.1" version = "0.8.1"
@ -2240,6 +2293,18 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "polyval"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1"
dependencies = [
"cfg-if 1.0.0",
"cpufeatures",
"opaque-debug",
"universal-hash",
]
[[package]] [[package]]
name = "ppv-lite86" name = "ppv-lite86"
version = "0.2.16" version = "0.2.16"
@ -2548,6 +2613,21 @@ dependencies = [
"windows 0.29.0", "windows 0.29.0",
] ]
[[package]]
name = "ring"
version = "0.16.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
dependencies = [
"cc",
"libc",
"once_cell",
"spin",
"untrusted",
"web-sys",
"winapi",
]
[[package]] [[package]]
name = "rust-argon2" name = "rust-argon2"
version = "0.8.3" version = "0.8.3"
@ -2765,6 +2845,26 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "serialize-to-javascript"
version = "0.1.0"
source = "git+https://github.com/chippers/serialize-to-javascript#38d5026f371bfba4f5197ed143a6667a09375fbd"
dependencies = [
"serde",
"serde_json",
"serialize-to-javascript-impl",
]
[[package]]
name = "serialize-to-javascript-impl"
version = "0.1.0"
source = "git+https://github.com/chippers/serialize-to-javascript#38d5026f371bfba4f5197ed143a6667a09375fbd"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "servo_arc" name = "servo_arc"
version = "0.1.1" version = "0.1.1"
@ -2849,6 +2949,12 @@ dependencies = [
"system-deps 5.0.0", "system-deps 5.0.0",
] ]
[[package]]
name = "spin"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]] [[package]]
name = "stable_deref_trait" name = "stable_deref_trait"
version = "1.2.0" version = "1.2.0"
@ -3054,6 +3160,7 @@ dependencies = [
"bincode", "bincode",
"cfg_aliases", "cfg_aliases",
"clap", "clap",
"data-url",
"dirs-next", "dirs-next",
"either", "either",
"embed_plist", "embed_plist",
@ -3080,6 +3187,7 @@ dependencies = [
"serde", "serde",
"serde_json", "serde_json",
"serde_repr", "serde_repr",
"serialize-to-javascript",
"shared_child", "shared_child",
"state", "state",
"tar", "tar",
@ -3102,6 +3210,7 @@ dependencies = [
"anyhow", "anyhow",
"cargo_toml", "cargo_toml",
"serde_json", "serde_json",
"tauri-codegen",
"tauri-utils", "tauri-utils",
"winres", "winres",
] ]
@ -3112,7 +3221,6 @@ version = "1.0.0-beta.4"
dependencies = [ dependencies = [
"base64", "base64",
"blake3", "blake3",
"kuchiki",
"proc-macro2", "proc-macro2",
"quote", "quote",
"regex", "regex",
@ -3121,6 +3229,7 @@ dependencies = [
"sha2", "sha2",
"tauri-utils", "tauri-utils",
"thiserror", "thiserror",
"uuid",
"walkdir", "walkdir",
"zstd", "zstd",
] ]
@ -3129,7 +3238,7 @@ dependencies = [
name = "tauri-macros" name = "tauri-macros"
version = "1.0.0-beta.5" version = "1.0.0-beta.5"
dependencies = [ dependencies = [
"heck", "heck 0.3.3",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn",
@ -3173,15 +3282,21 @@ dependencies = [
name = "tauri-utils" name = "tauri-utils"
version = "1.0.0-beta.3" version = "1.0.0-beta.3"
dependencies = [ dependencies = [
"aes-gcm",
"base64",
"heck 0.4.0", "heck 0.4.0",
"html5ever", "html5ever",
"kuchiki", "kuchiki",
"once_cell",
"phf 0.10.1", "phf 0.10.1",
"proc-macro2", "proc-macro2",
"quote", "quote",
"ring",
"serde", "serde",
"serde_json", "serde_json",
"serde_with", "serde_with",
"serialize-to-javascript",
"sha2",
"thiserror", "thiserror",
"url", "url",
"zstd", "zstd",
@ -3320,18 +3435,6 @@ dependencies = [
"tracing-core", "tracing-core",
] ]
[[package]]
name = "tracing"
version = "0.1.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105"
dependencies = [
"cfg-if 1.0.0",
"pin-project-lite",
"tracing-attributes",
"tracing-core",
]
[[package]] [[package]]
name = "tracing-attributes" name = "tracing-attributes"
version = "0.1.18" version = "0.1.18"
@ -3420,6 +3523,22 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "universal-hash"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05"
dependencies = [
"generic-array",
"subtle",
]
[[package]]
name = "untrusted"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
[[package]] [[package]]
name = "url" name = "url"
version = "2.2.2" version = "2.2.2"

View File

@ -3,16 +3,16 @@ name = "api"
version = "0.1.0" version = "0.1.0"
description = "An example Tauri Application showcasing the api" description = "An example Tauri Application showcasing the api"
edition = "2021" edition = "2021"
rust-version = "1.56" rust-version = "1.57"
license = "Apache-2.0 OR MIT" license = "Apache-2.0 OR MIT"
[build-dependencies] [build-dependencies]
tauri-build = { path = "../../../core/tauri-build" } tauri-build = { path = "../../../core/tauri-build", features = ["isolation"] }
[dependencies] [dependencies]
serde_json = "1.0" serde_json = "1.0"
serde = { version = "1.0", features = [ "derive" ] } serde = { version = "1.0", features = [ "derive" ] }
tauri = { path = "../../../core/tauri", features = ["api-all", "cli", "macos-private-api", "system-tray", "updater"] } tauri = { path = "../../../core/tauri", features = ["api-all", "cli", "isolation", "macos-private-api", "system-tray", "updater"] }
[features] [features]
default = [ "custom-protocol" ] default = [ "custom-protocol" ]

View File

@ -10,6 +10,12 @@
"version": "../package.json" "version": "../package.json"
}, },
"tauri": { "tauri": {
"pattern": {
"use": "isolation",
"options": {
"dir": "../isolation-dist/"
}
},
"macOSPrivateApi": true, "macOSPrivateApi": true,
"cli": { "cli": {
"description": "Tauri API example", "description": "Tauri API example",

View File

@ -3,7 +3,7 @@ name = "commands"
version = "0.1.0" version = "0.1.0"
description = "A simple Tauri Application showcasing the commands API" description = "A simple Tauri Application showcasing the commands API"
edition = "2021" edition = "2021"
rust-version = "1.56" rust-version = "1.57"
[build-dependencies] [build-dependencies]
tauri-build = { path = "../../../core/tauri-build", features = [ "codegen" ] } tauri-build = { path = "../../../core/tauri-build", features = [ "codegen" ] }

View File

@ -3,7 +3,7 @@ name = "helloworld"
version = "0.1.0" version = "0.1.0"
description = "A very simple Tauri Appplication" description = "A very simple Tauri Appplication"
edition = "2021" edition = "2021"
rust-version = "1.56" rust-version = "1.57"
[build-dependencies] [build-dependencies]
tauri-build = { path = "../../../core/tauri-build", features = [ "codegen" ] } tauri-build = { path = "../../../core/tauri-build", features = [ "codegen" ] }

View File

@ -48,9 +48,6 @@
], ],
"security": { "security": {
"csp": "default-src 'self'" "csp": "default-src 'self'"
},
"updater": {
"active": false
} }
} }
} }

84
examples/isolation/dist/index.html vendored Normal file
View File

@ -0,0 +1,84 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<script>
// malicious
window.__TAURI_PATTERN__ = Object.freeze({pattern: "malicious"});
</script>
<script>
// malicious defineProperty
Object.defineProperty(window, "__TAURI_PATTERN__", {value: Object.freeze({pattern: "malicious"})});
</script>
<meta charset="UTF-8">
<title>Hello Tauri!</title>
<style>
body {
/* Add a nice colorscheme to our page and text */
background-color: #222831;
color: #ececec;
/* Make the body tag the exact size of the window */
margin: 0;
height: 100vh;
width: 100vw;
/* Vertically and horizontally center children of the body tag */
display: flex;
justify-content: center;
align-items: center;
}
div {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
</style>
<script type="text/javascript">console.log("inline", window.__TAURI_PATTERN__);</script>
</head>
<body>
<div>
<h1>Hello, Tauri!</h1>
<pre><code></code></pre>
<div>
<button id="ping">ping</button>
<span id="pong"></span></div>
</div>
<script>
const code = document.querySelector("code");
const obj = {};
function updateCode(key, value) {
obj[key] = value;
code.innerText = JSON.stringify(obj, null, 2);
}
const cb = window.__TAURI__.transformCallback(v => updateCode('response', v));
const error = window.__TAURI__.transformCallback(e => updateCode('response', e));
window.ipc.postMessage(JSON.stringify({
cmd: "ping",
callback: cb,
error,
}));
updateCode('__TAURI_PATTERN__', window.__TAURI_PATTERN__);
</script>
<!-- set up click handlers on our ping command button -->
<script>
const ping = document.querySelector("#ping")
const pong = document.querySelector('#pong')
ping.addEventListener("click", () => {
window.__TAURI_INVOKE__("ping")
.then(() => {
pong.innerText = `ok: ${Date.now()}`
})
.catch(() => {
pong.innerText = `error: ${Date.now()}`
})
})
</script>
</body>
</html>

1
examples/isolation/dist/linked.js vendored Normal file
View File

@ -0,0 +1 @@
console.log("linked", window.__TAURI_PATTERN__);

View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Isolation Secure Script</title>
</head>
<body>
<script src="index.js"></script>
</body>
</html>

View File

@ -0,0 +1,4 @@
window.__TAURI_ISOLATION_HOOK__= (payload) => {
console.log('hook', payload)
return payload
}

View File

@ -0,0 +1,10 @@
{
"name": "isolation",
"license": "Apache-2.0 OR MIT",
"scripts": {
"tauri": "node ../../tooling/cli.js/bin/tauri"
},
"dependencies": {
"@tauri-apps/api": "../../tooling/api/dist"
}
}

View File

@ -0,0 +1,5 @@
# Generated by Cargo
# will have compiled files and executables
/target/
WixTools
.cargo

View File

@ -0,0 +1 @@
../../../.license_template

3448
examples/isolation/src-tauri/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,21 @@
[package]
name = "app"
version = "0.1.0"
description = "An example Tauri Application showcasing the isolation pattern"
edition = "2021"
license = "Apache-2.0 OR MIT"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[build-dependencies]
tauri-build = { path = "../../../core/tauri-build", features = [ "isolation" ] }
[dependencies]
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
tauri = { path = "../../../core/tauri", features = [ "isolation" ] }
[features]
default = [ "custom-protocol", "isolation" ]
isolation = [ "tauri/isolation", "tauri-build/isolation" ]
custom-protocol = [ "tauri/custom-protocol" ]

View File

@ -0,0 +1 @@
../../../LICENSE_APACHE-2.0

View File

@ -0,0 +1 @@
../../../LICENSE_MIT

View File

@ -0,0 +1,13 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use tauri_build::{try_build, Attributes, WindowsAttributes};
fn main() {
try_build(
Attributes::new()
.windows_attributes(WindowsAttributes::new().window_icon_path("../../.icons/icon.ico")),
)
.expect("could not build tauri application");
}

View File

@ -0,0 +1,30 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
#![cfg_attr(
all(not(debug_assertions), target_os = "windows"),
windows_subsystem = "windows"
)]
use std::time::Instant;
#[tauri::command]
fn ping() {
dbg!(format!("ping: {:?}", Instant::now()));
}
#[cfg(not(feature = "isolation"))]
fn main() {
compile_error!("Feature `isolation` is required to run this example");
}
#[cfg(feature = "isolation")]
fn main() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![ping])
.run(tauri::generate_context!(
"../../examples/isolation/src-tauri/tauri.conf.json"
))
.expect("error while running tauri application");
}

View File

@ -0,0 +1,71 @@
{
"package": {
"productName": "isolation",
"version": "0.1.0"
},
"build": {
"distDir": "../dist",
"devPath": "../dist",
"beforeDevCommand": "",
"beforeBuildCommand": "",
"withGlobalTauri": true
},
"tauri": {
"pattern": {
"use": "isolation",
"options": {
"dir": "../isolation-dist"
}
},
"bundle": {
"active": true,
"targets": "all",
"identifier": "com.tauri.isolation",
"icon": [
"../../.icons/32x32.png",
"../../.icons/128x128.png",
"../../.icons/128x128@2x.png",
"../../.icons/icon.icns",
"../../.icons/icon.ico"
],
"resources": [],
"externalBin": [],
"copyright": "",
"category": "DeveloperTool",
"shortDescription": "",
"longDescription": "",
"deb": {
"depends": [],
"useBootstrapper": false
},
"macOS": {
"frameworks": [],
"minimumSystemVersion": "",
"useBootstrapper": false,
"exceptionDomain": "",
"signingIdentity": null,
"entitlements": null
},
"windows": {
"certificateThumbprint": null,
"digestAlgorithm": "sha256",
"timestampUrl": ""
}
},
"updater": {
"active": false
},
"windows": [
{
"title": "Isolation",
"width": 800,
"height": 600,
"resizable": true,
"fullscreen": false
}
],
"security": {
"csp": "default-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self' img-src: 'self'"
}
}
}

View File

@ -0,0 +1,13 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@tauri-apps/api@../../tooling/api/dist":
version "1.0.0-beta.8"
dependencies:
type-fest "2.5.1"
type-fest@2.5.1:
version "2.5.1"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.5.1.tgz#17ba4f36a6abfabf0a92005d045dca77564607b0"
integrity sha512-JDcsxbLR6Z6OcL7TnGAAAGQrY4g7Q4EEALMT4Kp6FQuIc0JLQvOF3l7ejFvx8o5GmLlfMseTWUL++sYFP+o8kw==

View File

@ -3,7 +3,7 @@ name = "multiwindow"
version = "0.1.0" version = "0.1.0"
description = "An example Tauri Multi-Window Application" description = "An example Tauri Multi-Window Application"
edition = "2021" edition = "2021"
rust-version = "1.56" rust-version = "1.57"
license = "Apache-2.0 OR MIT" license = "Apache-2.0 OR MIT"
[build-dependencies] [build-dependencies]

View File

@ -3,7 +3,7 @@ name = "navigation"
version = "0.1.0" version = "0.1.0"
description = "A very simple Tauri Appplication with navigation" description = "A very simple Tauri Appplication with navigation"
edition = "2021" edition = "2021"
rust-version = "1.56" rust-version = "1.57"
[build-dependencies] [build-dependencies]
tauri-build = { path = "../../../core/tauri-build", features = [ "codegen" ] } tauri-build = { path = "../../../core/tauri-build", features = [ "codegen" ] }

View File

@ -3,7 +3,7 @@ name = "resources"
version = "0.1.0" version = "0.1.0"
description = "A Tauri application that uses Node.js with app resources" description = "A Tauri application that uses Node.js with app resources"
edition = "2021" edition = "2021"
rust-version = "1.56" rust-version = "1.57"
[build-dependencies] [build-dependencies]
tauri-build = { path = "../../../core/tauri-build", features = [ "codegen" ] } tauri-build = { path = "../../../core/tauri-build", features = [ "codegen" ] }

View File

@ -3,7 +3,7 @@ name = "sidecar"
version = "0.1.0" version = "0.1.0"
description = "A Tauri application with a sidecar binary" description = "A Tauri application with a sidecar binary"
edition = "2021" edition = "2021"
rust-version = "1.56" rust-version = "1.57"
[build-dependencies] [build-dependencies]
tauri-build = { path = "../../../core/tauri-build", features = [ "codegen" ] } tauri-build = { path = "../../../core/tauri-build", features = [ "codegen" ] }

View File

@ -3,7 +3,7 @@ name = "splashscreen"
version = "0.1.0" version = "0.1.0"
description = "An example Tauri Application with a splashscreen" description = "An example Tauri Application with a splashscreen"
edition = "2021" edition = "2021"
rust-version = "1.56" rust-version = "1.57"
license = "Apache-2.0 OR MIT" license = "Apache-2.0 OR MIT"
[build-dependencies] [build-dependencies]

View File

@ -3,7 +3,7 @@ name = "state"
version = "0.1.0" version = "0.1.0"
description = "A simple Tauri Appplication showcase the state functionality" description = "A simple Tauri Appplication showcase the state functionality"
edition = "2021" edition = "2021"
rust-version = "1.56" rust-version = "1.57"
[build-dependencies] [build-dependencies]
tauri-build = { path = "../../../core/tauri-build", features = [ "codegen" ] } tauri-build = { path = "../../../core/tauri-build", features = [ "codegen" ] }

View File

@ -3,7 +3,7 @@ name = "streaming"
version = "0.1.0" version = "0.1.0"
description = "A very simple Tauri Appplication" description = "A very simple Tauri Appplication"
edition = "2021" edition = "2021"
rust-version = "1.56" rust-version = "1.57"
[build-dependencies] [build-dependencies]
tauri-build = { path = "../../../core/tauri-build", features = [ "codegen" ] } tauri-build = { path = "../../../core/tauri-build", features = [ "codegen" ] }

View File

@ -3,7 +3,7 @@ name = "updater-example"
version = "0.1.0" version = "0.1.0"
description = "A very simple Tauri Appplication" description = "A very simple Tauri Appplication"
edition = "2021" edition = "2021"
rust-version = "1.56" rust-version = "1.57"
license = "Apache-2.0 OR MIT" license = "Apache-2.0 OR MIT"
[build-dependencies] [build-dependencies]

View File

@ -19,5 +19,8 @@
"covector": "^0.5.3", "covector": "^0.5.3",
"husky": "^6.0.0", "husky": "^6.0.0",
"prettier": "^2.2.1" "prettier": "^2.2.1"
},
"dependencies": {
"typescript": "^4.5.4"
} }
} }

View File

@ -13,17 +13,13 @@
declare global { declare global {
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
interface Window { interface Window {
__TAURI_POST_MESSAGE__: ( __TAURI_IPC__: (message: any) => void,
command: string, ipc: {
args?: { [key: string]: unknown } postMessage: (args: string) => void
) => void }
} }
} }
// the `__TAURI_INVOKE_KEY__` variable is injected at runtime by Tauri
// eslint-disable-next-line @typescript-eslint/naming-convention
declare let __TAURI_INVOKE_KEY__: number
/** @ignore */ /** @ignore */
function uid(): number { function uid(): number {
return window.crypto.getRandomValues(new Uint32Array(1))[0] return window.crypto.getRandomValues(new Uint32Array(1))[0]
@ -80,12 +76,14 @@ async function invoke<T>(cmd: string, args: InvokeArgs = {}): Promise<T> {
Reflect.deleteProperty(window, callback) Reflect.deleteProperty(window, callback)
}, true) }, true)
window.__TAURI_POST_MESSAGE__(cmd, { window.__TAURI_IPC__(
__invokeKey: __TAURI_INVOKE_KEY__, {
callback, cmd,
error, callback,
...args error,
}) ...args
}
)
}) })
} }

View File

@ -5,7 +5,7 @@ name = "tauri_bench"
version = "0.1.0" version = "0.1.0"
authors = [ "Tauri Programme within The Commons Conservancy" ] authors = [ "Tauri Programme within The Commons Conservancy" ]
edition = "2021" edition = "2021"
rust-version = "1.56" rust-version = "1.57"
license = "Apache-2.0 OR MIT" license = "Apache-2.0 OR MIT"
description = "Cross-platform WebView rendering library" description = "Cross-platform WebView rendering library"
repository = "https://github.com/tauri-apps/wry" repository = "https://github.com/tauri-apps/wry"

View File

@ -3,7 +3,7 @@ name = "bench_cpu_intensive"
version = "0.1.0" version = "0.1.0"
description = "A very simple Tauri Appplication" description = "A very simple Tauri Appplication"
edition = "2021" edition = "2021"
rust-version = "1.56" rust-version = "1.57"
[build-dependencies] [build-dependencies]
tauri-build = { path = "../../../../../core/tauri-build", features = [ "codegen" ] } tauri-build = { path = "../../../../../core/tauri-build", features = [ "codegen" ] }

View File

@ -3,7 +3,7 @@ name = "bench_files_transfer"
version = "0.1.0" version = "0.1.0"
description = "A very simple Tauri Appplication" description = "A very simple Tauri Appplication"
edition = "2021" edition = "2021"
rust-version = "1.56" rust-version = "1.57"
[build-dependencies] [build-dependencies]
tauri-build = { path = "../../../../../core/tauri-build", features = [ "codegen" ] } tauri-build = { path = "../../../../../core/tauri-build", features = [ "codegen" ] }

View File

@ -3,7 +3,7 @@ name = "bench_helloworld"
version = "0.1.0" version = "0.1.0"
description = "A very simple Tauri Appplication" description = "A very simple Tauri Appplication"
edition = "2021" edition = "2021"
rust-version = "1.56" rust-version = "1.57"
[build-dependencies] [build-dependencies]
tauri-build = { path = "../../../../../core/tauri-build", features = [ "codegen" ] } tauri-build = { path = "../../../../../core/tauri-build", features = [ "codegen" ] }

View File

@ -13,7 +13,7 @@ keywords = [ "bundle", "cargo", "tauri" ]
repository = "https://github.com/tauri-apps/tauri" repository = "https://github.com/tauri-apps/tauri"
description = "Wrap rust executables in OS-specific app bundles for Tauri" description = "Wrap rust executables in OS-specific app bundles for Tauri"
edition = "2021" edition = "2021"
rust-version = "1.56" rust-version = "1.57"
exclude = [ exclude = [
".license_template", ".license_template",
"CHANGELOG.md", "CHANGELOG.md",

View File

@ -8,7 +8,7 @@ authors = [ "Tauri Programme within The Commons Conservancy" ]
license = "" license = ""
repository = "" repository = ""
edition = "2021" edition = "2021"
rust-version = "1.56" rust-version = "1.57"
[package.metadata.bundle] [package.metadata.bundle]
identifier = "com.tauri.dev" identifier = "com.tauri.dev"

View File

@ -14,6 +14,41 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
[[package]]
name = "aead"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877"
dependencies = [
"generic-array 0.14.4",
]
[[package]]
name = "aes"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8"
dependencies = [
"cfg-if 1.0.0",
"cipher",
"cpufeatures",
"opaque-debug 0.3.0",
]
[[package]]
name = "aes-gcm"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df5f85a83a7d8b0442b6aa7b504b8212c1733da07b98aae43d4bc21b2cb3cdf6"
dependencies = [
"aead",
"aes",
"cipher",
"ctr",
"ghash",
"subtle",
]
[[package]] [[package]]
name = "aho-corasick" name = "aho-corasick"
version = "0.7.18" version = "0.7.18"
@ -112,6 +147,15 @@ dependencies = [
"generic-array 0.14.5", "generic-array 0.14.5",
] ]
[[package]]
name = "block-buffer"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1d36a02058e76b040de25a4464ba1c80935655595b661505c8b39b664828b95"
dependencies = [
"generic-array 0.14.4",
]
[[package]] [[package]]
name = "block-padding" name = "block-padding"
version = "0.1.5" version = "0.1.5"
@ -410,6 +454,15 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "ctr"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea"
dependencies = [
"cipher",
]
[[package]] [[package]]
name = "darling" name = "darling"
version = "0.13.1" version = "0.13.1"
@ -501,6 +554,18 @@ dependencies = [
"subtle", "subtle",
] ]
[[package]]
name = "digest"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b697d66081d42af4fba142d56918a3cb21dc8eb63372c6b85d14f44fb9c5979b"
dependencies = [
"block-buffer 0.10.0",
"crypto-common",
"generic-array 0.14.4",
"subtle",
]
[[package]] [[package]]
name = "dirs-next" name = "dirs-next"
version = "2.0.0" version = "2.0.0"
@ -720,6 +785,16 @@ dependencies = [
"wasi 0.10.2+wasi-snapshot-preview1", "wasi 0.10.2+wasi-snapshot-preview1",
] ]
[[package]]
name = "ghash"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99"
dependencies = [
"opaque-debug 0.3.0",
"polyval",
]
[[package]] [[package]]
name = "gif" name = "gif"
version = "0.11.3" version = "0.11.3"
@ -1586,6 +1661,18 @@ dependencies = [
"miniz_oxide 0.3.7", "miniz_oxide 0.3.7",
] ]
[[package]]
name = "polyval"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1"
dependencies = [
"cfg-if 1.0.0",
"cpufeatures",
"opaque-debug 0.3.0",
"universal-hash",
]
[[package]] [[package]]
name = "ppv-lite86" name = "ppv-lite86"
version = "0.2.16" version = "0.2.16"
@ -1961,7 +2048,7 @@ dependencies = [
"hmac", "hmac",
"pbkdf2", "pbkdf2",
"salsa20", "salsa20",
"sha2", "sha2 0.10.0",
] ]
[[package]] [[package]]
@ -2088,6 +2175,26 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "serialize-to-javascript"
version = "0.1.0"
source = "git+https://github.com/chippers/serialize-to-javascript#38d5026f371bfba4f5197ed143a6667a09375fbd"
dependencies = [
"serde",
"serde_json",
"serialize-to-javascript-impl",
]
[[package]]
name = "serialize-to-javascript-impl"
version = "0.1.0"
source = "git+https://github.com/chippers/serialize-to-javascript#38d5026f371bfba4f5197ed143a6667a09375fbd"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "servo_arc" name = "servo_arc"
version = "0.1.1" version = "0.1.1"
@ -2127,6 +2234,17 @@ dependencies = [
"digest 0.10.1", "digest 0.10.1",
] ]
[[package]]
name = "sha2"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "900d964dd36bb15bcf2f2b35694c072feab74969a54f2bbeec7a2d725d2bdcb6"
dependencies = [
"cfg-if 1.0.0",
"cpufeatures",
"digest 0.10.1",
]
[[package]] [[package]]
name = "shared_child" name = "shared_child"
version = "1.0.0" version = "1.0.0"
@ -2261,7 +2379,7 @@ dependencies = [
"regex", "regex",
"serde", "serde",
"serde_json", "serde_json",
"sha2", "sha2 0.9.8",
"strsim", "strsim",
"tar", "tar",
"tempfile", "tempfile",
@ -2320,6 +2438,8 @@ dependencies = [
name = "tauri-utils" name = "tauri-utils"
version = "1.0.0-beta.3" version = "1.0.0-beta.3"
dependencies = [ dependencies = [
"aes-gcm",
"base64",
"heck", "heck",
"html5ever", "html5ever",
"kuchiki", "kuchiki",
@ -2328,6 +2448,8 @@ dependencies = [
"serde", "serde",
"serde_json", "serde_json",
"serde_with", "serde_with",
"serialize-to-javascript",
"sha2 0.9.8",
"thiserror", "thiserror",
"url", "url",
] ]
@ -2522,6 +2644,16 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "universal-hash"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05"
dependencies = [
"generic-array 0.14.4",
"subtle",
]
[[package]] [[package]]
name = "untrusted" name = "untrusted"
version = "0.7.1" version = "0.7.1"

Some files were not shown because too many files have changed in this diff Show More