From 33a5ca845a5700e28c1d886cbf151aebdf6af791 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 6 Jun 2025 13:12:40 +0800 Subject: [PATCH 1/6] Support Msix FireFox bookmarks --- .../FirefoxBookmarkLoader.cs | 133 +++++++++++------- 1 file changed, 82 insertions(+), 51 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs index acace2506d4..42a288e3a97 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs @@ -264,83 +264,114 @@ public class FirefoxBookmarkLoader : FirefoxBookmarkLoaderBase /// public override List GetBookmarks() { - return GetBookmarksFromPath(PlacesPath); + var bookmarks1 = GetBookmarksFromPath(PlacesPath); + var bookmarks2 = GetBookmarksFromPath(MsixPlacesPath); + return bookmarks1.Concat(bookmarks2).ToList(); } /// - /// Path to places.sqlite + /// Path to places.sqlite of Msi installer + /// E.g. C:\Users\{UserName}\AppData\Roaming\Mozilla\Firefox + /// /// - /// private static string PlacesPath { get { var profileFolderPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"Mozilla\Firefox"); - var profileIni = Path.Combine(profileFolderPath, @"profiles.ini"); - - if (!File.Exists(profileIni)) - return string.Empty; + return GetProfileIniPath(profileFolderPath); + } + } - // get firefox default profile directory from profiles.ini - using var sReader = new StreamReader(profileIni); - var ini = sReader.ReadToEnd(); + /// + /// Path to places.sqlite of MSIX installer + /// E.g. C:\Users\{UserName}\AppData\Local\Packages\Mozilla.Firefox_n80bbvh6b1yt2\LocalCache\Roaming\Mozilla\Firefox + /// + /// + public static string MsixPlacesPath + { + get + { + var platformPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + var packagesPath = Path.Combine(platformPath, "Packages"); + + // Search for folder with Mozilla.Firefox prefix + var firefoxPackageFolder = Directory.EnumerateDirectories(packagesPath, "Mozilla.Firefox*", + SearchOption.TopDirectoryOnly).FirstOrDefault(); + + // Msix FireFox not installed + if (firefoxPackageFolder == null) return string.Empty; + + var profileFolderPath = Path.Combine(firefoxPackageFolder, @"LocalCache\Roaming\Mozilla\Firefox"); + return GetProfileIniPath(profileFolderPath); + } + } - var lines = ini.Split("\r\n").ToList(); + private static string GetProfileIniPath(string profileFolderPath) + { + var profileIni = Path.Combine(profileFolderPath, @"profiles.ini"); + if (!File.Exists(profileIni)) + return string.Empty; - var defaultProfileFolderNameRaw = lines.FirstOrDefault(x => x.Contains("Default=") && x != "Default=1") ?? string.Empty; + // get firefox default profile directory from profiles.ini + using var sReader = new StreamReader(profileIni); + var ini = sReader.ReadToEnd(); - if (string.IsNullOrEmpty(defaultProfileFolderNameRaw)) - return string.Empty; + var lines = ini.Split("\r\n").ToList(); - var defaultProfileFolderName = defaultProfileFolderNameRaw.Split('=').Last(); + var defaultProfileFolderNameRaw = lines.FirstOrDefault(x => x.Contains("Default=") && x != "Default=1") ?? string.Empty; - var indexOfDefaultProfileAttributePath = lines.IndexOf("Path=" + defaultProfileFolderName); + if (string.IsNullOrEmpty(defaultProfileFolderNameRaw)) + return string.Empty; - /* - Current profiles.ini structure example as of Firefox version 69.0.1 + var defaultProfileFolderName = defaultProfileFolderNameRaw.Split('=').Last(); - [Install736426B0AF4A39CB] - Default=Profiles/7789f565.default-release <== this is the default profile this plugin will get the bookmarks from. When opened Firefox will load the default profile - Locked=1 + var indexOfDefaultProfileAttributePath = lines.IndexOf("Path=" + defaultProfileFolderName); - [Profile2] - Name=newblahprofile - IsRelative=0 - Path=C:\t6h2yuq8.newblahprofile <== Note this is a custom location path for the profile user can set, we need to cater for this in code. + /* + Current profiles.ini structure example as of Firefox version 69.0.1 - [Profile1] - Name=default - IsRelative=1 - Path=Profiles/cydum7q4.default - Default=1 + [Install736426B0AF4A39CB] + Default=Profiles/7789f565.default-release <== this is the default profile this plugin will get the bookmarks from. When opened Firefox will load the default profile + Locked=1 - [Profile0] - Name=default-release - IsRelative=1 - Path=Profiles/7789f565.default-release + [Profile2] + Name=newblahprofile + IsRelative=0 + Path=C:\t6h2yuq8.newblahprofile <== Note this is a custom location path for the profile user can set, we need to cater for this in code. - [General] - StartWithLastProfile=1 - Version=2 - */ - // Seen in the example above, the IsRelative attribute is always above the Path attribute + [Profile1] + Name=default + IsRelative=1 + Path=Profiles/cydum7q4.default + Default=1 - var relativePath = Path.Combine(defaultProfileFolderName, "places.sqlite"); - var absoluePath = Path.Combine(profileFolderPath, relativePath); + [Profile0] + Name=default-release + IsRelative=1 + Path=Profiles/7789f565.default-release - // If the index is out of range, it means that the default profile is in a custom location or the file is malformed - // If the profile is in a custom location, we need to check - if (indexOfDefaultProfileAttributePath - 1 < 0 || - indexOfDefaultProfileAttributePath - 1 >= lines.Count) - { - return Directory.Exists(absoluePath) ? absoluePath : relativePath; - } + [General] + StartWithLastProfile=1 + Version=2 + */ + // Seen in the example above, the IsRelative attribute is always above the Path attribute - var relativeAttribute = lines[indexOfDefaultProfileAttributePath - 1]; + var relativePath = Path.Combine(defaultProfileFolderName, "places.sqlite"); + var absoluePath = Path.Combine(profileFolderPath, relativePath); - return relativeAttribute == "0" // See above, the profile is located in a custom location, path is not relative, so IsRelative=0 - ? relativePath : absoluePath; + // If the index is out of range, it means that the default profile is in a custom location or the file is malformed + // If the profile is in a custom location, we need to check + if (indexOfDefaultProfileAttributePath - 1 < 0 || + indexOfDefaultProfileAttributePath - 1 >= lines.Count) + { + return Directory.Exists(absoluePath) ? absoluePath : relativePath; } + + var relativeAttribute = lines[indexOfDefaultProfileAttributePath - 1]; + + return relativeAttribute == "0" // See above, the profile is located in a custom location, path is not relative, so IsRelative=0 + ? relativePath : absoluePath; } } From 57470a9799d9e968932f1b4e37c194f19bf428ad Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 6 Jun 2025 13:21:51 +0800 Subject: [PATCH 2/6] Fix IsRelative logic. --- .../FirefoxBookmarkLoader.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs index 42a288e3a97..61fd0507350 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs @@ -370,7 +370,8 @@ Current profiles.ini structure example as of Firefox version 69.0.1 var relativeAttribute = lines[indexOfDefaultProfileAttributePath - 1]; - return relativeAttribute == "0" // See above, the profile is located in a custom location, path is not relative, so IsRelative=0 + // See above, the profile is located in a custom location, path is not relative, so IsRelative=0 + return (relativeAttribute == "0" || relativeAttribute == "IsRelative=0") ? relativePath : absoluePath; } } From f59e2399b9d0a58ea59eac2cdee87810a9fcb350 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 6 Jun 2025 13:25:43 +0800 Subject: [PATCH 3/6] Add error handling for directory operation --- .../FirefoxBookmarkLoader.cs | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs index 61fd0507350..ac382275fc6 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs @@ -294,16 +294,22 @@ public static string MsixPlacesPath { var platformPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); var packagesPath = Path.Combine(platformPath, "Packages"); - - // Search for folder with Mozilla.Firefox prefix - var firefoxPackageFolder = Directory.EnumerateDirectories(packagesPath, "Mozilla.Firefox*", - SearchOption.TopDirectoryOnly).FirstOrDefault(); + try + { + // Search for folder with Mozilla.Firefox prefix + var firefoxPackageFolder = Directory.EnumerateDirectories(packagesPath, "Mozilla.Firefox*", + SearchOption.TopDirectoryOnly).FirstOrDefault(); - // Msix FireFox not installed - if (firefoxPackageFolder == null) return string.Empty; + // Msix FireFox not installed + if (firefoxPackageFolder == null) return string.Empty; - var profileFolderPath = Path.Combine(firefoxPackageFolder, @"LocalCache\Roaming\Mozilla\Firefox"); - return GetProfileIniPath(profileFolderPath); + var profileFolderPath = Path.Combine(firefoxPackageFolder, @"LocalCache\Roaming\Mozilla\Firefox"); + return GetProfileIniPath(profileFolderPath); + } + catch + { + return string.Empty; + } } } From c323646b1a321e566a67b5b41a73e0ede756d4af Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Sun, 8 Jun 2025 12:09:07 +0800 Subject: [PATCH 4/6] Use AddRange --- .../FirefoxBookmarkLoader.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs index ac382275fc6..dcf7763c354 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs @@ -264,9 +264,10 @@ public class FirefoxBookmarkLoader : FirefoxBookmarkLoaderBase /// public override List GetBookmarks() { - var bookmarks1 = GetBookmarksFromPath(PlacesPath); - var bookmarks2 = GetBookmarksFromPath(MsixPlacesPath); - return bookmarks1.Concat(bookmarks2).ToList(); + var bookmarks = new List(); + bookmarks.AddRange(GetBookmarksFromPath(PlacesPath)); + bookmarks.AddRange(GetBookmarksFromPath(MsixPlacesPath)); + return bookmarks; } /// From 6f516ee1207bc153281503e0e053e7815fee79da Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 11 Jun 2025 21:14:05 +0800 Subject: [PATCH 5/6] Change code comments --- .../FirefoxBookmarkLoader.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs index dcf7763c354..f214997c330 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs @@ -343,9 +343,9 @@ Current profiles.ini structure example as of Firefox version 69.0.1 Locked=1 [Profile2] - Name=newblahprofile + Name=dummyprofile IsRelative=0 - Path=C:\t6h2yuq8.newblahprofile <== Note this is a custom location path for the profile user can set, we need to cater for this in code. + Path=C:\t6h2yuq8.dummyprofile <== Note this is a custom location path for the profile user can set, we need to cater for this in code. [Profile1] Name=default From b6cca4547cf87733a1adb3c60edd8c86856a69cb Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 12 Jun 2025 16:27:48 +0800 Subject: [PATCH 6/6] Fix typos --- .../FirefoxBookmarkLoader.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs index f214997c330..8dffeecdc66 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs @@ -365,21 +365,21 @@ Current profiles.ini structure example as of Firefox version 69.0.1 // Seen in the example above, the IsRelative attribute is always above the Path attribute var relativePath = Path.Combine(defaultProfileFolderName, "places.sqlite"); - var absoluePath = Path.Combine(profileFolderPath, relativePath); + var absolutePath = Path.Combine(profileFolderPath, relativePath); // If the index is out of range, it means that the default profile is in a custom location or the file is malformed // If the profile is in a custom location, we need to check if (indexOfDefaultProfileAttributePath - 1 < 0 || indexOfDefaultProfileAttributePath - 1 >= lines.Count) { - return Directory.Exists(absoluePath) ? absoluePath : relativePath; + return Directory.Exists(absolutePath) ? absolutePath : relativePath; } var relativeAttribute = lines[indexOfDefaultProfileAttributePath - 1]; // See above, the profile is located in a custom location, path is not relative, so IsRelative=0 return (relativeAttribute == "0" || relativeAttribute == "IsRelative=0") - ? relativePath : absoluePath; + ? relativePath : absolutePath; } }