mirror of
https://github.com/tauri-apps/tauri.git
synced 2026-04-03 10:11:15 +02:00
fix(core): fix raw invoke body for isolation pattern (#10167)
* fix(core): fix raw invoke body for isolation pattern The `isolation` pattern requests are made using JSON but the payload could be raw bytes, so we send the original `Content-Type` from frontend and make sure to deserialize the payload using that one instead of `Content-Type` from request headers * clippy * disable plist embed in generate_context in tests * change file * docs [skip ci] * move unused_variables [skip ci] * last commit regression [skip ci] * fix test * add example, do not text encode raw request * check type instead of contenttype --------- Co-authored-by: Lucas Nogueira <lucas@tauri.app>
This commit is contained in:
6
.changes/generate_context-test.md
Normal file
6
.changes/generate_context-test.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"tauri-macros": "patch"
|
||||
"tauri-codegen": "patch"
|
||||
---
|
||||
|
||||
Add support for `test = true` in `generate_context!` macro to skip some code generation that could affect some tests, for now it only skips empedding a plist on macOS.
|
||||
5
.changes/isolation-raw-request.md
Normal file
5
.changes/isolation-raw-request.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"tauri": "patch:bug"
|
||||
---
|
||||
|
||||
Fix deserialization of raw invoke requests when using `isolation` pattern.
|
||||
5
.changes/utils-raw-isolation-payload.md
Normal file
5
.changes/utils-raw-isolation-payload.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"tauri-utils": "patch:feat"
|
||||
---
|
||||
|
||||
Add `RawIsolationPayload::content_type` method.
|
||||
@@ -129,6 +129,7 @@ impl CodegenContext {
|
||||
root: quote::quote!(::tauri),
|
||||
capabilities: self.capabilities,
|
||||
assets: None,
|
||||
test: false,
|
||||
})?;
|
||||
|
||||
// get the full output file path
|
||||
|
||||
@@ -43,6 +43,8 @@ pub struct ContextData {
|
||||
pub capabilities: Option<Vec<PathBuf>>,
|
||||
/// The custom assets implementation
|
||||
pub assets: Option<Expr>,
|
||||
/// Skip runtime-only types generation for tests (e.g. embed-plist usage).
|
||||
pub test: bool,
|
||||
}
|
||||
|
||||
fn inject_script_hashes(document: &NodeRef, key: &AssetKey, csp_hashes: &mut CspHashes) {
|
||||
@@ -140,8 +142,12 @@ pub fn context_codegen(data: ContextData) -> EmbeddedAssetsResult<TokenStream> {
|
||||
root,
|
||||
capabilities: additional_capabilities,
|
||||
assets,
|
||||
test,
|
||||
} = data;
|
||||
|
||||
#[allow(unused_variables)]
|
||||
let running_tests = test;
|
||||
|
||||
let target = std::env::var("TAURI_ENV_TARGET_TRIPLE")
|
||||
.as_deref()
|
||||
.map(Target::from_triple)
|
||||
@@ -291,7 +297,7 @@ pub fn context_codegen(data: ContextData) -> EmbeddedAssetsResult<TokenStream> {
|
||||
};
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
let info_plist = if target == Target::MacOS && dev {
|
||||
let info_plist = if target == Target::MacOS && dev && !running_tests {
|
||||
let info_plist_path = config_parent.join("Info.plist");
|
||||
let mut info_plist = if info_plist_path.exists() {
|
||||
plist::Value::from_file(&info_plist_path)
|
||||
|
||||
@@ -8,7 +8,7 @@ use std::path::PathBuf;
|
||||
use syn::{
|
||||
parse::{Parse, ParseBuffer},
|
||||
punctuated::Punctuated,
|
||||
Expr, ExprLit, Lit, LitStr, Meta, PathArguments, PathSegment, Token,
|
||||
Expr, ExprLit, Lit, LitBool, LitStr, Meta, PathArguments, PathSegment, Token,
|
||||
};
|
||||
use tauri_codegen::{context_codegen, get_config, ContextData};
|
||||
use tauri_utils::{config::parse::does_supported_file_name_exist, platform::Target};
|
||||
@@ -18,6 +18,7 @@ pub(crate) struct ContextItems {
|
||||
root: syn::Path,
|
||||
capabilities: Option<Vec<PathBuf>>,
|
||||
assets: Option<Expr>,
|
||||
test: bool,
|
||||
}
|
||||
|
||||
impl Parse for ContextItems {
|
||||
@@ -31,6 +32,7 @@ impl Parse for ContextItems {
|
||||
let mut root = None;
|
||||
let mut capabilities = None;
|
||||
let mut assets = None;
|
||||
let mut test = false;
|
||||
let config_file = input.parse::<LitStr>().ok().map(|raw| {
|
||||
let _ = input.parse::<Token![,]>();
|
||||
let path = PathBuf::from(raw.value());
|
||||
@@ -93,6 +95,17 @@ impl Parse for ContextItems {
|
||||
"assets" => {
|
||||
assets.replace(v.value);
|
||||
}
|
||||
"test" => {
|
||||
if let Expr::Lit(ExprLit {
|
||||
lit: Lit::Bool(LitBool { value, .. }),
|
||||
..
|
||||
}) = v.value
|
||||
{
|
||||
test = value;
|
||||
} else {
|
||||
return Err(syn::Error::new(input.span(), "unexpected value for test"));
|
||||
}
|
||||
}
|
||||
name => {
|
||||
return Err(syn::Error::new(
|
||||
input.span(),
|
||||
@@ -105,6 +118,8 @@ impl Parse for ContextItems {
|
||||
return Err(syn::Error::new(input.span(), "unexpected list input"));
|
||||
}
|
||||
}
|
||||
|
||||
let _ = input.parse::<Token![,]>();
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
@@ -128,6 +143,7 @@ impl Parse for ContextItems {
|
||||
}),
|
||||
capabilities,
|
||||
assets,
|
||||
test,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -142,6 +158,7 @@ pub(crate) fn generate_context(context: ContextItems) -> TokenStream {
|
||||
root: context.root.to_token_stream(),
|
||||
capabilities: context.capabilities,
|
||||
assets: context.assets,
|
||||
test: context.test,
|
||||
})
|
||||
.and_then(|data| context_codegen(data).map_err(|e| e.to_string()));
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@ impl<'a> Builder<'a> {
|
||||
acl::build::generate_docs(
|
||||
&permissions,
|
||||
&autogenerated,
|
||||
&name.strip_prefix("tauri-plugin-").unwrap_or(&name),
|
||||
name.strip_prefix("tauri-plugin-").unwrap_or(&name),
|
||||
)?;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,20 +37,27 @@
|
||||
* @param {object} data
|
||||
* @return {Promise<{nonce: number[], payload: number[]}>}
|
||||
*/
|
||||
async function encrypt(data) {
|
||||
async function encrypt(payload) {
|
||||
const algorithm = Object.create(null)
|
||||
algorithm.name = 'AES-GCM'
|
||||
algorithm.iv = window.crypto.getRandomValues(new Uint8Array(12))
|
||||
|
||||
const encoder = new TextEncoder()
|
||||
const encoded = encoder.encode(__RAW_process_ipc_message_fn__(data).data)
|
||||
const { contentType, data } = __RAW_process_ipc_message_fn__(payload)
|
||||
|
||||
const message =
|
||||
typeof data === 'string'
|
||||
? new TextEncoder().encode(data)
|
||||
: ArrayBuffer.isView(data) || data instanceof ArrayBuffer
|
||||
? data
|
||||
: new Uint8Array(data)
|
||||
|
||||
return window.crypto.subtle
|
||||
.encrypt(algorithm, aesGcmKey, encoded)
|
||||
.encrypt(algorithm, aesGcmKey, message)
|
||||
.then((payload) => {
|
||||
const result = Object.create(null)
|
||||
result.nonce = Array.from(new Uint8Array(algorithm.iv))
|
||||
result.payload = Array.from(new Uint8Array(payload))
|
||||
result.contentType = contentType
|
||||
return result
|
||||
})
|
||||
}
|
||||
@@ -66,7 +73,9 @@
|
||||
const keys = data.payload ? Object.keys(data.payload) : []
|
||||
return (
|
||||
keys.length > 0 &&
|
||||
keys.every((key) => key === 'nonce' || key === 'payload')
|
||||
keys.every(
|
||||
(key) => key === 'nonce' || key === 'payload' || key === 'contentType'
|
||||
)
|
||||
)
|
||||
}
|
||||
return false
|
||||
|
||||
@@ -73,6 +73,14 @@ impl AesGcmPair {
|
||||
pub fn key(&self) -> &Aes256Gcm {
|
||||
&self.key
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn encrypt(&self, nonce: &[u8; 12], payload: &[u8]) -> Result<Vec<u8>, Error> {
|
||||
self
|
||||
.key
|
||||
.encrypt(nonce.into(), payload)
|
||||
.map_err(|_| self::Error::Aes)
|
||||
}
|
||||
}
|
||||
|
||||
/// All cryptographic keys required for Isolation encryption
|
||||
@@ -97,7 +105,7 @@ impl Keys {
|
||||
|
||||
/// Decrypts a message using the generated keys.
|
||||
pub fn decrypt(&self, raw: RawIsolationPayload<'_>) -> Result<Vec<u8>, Error> {
|
||||
let RawIsolationPayload { nonce, payload } = raw;
|
||||
let RawIsolationPayload { nonce, payload, .. } = raw;
|
||||
let nonce: [u8; 12] = nonce.as_ref().try_into()?;
|
||||
self
|
||||
.aes_gcm
|
||||
@@ -109,9 +117,18 @@ impl Keys {
|
||||
|
||||
/// Raw representation of
|
||||
#[derive(Debug, serde::Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RawIsolationPayload<'a> {
|
||||
nonce: Cow<'a, [u8]>,
|
||||
payload: Cow<'a, [u8]>,
|
||||
content_type: Cow<'a, str>,
|
||||
}
|
||||
|
||||
impl<'a> RawIsolationPayload<'a> {
|
||||
/// Content type of this payload.
|
||||
pub fn content_type(&self) -> &Cow<'a, str> {
|
||||
&self.content_type
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a Vec<u8>> for RawIsolationPayload<'a> {
|
||||
|
||||
@@ -39,7 +39,9 @@
|
||||
const keys = Object.keys(event.data.payload || {})
|
||||
return (
|
||||
keys.length > 0 &&
|
||||
keys.every((key) => key === 'nonce' || key === 'payload')
|
||||
keys.every(
|
||||
(key) => key === 'contentType' || key === 'nonce' || key === 'payload'
|
||||
)
|
||||
)
|
||||
}
|
||||
return false
|
||||
|
||||
@@ -46,6 +46,7 @@ pub type OwnedInvokeResponder<R> =
|
||||
|
||||
/// Possible values of an IPC payload.
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub enum InvokeBody {
|
||||
/// Json payload.
|
||||
Json(JsonValue),
|
||||
|
||||
@@ -388,6 +388,15 @@ fn parse_invoke_request<R: Runtime>(
|
||||
// so we must ignore it because some commands use the IPC for faster response
|
||||
let has_payload = !body.is_empty();
|
||||
|
||||
#[allow(unused_mut)]
|
||||
let mut content_type = parts
|
||||
.headers
|
||||
.get(http::header::CONTENT_TYPE)
|
||||
.and_then(|h| h.to_str().ok())
|
||||
.map(|mime| mime.parse())
|
||||
.unwrap_or(Ok(mime::APPLICATION_OCTET_STREAM))
|
||||
.map_err(|_| "unknown content type")?;
|
||||
|
||||
#[cfg(feature = "isolation")]
|
||||
if let crate::Pattern::Isolation { crypto_keys, .. } = &*manager.pattern {
|
||||
// if the platform does not support request body, we ignore it
|
||||
@@ -395,8 +404,18 @@ fn parse_invoke_request<R: Runtime>(
|
||||
#[cfg(feature = "tracing")]
|
||||
let _span = tracing::trace_span!("ipc::request::decrypt_isolation_payload").entered();
|
||||
|
||||
body = crate::utils::pattern::isolation::RawIsolationPayload::try_from(&body)
|
||||
.and_then(|raw| crypto_keys.decrypt(raw))
|
||||
(body, content_type) = crate::utils::pattern::isolation::RawIsolationPayload::try_from(&body)
|
||||
.and_then(|raw| {
|
||||
let content_type = raw.content_type().clone();
|
||||
crypto_keys.decrypt(raw).map(|decrypted| {
|
||||
(
|
||||
decrypted,
|
||||
content_type
|
||||
.parse()
|
||||
.unwrap_or(mime::APPLICATION_OCTET_STREAM),
|
||||
)
|
||||
})
|
||||
})
|
||||
.map_err(|e| e.to_string())?;
|
||||
}
|
||||
}
|
||||
@@ -440,14 +459,6 @@ fn parse_invoke_request<R: Runtime>(
|
||||
.map_err(|_| "Tauri error header value must be a numeric string")?,
|
||||
);
|
||||
|
||||
let content_type = parts
|
||||
.headers
|
||||
.get(http::header::CONTENT_TYPE)
|
||||
.and_then(|h| h.to_str().ok())
|
||||
.map(|mime| mime.parse())
|
||||
.unwrap_or(Ok(mime::APPLICATION_OCTET_STREAM))
|
||||
.map_err(|_| "unknown content type")?;
|
||||
|
||||
#[cfg(feature = "tracing")]
|
||||
let span = tracing::trace_span!("ipc::request::deserialize").entered();
|
||||
|
||||
@@ -481,3 +492,194 @@ fn parse_invoke_request<R: Runtime>(
|
||||
|
||||
Ok(payload)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::str::FromStr;
|
||||
|
||||
use super::*;
|
||||
use crate::{manager::AppManager, plugin::PluginStore, StateManager, Wry};
|
||||
use http::header::*;
|
||||
use serde_json::json;
|
||||
use tauri_macros::generate_context;
|
||||
|
||||
#[test]
|
||||
fn parse_invoke_request() {
|
||||
let context = generate_context!("test/fixture/src-tauri/tauri.conf.json", crate, test = true);
|
||||
let manager: AppManager<Wry> = AppManager::with_handlers(
|
||||
context,
|
||||
PluginStore::default(),
|
||||
Box::new(|_| false),
|
||||
None,
|
||||
Default::default(),
|
||||
StateManager::new(),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
(None, "".into()),
|
||||
crate::generate_invoke_key().unwrap(),
|
||||
);
|
||||
|
||||
let cmd = "write_something";
|
||||
let url = "tauri://localhost";
|
||||
let invoke_key = "1234ahdsjkl123";
|
||||
let callback = 12378123;
|
||||
let error = 6243;
|
||||
let headers = HeaderMap::from_iter(vec![
|
||||
(
|
||||
CONTENT_TYPE,
|
||||
HeaderValue::from_str(mime::APPLICATION_OCTET_STREAM.as_ref()).unwrap(),
|
||||
),
|
||||
(
|
||||
HeaderName::from_str(TAURI_INVOKE_KEY_HEADER_NAME).unwrap(),
|
||||
HeaderValue::from_str(invoke_key).unwrap(),
|
||||
),
|
||||
(
|
||||
HeaderName::from_str(TAURI_CALLBACK_HEADER_NAME).unwrap(),
|
||||
HeaderValue::from_str(&callback.to_string()).unwrap(),
|
||||
),
|
||||
(
|
||||
HeaderName::from_str(TAURI_ERROR_HEADER_NAME).unwrap(),
|
||||
HeaderValue::from_str(&error.to_string()).unwrap(),
|
||||
),
|
||||
(ORIGIN, HeaderValue::from_str("tauri://localhost").unwrap()),
|
||||
]);
|
||||
|
||||
let mut request = Request::builder().uri(format!("ipc://localhost/{cmd}"));
|
||||
*request.headers_mut().unwrap() = headers.clone();
|
||||
|
||||
let body = vec![123, 31, 45];
|
||||
let request = request.body(body.clone()).unwrap();
|
||||
let invoke_request = super::parse_invoke_request(&manager, request).unwrap();
|
||||
|
||||
assert_eq!(invoke_request.cmd, cmd);
|
||||
assert_eq!(invoke_request.callback.0, callback);
|
||||
assert_eq!(invoke_request.error.0, error);
|
||||
assert_eq!(invoke_request.invoke_key, invoke_key);
|
||||
assert_eq!(invoke_request.url, url.parse().unwrap());
|
||||
assert_eq!(invoke_request.headers, headers);
|
||||
assert_eq!(invoke_request.body, InvokeBody::Raw(body));
|
||||
|
||||
let body = json!({
|
||||
"key": 1,
|
||||
"anotherKey": "asda",
|
||||
});
|
||||
|
||||
let mut headers = headers.clone();
|
||||
headers.insert(
|
||||
CONTENT_TYPE,
|
||||
HeaderValue::from_str(mime::APPLICATION_JSON.as_ref()).unwrap(),
|
||||
);
|
||||
|
||||
let mut request = Request::builder().uri(format!("ipc://localhost/{cmd}"));
|
||||
*request.headers_mut().unwrap() = headers.clone();
|
||||
|
||||
let request = request.body(serde_json::to_vec(&body).unwrap()).unwrap();
|
||||
let invoke_request = super::parse_invoke_request(&manager, request).unwrap();
|
||||
|
||||
assert_eq!(invoke_request.headers, headers);
|
||||
assert_eq!(invoke_request.body, InvokeBody::Json(body));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "isolation")]
|
||||
fn parse_invoke_request_isolation() {
|
||||
let context = generate_context!(
|
||||
"test/fixture/isolation/src-tauri/tauri.conf.json",
|
||||
crate,
|
||||
test = false
|
||||
);
|
||||
|
||||
let crate::pattern::Pattern::Isolation { crypto_keys, .. } = &context.pattern else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let mut nonce = [0u8; 12];
|
||||
getrandom::getrandom(&mut nonce).unwrap();
|
||||
|
||||
let body_raw = vec![1, 41, 65, 12, 78];
|
||||
let body_bytes = crypto_keys.aes_gcm().encrypt(&nonce, &body_raw).unwrap();
|
||||
let isolation_payload_raw = json!({
|
||||
"nonce": nonce,
|
||||
"payload": body_bytes,
|
||||
"contentType": mime::APPLICATION_OCTET_STREAM.to_string(),
|
||||
});
|
||||
|
||||
let body_json = json!({
|
||||
"key": 1,
|
||||
"anotherKey": "string"
|
||||
});
|
||||
let body_bytes = crypto_keys
|
||||
.aes_gcm()
|
||||
.encrypt(&nonce, &serde_json::to_vec(&body_json).unwrap())
|
||||
.unwrap();
|
||||
let isolation_payload_json = json!({
|
||||
"nonce": nonce,
|
||||
"payload": body_bytes,
|
||||
"contentType": mime::APPLICATION_JSON.to_string(),
|
||||
});
|
||||
|
||||
let manager: AppManager<Wry> = AppManager::with_handlers(
|
||||
context,
|
||||
PluginStore::default(),
|
||||
Box::new(|_| false),
|
||||
None,
|
||||
Default::default(),
|
||||
StateManager::new(),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
(None, "".into()),
|
||||
crate::generate_invoke_key().unwrap(),
|
||||
);
|
||||
|
||||
let cmd = "write_something";
|
||||
let url = "tauri://localhost";
|
||||
let invoke_key = "1234ahdsjkl123";
|
||||
let callback = 12378123;
|
||||
let error = 6243;
|
||||
|
||||
let headers = HeaderMap::from_iter(vec![
|
||||
(
|
||||
CONTENT_TYPE,
|
||||
HeaderValue::from_str(mime::APPLICATION_JSON.as_ref()).unwrap(),
|
||||
),
|
||||
(
|
||||
HeaderName::from_str(TAURI_INVOKE_KEY_HEADER_NAME).unwrap(),
|
||||
HeaderValue::from_str(invoke_key).unwrap(),
|
||||
),
|
||||
(
|
||||
HeaderName::from_str(TAURI_CALLBACK_HEADER_NAME).unwrap(),
|
||||
HeaderValue::from_str(&callback.to_string()).unwrap(),
|
||||
),
|
||||
(
|
||||
HeaderName::from_str(TAURI_ERROR_HEADER_NAME).unwrap(),
|
||||
HeaderValue::from_str(&error.to_string()).unwrap(),
|
||||
),
|
||||
(ORIGIN, HeaderValue::from_str("tauri://localhost").unwrap()),
|
||||
]);
|
||||
|
||||
let mut request = Request::builder().uri(format!("ipc://localhost/{cmd}"));
|
||||
*request.headers_mut().unwrap() = headers.clone();
|
||||
let body = serde_json::to_vec(&isolation_payload_raw).unwrap();
|
||||
let request = request.body(body).unwrap();
|
||||
let invoke_request = super::parse_invoke_request(&manager, request).unwrap();
|
||||
|
||||
assert_eq!(invoke_request.cmd, cmd);
|
||||
assert_eq!(invoke_request.callback.0, callback);
|
||||
assert_eq!(invoke_request.error.0, error);
|
||||
assert_eq!(invoke_request.invoke_key, invoke_key);
|
||||
assert_eq!(invoke_request.url, url.parse().unwrap());
|
||||
assert_eq!(invoke_request.headers, headers);
|
||||
assert_eq!(invoke_request.body, InvokeBody::Raw(body_raw));
|
||||
|
||||
let mut request = Request::builder().uri(format!("ipc://localhost/{cmd}"));
|
||||
*request.headers_mut().unwrap() = headers.clone();
|
||||
let body = serde_json::to_vec(&isolation_payload_json).unwrap();
|
||||
let request = request.body(body).unwrap();
|
||||
let invoke_request = super::parse_invoke_request(&manager, request).unwrap();
|
||||
|
||||
assert_eq!(invoke_request.headers, headers);
|
||||
assert_eq!(invoke_request.body, InvokeBody::Json(body_json));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -692,7 +692,7 @@ mod test {
|
||||
|
||||
#[test]
|
||||
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, test = true);
|
||||
let manager: AppManager<Wry> = AppManager::with_handlers(
|
||||
context,
|
||||
PluginStore::default(),
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#![cfg(target_os = "macos")]
|
||||
#![allow(deprecated)]
|
||||
|
||||
use crate::utils::config::WindowEffectsConfig;
|
||||
|
||||
6
core/tauri/test/fixture/isolation/dist/index.html
vendored
Normal file
6
core/tauri/test/fixture/isolation/dist/index.html
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
<html>
|
||||
<head></head>
|
||||
<body>
|
||||
<iframe id="mainframe"></iframe>
|
||||
</body>
|
||||
</html>
|
||||
10
core/tauri/test/fixture/isolation/isolation-dist/index.html
Normal file
10
core/tauri/test/fixture/isolation/isolation-dist/index.html
Normal 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>
|
||||
@@ -0,0 +1,8 @@
|
||||
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
window.__TAURI_ISOLATION_HOOK__ = (payload, options) => {
|
||||
console.log('hook', payload, options)
|
||||
return payload
|
||||
}
|
||||
BIN
core/tauri/test/fixture/isolation/src-tauri/icons/icon.ico
Normal file
BIN
core/tauri/test/fixture/isolation/src-tauri/icons/icon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 85 KiB |
BIN
core/tauri/test/fixture/isolation/src-tauri/icons/icon.ico~dev
Normal file
BIN
core/tauri/test/fixture/isolation/src-tauri/icons/icon.ico~dev
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 56 KiB |
BIN
core/tauri/test/fixture/isolation/src-tauri/icons/icon.png
Normal file
BIN
core/tauri/test/fixture/isolation/src-tauri/icons/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 974 B |
27
core/tauri/test/fixture/isolation/src-tauri/tauri.conf.json
Normal file
27
core/tauri/test/fixture/isolation/src-tauri/tauri.conf.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"$schema": "../../../../../../core/tauri-config-schema/schema.json",
|
||||
"identifier": "isolation.tauri.example",
|
||||
"build": {
|
||||
"frontendDist": "../dist",
|
||||
"devUrl": "http://localhost:4000"
|
||||
},
|
||||
"app": {
|
||||
"windows": [
|
||||
{
|
||||
"title": "Isolation Tauri App"
|
||||
}
|
||||
],
|
||||
"security": {
|
||||
"csp": "default-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self'; connect-src ipc: http://ipc.localhost",
|
||||
"pattern": {
|
||||
"use": "isolation",
|
||||
"options": {
|
||||
"dir": "../isolation-dist"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"bundle": {
|
||||
"active": true
|
||||
}
|
||||
}
|
||||
@@ -10,9 +10,11 @@ fn main() {
|
||||
"app-menu",
|
||||
tauri_build::InlinedPlugin::new().commands(&["toggle", "popup"]),
|
||||
)
|
||||
.app_manifest(
|
||||
tauri_build::AppManifest::new().commands(&["log_operation", "perform_request"]),
|
||||
),
|
||||
.app_manifest(tauri_build::AppManifest::new().commands(&[
|
||||
"log_operation",
|
||||
"perform_request",
|
||||
"echo",
|
||||
])),
|
||||
)
|
||||
.expect("failed to run tauri-build");
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
]
|
||||
},
|
||||
"allow-perform-request",
|
||||
"allow-echo",
|
||||
"app-menu:default",
|
||||
"sample:allow-ping-scoped",
|
||||
"sample:global-scope",
|
||||
@@ -68,4 +69,4 @@
|
||||
"webview:allow-create-webview-window",
|
||||
"webview:allow-print"
|
||||
]
|
||||
}
|
||||
}
|
||||
11
examples/api/src-tauri/permissions/autogenerated/echo.toml
Normal file
11
examples/api/src-tauri/permissions/autogenerated/echo.toml
Normal file
@@ -0,0 +1,11 @@
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-echo"
|
||||
description = "Enables the echo command without any pre-configured scope."
|
||||
commands.allow = ["echo"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-echo"
|
||||
description = "Denies the echo command without any pre-configured scope."
|
||||
commands.deny = ["echo"]
|
||||
@@ -45,3 +45,8 @@ pub fn perform_request(endpoint: String, body: RequestBody) -> ApiResponse {
|
||||
message: "message response".into(),
|
||||
}
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub fn echo(request: tauri::ipc::Request<'_>) -> tauri::ipc::Response {
|
||||
tauri::ipc::Response::new(request.body().clone())
|
||||
}
|
||||
|
||||
@@ -141,6 +141,7 @@ pub fn run_app<R: Runtime, F: FnOnce(&App<R>) + Send + 'static>(
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
cmd::log_operation,
|
||||
cmd::perform_request,
|
||||
cmd::echo
|
||||
])
|
||||
.build(tauri::tauri_build_context!())
|
||||
.expect("error while building tauri application");
|
||||
|
||||
@@ -83,12 +83,22 @@
|
||||
let messages = writable([])
|
||||
let consoleTextEl
|
||||
async function onMessage(value) {
|
||||
const valueStr =
|
||||
typeof value === 'string'
|
||||
? value
|
||||
: JSON.stringify(
|
||||
value instanceof ArrayBuffer
|
||||
? Array.from(new Uint8Array(value))
|
||||
: value,
|
||||
null,
|
||||
1
|
||||
)
|
||||
messages.update((r) => [
|
||||
...r,
|
||||
{
|
||||
html:
|
||||
`<pre><strong class="text-accent dark:text-darkAccent">[${new Date().toLocaleTimeString()}]:</strong> ` +
|
||||
(typeof value === 'string' ? value : JSON.stringify(value, null, 1)) +
|
||||
valueStr +
|
||||
'</pre>'
|
||||
}
|
||||
])
|
||||
|
||||
@@ -36,6 +36,16 @@
|
||||
.catch(onMessage)
|
||||
}
|
||||
|
||||
function echo() {
|
||||
invoke('echo', {
|
||||
message: 'Tauri JSON request!'
|
||||
})
|
||||
.then(onMessage)
|
||||
.catch(onMessage)
|
||||
|
||||
invoke('echo', [1, 2, 3]).then(onMessage).catch(onMessage)
|
||||
}
|
||||
|
||||
function emitEvent() {
|
||||
webviewWindow.emit('js-event', 'this is the payload string')
|
||||
}
|
||||
@@ -49,4 +59,5 @@
|
||||
<button class="btn" id="event" on:click={emitEvent}>
|
||||
Send event to Rust
|
||||
</button>
|
||||
<button class="btn" id="request" on:click={echo}> Echo </button>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user