1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 04:17:35 +00:00

LibWeb: Bring handling of anchor elements closer to spec

This commit moves the regular handling of links to the anchor elements'
activation behavior, and implements a few auxiliary algorithms as
defined by the HTML specification.

Note that certain things such as javascript links, fragments and opening
a new tab are still handled directly in EventHandler, but they have been
moved to handle_mouseup so that it behaves closer to how it would if it
was entirely up-to-spec.
This commit is contained in:
sin-ack 2022-03-15 14:37:58 +00:00 committed by Andreas Kling
parent 034c57f1f9
commit aaa954f900
8 changed files with 357 additions and 40 deletions

View file

@ -194,7 +194,46 @@ bool EventHandler::handle_mouseup(const Gfx::IntPoint& position, unsigned button
node->dispatch_event(UIEvents::MouseEvent::create(UIEvents::EventNames::mouseup, offset.x(), offset.y(), position.x(), position.y()));
handled_event = true;
if (node.ptr() == m_mousedown_target) {
bool should_dispatch_event = true;
// FIXME: This is ad-hoc and incorrect. The reason this exists is
// because we are missing browsing context navigation:
//
// https://html.spec.whatwg.org/multipage/browsing-the-web.html#navigate
//
// Additionally, we currently cannot spawn a new top-level
// browsing context for new tab operations, because the new
// top-level browsing context would be in another process. To
// fix this, there needs to be some way to be able to
// communicate with browsing contexts in remote WebContent
// processes, and then step 8 of this algorithm needs to be
// implemented in BrowsingContext::choose_a_browsing_context:
//
// https://html.spec.whatwg.org/multipage/browsers.html#the-rules-for-choosing-a-browsing-context-given-a-browsing-context-name
if (RefPtr<HTML::HTMLAnchorElement> link = node->enclosing_link_element()) {
NonnullRefPtr document = *m_browsing_context.active_document();
auto href = link->href();
auto url = document->parse_url(href);
dbgln("Web::EventHandler: Clicking on a link to {}", url);
if (button == GUI::MouseButton::Primary) {
if (href.starts_with("javascript:")) {
document->run_javascript(href.substring_view(11, href.length() - 11));
} else if (!url.fragment().is_null() && url.equals(document->url(), AK::URL::ExcludeFragment::Yes)) {
m_browsing_context.scroll_to_anchor(url.fragment());
} else if (modifiers != 0) {
if (m_browsing_context.is_top_level()) {
if (auto* page = m_browsing_context.page())
page->client().page_did_click_link(url, link->target(), modifiers);
}
}
} else if (button == GUI::MouseButton::Middle) {
if (auto* page = m_browsing_context.page())
page->client().page_did_middle_click_link(url, link->target(), modifiers);
should_dispatch_event = false;
}
}
if (node.ptr() == m_mousedown_target && should_dispatch_event) {
node->dispatch_event(UIEvents::MouseEvent::create(UIEvents::EventNames::click, offset.x(), offset.y(), position.x(), position.y()));
}
}
@ -272,37 +311,10 @@ bool EventHandler::handle_mousedown(const Gfx::IntPoint& position, unsigned butt
return true;
}
if (RefPtr<HTML::HTMLAnchorElement> link = node->enclosing_link_element()) {
auto href = link->href();
auto url = document->parse_url(href);
dbgln("Web::EventHandler: Clicking on a link to {}", url);
if (button == GUI::MouseButton::Primary) {
if (href.starts_with("javascript:")) {
document->run_javascript(href.substring_view(11, href.length() - 11));
} else if (!url.fragment().is_null() && url.equals(document->url(), AK::URL::ExcludeFragment::Yes)) {
m_browsing_context.scroll_to_anchor(url.fragment());
} else {
document->set_active_element(link);
if (m_browsing_context.is_top_level()) {
if (auto* page = m_browsing_context.page())
page->client().page_did_click_link(url, link->target(), modifiers);
} else {
// FIXME: Handle different targets!
m_browsing_context.loader().load(url, FrameLoader::Type::Navigation);
}
}
} else if (button == GUI::MouseButton::Secondary) {
if (auto* page = m_browsing_context.page())
page->client().page_did_request_link_context_menu(m_browsing_context.to_top_level_position(position), url, link->target(), modifiers);
} else if (button == GUI::MouseButton::Middle) {
if (auto* page = m_browsing_context.page())
page->client().page_did_middle_click_link(url, link->target(), modifiers);
}
} else {
if (button == GUI::MouseButton::Primary) {
auto result = paint_root()->hit_test(position.to_type<float>(), Painting::HitTestType::TextCursor);
if (result.has_value() && result->dom_node()) {
if (button == GUI::MouseButton::Primary) {
if (auto result = paint_root()->hit_test(position.to_type<float>(), Painting::HitTestType::TextCursor); result.has_value()) {
auto paintable = result->paintable;
if (paintable->dom_node()) {
// See if we want to focus something.
bool did_focus_something = false;
for (auto candidate = node; candidate; candidate = candidate->parent()) {
@ -316,15 +328,15 @@ bool EventHandler::handle_mousedown(const Gfx::IntPoint& position, unsigned butt
// If we didn't focus anything, place the document text cursor at the mouse position.
// FIXME: This is all rather strange. Find a better solution.
if (!did_focus_something) {
m_browsing_context.set_cursor_position(DOM::Position(*result->dom_node(), result->index_in_node));
layout_root()->set_selection({ { result->paintable->layout_node(), result->index_in_node }, {} });
m_browsing_context.set_cursor_position(DOM::Position(*paintable->dom_node(), result->index_in_node));
layout_root()->set_selection({ { paintable->layout_node(), result->index_in_node }, {} });
m_in_mouse_selection = true;
}
}
} else if (button == GUI::MouseButton::Secondary) {
if (auto* page = m_browsing_context.page())
page->client().page_did_request_context_menu(m_browsing_context.to_top_level_position(position));
}
} else if (button == GUI::MouseButton::Secondary) {
if (auto* page = m_browsing_context.page())
page->client().page_did_request_context_menu(m_browsing_context.to_top_level_position(position));
}
return true;
}