Sfoglia il codice sorgente

LibHTML: Add Document::get_element_by_id() and get_elements_by_name()

These will be useful for implementing various things. They don't do any
caching at the moment, but that might become valuable in the future.

To facilitate this change, I also made it possible to abort a tree walk
with for_each_in_subtree() by returning IterationDecision::Break from
the callback.
Andreas Kling 5 anni fa
parent
commit
4d9740ecef

+ 27 - 3
Libraries/LibHTML/DOM/Document.cpp

@@ -193,9 +193,9 @@ void Document::layout()
 void Document::update_style()
 void Document::update_style()
 {
 {
     for_each_in_subtree([&](Node& node) {
     for_each_in_subtree([&](Node& node) {
-        if (!node.needs_style_update())
-            return;
-        to<Element>(node).recompute_style();
+        if (node.needs_style_update())
+            to<Element>(node).recompute_style();
+        return IterationDecision::Continue;
     });
     });
     update_layout();
     update_layout();
 }
 }
@@ -242,3 +242,27 @@ void Document::set_hovered_node(Node* node)
 
 
     invalidate_style();
     invalidate_style();
 }
 }
+
+const Element* Document::get_element_by_id(const String& id) const
+{
+    const Element* element = nullptr;
+    for_each_in_subtree([&](auto& node) {
+        if (is<Element>(node) && to<Element>(node).attribute("id") == id) {
+            element = &to<Element>(node);
+            return IterationDecision::Break;
+        }
+        return IterationDecision::Continue;
+    });
+    return element;
+}
+
+Vector<const Element*> Document::get_elements_by_name(const String& name) const
+{
+    Vector<const Element*> elements;
+    for_each_in_subtree([&](auto& node) {
+        if (is<Element>(node) && to<Element>(node).attribute("name") == name)
+            elements.append(&to<Element>(node));
+        return IterationDecision::Continue;
+    });
+    return elements;
+}

+ 3 - 0
Libraries/LibHTML/DOM/Document.h

@@ -80,6 +80,9 @@ public:
 
 
     void schedule_style_update();
     void schedule_style_update();
 
 
+    const Element* get_element_by_id(const String&) const;
+    Vector<const Element*> get_elements_by_name(const String&) const;
+
 private:
 private:
     virtual RefPtr<LayoutNode> create_layout_node(const StyleProperties* parent_style) const override;
     virtual RefPtr<LayoutNode> create_layout_node(const StyleProperties* parent_style) const override;
 
 

+ 1 - 0
Libraries/LibHTML/DOM/Node.cpp

@@ -77,6 +77,7 @@ void Node::invalidate_style()
     for_each_in_subtree([&](auto& node) {
     for_each_in_subtree([&](auto& node) {
         if (is<Element>(node))
         if (is<Element>(node))
             node.set_needs_style_update(true);
             node.set_needs_style_update(true);
+        return IterationDecision::Continue;
     });
     });
     document().schedule_style_update();
     document().schedule_style_update();
 }
 }

+ 8 - 5
Libraries/LibHTML/HtmlView.cpp

@@ -321,11 +321,14 @@ void HtmlView::scroll_to_anchor(const StringView& name)
 
 
     HTMLAnchorElement* element = nullptr;
     HTMLAnchorElement* element = nullptr;
     document()->for_each_in_subtree([&](auto& node) {
     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 (is<HTMLAnchorElement>(node)) {
+            auto& anchor_element = to<HTMLAnchorElement>(node);
+            if (anchor_element.name() == name) {
+                element = &anchor_element;
+                return IterationDecision::Break;
+            }
+        }
+        return IterationDecision::Continue;
     });
     });
 
 
     if (!element) {
     if (!element) {

+ 18 - 3
Libraries/LibHTML/TreeNode.h

@@ -54,12 +54,27 @@ public:
     bool is_child_allowed(const T&) const { return true; }
     bool is_child_allowed(const T&) const { return true; }
 
 
     template<typename Callback>
     template<typename Callback>
-    void for_each_in_subtree(Callback callback)
+    IterationDecision for_each_in_subtree(Callback callback) const
     {
     {
-        callback(static_cast<T&>(*this));
+        if (callback(static_cast<const T&>(*this)) == IterationDecision::Break)
+            return IterationDecision::Break;
         for (auto* child = first_child(); child; child = child->next_sibling()) {
         for (auto* child = first_child(); child; child = child->next_sibling()) {
-            child->for_each_in_subtree(callback);
+            if (child->for_each_in_subtree(callback) == IterationDecision::Break)
+                return IterationDecision::Break;
         }
         }
+        return IterationDecision::Continue;
+    }
+
+    template<typename Callback>
+    IterationDecision for_each_in_subtree(Callback callback)
+    {
+        if (callback(static_cast<T&>(*this)) == IterationDecision::Break)
+            return IterationDecision::Break;
+        for (auto* child = first_child(); child; child = child->next_sibling()) {
+            if (child->for_each_in_subtree(callback) == IterationDecision::Break)
+                return IterationDecision::Break;
+        }
+        return IterationDecision::Continue;
     }
     }
 
 
 protected:
 protected: