@@ -264,83 +264,114 @@ public class FirefoxBookmarkLoader : FirefoxBookmarkLoaderBase
264
264
/// </summary>
265
265
public override List < Bookmark > GetBookmarks ( )
266
266
{
267
- return GetBookmarksFromPath ( PlacesPath ) ;
267
+ var bookmarks1 = GetBookmarksFromPath ( PlacesPath ) ;
268
+ var bookmarks2 = GetBookmarksFromPath ( MsixPlacesPath ) ;
269
+ return bookmarks1 . Concat ( bookmarks2 ) . ToList ( ) ;
268
270
}
269
271
270
272
/// <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"/>
272
276
/// </summary>
273
- /// <remarks></remarks>
274
277
private static string PlacesPath
275
278
{
276
279
get
277
280
{
278
281
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
+ }
283
285
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
+ }
287
309
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 ;
289
315
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 ( ) ;
291
319
292
- if ( string . IsNullOrEmpty ( defaultProfileFolderNameRaw ) )
293
- return string . Empty ;
320
+ var lines = ini . Split ( "\r \n " ) . ToList ( ) ;
294
321
295
- var defaultProfileFolderName = defaultProfileFolderNameRaw . Split ( '=' ) . Last ( ) ;
322
+ var defaultProfileFolderNameRaw = lines . FirstOrDefault ( x => x . Contains ( "Default=" ) && x != "Default=1" ) ?? string . Empty ;
296
323
297
- var indexOfDefaultProfileAttributePath = lines . IndexOf ( "Path=" + defaultProfileFolderName ) ;
324
+ if ( string . IsNullOrEmpty ( defaultProfileFolderNameRaw ) )
325
+ return string . Empty ;
298
326
299
- /*
300
- Current profiles.ini structure example as of Firefox version 69.0.1
327
+ var defaultProfileFolderName = defaultProfileFolderNameRaw . Split ( '=' ) . Last ( ) ;
301
328
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 ) ;
305
330
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
310
333
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
316
337
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.
321
342
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
327
348
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
330
353
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
338
359
339
- var relativeAttribute = lines [ indexOfDefaultProfileAttributePath - 1 ] ;
360
+ var relativePath = Path . Combine ( defaultProfileFolderName , "places.sqlite" ) ;
361
+ var absoluePath = Path . Combine ( profileFolderPath , relativePath ) ;
340
362
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 ;
343
369
}
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 ;
344
375
}
345
376
}
346
377
0 commit comments