mirror of
https://github.com/RGBCube/serenity
synced 2025-07-23 20:57:41 +00:00
LibHTML: Add TreeNode::for_each_in_subtree_of_type<T>()
This allows you to iterate a subtree and get a callback for every node where is<T>(node) == true. This makes for quite pleasant DOM traversal.
This commit is contained in:
parent
54bd322881
commit
1aea8f116b
5 changed files with 56 additions and 25 deletions
|
@ -177,9 +177,9 @@ void Document::layout()
|
||||||
|
|
||||||
void Document::update_style()
|
void Document::update_style()
|
||||||
{
|
{
|
||||||
for_each_in_subtree([&](Node& node) {
|
for_each_in_subtree_of_type<Element>([&](auto& element) {
|
||||||
if (node.needs_style_update())
|
if (element.needs_style_update())
|
||||||
to<Element>(node).recompute_style();
|
element.recompute_style();
|
||||||
return IterationDecision::Continue;
|
return IterationDecision::Continue;
|
||||||
});
|
});
|
||||||
update_layout();
|
update_layout();
|
||||||
|
@ -252,23 +252,23 @@ void Document::set_hovered_node(Node* node)
|
||||||
|
|
||||||
const Element* Document::get_element_by_id(const String& id) const
|
const Element* Document::get_element_by_id(const String& id) const
|
||||||
{
|
{
|
||||||
const Element* element = nullptr;
|
const Element* found_element = nullptr;
|
||||||
for_each_in_subtree([&](auto& node) {
|
for_each_in_subtree_of_type<Element>([&](auto& element) {
|
||||||
if (is<Element>(node) && to<Element>(node).attribute("id") == id) {
|
if (element.attribute("id") == id) {
|
||||||
element = &to<Element>(node);
|
found_element = &element;
|
||||||
return IterationDecision::Break;
|
return IterationDecision::Break;
|
||||||
}
|
}
|
||||||
return IterationDecision::Continue;
|
return IterationDecision::Continue;
|
||||||
});
|
});
|
||||||
return element;
|
return found_element;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector<const Element*> Document::get_elements_by_name(const String& name) const
|
Vector<const Element*> Document::get_elements_by_name(const String& name) const
|
||||||
{
|
{
|
||||||
Vector<const Element*> elements;
|
Vector<const Element*> elements;
|
||||||
for_each_in_subtree([&](auto& node) {
|
for_each_in_subtree_of_type<Element>([&](auto& element) {
|
||||||
if (is<Element>(node) && to<Element>(node).attribute("name") == name)
|
if (element.attribute("name") == name)
|
||||||
elements.append(&to<Element>(node));
|
elements.append(&element);
|
||||||
return IterationDecision::Continue;
|
return IterationDecision::Continue;
|
||||||
});
|
});
|
||||||
return elements;
|
return elements;
|
||||||
|
|
|
@ -34,12 +34,10 @@ void HTMLFormElement::submit()
|
||||||
|
|
||||||
Vector<NameAndValue> parameters;
|
Vector<NameAndValue> parameters;
|
||||||
|
|
||||||
for_each_in_subtree([&](auto& node) {
|
for_each_in_subtree_of_type<HTMLInputElement>([&](auto& node) {
|
||||||
if (is<HTMLInputElement>(node)) {
|
|
||||||
auto& input = to<HTMLInputElement>(node);
|
auto& input = to<HTMLInputElement>(node);
|
||||||
if (!input.name().is_null())
|
if (!input.name().is_null())
|
||||||
parameters.append({ input.name(), input.value() });
|
parameters.append({ input.name(), input.value() });
|
||||||
}
|
|
||||||
return IterationDecision::Continue;
|
return IterationDecision::Continue;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -74,9 +74,8 @@ RefPtr<LayoutNode> Node::create_layout_node(const StyleProperties*) const
|
||||||
|
|
||||||
void Node::invalidate_style()
|
void Node::invalidate_style()
|
||||||
{
|
{
|
||||||
for_each_in_subtree([&](auto& node) {
|
for_each_in_subtree_of_type<Element>([&](auto& element) {
|
||||||
if (is<Element>(node))
|
element.set_needs_style_update(true);
|
||||||
node.set_needs_style_update(true);
|
|
||||||
return IterationDecision::Continue;
|
return IterationDecision::Continue;
|
||||||
});
|
});
|
||||||
document().schedule_style_update();
|
document().schedule_style_update();
|
||||||
|
|
|
@ -34,11 +34,8 @@ void LayoutDocument::layout()
|
||||||
void LayoutDocument::did_set_viewport_rect(Badge<Frame>, const Rect& a_viewport_rect)
|
void LayoutDocument::did_set_viewport_rect(Badge<Frame>, const Rect& a_viewport_rect)
|
||||||
{
|
{
|
||||||
FloatRect viewport_rect(a_viewport_rect.x(), a_viewport_rect.y(), a_viewport_rect.width(), a_viewport_rect.height());
|
FloatRect viewport_rect(a_viewport_rect.x(), a_viewport_rect.y(), a_viewport_rect.width(), a_viewport_rect.height());
|
||||||
for_each_in_subtree([&](auto& layout_node) {
|
for_each_in_subtree_of_type<LayoutImage>([&](auto& layout_image) {
|
||||||
if (is<LayoutImage>(layout_node)) {
|
const_cast<HTMLImageElement&>(layout_image.node()).set_volatile({}, !viewport_rect.intersects(layout_image.rect()));
|
||||||
auto& image = to<LayoutImage>(layout_node);
|
|
||||||
const_cast<HTMLImageElement&>(image.node()).set_volatile({}, !viewport_rect.intersects(image.rect()));
|
|
||||||
}
|
|
||||||
return IterationDecision::Continue;
|
return IterationDecision::Continue;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,15 @@
|
||||||
#include <AK/NonnullRefPtr.h>
|
#include <AK/NonnullRefPtr.h>
|
||||||
#include <AK/Weakable.h>
|
#include <AK/Weakable.h>
|
||||||
|
|
||||||
|
// FIXME: I wish I didn't have to forward declare these, but I can't seem to avoid
|
||||||
|
// it if I still want to have for_each_in_subtree_of_type<U> inline here.
|
||||||
|
class Node;
|
||||||
|
class LayoutNode;
|
||||||
|
template<typename T>
|
||||||
|
bool is(const Node&);
|
||||||
|
template<typename T>
|
||||||
|
bool is(const LayoutNode&);
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
class TreeNode : public Weakable<T> {
|
class TreeNode : public Weakable<T> {
|
||||||
public:
|
public:
|
||||||
|
@ -122,6 +131,34 @@ public:
|
||||||
return IterationDecision::Continue;
|
return IterationDecision::Continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename U, typename Callback>
|
||||||
|
IterationDecision for_each_in_subtree_of_type(Callback callback)
|
||||||
|
{
|
||||||
|
if (is<U>(static_cast<const T&>(*this))) {
|
||||||
|
if (callback(static_cast<U&>(*this)) == IterationDecision::Break)
|
||||||
|
return IterationDecision::Break;
|
||||||
|
}
|
||||||
|
for (auto* child = first_child(); child; child = child->next_sibling()) {
|
||||||
|
if (child->template for_each_in_subtree_of_type<U>(callback) == IterationDecision::Break)
|
||||||
|
return IterationDecision::Break;
|
||||||
|
}
|
||||||
|
return IterationDecision::Continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename U, typename Callback>
|
||||||
|
IterationDecision for_each_in_subtree_of_type(Callback callback) const
|
||||||
|
{
|
||||||
|
if (is<U>(static_cast<const T&>(*this))) {
|
||||||
|
if (callback(static_cast<const U&>(*this)) == IterationDecision::Break)
|
||||||
|
return IterationDecision::Break;
|
||||||
|
}
|
||||||
|
for (auto* child = first_child(); child; child = child->next_sibling()) {
|
||||||
|
if (child->template for_each_in_subtree_of_type<U>(callback) == IterationDecision::Break)
|
||||||
|
return IterationDecision::Break;
|
||||||
|
}
|
||||||
|
return IterationDecision::Continue;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
TreeNode() {}
|
TreeNode() {}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue