mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-06-09 09:34:57 +09:00
LibWebView: Normalize source-code text before highlighting it
The previous code to determine the SourceDocument's lines was too naive: the source text can contain other newline characters and sequences, and the HTML/CSS/JS syntax highlighters would take those into account when determining what line a token is on. This disagreement would cause incorrect highlighting, or even crashes, if the source didn't solely use `\n` for its newlines. In order to have everyone agree on what a line is, this patch first processes the source to replace all newlines with `\n`. The need to copy the source like this is unfortunate, but viewing the source is a rare enough action that this should not cause any noticeable performance problems. As the callers have a String, and we want a String, this also changes the function parameters to keep the source as a String instead of converting it to StringView and back. Fixes https://github.com/LadybirdBrowser/ladybird/issues/3169
This commit is contained in:
parent
b16b24c615
commit
6147557999
Notes:
github-actions[bot]
2025-02-04 08:50:24 +00:00
Author: https://github.com/AtkinsSJ
Commit: 6147557999
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3181
Reviewed-by: https://github.com/trflynn89
2 changed files with 45 additions and 12 deletions
|
@ -17,12 +17,45 @@
|
||||||
|
|
||||||
namespace WebView {
|
namespace WebView {
|
||||||
|
|
||||||
SourceDocument::SourceDocument(StringView source)
|
SourceDocument::SourceDocument(String const& source)
|
||||||
: m_source(source)
|
|
||||||
{
|
{
|
||||||
m_source.for_each_split_view('\n', AK::SplitBehavior::KeepEmpty, [&](auto line) {
|
// HTML, CSS and JS differ slightly on what they consider a newline to be.
|
||||||
m_lines.append(Syntax::TextDocumentLine { *this, line });
|
// In order to make them get along in documents that include a mix of the three, process the source to make the
|
||||||
});
|
// newlines consistent before doing any highlighting.
|
||||||
|
|
||||||
|
// Optimization: If all the newlines are \n, just use the input string.
|
||||||
|
if (!source.code_points().contains_any_of(Array<u32, 3> { '\r', 0x2028, 0x2029 })) {
|
||||||
|
m_source = source;
|
||||||
|
} else {
|
||||||
|
StringBuilder builder { source.byte_count() };
|
||||||
|
// Convert any '\r\n', \r, <LS> or <PS> to \n
|
||||||
|
bool previous_was_cr = false;
|
||||||
|
for (u32 code_point : source.code_points()) {
|
||||||
|
if (previous_was_cr && code_point != '\n')
|
||||||
|
builder.append('\n');
|
||||||
|
previous_was_cr = false;
|
||||||
|
|
||||||
|
switch (code_point) {
|
||||||
|
case '\r':
|
||||||
|
previous_was_cr = true;
|
||||||
|
break;
|
||||||
|
case JS::LINE_SEPARATOR:
|
||||||
|
case JS::PARAGRAPH_SEPARATOR:
|
||||||
|
builder.append('\n');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
builder.append_code_point(code_point);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_source = builder.to_string_without_validation();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_source.code_points().for_each_split_view(
|
||||||
|
[](u32 it) { return it == '\n'; },
|
||||||
|
SplitBehavior::KeepEmpty,
|
||||||
|
[&](auto line) {
|
||||||
|
m_lines.append(Syntax::TextDocumentLine { *this, line.as_string() });
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Syntax::TextDocumentLine& SourceDocument::line(size_t line_index)
|
Syntax::TextDocumentLine& SourceDocument::line(size_t line_index)
|
||||||
|
@ -35,7 +68,7 @@ Syntax::TextDocumentLine const& SourceDocument::line(size_t line_index) const
|
||||||
return m_lines[line_index];
|
return m_lines[line_index];
|
||||||
}
|
}
|
||||||
|
|
||||||
SourceHighlighterClient::SourceHighlighterClient(StringView source, Syntax::Language language)
|
SourceHighlighterClient::SourceHighlighterClient(String const& source, Syntax::Language language)
|
||||||
: m_document(SourceDocument::create(source))
|
: m_document(SourceDocument::create(source))
|
||||||
{
|
{
|
||||||
// HACK: Syntax highlighters require a palette, but we don't actually care about the output styling, only the type of token for each span.
|
// HACK: Syntax highlighters require a palette, but we don't actually care about the output styling, only the type of token for each span.
|
||||||
|
@ -114,7 +147,7 @@ void SourceHighlighterClient::highlighter_did_set_folding_regions(Vector<Syntax:
|
||||||
document().set_folding_regions(move(folding_regions));
|
document().set_folding_regions(move(folding_regions));
|
||||||
}
|
}
|
||||||
|
|
||||||
String highlight_source(URL::URL const& url, URL::URL const& base_url, StringView source, Syntax::Language language, HighlightOutputMode mode)
|
String highlight_source(URL::URL const& url, URL::URL const& base_url, String const& source, Syntax::Language language, HighlightOutputMode mode)
|
||||||
{
|
{
|
||||||
SourceHighlighterClient highlighter_client { source, language };
|
SourceHighlighterClient highlighter_client { source, language };
|
||||||
return highlighter_client.to_html_string(url, base_url, mode);
|
return highlighter_client.to_html_string(url, base_url, mode);
|
||||||
|
|
|
@ -24,7 +24,7 @@ enum class HighlightOutputMode {
|
||||||
|
|
||||||
class SourceDocument final : public Syntax::Document {
|
class SourceDocument final : public Syntax::Document {
|
||||||
public:
|
public:
|
||||||
static NonnullRefPtr<SourceDocument> create(StringView source)
|
static NonnullRefPtr<SourceDocument> create(String const& source)
|
||||||
{
|
{
|
||||||
return adopt_ref(*new (nothrow) SourceDocument(source));
|
return adopt_ref(*new (nothrow) SourceDocument(source));
|
||||||
}
|
}
|
||||||
|
@ -38,18 +38,18 @@ public:
|
||||||
virtual Syntax::TextDocumentLine& line(size_t line_index) override;
|
virtual Syntax::TextDocumentLine& line(size_t line_index) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SourceDocument(StringView source);
|
SourceDocument(String const& source);
|
||||||
|
|
||||||
// ^ Syntax::Document
|
// ^ Syntax::Document
|
||||||
virtual void update_views(Badge<Syntax::TextDocumentLine>) override { }
|
virtual void update_views(Badge<Syntax::TextDocumentLine>) override { }
|
||||||
|
|
||||||
StringView m_source;
|
String m_source;
|
||||||
Vector<Syntax::TextDocumentLine> m_lines;
|
Vector<Syntax::TextDocumentLine> m_lines;
|
||||||
};
|
};
|
||||||
|
|
||||||
class SourceHighlighterClient final : public Syntax::HighlighterClient {
|
class SourceHighlighterClient final : public Syntax::HighlighterClient {
|
||||||
public:
|
public:
|
||||||
SourceHighlighterClient(StringView source, Syntax::Language);
|
SourceHighlighterClient(String const& source, Syntax::Language);
|
||||||
virtual ~SourceHighlighterClient() = default;
|
virtual ~SourceHighlighterClient() = default;
|
||||||
|
|
||||||
String to_html_string(URL::URL const& url, URL::URL const& base_url, HighlightOutputMode) const;
|
String to_html_string(URL::URL const& url, URL::URL const& base_url, HighlightOutputMode) const;
|
||||||
|
@ -75,7 +75,7 @@ private:
|
||||||
OwnPtr<Syntax::Highlighter> m_highlighter;
|
OwnPtr<Syntax::Highlighter> m_highlighter;
|
||||||
};
|
};
|
||||||
|
|
||||||
String highlight_source(URL::URL const& url, URL::URL const& base_url, StringView, Syntax::Language, HighlightOutputMode);
|
String highlight_source(URL::URL const& url, URL::URL const& base_url, String const& source, Syntax::Language, HighlightOutputMode);
|
||||||
|
|
||||||
constexpr inline StringView HTML_HIGHLIGHTER_STYLE = R"~~~(
|
constexpr inline StringView HTML_HIGHLIGHTER_STYLE = R"~~~(
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue