fix ext fetch
This commit is contained in:
@@ -1,6 +1,5 @@
|
|||||||
use std::{
|
use std::{
|
||||||
collections::{BTreeMap, BTreeSet},
|
collections::{BTreeMap, BTreeSet},
|
||||||
fs,
|
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -14,7 +13,7 @@ use crate::{
|
|||||||
BrowserConfigEntry, BrowserStats, BrowserView, ExtensionSummary, ProfileSummary,
|
BrowserConfigEntry, BrowserStats, BrowserView, ExtensionSummary, ProfileSummary,
|
||||||
ScanResponse, TempBookmark, TempExtension,
|
ScanResponse, TempBookmark, TempExtension,
|
||||||
},
|
},
|
||||||
utils::{first_non_empty, load_image_as_data_url, pick_latest_subdirectory, read_json_file},
|
utils::{first_non_empty, load_image_as_data_url, read_json_file},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn scan_browsers(app: &AppHandle) -> Result<ScanResponse, String> {
|
pub fn scan_browsers(app: &AppHandle) -> Result<ScanResponse, String> {
|
||||||
@@ -192,37 +191,38 @@ fn scan_extensions_for_profile(
|
|||||||
profile: &ProfileSummary,
|
profile: &ProfileSummary,
|
||||||
extensions: &mut BTreeMap<String, TempExtension>,
|
extensions: &mut BTreeMap<String, TempExtension>,
|
||||||
) {
|
) {
|
||||||
let extensions_root = profile_path.join("Extensions");
|
let secure_preferences_path = profile_path.join("Secure Preferences");
|
||||||
let Ok(entries) = fs::read_dir(&extensions_root) else {
|
let Some(secure_preferences) = read_json_file(&secure_preferences_path) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
for entry in entries.flatten() {
|
let Some(extension_settings) = secure_preferences
|
||||||
let Ok(file_type) = entry.file_type() else {
|
.get("extensions")
|
||||||
continue;
|
.and_then(|value| value.get("settings"))
|
||||||
|
.and_then(Value::as_object)
|
||||||
|
else {
|
||||||
|
return;
|
||||||
};
|
};
|
||||||
if !file_type.is_dir() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let extension_id = entry.file_name().to_string_lossy().to_string();
|
for (extension_id, extension_value) in extension_settings {
|
||||||
let Some(version_path) = pick_latest_subdirectory(&entry.path()) else {
|
let Some(manifest) = extension_value.get("manifest") else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
let manifest_path = version_path.join("manifest.json");
|
let Some(install_dir) =
|
||||||
let Some(manifest) = read_json_file(&manifest_path) else {
|
resolve_extension_install_dir(profile_path, extension_id, extension_value)
|
||||||
|
else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
let name = resolve_extension_name(&manifest, &version_path)
|
let name = resolve_extension_name(manifest, &install_dir)
|
||||||
.filter(|value| !value.is_empty())
|
.filter(|value| !value.is_empty())
|
||||||
.unwrap_or_else(|| extension_id.clone());
|
.unwrap_or_else(|| extension_id.clone());
|
||||||
let version = manifest
|
let version = manifest
|
||||||
.get("version")
|
.get("version")
|
||||||
.and_then(Value::as_str)
|
.and_then(Value::as_str)
|
||||||
.map(str::to_string);
|
.map(str::to_string);
|
||||||
let icon_data_url = resolve_extension_icon(&manifest, &version_path);
|
let icon_data_url = resolve_extension_icon(manifest, &install_dir);
|
||||||
|
|
||||||
let entry = extensions
|
let entry = extensions
|
||||||
.entry(extension_id.clone())
|
.entry(extension_id.clone())
|
||||||
@@ -235,7 +235,7 @@ fn scan_extensions_for_profile(
|
|||||||
profiles: BTreeMap::new(),
|
profiles: BTreeMap::new(),
|
||||||
});
|
});
|
||||||
|
|
||||||
if entry.name == entry.id && name != extension_id {
|
if entry.name == entry.id && name != *extension_id {
|
||||||
entry.name = name.clone();
|
entry.name = name.clone();
|
||||||
}
|
}
|
||||||
if entry.version.is_none() {
|
if entry.version.is_none() {
|
||||||
@@ -260,6 +260,30 @@ fn scan_extensions_for_profile(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resolve_extension_install_dir(
|
||||||
|
profile_path: &Path,
|
||||||
|
extension_id: &str,
|
||||||
|
extension_value: &Value,
|
||||||
|
) -> Option<PathBuf> {
|
||||||
|
let raw_path = extension_value
|
||||||
|
.get("path")
|
||||||
|
.and_then(Value::as_str)
|
||||||
|
.map(str::trim)
|
||||||
|
.filter(|value| !value.is_empty())?;
|
||||||
|
|
||||||
|
let normalized_path = raw_path.trim_start_matches('/');
|
||||||
|
let candidate = PathBuf::from(normalized_path);
|
||||||
|
let resolved = if normalized_path.starts_with(extension_id) {
|
||||||
|
profile_path.join("Extensions").join(candidate)
|
||||||
|
} else if candidate.is_absolute() {
|
||||||
|
candidate
|
||||||
|
} else {
|
||||||
|
PathBuf::from(raw_path)
|
||||||
|
};
|
||||||
|
|
||||||
|
resolved.is_dir().then_some(resolved)
|
||||||
|
}
|
||||||
|
|
||||||
fn resolve_extension_name(manifest: &Value, version_path: &Path) -> Option<String> {
|
fn resolve_extension_name(manifest: &Value, version_path: &Path) -> Option<String> {
|
||||||
let raw_name = manifest.get("name").and_then(Value::as_str)?;
|
let raw_name = manifest.get("name").and_then(Value::as_str)?;
|
||||||
if let Some(localized_name) = resolve_localized_manifest_value(raw_name, manifest, version_path)
|
if let Some(localized_name) = resolve_localized_manifest_value(raw_name, manifest, version_path)
|
||||||
@@ -333,9 +357,10 @@ fn resolve_extension_icon(manifest: &Value, version_path: &Path) -> Option<Strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
candidates.sort_by(|left, right| right.0.cmp(&left.0));
|
candidates.sort_by(|left, right| right.0.cmp(&left.0));
|
||||||
candidates
|
candidates.into_iter().find_map(|(_, relative_path)| {
|
||||||
.into_iter()
|
let normalized_path = relative_path.trim_start_matches('/');
|
||||||
.find_map(|(_, relative_path)| load_image_as_data_url(&version_path.join(relative_path)))
|
load_image_as_data_url(&version_path.join(normalized_path))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn icon_candidates_from_object(map: &serde_json::Map<String, Value>) -> Vec<(u32, String)> {
|
fn icon_candidates_from_object(map: &serde_json::Map<String, Value>) -> Vec<(u32, String)> {
|
||||||
|
|||||||
@@ -14,29 +14,6 @@ pub fn local_app_data_dir() -> Option<PathBuf> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pick_latest_subdirectory(root: &Path) -> Option<PathBuf> {
|
|
||||||
let entries = fs::read_dir(root).ok()?;
|
|
||||||
let mut candidates = entries
|
|
||||||
.flatten()
|
|
||||||
.filter_map(|entry| {
|
|
||||||
let file_type = entry.file_type().ok()?;
|
|
||||||
if !file_type.is_dir() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let metadata = entry.metadata().ok()?;
|
|
||||||
let modified = metadata.modified().ok();
|
|
||||||
Some((
|
|
||||||
modified,
|
|
||||||
entry.file_name().to_string_lossy().to_string(),
|
|
||||||
entry.path(),
|
|
||||||
))
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
candidates.sort_by(|left, right| right.0.cmp(&left.0).then_with(|| right.1.cmp(&left.1)));
|
|
||||||
candidates.into_iter().next().map(|(_, _, path)| path)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_image_as_data_url(path: &Path) -> Option<String> {
|
pub fn load_image_as_data_url(path: &Path) -> Option<String> {
|
||||||
let bytes = fs::read(path).ok()?;
|
let bytes = fs::read(path).ok()?;
|
||||||
let extension = path
|
let extension = path
|
||||||
|
|||||||
Reference in New Issue
Block a user