LibWeb: Make Node::parent_element return GC::Ptr

This is useful for people like myself who run with debug mode to
more reliably get stacktraces without spinning up a debugger.
This commit is contained in:
Shannon Booth 2025-04-18 14:19:19 +12:00 committed by Andreas Kling
parent a14481ee05
commit 3e17b1c9ae
Notes: github-actions[bot] 2025-04-18 09:08:36 +00:00
18 changed files with 48 additions and 54 deletions

View file

@ -42,84 +42,84 @@ Optional<Role> ARIAMixin::role_from_role_attribute_value() const
// without the required accessible parent of role list), User Agents MUST ignore the role token, and return the
// computedrole as if the ignored role token had not been included.
if (role == ARIA::Role::columnheader) {
for (auto const* ancestor = to_element()->parent_element(); ancestor; ancestor = ancestor->parent_element()) {
for (auto ancestor = to_element()->parent_element(); ancestor; ancestor = ancestor->parent_element()) {
if (ancestor->role_or_default() == ARIA::Role::row)
return ARIA::Role::columnheader;
}
continue;
}
if (role == ARIA::Role::gridcell) {
for (auto const* ancestor = to_element()->parent_element(); ancestor; ancestor = ancestor->parent_element()) {
for (auto ancestor = to_element()->parent_element(); ancestor; ancestor = ancestor->parent_element()) {
if (ancestor->role_or_default() == ARIA::Role::row)
return ARIA::Role::gridcell;
}
continue;
}
if (role == ARIA::Role::listitem) {
for (auto const* ancestor = to_element()->parent_element(); ancestor; ancestor = ancestor->parent_element()) {
for (auto ancestor = to_element()->parent_element(); ancestor; ancestor = ancestor->parent_element()) {
if (first_is_one_of(ancestor->role_or_default(), ARIA::Role::directory, ARIA::Role::list))
return ARIA::Role::listitem;
}
continue;
}
if (role == ARIA::Role::menuitem) {
for (auto const* ancestor = to_element()->parent_element(); ancestor; ancestor = ancestor->parent_element()) {
for (auto ancestor = to_element()->parent_element(); ancestor; ancestor = ancestor->parent_element()) {
if (first_is_one_of(ancestor->role_or_default(), ARIA::Role::menu, ARIA::Role::menubar))
return ARIA::Role::menuitem;
}
continue;
}
if (role == ARIA::Role::menuitemcheckbox) {
for (auto const* ancestor = to_element()->parent_element(); ancestor; ancestor = ancestor->parent_element()) {
for (auto ancestor = to_element()->parent_element(); ancestor; ancestor = ancestor->parent_element()) {
if (first_is_one_of(ancestor->role_or_default(), ARIA::Role::menu, ARIA::Role::menubar))
return ARIA::Role::menuitemcheckbox;
}
continue;
}
if (role == ARIA::Role::menuitemradio) {
for (auto const* ancestor = to_element()->parent_element(); ancestor; ancestor = ancestor->parent_element()) {
for (auto ancestor = to_element()->parent_element(); ancestor; ancestor = ancestor->parent_element()) {
if (first_is_one_of(ancestor->role_or_default(), ARIA::Role::menu, ARIA::Role::menubar))
return ARIA::Role::menuitemradio;
}
continue;
}
if (role == ARIA::Role::option) {
for (auto const* ancestor = to_element()->parent_element(); ancestor; ancestor = ancestor->parent_element()) {
for (auto ancestor = to_element()->parent_element(); ancestor; ancestor = ancestor->parent_element()) {
if (ancestor->role_or_default() == ARIA::Role::listbox)
return ARIA::Role::option;
}
continue;
}
if (role == ARIA::Role::row) {
for (auto const* ancestor = to_element()->parent_element(); ancestor; ancestor = ancestor->parent_element()) {
for (auto ancestor = to_element()->parent_element(); ancestor; ancestor = ancestor->parent_element()) {
if (first_is_one_of(ancestor->role_or_default(), ARIA::Role::table, ARIA::Role::grid, ARIA::Role::treegrid))
return ARIA::Role::row;
}
continue;
}
if (role == ARIA::Role::rowgroup) {
for (auto const* ancestor = to_element()->parent_element(); ancestor; ancestor = ancestor->parent_element()) {
for (auto ancestor = to_element()->parent_element(); ancestor; ancestor = ancestor->parent_element()) {
if (first_is_one_of(ancestor->role_or_default(), ARIA::Role::table, ARIA::Role::grid, ARIA::Role::treegrid))
return ARIA::Role::rowgroup;
}
continue;
}
if (role == ARIA::Role::rowheader) {
for (auto const* ancestor = to_element()->parent_element(); ancestor; ancestor = ancestor->parent_element()) {
for (auto ancestor = to_element()->parent_element(); ancestor; ancestor = ancestor->parent_element()) {
if (ancestor->role_or_default() == ARIA::Role::row)
return ARIA::Role::rowheader;
}
continue;
}
if (role == ARIA::Role::tab) {
for (auto const* ancestor = to_element()->parent_element(); ancestor; ancestor = ancestor->parent_element()) {
for (auto ancestor = to_element()->parent_element(); ancestor; ancestor = ancestor->parent_element()) {
if (ancestor->role_or_default() == ARIA::Role::tablist)
return ARIA::Role::tab;
}
continue;
}
if (role == ARIA::Role::treeitem) {
for (auto const* ancestor = to_element()->parent_element(); ancestor; ancestor = ancestor->parent_element()) {
for (auto ancestor = to_element()->parent_element(); ancestor; ancestor = ancestor->parent_element()) {
if (ancestor->role_or_default() == ARIA::Role::tree)
return ARIA::Role::treeitem;
}

View file

@ -973,7 +973,7 @@ void KeyframeEffect::update_computed_properties()
if (invalidation.rebuild_layout_tree) {
// We mark layout tree for rebuild starting from parent element to correctly invalidate
// "display" property change to/from "contents" value.
if (auto* parent_element = target->parent_element()) {
if (auto parent_element = target->parent_element()) {
parent_element->set_needs_layout_tree_update(true);
} else {
target->set_needs_layout_tree_update(true);

View file

@ -2280,7 +2280,7 @@ static BoxTypeTransformation required_box_type_transformation(ComputedProperties
// FIXME: Containment in a ruby container inlinifies the boxs display type, as described in [CSS-RUBY-1].
// NOTE: If we're computing style for a pseudo-element, the effective parent will be the originating element itself, not its parent.
auto const* parent = pseudo_element.has_value() ? &element : element.parent_element();
auto parent = pseudo_element.has_value() ? GC::Ptr<DOM::Element const> { &element } : element.parent_element();
// A parent with a grid or flex display value blockifies the boxs display type. [CSS-GRID-1] [CSS-FLEXBOX-1]
if (parent && parent->computed_properties()) {
@ -2505,7 +2505,7 @@ RefPtr<CSSStyleValue const> StyleComputer::recascade_font_size_if_needed(
Vector<DOM::Element&> ancestors;
if (pseudo_element.has_value())
ancestors.append(element);
for (auto* ancestor = element.parent_element(); ancestor; ancestor = ancestor->parent_element())
for (auto ancestor = element.parent_element(); ancestor; ancestor = ancestor->parent_element())
ancestors.append(*ancestor);
NonnullRefPtr<CSSStyleValue const> new_font_size = CSS::LengthStyleValue::create(CSS::Length::make_px(default_monospace_font_size_in_px));

View file

@ -1440,7 +1440,7 @@ void Document::update_layout(UpdateLayoutReason reason)
if (node_invalidation.rebuild_layout_tree) {
// We mark layout tree for rebuild starting from parent element to correctly invalidate
// "display" property change to/from "contents" value.
if (auto* parent_element = node.parent_element()) {
if (auto parent_element = node.parent_element()) {
parent_element->set_needs_layout_tree_update(true);
} else {
node.set_needs_layout_tree_update(true);

View file

@ -2626,7 +2626,7 @@ Optional<String> Element::locate_a_namespace_prefix(Optional<String> const& name
}
// 3. If elements parent element is not null, then return the result of running locate a namespace prefix on that element using namespace.
if (auto* parent = this->parent_element())
if (auto parent = this->parent_element())
return parent->locate_a_namespace_prefix(namespace_);
// 4. Return null
@ -2881,7 +2881,7 @@ bool Element::check_visibility(Optional<CheckVisibilityOptions> options)
return false;
// 2. If an ancestor of this in the flat tree has content-visibility: hidden, return false.
for (auto* element = parent_element(); element; element = element->parent_element()) {
for (auto element = parent_element(); element; element = element->parent_element()) {
if (element->computed_properties()->content_visibility() == CSS::ContentVisibility::Hidden)
return false;
}
@ -3151,7 +3151,7 @@ Element const* Element::list_owner() const
return nullptr;
// 2. Let ancestor be the element's parent.
auto const* ancestor = parent_element();
auto ancestor = parent_element();
// AC-HOC: There may not be any parent element in a shadow tree.
if (!ancestor)
@ -3692,7 +3692,7 @@ void Element::inherit_counters()
{
// 1. If element is the root of its document tree, the element has an initially-empty CSS counters set.
// Return.
auto* parent = parent_element();
auto parent = parent_element();
if (parent == nullptr) {
// NOTE: We represent an empty counters set with `m_counters_set = nullptr`.
m_counters_set = nullptr;
@ -3765,14 +3765,14 @@ Optional<String> Element::lang() const
// 3. If the node's parent is a shadow root
// Use the language of that shadow root's host.
if (auto const* parent = parent_element()) {
if (auto parent = parent_element()) {
if (parent->is_shadow_root())
return parent->shadow_root()->host()->lang();
}
// 4. If the node's parent element is not null
// Use the language of that parent element.
if (auto const* parent = parent_element())
if (auto parent = parent_element())
return parent->lang();
// 5. Otherwise

View file

@ -582,20 +582,14 @@ private:
template<>
inline bool Node::fast_is<Element>() const { return is_element(); }
inline Element* Node::parent_element()
inline GC::Ptr<Element> Node::parent_element()
{
auto* parent = this->parent();
if (!parent || !is<Element>(parent))
return nullptr;
return static_cast<Element*>(parent);
return as_if<Element>(this->parent());
}
inline Element const* Node::parent_element() const
inline GC::Ptr<Element const> Node::parent_element() const
{
auto const* parent = this->parent();
if (!parent || !is<Element>(parent))
return nullptr;
return static_cast<Element const*>(parent);
return as_if<Element>(this->parent());
}
inline bool Element::has_class(FlyString const& class_name, CaseSensitivity case_sensitivity) const

View file

@ -2051,7 +2051,7 @@ Optional<String> Node::locate_a_namespace(Optional<String> const& prefix) const
}
// 5. If its parent element is null, then return null.
auto* parent_element = element.parent_element();
auto parent_element = element.parent_element();
if (!element.parent_element())
return {};
@ -2090,7 +2090,7 @@ Optional<String> Node::locate_a_namespace(Optional<String> const& prefix) const
// Otherwise
// 1. If its parent element is null, then return null.
auto* parent_element = this->parent_element();
auto parent_element = this->parent_element();
if (!parent_element)
return {};
@ -2153,7 +2153,7 @@ Optional<String> Node::lookup_prefix(Optional<String> namespace_) const
// Otherwise
// Return the result of locating a namespace prefix for its parent element, if its parent element is non-null; otherwise null.
auto* parent_element = this->parent_element();
auto parent_element = this->parent_element();
if (!parent_element)
return {};

View file

@ -261,8 +261,8 @@ public:
Node* parent_node() { return parent(); }
Node const* parent_node() const { return parent(); }
Element* parent_element();
Element const* parent_element() const;
GC::Ptr<Element> parent_element();
GC::Ptr<Element const> parent_element() const;
virtual void inserted();
virtual void post_connection();

View file

@ -56,7 +56,7 @@ bool is_an_assigned_slottable(GC::Ref<Node> node)
GC::Ptr<HTML::HTMLSlotElement> find_a_slot(Slottable const& slottable, OpenFlag open_flag)
{
// 1. If slottables parent is null, then return null.
auto* parent = slottable.visit([](auto& node) { return node->parent_element(); });
auto parent = slottable.visit([](auto& node) { return node->parent_element(); });
if (!parent)
return nullptr;

View file

@ -467,15 +467,15 @@ GC::Ptr<DOM::Element> HTMLElement::offset_parent() const
// - The computed value of the position property of the element is static
// and the ancestor is one of the following HTML elements: td, th, or table.
for (auto* ancestor = parent_element(); ancestor; ancestor = ancestor->parent_element()) {
for (auto ancestor = parent_element(); ancestor; ancestor = ancestor->parent_element()) {
if (!ancestor->layout_node())
continue;
if (ancestor->layout_node()->is_positioned())
return const_cast<Element*>(ancestor);
return const_cast<Element*>(ancestor.ptr());
if (is<HTML::HTMLBodyElement>(*ancestor))
return const_cast<Element*>(ancestor);
return const_cast<Element*>(ancestor.ptr());
if (!ancestor->layout_node()->is_positioned() && ancestor->local_name().is_one_of(HTML::TagNames::td, HTML::TagNames::th, HTML::TagNames::table))
return const_cast<Element*>(ancestor);
return const_cast<Element*>(ancestor.ptr());
}
// 3. Return null.
@ -843,7 +843,7 @@ Optional<ARIA::Role> HTMLElement::default_role() const
// https://www.w3.org/TR/html-aria/#el-aside
if (local_name() == TagNames::aside) {
// https://w3c.github.io/html-aam/#el-aside
for (auto const* ancestor = parent_element(); ancestor; ancestor = ancestor->parent_element()) {
for (auto ancestor = parent_element(); ancestor; ancestor = ancestor->parent_element()) {
if (ancestor->local_name().is_one_of(TagNames::article, TagNames::aside, TagNames::nav, TagNames::section)
&& accessible_name(document()).value().is_empty())
return ARIA::Role::generic;
@ -887,7 +887,7 @@ Optional<ARIA::Role> HTMLElement::default_role() const
// If not a descendant of an article, aside, main, nav or section element, or an element with role=article,
// complementary, main, navigation or region then (footer) role=contentinfo (header) role=banner. Otherwise,
// role=generic.
for (auto const* ancestor = parent_element(); ancestor; ancestor = ancestor->parent_element()) {
for (auto ancestor = parent_element(); ancestor; ancestor = ancestor->parent_element()) {
if (ancestor->local_name().is_one_of(TagNames::article, TagNames::aside, TagNames::main, TagNames::nav, TagNames::section)) {
if (local_name() == TagNames::footer)
return ARIA::Role::sectionfooter;

View file

@ -24,7 +24,7 @@ public:
// https://www.w3.org/TR/html-aria/#el-li
virtual Optional<ARIA::Role> default_role() const override
{
for (auto const* ancestor = parent_element(); ancestor; ancestor = ancestor->parent_element()) {
for (auto ancestor = parent_element(); ancestor; ancestor = ancestor->parent_element()) {
if (ancestor->role_or_default() == ARIA::Role::list)
return ARIA::Role::listitem;
}

View file

@ -32,8 +32,8 @@ HTMLFormElement* HTMLLegendElement::form()
{
// The form IDL attribute's behavior depends on whether the legend element is in a fieldset element or not.
// If the legend has a fieldset element as its parent, then the form IDL attribute must return the same value as the form IDL attribute on that fieldset element.
if (is<HTML::HTMLFieldSetElement>(parent_element())) {
return as<HTML::HTMLFieldSetElement>(parent_element())->form();
if (auto* field_set = as_if<HTML::HTMLFieldSetElement>(parent_element().ptr())) {
return field_set->form();
}
// Otherwise, it must return null.

View file

@ -250,7 +250,7 @@ void HTMLOptionElement::removed_from(Node* old_parent, Node& old_root)
if (old_parent) {
if (is<HTMLSelectElement>(*old_parent))
static_cast<HTMLSelectElement&>(*old_parent).update_selectedness();
else if (is<HTMLOptGroupElement>(*old_parent) && old_parent->parent_element() && is<HTMLSelectElement>(old_parent->parent_element()))
else if (is<HTMLOptGroupElement>(*old_parent) && old_parent->parent_element() && is<HTMLSelectElement>(*old_parent->parent_element()))
static_cast<HTMLSelectElement&>(*old_parent->parent_element()).update_selectedness();
}
}

View file

@ -31,7 +31,7 @@ void HTMLSummaryElement::activation_behavior(DOM::Event const&)
return;
// 2. Let parent be this summary element's parent.
auto* parent = this->parent_element();
auto parent = this->parent_element();
// 3. If the open attribute is present on parent, then remove it. Otherwise, set parent's open attribute to the empty string.
if (parent->has_attribute(HTML::AttributeNames::open))
@ -50,7 +50,7 @@ bool HTMLSummaryElement::is_summary_for_its_parent_details()
return false;
// 2. Let parent be this summary element's parent.
auto* parent = this->parent_element();
auto parent = this->parent_element();
// 3. If parent is not a details element, then return false.
if (!is<HTMLDetailsElement>(*parent))

View file

@ -215,7 +215,7 @@ WebIDL::Long HTMLTableCellElement::cell_index() const
Optional<ARIA::Role> HTMLTableCellElement::default_role() const
{
if (local_name() == TagNames::th) {
for (auto const* ancestor = parent_element(); ancestor; ancestor = ancestor->parent_element()) {
for (auto ancestor = parent_element(); ancestor; ancestor = ancestor->parent_element()) {
// AD-HOC: The ancestor checks here arent explicitly defined in the spec, but implicitly follow from what
// the spec does state, and from the physical placement/layout of elements. Also, the el-th and el-th-in-row
// tests at https://wpt.fyi/results/html-aam/table-roles.html require doing these ancestor checks — and

View file

@ -151,7 +151,7 @@ void HTMLTrackElement::start_the_track_processing_model()
return;
// 3. If the text track's track element does not have a media element as a parent, return.
if (!is<HTMLMediaElement>(parent_element()))
if (!is<HTMLMediaElement>(parent_element().ptr()))
return;
// 4. Run the remainder of these steps in parallel, allowing whatever caused these steps to run to continue.

View file

@ -342,7 +342,7 @@ void TextNode::compute_text_for_rendering()
if (dom_node().is_editable() && !dom_node().is_uninteresting_whitespace_node())
collapse = false;
auto const* parent_element = dom_node().parent_element();
auto parent_element = dom_node().parent_element();
auto const maybe_lang = parent_element ? parent_element->lang() : Optional<String> {};
auto const lang = maybe_lang.has_value() ? maybe_lang.value() : Optional<StringView> {};

View file

@ -727,7 +727,7 @@ void Page::update_find_in_page_selection(Vector<GC::Root<DOM::Range>> matches)
selection->add_range(*current_range);
if (auto* element = common_ancestor_container->parent_element()) {
if (auto element = common_ancestor_container->parent_element()) {
DOM::ScrollIntoViewOptions scroll_options;
scroll_options.block = Bindings::ScrollLogicalPosition::Nearest;
scroll_options.inline_ = Bindings::ScrollLogicalPosition::Nearest;