@@ -264,83 +264,114 @@ public class FirefoxBookmarkLoader : FirefoxBookmarkLoaderBase
264264 /// </summary>
265265 public override List < Bookmark > GetBookmarks ( )
266266 {
267- return GetBookmarksFromPath ( PlacesPath ) ;
267+ var bookmarks1 = GetBookmarksFromPath ( PlacesPath ) ;
268+ var bookmarks2 = GetBookmarksFromPath ( MsixPlacesPath ) ;
269+ return bookmarks1 . Concat ( bookmarks2 ) . ToList ( ) ;
268270 }
269271
270272 /// <summary>
271- /// Path to places.sqlite
273+ /// Path to places.sqlite of Msi installer
274+ /// E.g. C:\Users\{UserName}\AppData\Roaming\Mozilla\Firefox
275+ /// <see href="https://support.mozilla.org/en-US/kb/profiles-where-firefox-stores-user-data#w_finding-your-profile-without-opening-firefox"/>
272276 /// </summary>
273- /// <remarks></remarks>
274277 private static string PlacesPath
275278 {
276279 get
277280 {
278281 var profileFolderPath = Path . Combine ( Environment . GetFolderPath ( Environment . SpecialFolder . ApplicationData ) , @"Mozilla\Firefox" ) ;
279- var profileIni = Path . Combine ( profileFolderPath , @"profiles.ini" ) ;
280-
281- if ( ! File . Exists ( profileIni ) )
282- return string . Empty ;
282+ return GetProfileIniPath ( profileFolderPath ) ;
283+ }
284+ }
283285
284- // get firefox default profile directory from profiles.ini
285- using var sReader = new StreamReader ( profileIni ) ;
286- var ini = sReader . ReadToEnd ( ) ;
286+ /// <summary>
287+ /// Path to places.sqlite of MSIX installer
288+ /// E.g. C:\Users\{UserName}\AppData\Local\Packages\Mozilla.Firefox_n80bbvh6b1yt2\LocalCache\Roaming\Mozilla\Firefox
289+ /// <see href="https://support.mozilla.org/en-US/kb/profiles-where-firefox-stores-user-data#w_finding-your-profile-without-opening-firefox"/>
290+ /// </summary>
291+ public static string MsixPlacesPath
292+ {
293+ get
294+ {
295+ var platformPath = Environment . GetFolderPath ( Environment . SpecialFolder . LocalApplicationData ) ;
296+ var packagesPath = Path . Combine ( platformPath , "Packages" ) ;
297+
298+ // Search for folder with Mozilla.Firefox prefix
299+ var firefoxPackageFolder = Directory . EnumerateDirectories ( packagesPath , "Mozilla.Firefox*" ,
300+ SearchOption . TopDirectoryOnly ) . FirstOrDefault ( ) ;
301+
302+ // Msix FireFox not installed
303+ if ( firefoxPackageFolder == null ) return string . Empty ;
304+
305+ var profileFolderPath = Path . Combine ( firefoxPackageFolder , @"LocalCache\Roaming\Mozilla\Firefox" ) ;
306+ return GetProfileIniPath ( profileFolderPath ) ;
307+ }
308+ }
287309
288- var lines = ini . Split ( "\r \n " ) . ToList ( ) ;
310+ private static string GetProfileIniPath ( string profileFolderPath )
311+ {
312+ var profileIni = Path . Combine ( profileFolderPath , @"profiles.ini" ) ;
313+ if ( ! File . Exists ( profileIni ) )
314+ return string . Empty ;
289315
290- var defaultProfileFolderNameRaw = lines . FirstOrDefault ( x => x . Contains ( "Default=" ) && x != "Default=1" ) ?? string . Empty ;
316+ // get firefox default profile directory from profiles.ini
317+ using var sReader = new StreamReader ( profileIni ) ;
318+ var ini = sReader . ReadToEnd ( ) ;
291319
292- if ( string . IsNullOrEmpty ( defaultProfileFolderNameRaw ) )
293- return string . Empty ;
320+ var lines = ini . Split ( "\r \n " ) . ToList ( ) ;
294321
295- var defaultProfileFolderName = defaultProfileFolderNameRaw . Split ( '=' ) . Last ( ) ;
322+ var defaultProfileFolderNameRaw = lines . FirstOrDefault ( x => x . Contains ( "Default=" ) && x != "Default=1" ) ?? string . Empty ;
296323
297- var indexOfDefaultProfileAttributePath = lines . IndexOf ( "Path=" + defaultProfileFolderName ) ;
324+ if ( string . IsNullOrEmpty ( defaultProfileFolderNameRaw ) )
325+ return string . Empty ;
298326
299- /*
300- Current profiles.ini structure example as of Firefox version 69.0.1
327+ var defaultProfileFolderName = defaultProfileFolderNameRaw . Split ( '=' ) . Last ( ) ;
301328
302- [Install736426B0AF4A39CB]
303- 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
304- Locked=1
329+ var indexOfDefaultProfileAttributePath = lines . IndexOf ( "Path=" + defaultProfileFolderName ) ;
305330
306- [Profile2]
307- Name=newblahprofile
308- IsRelative=0
309- 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.
331+ /*
332+ Current profiles.ini structure example as of Firefox version 69.0.1
310333
311- [Profile1]
312- Name=default
313- IsRelative=1
314- Path=Profiles/cydum7q4.default
315- Default=1
334+ [Install736426B0AF4A39CB]
335+ 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
336+ Locked=1
316337
317- [Profile0 ]
318- Name=default-release
319- IsRelative=1
320- Path=Profiles/7789f565.default-release
338+ [Profile2 ]
339+ Name=newblahprofile
340+ IsRelative=0
341+ 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.
321342
322- [General ]
323- StartWithLastProfile=1
324- Version=2
325- */
326- // Seen in the example above, the IsRelative attribute is always above the Path attribute
343+ [Profile1 ]
344+ Name=default
345+ IsRelative=1
346+ Path=Profiles/cydum7q4.default
347+ Default=1
327348
328- var relativePath = Path . Combine ( defaultProfileFolderName , "places.sqlite" ) ;
329- var absoluePath = Path . Combine ( profileFolderPath , relativePath ) ;
349+ [Profile0]
350+ Name=default-release
351+ IsRelative=1
352+ Path=Profiles/7789f565.default-release
330353
331- // If the index is out of range, it means that the default profile is in a custom location or the file is malformed
332- // If the profile is in a custom location, we need to check
333- if ( indexOfDefaultProfileAttributePath - 1 < 0 ||
334- indexOfDefaultProfileAttributePath - 1 >= lines . Count )
335- {
336- return Directory . Exists ( absoluePath ) ? absoluePath : relativePath ;
337- }
354+ [General]
355+ StartWithLastProfile=1
356+ Version=2
357+ */
358+ // Seen in the example above, the IsRelative attribute is always above the Path attribute
338359
339- var relativeAttribute = lines [ indexOfDefaultProfileAttributePath - 1 ] ;
360+ var relativePath = Path . Combine ( defaultProfileFolderName , "places.sqlite" ) ;
361+ var absoluePath = Path . Combine ( profileFolderPath , relativePath ) ;
340362
341- return relativeAttribute == "0" // See above, the profile is located in a custom location, path is not relative, so IsRelative=0
342- ? relativePath : absoluePath ;
363+ // If the index is out of range, it means that the default profile is in a custom location or the file is malformed
364+ // If the profile is in a custom location, we need to check
365+ if ( indexOfDefaultProfileAttributePath - 1 < 0 ||
366+ indexOfDefaultProfileAttributePath - 1 >= lines . Count )
367+ {
368+ return Directory . Exists ( absoluePath ) ? absoluePath : relativePath ;
343369 }
370+
371+ var relativeAttribute = lines [ indexOfDefaultProfileAttributePath - 1 ] ;
372+
373+ return relativeAttribute == "0" // See above, the profile is located in a custom location, path is not relative, so IsRelative=0
374+ ? relativePath : absoluePath ;
344375 }
345376}
346377
0 commit comments