1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 06:58:11 +00:00

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.
This commit is contained in:
Andreas Kling 2019-10-21 12:01:30 +02:00
parent 465a33443c
commit 4d9740ecef
5 changed files with 57 additions and 11 deletions

View file

@ -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()) if (node.needs_style_update())
return; to<Element>(node).recompute_style();
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;
}

View file

@ -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;

View file

@ -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();
} }

View file

@ -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)) if (is<HTMLAnchorElement>(node)) {
return; auto& anchor_element = to<HTMLAnchorElement>(node);
auto& anchor_element = to<HTMLAnchorElement>(node); if (anchor_element.name() == name) {
if (anchor_element.name() == name) element = &anchor_element;
element = &anchor_element; return IterationDecision::Break;
}
}
return IterationDecision::Continue;
}); });
if (!element) { if (!element) {

View file

@ -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: