refactor(fs): cfg gate some ios code (#3510)

This commit is contained in:
Tony
2026-07-21 14:42:45 +08:00
committed by GitHub
parent 526726162f
commit 7e45774610
+106 -109
View File
@@ -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());
}
}
}