summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Applications/Browser/main.cpp6
-rw-r--r--Base/home/anon/www/afrag.html23
-rw-r--r--Base/home/anon/www/welcome.html1
-rw-r--r--Libraries/LibHTML/HtmlView.cpp24
-rw-r--r--Libraries/LibHTML/HtmlView.h1
-rw-r--r--Libraries/LibHTML/Layout/LayoutBlock.h21
-rw-r--r--Libraries/LibHTML/Layout/LayoutNode.cpp32
-rw-r--r--Libraries/LibHTML/Layout/LayoutNode.h5
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*);