mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-21 23:20:20 +00:00
LibWebView: Generate hyperlinks for attributes that represent links
On the view-source page, generate anchor tags for any 'href' or 'src' attribute value we come across. This handles both when the attribute contains an absolute URL and a URL relative to the page. This requires sending the document's base URL over IPC to resolve relative URLs.
This commit is contained in:
parent
0703ba118b
commit
1aab7b51ea
Notes:
github-actions[bot]
2024-10-20 06:50:51 +00:00
Author: https://github.com/trflynn89 Commit: https://github.com/LadybirdBrowser/ladybird/commit/1aab7b51ea9 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/1861
10 changed files with 60 additions and 26 deletions
|
@ -1051,12 +1051,12 @@ static void copy_data_to_clipboard(StringView data, NSPasteboardType pasteboard_
|
|||
return Ladybird::ns_rect_to_gfx_rect([[self window] frame]);
|
||||
};
|
||||
|
||||
m_web_view_bridge->on_received_source = [weak_self](auto const& url, auto const& source) {
|
||||
m_web_view_bridge->on_received_source = [weak_self](auto const& url, auto const& base_url, auto const& source) {
|
||||
LadybirdWebView* self = weak_self;
|
||||
if (self == nil) {
|
||||
return;
|
||||
}
|
||||
auto html = WebView::highlight_source(MUST(url.to_string()), source, Syntax::Language::HTML, WebView::HighlightOutputMode::FullDocument);
|
||||
auto html = WebView::highlight_source(url, base_url, source, Syntax::Language::HTML, WebView::HighlightOutputMode::FullDocument);
|
||||
|
||||
[self.observer onCreateNewTab:html
|
||||
url:url
|
||||
|
|
|
@ -321,8 +321,8 @@ Tab::Tab(BrowserWindow* window, RefPtr<WebView::WebContentClient> parent_client,
|
|||
|
||||
QObject::connect(focus_location_editor_action, &QAction::triggered, this, &Tab::focus_location_editor);
|
||||
|
||||
view().on_received_source = [this](auto const& url, auto const& source) {
|
||||
auto html = WebView::highlight_source(MUST(url.to_string()), source, Syntax::Language::HTML, WebView::HighlightOutputMode::FullDocument);
|
||||
view().on_received_source = [this](auto const& url, auto const& base_url, auto const& source) {
|
||||
auto html = WebView::highlight_source(url, base_url, source, Syntax::Language::HTML, WebView::HighlightOutputMode::FullDocument);
|
||||
m_window->new_tab_from_content(html, Web::HTML::ActivateTab::Yes);
|
||||
};
|
||||
|
||||
|
|
|
@ -128,8 +128,8 @@ InspectorClient::InspectorClient(ViewImplementation& content_web_view, ViewImple
|
|||
m_inspector_web_view.run_javascript(builder.string_view());
|
||||
};
|
||||
|
||||
m_content_web_view.on_received_style_sheet_source = [this](Web::CSS::StyleSheetIdentifier const& identifier, String const& source) {
|
||||
auto html = highlight_source(identifier.url.value_or({}), source, Syntax::Language::CSS, HighlightOutputMode::SourceOnly);
|
||||
m_content_web_view.on_received_style_sheet_source = [this](Web::CSS::StyleSheetIdentifier const& identifier, auto const& base_url, String const& source) {
|
||||
auto html = highlight_source(identifier.url.value_or({}), base_url, source, Syntax::Language::CSS, HighlightOutputMode::SourceOnly);
|
||||
auto script = MUST(String::formatted("inspector.setStyleSheetSource({}, \"{}\");",
|
||||
style_sheet_identifier_to_json(identifier),
|
||||
MUST(encode_base64(html.bytes()))));
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <LibURL/URL.h>
|
||||
#include <LibWeb/CSS/Parser/Token.h>
|
||||
#include <LibWeb/CSS/SyntaxHighlighter/SyntaxHighlighter.h>
|
||||
#include <LibWeb/DOMURL/DOMURL.h>
|
||||
#include <LibWeb/HTML/SyntaxHighlighter/SyntaxHighlighter.h>
|
||||
#include <LibWebView/SourceHighlighter.h>
|
||||
|
||||
|
@ -113,10 +114,10 @@ void SourceHighlighterClient::highlighter_did_set_folding_regions(Vector<Syntax:
|
|||
document().set_folding_regions(move(folding_regions));
|
||||
}
|
||||
|
||||
String highlight_source(String const& url, StringView source, Syntax::Language language, HighlightOutputMode mode)
|
||||
String highlight_source(URL::URL const& url, URL::URL const& base_url, StringView source, Syntax::Language language, HighlightOutputMode mode)
|
||||
{
|
||||
SourceHighlighterClient highlighter_client { source, language };
|
||||
return highlighter_client.to_html_string(url, mode);
|
||||
return highlighter_client.to_html_string(url, base_url, mode);
|
||||
}
|
||||
|
||||
StringView SourceHighlighterClient::class_for_token(u64 token_type) const
|
||||
|
@ -232,7 +233,7 @@ StringView SourceHighlighterClient::class_for_token(u64 token_type) const
|
|||
}
|
||||
}
|
||||
|
||||
String SourceHighlighterClient::to_html_string(String const& url, HighlightOutputMode mode) const
|
||||
String SourceHighlighterClient::to_html_string(URL::URL const& url, URL::URL const& base_url, HighlightOutputMode mode) const
|
||||
{
|
||||
StringBuilder builder;
|
||||
|
||||
|
@ -266,7 +267,7 @@ String SourceHighlighterClient::to_html_string(String const& url, HighlightOutpu
|
|||
<head>
|
||||
<meta name="color-scheme" content="dark light">)~~~"sv);
|
||||
|
||||
builder.appendff("<title>View Source - {}</title>", escape_html_entities(url));
|
||||
builder.appendff("<title>View Source - {}</title>", escape_html_entities(url.serialize_for_display()));
|
||||
builder.appendff("<style type=\"text/css\">{}</style>", HTML_HIGHLIGHTER_STYLE);
|
||||
builder.append(R"~~~(
|
||||
</head>
|
||||
|
@ -274,6 +275,22 @@ String SourceHighlighterClient::to_html_string(String const& url, HighlightOutpu
|
|||
}
|
||||
builder.append("<pre class=\"html\">"sv);
|
||||
|
||||
static constexpr auto href = to_array<u32>({ 'h', 'r', 'e', 'f' });
|
||||
static constexpr auto src = to_array<u32>({ 's', 'r', 'c' });
|
||||
bool linkify_attribute = false;
|
||||
|
||||
auto resolve_url_for_attribute = [&](Utf32View const& attribute_value) -> Optional<URL::URL> {
|
||||
if (!linkify_attribute)
|
||||
return {};
|
||||
|
||||
auto attribute_url = MUST(String::formatted("{}", attribute_value));
|
||||
auto attribute_url_without_quotes = attribute_url.bytes_as_string_view().trim("\""sv);
|
||||
|
||||
if (auto resolved = Web::DOMURL::parse(attribute_url_without_quotes, base_url); resolved.is_valid())
|
||||
return resolved;
|
||||
return {};
|
||||
};
|
||||
|
||||
size_t span_index = 0;
|
||||
for (size_t line_index = 0; line_index < document().line_count(); ++line_index) {
|
||||
auto& line = document().line(line_index);
|
||||
|
@ -286,11 +303,27 @@ String SourceHighlighterClient::to_html_string(String const& url, HighlightOutpu
|
|||
size_t length = end - start;
|
||||
if (length == 0)
|
||||
return;
|
||||
|
||||
auto text = line_view.substring_view(start, length);
|
||||
|
||||
if (span.has_value()) {
|
||||
bool append_anchor_close = false;
|
||||
|
||||
if (span->data == to_underlying(Web::HTML::AugmentedTokenKind::AttributeName)) {
|
||||
linkify_attribute = text == Utf32View { href } || text == Utf32View { src };
|
||||
} else if (span->data == to_underlying(Web::HTML::AugmentedTokenKind::AttributeValue)) {
|
||||
if (auto href = resolve_url_for_attribute(text); href.has_value()) {
|
||||
builder.appendff("<a href=\"{}\">", *href);
|
||||
append_anchor_close = true;
|
||||
}
|
||||
}
|
||||
|
||||
start_token(span->data);
|
||||
append_escaped(text);
|
||||
end_token();
|
||||
|
||||
if (append_anchor_close)
|
||||
builder.append("</a>"sv);
|
||||
} else {
|
||||
append_escaped(text);
|
||||
}
|
||||
|
|
|
@ -7,11 +7,13 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/StringView.h>
|
||||
#include <LibSyntax/Document.h>
|
||||
#include <LibSyntax/HighlighterClient.h>
|
||||
#include <LibSyntax/Language.h>
|
||||
#include <LibURL/Forward.h>
|
||||
|
||||
namespace WebView {
|
||||
|
||||
|
@ -50,7 +52,7 @@ public:
|
|||
SourceHighlighterClient(StringView source, Syntax::Language);
|
||||
virtual ~SourceHighlighterClient() = default;
|
||||
|
||||
String to_html_string(String const&, HighlightOutputMode) const;
|
||||
String to_html_string(URL::URL const& url, URL::URL const& base_url, HighlightOutputMode) const;
|
||||
|
||||
private:
|
||||
// ^ Syntax::HighlighterClient
|
||||
|
@ -73,7 +75,7 @@ private:
|
|||
OwnPtr<Syntax::Highlighter> m_highlighter;
|
||||
};
|
||||
|
||||
String highlight_source(String const&, StringView, Syntax::Language, HighlightOutputMode);
|
||||
String highlight_source(URL::URL const& url, URL::URL const& base_url, StringView, Syntax::Language, HighlightOutputMode);
|
||||
|
||||
constexpr inline StringView HTML_HIGHLIGHTER_STYLE = R"~~~(
|
||||
@media (prefers-color-scheme: dark) {
|
||||
|
|
|
@ -185,13 +185,13 @@ public:
|
|||
Function<void(String const& message)> on_request_set_prompt_text;
|
||||
Function<void()> on_request_accept_dialog;
|
||||
Function<void()> on_request_dismiss_dialog;
|
||||
Function<void(URL::URL const&, ByteString const&)> on_received_source;
|
||||
Function<void(URL::URL const&, URL::URL const&, String const&)> on_received_source;
|
||||
Function<void(ByteString const&)> on_received_dom_tree;
|
||||
Function<void(Optional<DOMNodeProperties>)> on_received_dom_node_properties;
|
||||
Function<void(ByteString const&)> on_received_accessibility_tree;
|
||||
Function<void(Vector<Web::CSS::StyleSheetIdentifier>)> on_received_style_sheet_list;
|
||||
Function<void(Web::CSS::StyleSheetIdentifier const&)> on_inspector_requested_style_sheet_source;
|
||||
Function<void(Web::CSS::StyleSheetIdentifier const&, String const&)> on_received_style_sheet_source;
|
||||
Function<void(Web::CSS::StyleSheetIdentifier const&, URL::URL const&, String const&)> on_received_style_sheet_source;
|
||||
Function<void(i32 node_id)> on_received_hovered_node_id;
|
||||
Function<void(Optional<i32> const& node_id)> on_finshed_editing_dom_node;
|
||||
Function<void(String const&)> on_received_dom_node_html;
|
||||
|
|
|
@ -261,11 +261,11 @@ void WebContentClient::did_request_media_context_menu(u64 page_id, Gfx::IntPoint
|
|||
}
|
||||
}
|
||||
|
||||
void WebContentClient::did_get_source(u64 page_id, URL::URL const& url, ByteString const& source)
|
||||
void WebContentClient::did_get_source(u64 page_id, URL::URL const& url, URL::URL const& base_url, String const& source)
|
||||
{
|
||||
if (auto view = view_for_page_id(page_id); view.has_value()) {
|
||||
if (view->on_received_source)
|
||||
view->on_received_source(url, source);
|
||||
view->on_received_source(url, base_url, source);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -720,11 +720,11 @@ void WebContentClient::inspector_did_request_style_sheet_source(u64 page_id, Web
|
|||
}
|
||||
}
|
||||
|
||||
void WebContentClient::did_request_style_sheet_source(u64 page_id, Web::CSS::StyleSheetIdentifier const& identifier, String const& source)
|
||||
void WebContentClient::did_get_style_sheet_source(u64 page_id, Web::CSS::StyleSheetIdentifier const& identifier, URL::URL const& base_url, String const& source)
|
||||
{
|
||||
if (auto view = view_for_page_id(page_id); view.has_value()) {
|
||||
if (view->on_received_style_sheet_source)
|
||||
view->on_received_style_sheet_source(identifier, source);
|
||||
view->on_received_style_sheet_source(identifier, base_url, source);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@ private:
|
|||
virtual void did_request_link_context_menu(u64 page_id, Gfx::IntPoint, URL::URL const&, ByteString const&, unsigned) override;
|
||||
virtual void did_request_image_context_menu(u64 page_id, Gfx::IntPoint, URL::URL const&, ByteString const&, unsigned, Gfx::ShareableBitmap const&) override;
|
||||
virtual void did_request_media_context_menu(u64 page_id, Gfx::IntPoint, ByteString const&, unsigned, Web::Page::MediaContextMenu const&) override;
|
||||
virtual void did_get_source(u64 page_id, URL::URL const&, ByteString const&) override;
|
||||
virtual void did_get_source(u64 page_id, URL::URL const&, URL::URL const&, String const&) override;
|
||||
virtual void did_inspect_dom_tree(u64 page_id, ByteString const&) override;
|
||||
virtual void did_inspect_dom_node(u64 page_id, bool has_style, ByteString const& computed_style, ByteString const& resolved_style, ByteString const& custom_properties, ByteString const& node_box_sizing, ByteString const& aria_properties_state, ByteString const& fonts) override;
|
||||
virtual void did_inspect_accessibility_tree(u64 page_id, ByteString const&) override;
|
||||
|
@ -129,7 +129,7 @@ private:
|
|||
virtual Messages::WebContentClient::RequestWorkerAgentResponse request_worker_agent(u64 page_id) override;
|
||||
virtual void inspector_did_list_style_sheets(u64 page_id, Vector<Web::CSS::StyleSheetIdentifier> const& stylesheets) override;
|
||||
virtual void inspector_did_request_style_sheet_source(u64 page_id, Web::CSS::StyleSheetIdentifier const& identifier) override;
|
||||
virtual void did_request_style_sheet_source(u64 page_id, Web::CSS::StyleSheetIdentifier const& identifier, String const& source) override;
|
||||
virtual void did_get_style_sheet_source(u64 page_id, Web::CSS::StyleSheetIdentifier const& identifier, URL::URL const&, String const& source) override;
|
||||
|
||||
Optional<ViewImplementation&> view_for_page_id(u64, SourceLocation = SourceLocation::current());
|
||||
|
||||
|
|
|
@ -432,7 +432,7 @@ void ConnectionFromClient::get_source(u64 page_id)
|
|||
{
|
||||
if (auto page = this->page(page_id); page.has_value()) {
|
||||
if (auto* doc = page->page().top_level_browsing_context().active_document())
|
||||
async_did_get_source(page_id, doc->url(), doc->source().to_byte_string());
|
||||
async_did_get_source(page_id, doc->url(), doc->base_url(), doc->source());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -644,9 +644,8 @@ void ConnectionFromClient::request_style_sheet_source(u64 page_id, Web::CSS::Sty
|
|||
return;
|
||||
|
||||
if (auto* document = page->page().top_level_browsing_context().active_document()) {
|
||||
auto stylesheet = document->get_style_sheet_source(identifier);
|
||||
if (stylesheet.has_value())
|
||||
async_did_request_style_sheet_source(page_id, identifier, stylesheet.value());
|
||||
if (auto stylesheet = document->get_style_sheet_source(identifier); stylesheet.has_value())
|
||||
async_did_get_style_sheet_source(page_id, identifier, document->base_url(), stylesheet.value());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ endpoint WebContentClient
|
|||
did_request_set_prompt_text(u64 page_id, String message) =|
|
||||
did_request_accept_dialog(u64 page_id) =|
|
||||
did_request_dismiss_dialog(u64 page_id) =|
|
||||
did_get_source(u64 page_id, URL::URL url, ByteString source) =|
|
||||
did_get_source(u64 page_id, URL::URL url, URL::URL base_url, String source) =|
|
||||
|
||||
did_inspect_dom_tree(u64 page_id, ByteString dom_tree) =|
|
||||
did_inspect_dom_node(u64 page_id, bool has_style, ByteString computed_style, ByteString resolved_style, ByteString custom_properties, ByteString node_box_sizing, ByteString aria_properties_state, ByteString fonts) =|
|
||||
|
@ -59,7 +59,7 @@ endpoint WebContentClient
|
|||
|
||||
inspector_did_list_style_sheets(u64 page_id, Vector<Web::CSS::StyleSheetIdentifier> style_sheets) =|
|
||||
inspector_did_request_style_sheet_source(u64 page_id, Web::CSS::StyleSheetIdentifier identifier) =|
|
||||
did_request_style_sheet_source(u64 page_id, Web::CSS::StyleSheetIdentifier identifier, String source) =|
|
||||
did_get_style_sheet_source(u64 page_id, Web::CSS::StyleSheetIdentifier identifier, URL::URL base_url, String source) =|
|
||||
|
||||
did_take_screenshot(u64 page_id, Gfx::ShareableBitmap screenshot) =|
|
||||
|
||||
|
|
Loading…
Reference in a new issue