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