diff --git a/Applications/Browser/main.cpp b/Applications/Browser/main.cpp index 25365cfde1..95af1baeca 100644 --- a/Applications/Browser/main.cpp +++ b/Applications/Browser/main.cpp @@ -105,6 +105,10 @@ int main(int argc, char** argv) auto statusbar = GStatusBar::construct(widget); + html_widget->on_link_hover = [&](auto& href) { + statusbar->set_text(href); + }; + ResourceLoader::the().on_load_counter_change = [&] { if (ResourceLoader::the().pending_loads() == 0) { statusbar->set_text(""); diff --git a/Libraries/LibHTML/DOM/Element.h b/Libraries/LibHTML/DOM/Element.h index b8d342959d..7b5c4eb3df 100644 --- a/Libraries/LibHTML/DOM/Element.h +++ b/Libraries/LibHTML/DOM/Element.h @@ -31,6 +31,7 @@ public: virtual String tag_name() const final { return m_tag_name; } + bool has_attribute(const String& name) const { return !attribute(name).is_null(); } String attribute(const String& name) const; void set_attribute(const String& name, const String& value); diff --git a/Libraries/LibHTML/DOM/Node.cpp b/Libraries/LibHTML/DOM/Node.cpp index ef03875d98..8995a3e689 100644 --- a/Libraries/LibHTML/DOM/Node.cpp +++ b/Libraries/LibHTML/DOM/Node.cpp @@ -21,7 +21,11 @@ Node::~Node() const HTMLAnchorElement* Node::enclosing_link_element() const { - return first_ancestor_of_type(); + for (auto* node = this; node; node = node->parent()) { + if (is(*node) && to(*node).has_attribute("href")) + return to(node); + } + return nullptr; } const HTMLElement* Node::enclosing_html_element() const @@ -76,3 +80,11 @@ void Node::invalidate_style() }); document().schedule_style_update(); } + +bool Node::is_link() const +{ + auto* enclosing_link = enclosing_link_element(); + if (!enclosing_link) + return false; + return enclosing_link->has_attribute("href"); +} diff --git a/Libraries/LibHTML/DOM/Node.h b/Libraries/LibHTML/DOM/Node.h index 326f619670..70d31fb606 100644 --- a/Libraries/LibHTML/DOM/Node.h +++ b/Libraries/LibHTML/DOM/Node.h @@ -75,6 +75,8 @@ public: void invalidate_style(); + bool is_link() const; + protected: Node(Document&, NodeType); diff --git a/Libraries/LibHTML/HtmlView.cpp b/Libraries/LibHTML/HtmlView.cpp index 311223dc6b..a3b05db973 100644 --- a/Libraries/LibHTML/HtmlView.cpp +++ b/Libraries/LibHTML/HtmlView.cpp @@ -142,16 +142,18 @@ void HtmlView::mousemove_event(GMouseEvent& event) bool hovered_node_changed = false; bool is_hovering_link = false; + bool was_hovering_link = m_document->hovered_node() && m_document->hovered_node()->is_link(); auto result = layout_root()->hit_test(to_content_position(event.position())); + const HTMLAnchorElement* hovered_link_element = nullptr; if (result.layout_node) { auto* node = result.layout_node->node(); hovered_node_changed = node != m_document->hovered_node(); m_document->set_hovered_node(const_cast(node)); if (node) { - if (auto* link = node->enclosing_link_element()) { - UNUSED_PARAM(link); + hovered_link_element = node->enclosing_link_element(); + if (hovered_link_element) { #ifdef HTML_DEBUG - dbg() << "HtmlView: hovering over a link to " << link->href(); + dbg() << "HtmlView: hovering over a link to " << hovered_link_element->href(); #endif is_hovering_link = true; } @@ -169,6 +171,11 @@ void HtmlView::mousemove_event(GMouseEvent& event) GApplication::the().hide_tooltip(); } } + if (is_hovering_link != was_hovering_link) { + if (on_link_hover) { + on_link_hover(hovered_link_element ? m_document->complete_url(hovered_link_element->href()).to_string() : String()); + } + } event.accept(); } diff --git a/Libraries/LibHTML/HtmlView.h b/Libraries/LibHTML/HtmlView.h index 19c18e7335..b0d3d05d34 100644 --- a/Libraries/LibHTML/HtmlView.h +++ b/Libraries/LibHTML/HtmlView.h @@ -29,6 +29,7 @@ public: void set_should_show_line_box_borders(bool value) { m_should_show_line_box_borders = value; } Function on_link_click; + Function on_link_hover; Function on_title_change; Function on_load_start;