diff options
-rw-r--r-- | Applications/Browser/main.cpp | 6 | ||||
-rw-r--r-- | Base/home/anon/www/afrag.html | 23 | ||||
-rw-r--r-- | Base/home/anon/www/welcome.html | 1 | ||||
-rw-r--r-- | Libraries/LibHTML/HtmlView.cpp | 24 | ||||
-rw-r--r-- | Libraries/LibHTML/HtmlView.h | 1 | ||||
-rw-r--r-- | Libraries/LibHTML/Layout/LayoutBlock.h | 21 | ||||
-rw-r--r-- | Libraries/LibHTML/Layout/LayoutNode.cpp | 32 | ||||
-rw-r--r-- | Libraries/LibHTML/Layout/LayoutNode.h | 5 |
8 files changed, 100 insertions, 13 deletions
diff --git a/Applications/Browser/main.cpp b/Applications/Browser/main.cpp index 95af1baeca..69e86b16a9 100644 --- a/Applications/Browser/main.cpp +++ b/Applications/Browser/main.cpp @@ -91,7 +91,11 @@ int main(int argc, char** argv) }; html_widget->on_link_click = [&](auto& url) { - html_widget->load(html_widget->document()->complete_url(url)); + if (url.starts_with("#")) { + html_widget->scroll_to_anchor(url.substring_view(1, url.length() - 1)); + } else { + html_widget->load(html_widget->document()->complete_url(url)); + } }; html_widget->on_title_change = [&](auto& title) { diff --git a/Base/home/anon/www/afrag.html b/Base/home/anon/www/afrag.html new file mode 100644 index 0000000000..cf60bc5b0e --- /dev/null +++ b/Base/home/anon/www/afrag.html @@ -0,0 +1,23 @@ +<html> + <head><title>a#hash test</title></head> + <body> + <ul> + <li><a href="#section1">Section 1</a></li> + <li><a href="#section2">Section 2</a></li> + <li><a href="#section3">Section 3</a></li> + <li><a href="#section4">Section 4</a></li> + </ul> + <h1><a name="section1">Section 1</a></h1> + <br> <br> <br> <br> <br> <br> <br> <br> <br> <br> <br> <br> <br> <br> + <br> <br> <br> <br> <br> <br> <br> <br> <br> <br> <br> <br> <br> <br> + <h1><a name="section2">Section 2</a></h1> + <br> <br> <br> <br> <br> <br> <br> <br> <br> <br> <br> <br> <br> <br> + <br> <br> <br> <br> <br> <br> <br> <br> <br> <br> <br> <br> <br> <br> + <h1><a name="section3">Section 3</a></h1> + <br> <br> <br> <br> <br> <br> <br> <br> <br> <br> <br> <br> <br> <br> + <br> <br> <br> <br> <br> <br> <br> <br> <br> <br> <br> <br> <br> <br> + <h1><a name="section4">Section 4</a></h1> + <br> <br> <br> <br> <br> <br> <br> <br> <br> <br> <br> <br> <br> <br> + <br> <br> <br> <br> <br> <br> <br> <br> <br> <br> <br> <br> <br> <br> + </body> +</html> diff --git a/Base/home/anon/www/welcome.html b/Base/home/anon/www/welcome.html index 7b5ddf0239..2e78818e68 100644 --- a/Base/home/anon/www/welcome.html +++ b/Base/home/anon/www/welcome.html @@ -33,6 +33,7 @@ h1 { <li><a href="blink.html">blink element</a></li> <li><a href="br.html">br element</a></li> <li><a href="hover.html">hover element</a></li> + <li><a href="afrag.html">links with fragments</a></li> <li><a href="http://www.serenityos.org/">www.serenityos.org</a></li> </ul> </body> diff --git a/Libraries/LibHTML/HtmlView.cpp b/Libraries/LibHTML/HtmlView.cpp index a3b05db973..c454284267 100644 --- a/Libraries/LibHTML/HtmlView.cpp +++ b/Libraries/LibHTML/HtmlView.cpp @@ -312,3 +312,27 @@ LayoutDocument* HtmlView::layout_root() return nullptr; return const_cast<LayoutDocument*>(document()->layout_node()); } + +void HtmlView::scroll_to_anchor(const StringView& name) +{ + HTMLAnchorElement* element = nullptr; + m_document->for_each_in_subtree([&](auto& node) { + if (!is<HTMLAnchorElement>(node)) + return; + auto& anchor_element = to<HTMLAnchorElement>(node); + if (anchor_element.name() == name) + element = &anchor_element; + }); + + if (!element) { + dbg() << "HtmlView::scroll_to_anchor(): Anchor not found: '" << name << "'"; + return; + } + if (!element->layout_node()) { + dbg() << "HtmlView::scroll_to_anchor(): Anchor found but without layout node: '" << name << "'"; + return; + } + auto& layout_node = *element->layout_node(); + scroll_into_view({ layout_node.box_type_agnostic_position(), visible_content_rect().size() }, true, true); + window()->set_override_cursor(GStandardCursor::None); +} diff --git a/Libraries/LibHTML/HtmlView.h b/Libraries/LibHTML/HtmlView.h index b0d3d05d34..784950a692 100644 --- a/Libraries/LibHTML/HtmlView.h +++ b/Libraries/LibHTML/HtmlView.h @@ -23,6 +23,7 @@ public: void reload(); void load(const URL&); + void scroll_to_anchor(const StringView&); URL url() const; diff --git a/Libraries/LibHTML/Layout/LayoutBlock.h b/Libraries/LibHTML/Layout/LayoutBlock.h index 84e3ef7ef7..cd8dab8ccb 100644 --- a/Libraries/LibHTML/Layout/LayoutBlock.h +++ b/Libraries/LibHTML/Layout/LayoutBlock.h @@ -30,6 +30,11 @@ public: LayoutBlock* next_sibling() { return to<LayoutBlock>(LayoutNode::next_sibling()); } const LayoutBlock* next_sibling() const { return to<LayoutBlock>(LayoutNode::next_sibling()); } + template<typename Callback> + void for_each_fragment(Callback); + template<typename Callback> + void for_each_fragment(Callback) const; + private: virtual bool is_block() const override { return true; } @@ -46,10 +51,20 @@ private: }; template<typename Callback> -void LayoutNode::for_each_fragment_of_this(Callback callback) +void LayoutBlock::for_each_fragment(Callback callback) +{ + for (auto& line_box : line_boxes()) { + for (auto& fragment : line_box.fragments()) { + if (callback(fragment) == IterationDecision::Break) + return; + } + } +} + +template<typename Callback> +void LayoutBlock::for_each_fragment(Callback callback) const { - auto& block = *containing_block(); - for (auto& line_box : block.line_boxes()) { + for (auto& line_box : line_boxes()) { for (auto& fragment : line_box.fragments()) { if (callback(fragment) == IterationDecision::Break) return; diff --git a/Libraries/LibHTML/Layout/LayoutNode.cpp b/Libraries/LibHTML/Layout/LayoutNode.cpp index 95670127ed..12bff2146c 100644 --- a/Libraries/LibHTML/Layout/LayoutNode.cpp +++ b/Libraries/LibHTML/Layout/LayoutNode.cpp @@ -79,10 +79,30 @@ void LayoutNode::set_needs_display() auto* frame = document().frame(); ASSERT(frame); - for_each_fragment_of_this([&](auto& fragment) { - if (&fragment.layout_node() == this || is_ancestor_of(fragment.layout_node())) { - const_cast<Frame*>(frame)->set_needs_display(fragment.rect()); - } - return IterationDecision::Continue; - }); + if (auto* block = containing_block()) { + block->for_each_fragment([&](auto& fragment) { + if (&fragment.layout_node() == this || is_ancestor_of(fragment.layout_node())) { + const_cast<Frame*>(frame)->set_needs_display(fragment.rect()); + } + return IterationDecision::Continue; + }); + } +} + +Point LayoutNode::box_type_agnostic_position() const +{ + if (is_box()) + return to<LayoutBox>(*this).position(); + ASSERT(is_inline()); + Point position; + if (auto* block = containing_block()) { + block->for_each_fragment([&](auto& fragment) { + if (&fragment.layout_node() == this || is_ancestor_of(fragment.layout_node())) { + position = fragment.rect().location(); + return IterationDecision::Break; + } + return IterationDecision::Continue; + }); + } + return position; } diff --git a/Libraries/LibHTML/Layout/LayoutNode.h b/Libraries/LibHTML/Layout/LayoutNode.h index 0fc8f38ca7..a3dc1e2050 100644 --- a/Libraries/LibHTML/Layout/LayoutNode.h +++ b/Libraries/LibHTML/Layout/LayoutNode.h @@ -80,9 +80,6 @@ public: virtual void set_needs_display(); - template<typename Callback> - void for_each_fragment_of_this(Callback); - bool children_are_inline() const { return m_children_are_inline; } void set_children_are_inline(bool value) { m_children_are_inline = value; } @@ -104,6 +101,8 @@ public: template<typename T> T* first_ancestor_of_type(); + Point box_type_agnostic_position() const; + protected: explicit LayoutNode(const Node*); |