Merge pull request #3826 from dcog989/Fix-'100%-CPU'-BrowserBookmark-issue

Fix BrowserBookmark '100% CPU' issue
This commit is contained in:
Jack Ye 2025-08-07 02:38:21 +01:00 committed by GitHub
commit b0694e97aa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 85 additions and 28 deletions

View file

@ -2,6 +2,7 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Threading.Tasks;
using Flow.Launcher.Plugin.BrowserBookmark.Helper;
@ -134,10 +135,6 @@ public abstract class FirefoxBookmarkLoaderBase : IBookmarkLoader
try
{
if (string.IsNullOrEmpty(bookmark.Url))
return;
// Extract domain from URL
if (!Uri.TryCreate(bookmark.Url, UriKind.Absolute, out Uri uri))
return;
@ -146,43 +143,49 @@ public abstract class FirefoxBookmarkLoaderBase : IBookmarkLoader
// Query for latest Firefox version favicon structure
using var cmd = connection.CreateCommand();
cmd.CommandText = @"
SELECT i.data
SELECT i.id, i.data
FROM moz_icons i
JOIN moz_icons_to_pages ip ON i.id = ip.icon_id
JOIN moz_pages_w_icons p ON ip.page_id = p.id
WHERE p.page_url LIKE @url
AND i.data IS NOT NULL
ORDER BY i.width DESC -- Select largest icon available
WHERE p.page_url LIKE @domain
ORDER BY i.width DESC
LIMIT 1";
cmd.Parameters.AddWithValue("@url", $"%{domain}%");
cmd.Parameters.AddWithValue("@domain", $"%{domain}%");
using var reader = cmd.ExecuteReader();
if (!reader.Read() || reader.IsDBNull(0))
if (!reader.Read() || reader.IsDBNull(1))
return;
var iconId = reader.GetInt64(0).ToString();
var imageData = (byte[])reader["data"];
if (imageData is not { Length: > 0 })
return;
string faviconPath;
if (FaviconHelper.IsSvgData(imageData))
// Check if the image data is compressed (GZip)
if (imageData.Length > 2 && imageData[0] == 0x1f && imageData[1] == 0x8b)
{
faviconPath = Path.Combine(_faviconCacheDir, $"firefox_{domain}.svg");
}
else
{
faviconPath = Path.Combine(_faviconCacheDir, $"firefox_{domain}.png");
using var inputStream = new MemoryStream(imageData);
using var gZipStream = new GZipStream(inputStream, CompressionMode.Decompress);
using var outputStream = new MemoryStream();
gZipStream.CopyTo(outputStream);
imageData = outputStream.ToArray();
}
// Filter out duplicate favicons
if (savedPaths.TryAdd(faviconPath, true))
// Convert the image data to WebP format
var webpData = FaviconHelper.TryConvertToWebp(imageData);
if (webpData != null)
{
FaviconHelper.SaveBitmapData(imageData, faviconPath);
}
var faviconPath = Path.Combine(_faviconCacheDir, $"firefox_{domain}_{iconId}.webp");
bookmark.FaviconPath = faviconPath;
if (savedPaths.TryAdd(faviconPath, true))
{
FaviconHelper.SaveBitmapData(webpData, faviconPath);
}
bookmark.FaviconPath = faviconPath;
}
}
catch (Exception ex)
{

View file

@ -45,12 +45,14 @@
$(OutputPath)runtimes\linux-musl-arm;
$(OutputPath)runtimes\linux-musl-arm64;
$(OutputPath)runtimes\linux-musl-x64;
$(OutputPath)runtimes\linux-musl-s390x;
$(OutputPath)runtimes\linux-ppc64le;
$(OutputPath)runtimes\linux-s390x;
$(OutputPath)runtimes\linux-x64;
$(OutputPath)runtimes\linux-x86;
$(OutputPath)runtimes\maccatalyst-arm64;
$(OutputPath)runtimes\maccatalyst-x64;
$(OutputPath)runtimes\osx;
$(OutputPath)runtimes\osx-arm64;
$(OutputPath)runtimes\osx-x64"/>
</Target>
@ -64,12 +66,14 @@
$(PublishDir)runtimes\linux-musl-arm;
$(PublishDir)runtimes\linux-musl-arm64;
$(PublishDir)runtimes\linux-musl-x64;
$(PublishDir)runtimes\linux-musl-s390x;
$(PublishDir)runtimes\linux-ppc64le;
$(PublishDir)runtimes\linux-s390x;
$(PublishDir)runtimes\linux-x64;
$(PublishDir)runtimes\linux-x86;
$(PublishDir)runtimes\maccatalyst-arm64;
$(PublishDir)runtimes\maccatalyst-x64;
$(PublishDir)runtimes\osx;
$(PublishDir)runtimes\osx-arm64;
$(PublishDir)runtimes\osx-x64"/>
</Target>
@ -97,6 +101,8 @@
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageReference Include="Flow.Launcher.Localization" Version="0.0.4" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.8" />
<PackageReference Include="Svg.Skia" Version="3.0.4" />
<PackageReference Include="SkiaSharp" Version="3.119.0" />
</ItemGroup>
</Project>

View file

@ -1,5 +1,7 @@
using System;
using System.IO;
using SkiaSharp;
using Svg.Skia;
namespace Flow.Launcher.Plugin.BrowserBookmark.Helper;
@ -65,12 +67,58 @@ public static class FaviconHelper
}
}
public static bool IsSvgData(byte[] data)
public static byte[] TryConvertToWebp(byte[] data)
{
if (data.Length < 5)
return false;
string start = System.Text.Encoding.ASCII.GetString(data, 0, Math.Min(100, data.Length));
return start.Contains("<svg") ||
(start.StartsWith("<?xml") && start.Contains("<svg"));
if (data == null || data.Length == 0)
return null;
SKBitmap bitmap = null;
try
{
using (var ms = new MemoryStream(data))
{
var svg = new SKSvg();
if (svg.Load(ms) != null && svg.Picture != null)
{
bitmap = new SKBitmap((int)svg.Picture.CullRect.Width, (int)svg.Picture.CullRect.Height);
using (var canvas = new SKCanvas(bitmap))
{
canvas.Clear(SKColors.Transparent);
canvas.DrawPicture(svg.Picture);
canvas.Flush();
}
}
}
}
catch { /* Not an SVG */ }
if (bitmap == null)
{
try
{
bitmap = SKBitmap.Decode(data);
}
catch { /* Not a decodable bitmap */ }
}
if (bitmap != null)
{
try
{
using (var image = SKImage.FromBitmap(bitmap))
using (var webp = image.Encode(SKEncodedImageFormat.Webp, 65))
{
if (webp != null)
return webp.ToArray();
}
}
finally
{
bitmap.Dispose();
}
}
return null;
}
}