Skip to content

Commit 4a67ad6

Browse files
committed
review feedback updates
1 parent a963fd2 commit 4a67ad6

File tree

10 files changed

+97
-46
lines changed

10 files changed

+97
-46
lines changed

Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,8 @@
88
using Flow.Launcher.Plugin.BrowserBookmark.Services;
99
using System.ComponentModel;
1010
using System.Linq;
11-
using Flow.Launcher.Plugin.SharedCommands;
1211
using System.Collections.Specialized;
13-
using Flow.Launcher.Plugin.SharedModels;
1412
using System.IO;
15-
using System.Collections.Concurrent;
1613

1714
namespace Flow.Launcher.Plugin.BrowserBookmark;
1815

@@ -251,7 +248,7 @@ public List<Result> LoadContextMenus(Result selectedResult)
251248
Context.API.CopyToClipboard(url);
252249
return true;
253250
}
254-
catch(Exception ex)
251+
catch (Exception ex)
255252
{
256253
Context.API.LogException(nameof(Main), "Failed to copy URL to clipboard", ex);
257254
Context.API.ShowMsgError(Localize.flowlauncher_plugin_browserbookmark_copy_failed());

Plugins/Flow.Launcher.Plugin.BrowserBookmark/Models/CustomBrowser.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
using Flow.Launcher.Localization.Attributes;
2-
using System.Collections.Generic;
3-
using System.Linq;
42

53
namespace Flow.Launcher.Plugin.BrowserBookmark.Models;
64

Plugins/Flow.Launcher.Plugin.BrowserBookmark/Services/ChromiumBookmarkLoader.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
using System.Collections.Concurrent;
55
using System.Collections.Generic;
66
using System.IO;
7-
using System.Linq;
87
using System.Runtime.CompilerServices;
98
using System.Text.Json;
109
using System.Threading;
@@ -113,7 +112,7 @@ private void EnumerateFolderBookmark(JsonElement folderElement, ICollection<Book
113112
}
114113
else
115114
{
116-
_logException(nameof(ChromiumBookmarkLoader), $"type property not found for {subElement.GetString()}", null);
115+
_logException(nameof(ChromiumBookmarkLoader), "type property not found in bookmark node.", null);
117116
}
118117
}
119118
}

Plugins/Flow.Launcher.Plugin.BrowserBookmark/Services/FaviconService.cs

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public FaviconService(PluginInitContext context, Settings settings, string tempP
4040

4141
_faviconCacheDir = Path.Combine(context.CurrentPluginMetadata.PluginCacheDirectoryPath, "FaviconCache");
4242
Directory.CreateDirectory(_faviconCacheDir);
43-
43+
4444
var failsDir = Path.Combine(context.CurrentPluginMetadata.PluginCacheDirectoryPath, "FaviconFails");
4545
Directory.CreateDirectory(failsDir);
4646
_failsFilePath = Path.Combine(failsDir, "FaviconFails.json");
@@ -131,27 +131,36 @@ await Parallel.ForEachAsync(bookmarks, options, async (bookmark, token) =>
131131
});
132132
}
133133

134-
private Task<string?> GetFaviconFromWebAsync(Uri url, CancellationToken token)
134+
private async Task<string?> GetFaviconFromWebAsync(Uri url, CancellationToken token)
135135
{
136136
if (url is null || (url.Scheme != "http" && url.Scheme != "https"))
137-
{
138-
return Task.FromResult<string?>(null);
139-
}
137+
return null;
138+
140139
var authority = url.GetLeftPart(UriPartial.Authority);
141140

142-
if (_failedFetches.TryGetValue(authority, out var lastAttemptTime) &&
141+
if (_failedFetches.TryGetValue(authority, out var lastAttemptTime) &&
143142
(DateTime.UtcNow - lastAttemptTime < FailedFaviconCooldown))
144143
{
145-
_context.API.LogDebug(nameof(FaviconService), $"Skipping favicon fetch for {authority} due to recent failure (cooldown active).");
146-
return Task.FromResult<string?>(null);
144+
_context.API.LogDebug(nameof(FaviconService),
145+
$"Skipping favicon fetch for {authority} due to recent failure (cooldown active).");
146+
return null;
147147
}
148148

149-
return _ongoingFetches.GetOrAdd(authority, key => FetchAndCacheFaviconAsync(new Uri(key)));
149+
var fetchTask = _ongoingFetches.GetOrAdd(authority, key => FetchAndCacheFaviconAsync(new Uri(key)));
150+
try
151+
{
152+
return await fetchTask.WaitAsync(token);
153+
}
154+
catch (OperationCanceledException)
155+
{
156+
// Do not cancel the shared fetch; just stop waiting for this caller.
157+
return null;
158+
}
150159
}
151160

152161
private static string GetCachePath(string url, string cacheDir)
153162
{
154-
var hash = SHA1.HashData(Encoding.UTF8.GetBytes(url));
163+
var hash = SHA256.HashData(Encoding.UTF8.GetBytes(url));
155164
var sb = new StringBuilder(hash.Length * 2);
156165
foreach (byte b in hash)
157166
{
@@ -218,7 +227,7 @@ private static string GetCachePath(string url, string cacheDir)
218227
finally
219228
{
220229
_ongoingFetches.TryRemove(urlString, out _);
221-
230+
222231
if (fetchAttempted && !File.Exists(cachePath))
223232
{
224233
_failedFetches[urlString] = DateTime.UtcNow;
@@ -265,7 +274,7 @@ private async Task<FetchResult> FetchFromHtmlAsync(Uri pageUri, CancellationToke
265274
private async Task<FetchResult> FetchFromUrlAsync(Uri faviconUri, CancellationToken token)
266275
{
267276
await using var stream = await _webClient.DownloadFaviconAsync(faviconUri, token);
268-
if (stream == null)
277+
if (stream == null)
269278
return default;
270279

271280
var (pngData, size) = await _imageConverter.ToPngAsync(stream, token);

Plugins/Flow.Launcher.Plugin.BrowserBookmark/Services/FaviconWebClient.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@ public FaviconWebClient(PluginInitContext context)
2222
var handler = new HttpClientHandler
2323
{
2424
AllowAutoRedirect = true,
25-
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate | DecompressionMethods.Brotli,
26-
ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
25+
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate | DecompressionMethods.Brotli
2726
};
2827
_httpClient = new HttpClient(handler);
2928
_httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6409.0 Safari/537.36");
@@ -82,10 +81,10 @@ public FaviconWebClient(PluginInitContext context)
8281

8382
if (!response.IsSuccessStatusCode)
8483
return null;
85-
84+
8685
if (response.Content.Headers.ContentLength > MaxFaviconBytes)
8786
return null;
88-
87+
8988
await using var contentStream = await response.Content.ReadAsStreamAsync(token);
9089
var memoryStream = new MemoryStream();
9190

Plugins/Flow.Launcher.Plugin.BrowserBookmark/Services/FirefoxBookmarkLoader.cs

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,11 @@ public class FirefoxBookmarkLoader : IBookmarkLoader
2222
private const string QueryAllBookmarks = """
2323
SELECT moz_places.url, moz_bookmarks.title
2424
FROM moz_places
25-
INNER JOIN moz_bookmarks ON (
26-
moz_bookmarks.fk NOT NULL AND moz_bookmarks.title NOT NULL AND moz_bookmarks.fk = moz_places.id
27-
)
25+
INNER JOIN moz_bookmarks
26+
ON moz_bookmarks.fk = moz_places.id
27+
WHERE moz_bookmarks.fk IS NOT NULL
28+
AND moz_bookmarks.title IS NOT NULL
29+
AND moz_places.url IS NOT NULL
2830
ORDER BY moz_places.visit_count DESC
2931
""";
3032

@@ -69,10 +71,9 @@ public async IAsyncEnumerable<Bookmark> GetBookmarksAsync([EnumeratorCancellatio
6971
}
7072
finally
7173
{
72-
if (tempDbPath != null && File.Exists(tempDbPath))
74+
if (tempDbPath != null)
7375
{
74-
try { File.Delete(tempDbPath); }
75-
catch(Exception e) { _logException(nameof(FirefoxBookmarkLoader), $"Failed to delete temp db file {tempDbPath}", e); }
76+
CleanupTempFiles(tempDbPath);
7677
}
7778
}
7879

@@ -84,9 +85,10 @@ public async IAsyncEnumerable<Bookmark> GetBookmarksAsync([EnumeratorCancellatio
8485

8586
private async Task ReadBookmarksFromDb(string dbPath, ICollection<Bookmark> bookmarks, CancellationToken cancellationToken)
8687
{
87-
var profilePath = Path.GetDirectoryName(dbPath) ?? string.Empty;
88+
// Always use the original profile directory, even when reading from a temp DB copy
89+
var profilePath = Path.GetDirectoryName(_placesPath) ?? string.Empty;
8890
var connectionString = $"Data Source={dbPath};Mode=ReadOnly;Pooling=false;";
89-
91+
9092
await using var dbConnection = new SqliteConnection(connectionString);
9193
await dbConnection.OpenAsync(cancellationToken);
9294
await using var command = new SqliteCommand(QueryAllBookmarks, dbConnection);
@@ -103,4 +105,33 @@ private async Task ReadBookmarksFromDb(string dbPath, ICollection<Bookmark> book
103105
}
104106
}
105107
}
108+
109+
private void CleanupTempFiles(string mainTempDbPath)
110+
{
111+
// This method ensures that the main temp file and any of its associated files
112+
// (e.g., -wal, -shm) are deleted.
113+
try
114+
{
115+
var directory = Path.GetDirectoryName(mainTempDbPath);
116+
var baseName = Path.GetFileName(mainTempDbPath);
117+
if (directory == null || !Directory.Exists(directory)) return;
118+
119+
foreach (var file in Directory.GetFiles(directory, baseName + "*"))
120+
{
121+
try
122+
{
123+
File.Delete(file);
124+
}
125+
catch (Exception ex)
126+
{
127+
// Log failure to delete a specific chunk, but don't stop the process
128+
_logException(nameof(FirefoxBookmarkLoader), $"Failed to delete temporary file chunk: {file}", ex);
129+
}
130+
}
131+
}
132+
catch (Exception ex)
133+
{
134+
_logException(nameof(FirefoxBookmarkLoader), $"Failed to clean up temporary files for base: {mainTempDbPath}", ex);
135+
}
136+
}
106137
}

Plugins/Flow.Launcher.Plugin.BrowserBookmark/Services/LocalFaviconExtractor.cs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ SELECT b.image_data FROM favicon_bitmaps b
3939

4040
return GetFaviconFromDbAsync(bookmark, "Favicons", query, null, token);
4141
}
42-
42+
4343
private Task<byte[]?> GetFirefoxFaviconAsync(Bookmark bookmark, CancellationToken token)
4444
{
4545
const string query = @"
@@ -56,15 +56,21 @@ SELECT i.data FROM moz_icons i
5656
Func<byte[], CancellationToken, Task<byte[]>>? postProcessor, CancellationToken token)
5757
{
5858
var dbPath = Path.Combine(bookmark.ProfilePath, dbFileName);
59-
if (!File.Exists(dbPath))
59+
if (!File.Exists(dbPath))
6060
return null;
6161

6262
var tempDbPath = Path.Combine(_tempPath, $"{Path.GetFileNameWithoutExtension(dbFileName)}_{Guid.NewGuid()}{Path.GetExtension(dbFileName)}");
63-
63+
6464
try
6565
{
66-
File.Copy(dbPath, tempDbPath, true);
66+
var walPath = dbPath + "-wal";
67+
var shmPath = dbPath + "-shm";
6768

69+
File.Copy(dbPath, tempDbPath, true);
70+
if (File.Exists(walPath))
71+
File.Copy(walPath, tempDbPath + "-wal", true);
72+
if (File.Exists(shmPath))
73+
File.Copy(shmPath, tempDbPath + "-shm", true);
6874
var connectionString = $"Data Source={tempDbPath};Mode=ReadOnly;Pooling=false;";
6975
await using var connection = new SqliteConnection(connectionString);
7076
await connection.OpenAsync(token);
@@ -74,7 +80,7 @@ SELECT i.data FROM moz_icons i
7480

7581
if (await cmd.ExecuteScalarAsync(token) is not byte[] data || data.Length == 0)
7682
return null;
77-
83+
7884
_context.API.LogDebug(nameof(LocalFaviconExtractor), $"Extracted {data.Length} bytes for {bookmark.Url} from {dbFileName}.");
7985

8086
return postProcessor != null ? await postProcessor(data, token) : data;

Plugins/Flow.Launcher.Plugin.BrowserBookmark/ViewModels/CustomBrowserSettingViewModel.cs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@
66
using System;
77
using System.ComponentModel;
88
using System.IO;
9-
using System.Windows.Forms;
10-
using Flow.Launcher.Plugin.SharedCommands;
9+
using Microsoft.Win32;
1110

1211
namespace Flow.Launcher.Plugin.BrowserBookmark.ViewModels;
1312

@@ -87,15 +86,26 @@ private void Cancel()
8786
[RelayCommand]
8887
private void BrowseDataDirectory()
8988
{
90-
var dialog = new FolderBrowserDialog();
89+
var dialog = new OpenFileDialog
90+
{
91+
ValidateNames = false,
92+
CheckFileExists = false,
93+
CheckPathExists = true,
94+
FileName = "Folder Selection."
95+
};
96+
9197
if (!string.IsNullOrEmpty(EditableBrowser.DataDirectoryPath) && Directory.Exists(EditableBrowser.DataDirectoryPath))
9298
{
93-
dialog.SelectedPath = EditableBrowser.DataDirectoryPath;
99+
dialog.InitialDirectory = EditableBrowser.DataDirectoryPath;
94100
}
95101

96-
if (dialog.ShowDialog() == DialogResult.OK)
102+
if (dialog.ShowDialog() == true)
97103
{
98-
EditableBrowser.DataDirectoryPath = dialog.SelectedPath;
104+
var path = Path.GetDirectoryName(dialog.FileName);
105+
if (!string.IsNullOrEmpty(path))
106+
{
107+
EditableBrowser.DataDirectoryPath = path;
108+
}
99109
}
100110
}
101111
}

Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/CustomBrowserSetting.xaml.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,15 @@ public CustomBrowserSetting(CustomBrowser browser)
2222
});
2323
DataContext = _viewModel;
2424
}
25-
25+
2626
private void WindowKeyDown(object sender, KeyEventArgs e)
2727
{
2828
if (e.Key == System.Windows.Input.Key.Enter)
2929
{
30-
_viewModel.SaveCommand.Execute(null);
30+
if (_viewModel.SaveCommand.CanExecute(null))
31+
{
32+
_viewModel.SaveCommand.Execute(null);
33+
}
3134
}
3235
else if (e.Key == System.Windows.Input.Key.Escape)
3336
{

Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/SettingsControl.xaml.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using System.Windows;
22
using System.Windows.Controls;
33
using System.Windows.Input;
4-
using System.Threading.Tasks;
54
using Flow.Launcher.Plugin.BrowserBookmark.Models;
65

76
namespace Flow.Launcher.Plugin.BrowserBookmark.Views;

0 commit comments

Comments
 (0)