- Convert HtmlSanitizer to partial class with [GeneratedRegex] attributes - Replace 11 runtime Regex.Replace/Matches calls with generated methods - Eliminates regex compilation overhead for HTML sanitization operations - Update REFACTORING_SUMMARY.md to document the optimization Co-Authored-By: Oz <oz-agent@warp.dev>
82 lines
3.1 KiB
C#
82 lines
3.1 KiB
C#
using System.Net;
|
|
using System.Text.RegularExpressions;
|
|
|
|
namespace Journal.Core.Services.Entries;
|
|
|
|
public static partial class HtmlSanitizer
|
|
{
|
|
[GeneratedRegex("<(script|style)\\b[^>]*>.*?</\\1>", RegexOptions.IgnoreCase | RegexOptions.Singleline)]
|
|
private static partial Regex ScriptStyleRegex();
|
|
|
|
[GeneratedRegex("<br\\s*/?>", RegexOptions.IgnoreCase)]
|
|
private static partial Regex BrTagRegex();
|
|
|
|
[GeneratedRegex("</(p|div|h[1-6]|tr|table|ul|ol|blockquote)>", RegexOptions.IgnoreCase)]
|
|
private static partial Regex BlockEndTagRegex();
|
|
|
|
[GeneratedRegex("<li\\b[^>]*>", RegexOptions.IgnoreCase)]
|
|
private static partial Regex LiStartTagRegex();
|
|
|
|
[GeneratedRegex("</li>", RegexOptions.IgnoreCase)]
|
|
private static partial Regex LiEndTagRegex();
|
|
|
|
[GeneratedRegex("<(td|th)\\b[^>]*>", RegexOptions.IgnoreCase)]
|
|
private static partial Regex CellStartTagRegex();
|
|
|
|
[GeneratedRegex("</(td|th)>", RegexOptions.IgnoreCase)]
|
|
private static partial Regex CellEndTagRegex();
|
|
|
|
[GeneratedRegex("<hr\\b[^>]*>", RegexOptions.IgnoreCase)]
|
|
private static partial Regex HrTagRegex();
|
|
|
|
[GeneratedRegex("<[^>]+>", RegexOptions.Singleline)]
|
|
private static partial Regex AllTagsRegex();
|
|
|
|
[GeneratedRegex("[ \\t]{2,}")]
|
|
private static partial Regex MultipleSpacesRegex();
|
|
|
|
[GeneratedRegex("\n{3,}")]
|
|
private static partial Regex MultipleNewlinesRegex();
|
|
|
|
[GeneratedRegex("</?[a-z][^>]*>")]
|
|
private static partial Regex HtmlTagCountRegex();
|
|
public static string StripRichHtml(string content)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(content))
|
|
return content;
|
|
if (!LooksLikeRichHtml(content))
|
|
return content;
|
|
|
|
var text = content.Replace("\r\n", "\n").Replace("\r", "\n");
|
|
text = ScriptStyleRegex().Replace(text, "");
|
|
text = BrTagRegex().Replace(text, "\n");
|
|
text = BlockEndTagRegex().Replace(text, "\n");
|
|
text = LiStartTagRegex().Replace(text, "\n- ");
|
|
text = LiEndTagRegex().Replace(text, "\n");
|
|
text = CellStartTagRegex().Replace(text, " | ");
|
|
text = CellEndTagRegex().Replace(text, " ");
|
|
text = HrTagRegex().Replace(text, "\n---\n");
|
|
text = AllTagsRegex().Replace(text, "");
|
|
text = WebUtility.HtmlDecode(text)
|
|
.Replace('\u00a0', ' ')
|
|
.Replace("\u200b", "", StringComparison.Ordinal);
|
|
text = string.Join("\n", text.Split('\n').Select(line => line.TrimEnd()));
|
|
text = MultipleSpacesRegex().Replace(text, " ");
|
|
text = MultipleNewlinesRegex().Replace(text, "\n\n").Trim();
|
|
return string.IsNullOrEmpty(text) ? content : text;
|
|
}
|
|
|
|
public static bool LooksLikeRichHtml(string content)
|
|
{
|
|
var lowered = content.ToLowerInvariant();
|
|
string[] markers =
|
|
[
|
|
"<p", "</p>", "<div", "<span", "<table", "<tr", "<td", "<li", "<ul", "<ol",
|
|
"style=", "font-family:", "-webkit-text-stroke"
|
|
];
|
|
if (markers.Any(marker => lowered.Contains(marker, StringComparison.Ordinal)))
|
|
return true;
|
|
return HtmlTagCountRegex().Matches(lowered).Count >= 8;
|
|
}
|
|
}
|