mirror of
https://github.com/tauri-apps/plugins-workspace.git
synced 2026-07-22 17:10:53 +02:00
refactor(fs): cfg gate some ios code (#3510)
This commit is contained in:
+106
-109
@@ -74,7 +74,7 @@ pub type CommandResult<T> = std::result::Result<T, CommandError>;
|
||||
/// Represents either a plain PathBuf or a PathHandle that manages security-scoped resources.
|
||||
pub enum PathKind<R: Runtime> {
|
||||
/// A plain path that doesn't manage security-scoped resources.
|
||||
#[allow(dead_code)] // only used on mobile
|
||||
#[cfg(mobile)] // only used on mobile
|
||||
Path(PathBuf),
|
||||
/// A path handle that manages security-scoped resources and will clean them up on drop.
|
||||
Handle(PathHandle<R>),
|
||||
@@ -84,6 +84,7 @@ impl<R: Runtime> PathKind<R> {
|
||||
/// Get a reference to the underlying path.
|
||||
pub fn as_path(&self) -> &Path {
|
||||
match self {
|
||||
#[cfg(mobile)]
|
||||
PathKind::Path(p) => p.as_ref(),
|
||||
PathKind::Handle(h) => h.as_ref(),
|
||||
}
|
||||
@@ -92,6 +93,7 @@ impl<R: Runtime> PathKind<R> {
|
||||
/// Get a reference to the underlying PathBuf.
|
||||
pub fn as_path_buf(&self) -> &PathBuf {
|
||||
match self {
|
||||
#[cfg(mobile)]
|
||||
PathKind::Path(p) => p,
|
||||
PathKind::Handle(h) => h,
|
||||
}
|
||||
@@ -114,27 +116,13 @@ impl<R: Runtime> AsRef<PathBuf> for PathKind<R> {
|
||||
pub struct FileHandle<R: Runtime> {
|
||||
file: File,
|
||||
path: PathKind<R>,
|
||||
#[allow(dead_code)] // Used in Drop implementation
|
||||
#[cfg(target_os = "ios")]
|
||||
path_: SafeFilePath,
|
||||
#[allow(dead_code)] // Used in Drop implementation
|
||||
#[cfg(target_os = "ios")]
|
||||
app_handle: tauri::AppHandle<R>,
|
||||
}
|
||||
|
||||
impl<R: Runtime> FileHandle<R> {
|
||||
fn new(
|
||||
file: File,
|
||||
path: PathKind<R>,
|
||||
path_: SafeFilePath,
|
||||
app_handle: tauri::AppHandle<R>,
|
||||
) -> Self {
|
||||
Self {
|
||||
file,
|
||||
path,
|
||||
path_,
|
||||
app_handle,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the resolved path.
|
||||
pub fn path(&self) -> &Path {
|
||||
self.path.as_path()
|
||||
@@ -155,41 +143,39 @@ impl<R: Runtime> DerefMut for FileHandle<R> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "ios")]
|
||||
impl<R: Runtime> Drop for FileHandle<R> {
|
||||
fn drop(&mut self) {
|
||||
#[cfg(target_os = "ios")]
|
||||
{
|
||||
// Only clean up if we have a plain PathBuf, not a PathHandle
|
||||
// PathHandle will handle its own cleanup when it's dropped
|
||||
if let PathKind::Path(_) = &self.path {
|
||||
use crate::{FilePath, FsExt};
|
||||
// Convert SafeFilePath to FilePath
|
||||
let file_path: FilePath = match &self.path_ {
|
||||
SafeFilePath::Url(url) => FilePath::Url(url.clone()),
|
||||
SafeFilePath::Path(safe_path) => FilePath::Path(safe_path.as_ref().to_owned()),
|
||||
};
|
||||
// Only clean up if we have a plain PathBuf, not a PathHandle
|
||||
// PathHandle will handle its own cleanup when it's dropped
|
||||
if let PathKind::Path(_) = &self.path {
|
||||
use crate::{FilePath, FsExt};
|
||||
// Convert SafeFilePath to FilePath
|
||||
let file_path: FilePath = match &self.path_ {
|
||||
SafeFilePath::Url(url) => FilePath::Url(url.clone()),
|
||||
SafeFilePath::Path(safe_path) => FilePath::Path(safe_path.as_ref().to_owned()),
|
||||
};
|
||||
|
||||
// Only clean up if we're tracking this resource
|
||||
// If start_accessing_security_scoped_resource was used, it won't be in our tracking
|
||||
// and we shouldn't interfere
|
||||
if let FilePath::Url(url) = file_path {
|
||||
if url.scheme() == "file" {
|
||||
let security_scoped_resources =
|
||||
self.app_handle.state::<crate::SecurityScopedResources>();
|
||||
// Only clean up if we're tracking this resource
|
||||
// If start_accessing_security_scoped_resource was used, it won't be in our tracking
|
||||
// and we shouldn't interfere
|
||||
if let FilePath::Url(url) = file_path {
|
||||
if url.scheme() == "file" {
|
||||
let security_scoped_resources =
|
||||
self.app_handle.state::<crate::SecurityScopedResources>();
|
||||
|
||||
// Only clean up if it's not tracked manually
|
||||
if !security_scoped_resources.is_tracked_manually(url.as_str()) {
|
||||
log::debug!("Stopping accessing security-scoped resource for URL: {url} on drop");
|
||||
let _ = self
|
||||
.app_handle
|
||||
.fs()
|
||||
.stop_accessing_security_scoped_resource(FilePath::Url(
|
||||
url.clone(),
|
||||
));
|
||||
security_scoped_resources.remove(url.as_str());
|
||||
} else {
|
||||
log::debug!("Not cleaning up security-scoped resource for URL: {url} on drop (manually tracked via start_accessing_security_scoped_resource)");
|
||||
}
|
||||
// Only clean up if it's not tracked manually
|
||||
if !security_scoped_resources.is_tracked_manually(url.as_str()) {
|
||||
log::debug!(
|
||||
"Stopping accessing security-scoped resource for URL: {url} on drop"
|
||||
);
|
||||
let _ = self
|
||||
.app_handle
|
||||
.fs()
|
||||
.stop_accessing_security_scoped_resource(FilePath::Url(url.clone()));
|
||||
security_scoped_resources.remove(url.as_str());
|
||||
} else {
|
||||
log::debug!("Not cleaning up security-scoped resource for URL: {url} on drop (manually tracked via start_accessing_security_scoped_resource)");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -236,38 +222,36 @@ impl<R: Runtime> AsRef<PathBuf> for PathHandle<R> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "ios")]
|
||||
impl<R: Runtime> Drop for PathHandle<R> {
|
||||
fn drop(&mut self) {
|
||||
#[cfg(target_os = "ios")]
|
||||
{
|
||||
use crate::{FilePath, FsExt};
|
||||
// Convert SafeFilePath to FilePath
|
||||
let file_path: FilePath = match &self.path_ {
|
||||
SafeFilePath::Url(url) => FilePath::Url(url.clone()),
|
||||
SafeFilePath::Path(safe_path) => FilePath::Path(safe_path.as_ref().to_owned()),
|
||||
};
|
||||
use crate::{FilePath, FsExt};
|
||||
// Convert SafeFilePath to FilePath
|
||||
let file_path: FilePath = match &self.path_ {
|
||||
SafeFilePath::Url(url) => FilePath::Url(url.clone()),
|
||||
SafeFilePath::Path(safe_path) => FilePath::Path(safe_path.as_ref().to_owned()),
|
||||
};
|
||||
|
||||
// Only clean up if we're tracking this resource (i.e., resolve_path started it)
|
||||
// If start_accessing_security_scoped_resource was used, it won't be in our tracking
|
||||
// and we shouldn't interfere
|
||||
if let FilePath::Url(url) = file_path {
|
||||
if url.scheme() == "file" {
|
||||
let security_scoped_resources =
|
||||
self.app_handle.state::<crate::SecurityScopedResources>();
|
||||
// Only clean up if we're tracking this resource (i.e., resolve_path started it)
|
||||
// If start_accessing_security_scoped_resource was used, it won't be in our tracking
|
||||
// and we shouldn't interfere
|
||||
if let FilePath::Url(url) = file_path {
|
||||
if url.scheme() == "file" {
|
||||
let security_scoped_resources =
|
||||
self.app_handle.state::<crate::SecurityScopedResources>();
|
||||
|
||||
// Only clean up if it's not tracked manually
|
||||
if !security_scoped_resources.is_tracked_manually(url.as_str()) {
|
||||
log::debug!(
|
||||
"Stopping accessing security-scoped resource for URL: {url} on drop"
|
||||
);
|
||||
let _ = self
|
||||
.app_handle
|
||||
.fs()
|
||||
.stop_accessing_security_scoped_resource(FilePath::Url(url.clone()));
|
||||
security_scoped_resources.remove(url.as_str());
|
||||
} else {
|
||||
log::debug!("Not cleaning up security-scoped resource for URL: {url} on drop (manually tracked via start_accessing_security_scoped_resource)");
|
||||
}
|
||||
// Only clean up if it's not tracked manually
|
||||
if !security_scoped_resources.is_tracked_manually(url.as_str()) {
|
||||
log::debug!(
|
||||
"Stopping accessing security-scoped resource for URL: {url} on drop"
|
||||
);
|
||||
let _ = self
|
||||
.app_handle
|
||||
.fs()
|
||||
.stop_accessing_security_scoped_resource(FilePath::Url(url.clone()));
|
||||
security_scoped_resources.remove(url.as_str());
|
||||
} else {
|
||||
log::debug!("Not cleaning up security-scoped resource for URL: {url} on drop (manually tracked via start_accessing_security_scoped_resource)");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -288,6 +272,7 @@ pub fn create<R: Runtime>(
|
||||
path: SafeFilePath,
|
||||
options: Option<BaseOptions>,
|
||||
) -> CommandResult<ResourceId> {
|
||||
#[cfg(target_os = "ios")]
|
||||
let path_ = path.clone();
|
||||
let resolved_path_handle = resolve_path(
|
||||
"create",
|
||||
@@ -303,13 +288,16 @@ pub fn create<R: Runtime>(
|
||||
resolved_path_handle.display()
|
||||
)
|
||||
})?;
|
||||
#[cfg(target_os = "ios")]
|
||||
let app_handle = webview.app_handle().clone();
|
||||
let file_handle = FileHandle::new(
|
||||
let file_handle = FileHandle {
|
||||
file,
|
||||
PathKind::Handle(resolved_path_handle),
|
||||
path: PathKind::Handle(resolved_path_handle),
|
||||
#[cfg(target_os = "ios")]
|
||||
path_,
|
||||
#[cfg(target_os = "ios")]
|
||||
app_handle,
|
||||
);
|
||||
};
|
||||
let rid = webview
|
||||
.resources_table()
|
||||
.add(StdFileResource::new(file_handle));
|
||||
@@ -1417,6 +1405,7 @@ fn resolve_file_in_fs<R: Runtime>(
|
||||
path: SafeFilePath,
|
||||
open_options: OpenOptions,
|
||||
) -> CommandResult<FileHandle<R>> {
|
||||
#[cfg(target_os = "ios")]
|
||||
let path_ = path.clone();
|
||||
let resolved_path_handle = resolve_path(
|
||||
permission,
|
||||
@@ -1436,13 +1425,16 @@ fn resolve_file_in_fs<R: Runtime>(
|
||||
)
|
||||
})?;
|
||||
|
||||
#[cfg(target_os = "ios")]
|
||||
let app_handle = webview.app_handle().clone();
|
||||
Ok(FileHandle::new(
|
||||
Ok(FileHandle {
|
||||
file,
|
||||
PathKind::Handle(resolved_path_handle),
|
||||
path: PathKind::Handle(resolved_path_handle),
|
||||
#[cfg(target_os = "ios")]
|
||||
path_,
|
||||
#[cfg(target_os = "ios")]
|
||||
app_handle,
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(mobile)]
|
||||
@@ -1456,6 +1448,7 @@ pub fn resolve_file<R: Runtime>(
|
||||
) -> CommandResult<FileHandle<R>> {
|
||||
use crate::FsExt;
|
||||
|
||||
#[cfg(target_os = "ios")]
|
||||
let path_ = path.clone();
|
||||
match path {
|
||||
SafeFilePath::Url(url) => {
|
||||
@@ -1463,13 +1456,16 @@ pub fn resolve_file<R: Runtime>(
|
||||
let file = webview
|
||||
.fs()
|
||||
.open(SafeFilePath::Url(url.clone()), open_options.options)?;
|
||||
#[cfg(target_os = "ios")]
|
||||
let app_handle = webview.app_handle().clone();
|
||||
Ok(FileHandle::new(
|
||||
Ok(FileHandle {
|
||||
file,
|
||||
PathKind::Path(resolved_path),
|
||||
path: PathKind::Path(resolved_path),
|
||||
#[cfg(target_os = "ios")]
|
||||
path_,
|
||||
#[cfg(target_os = "ios")]
|
||||
app_handle,
|
||||
))
|
||||
})
|
||||
}
|
||||
SafeFilePath::Path(path) => resolve_file_in_fs(
|
||||
permission,
|
||||
@@ -1494,36 +1490,37 @@ pub fn resolve_path<R: Runtime>(
|
||||
// On iOS, start accessing security-scoped resource if the path is a file URL
|
||||
// Only if it hasn't been started already via start_accessing_security_scoped_resource
|
||||
#[cfg(target_os = "ios")]
|
||||
{
|
||||
if let SafeFilePath::Url(url) = &path {
|
||||
if url.scheme() == "file" {
|
||||
use objc2_foundation::{NSString, NSURL};
|
||||
if let SafeFilePath::Url(url) = &path {
|
||||
if url.scheme() == "file" {
|
||||
use objc2_foundation::{NSString, NSURL};
|
||||
|
||||
let security_scoped_resources = webview.state::<crate::SecurityScopedResources>();
|
||||
let security_scoped_resources = webview.state::<crate::SecurityScopedResources>();
|
||||
|
||||
// Check if already active (started via start_accessing_security_scoped_resource)
|
||||
if !security_scoped_resources.is_tracked_manually(url.as_str()) {
|
||||
let url_nsstring = NSString::from_str(url.as_str());
|
||||
let ns_url = unsafe { NSURL::URLWithString(&url_nsstring) };
|
||||
if let Some(ns_url) = ns_url {
|
||||
// Start accessing the security-scoped resource
|
||||
// This is required for files outside the app's sandbox (e.g., from file picker)
|
||||
unsafe {
|
||||
let success = ns_url.startAccessingSecurityScopedResource();
|
||||
if success {
|
||||
log::debug!("Started accessing security-scoped resource for URL: {} (via resolve_path)", url.as_str());
|
||||
// Track it so we know to clean it up
|
||||
security_scoped_resources.track_manually(url.as_str().to_string());
|
||||
} else {
|
||||
log::warn!("Failed to start accessing security-scoped resource for URL: {}", url.as_str());
|
||||
}
|
||||
// Check if already active (started via start_accessing_security_scoped_resource)
|
||||
if !security_scoped_resources.is_tracked_manually(url.as_str()) {
|
||||
let url_nsstring = NSString::from_str(url.as_str());
|
||||
let ns_url = unsafe { NSURL::URLWithString(&url_nsstring) };
|
||||
if let Some(ns_url) = ns_url {
|
||||
// Start accessing the security-scoped resource
|
||||
// This is required for files outside the app's sandbox (e.g., from file picker)
|
||||
unsafe {
|
||||
let success = ns_url.startAccessingSecurityScopedResource();
|
||||
if success {
|
||||
log::debug!("Started accessing security-scoped resource for URL: {} (via resolve_path)", url.as_str());
|
||||
// Track it so we know to clean it up
|
||||
security_scoped_resources.track_manually(url.as_str().to_string());
|
||||
} else {
|
||||
log::warn!(
|
||||
"Failed to start accessing security-scoped resource for URL: {}",
|
||||
url.as_str()
|
||||
);
|
||||
}
|
||||
} else {
|
||||
log::debug!("Failed to create NSURL from URL: {}, ignoring security-scoped resource access request", url.as_str());
|
||||
}
|
||||
} else {
|
||||
log::debug!("Security-scoped resource already active for URL: {} (started via start_accessing_security_scoped_resource), skipping", url.as_str());
|
||||
log::debug!("Failed to create NSURL from URL: {}, ignoring security-scoped resource access request", url.as_str());
|
||||
}
|
||||
} else {
|
||||
log::debug!("Security-scoped resource already active for URL: {} (started via start_accessing_security_scoped_resource), skipping", url.as_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user