Coverage Report

Created: 2025-11-02 07:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/Userland/Libraries/LibWeb/HTML/HTMLTableRowElement.cpp
Line
Count
Source
1
/*
2
 * Copyright (c) 2020-2022, Andreas Kling <kling@serenityos.org>
3
 *
4
 * SPDX-License-Identifier: BSD-2-Clause
5
 */
6
7
#include <LibWeb/Bindings/HTMLTableRowElementPrototype.h>
8
#include <LibWeb/Bindings/Intrinsics.h>
9
#include <LibWeb/CSS/Parser/Parser.h>
10
#include <LibWeb/CSS/Parser/ParsingContext.h>
11
#include <LibWeb/CSS/StyleProperties.h>
12
#include <LibWeb/CSS/StyleValues/CSSColorValue.h>
13
#include <LibWeb/CSS/StyleValues/CSSKeywordValue.h>
14
#include <LibWeb/CSS/StyleValues/ImageStyleValue.h>
15
#include <LibWeb/DOM/Document.h>
16
#include <LibWeb/DOM/ElementFactory.h>
17
#include <LibWeb/DOM/HTMLCollection.h>
18
#include <LibWeb/HTML/HTMLTableCellElement.h>
19
#include <LibWeb/HTML/HTMLTableElement.h>
20
#include <LibWeb/HTML/HTMLTableRowElement.h>
21
#include <LibWeb/HTML/HTMLTableSectionElement.h>
22
#include <LibWeb/HTML/Parser/HTMLParser.h>
23
#include <LibWeb/Namespace.h>
24
25
namespace Web::HTML {
26
27
JS_DEFINE_ALLOCATOR(HTMLTableRowElement);
28
29
HTMLTableRowElement::HTMLTableRowElement(DOM::Document& document, DOM::QualifiedName qualified_name)
30
0
    : HTMLElement(document, move(qualified_name))
31
0
{
32
0
}
33
34
0
HTMLTableRowElement::~HTMLTableRowElement() = default;
35
36
void HTMLTableRowElement::initialize(JS::Realm& realm)
37
0
{
38
0
    Base::initialize(realm);
39
0
    WEB_SET_PROTOTYPE_FOR_INTERFACE(HTMLTableRowElement);
40
0
}
41
42
void HTMLTableRowElement::apply_presentational_hints(CSS::StyleProperties& style) const
43
0
{
44
0
    Base::apply_presentational_hints(style);
45
0
    for_each_attribute([&](auto& name, auto& value) {
46
0
        if (name == HTML::AttributeNames::bgcolor) {
47
            // https://html.spec.whatwg.org/multipage/rendering.html#tables-2:rules-for-parsing-a-legacy-colour-value
48
0
            auto color = parse_legacy_color_value(value);
49
0
            if (color.has_value())
50
0
                style.set_property(CSS::PropertyID::BackgroundColor, CSS::CSSColorValue::create_from_color(color.value()));
51
0
        } else if (name == HTML::AttributeNames::background) {
52
0
            if (auto parsed_value = document().parse_url(value); parsed_value.is_valid())
53
0
                style.set_property(CSS::PropertyID::BackgroundImage, CSS::ImageStyleValue::create(parsed_value));
54
0
        } else if (name == HTML::AttributeNames::height) {
55
0
            if (auto parsed_value = parse_dimension_value(value))
56
0
                style.set_property(CSS::PropertyID::Height, *parsed_value);
57
0
        } else if (name == HTML::AttributeNames::valign) {
58
0
            if (auto parsed_value = parse_css_value(CSS::Parser::ParsingContext { document() }, value, CSS::PropertyID::VerticalAlign))
59
0
                style.set_property(CSS::PropertyID::VerticalAlign, parsed_value.release_nonnull());
60
0
        }
61
0
    });
62
0
}
63
64
void HTMLTableRowElement::visit_edges(Cell::Visitor& visitor)
65
0
{
66
0
    Base::visit_edges(visitor);
67
0
    visitor.visit(m_cells);
68
0
}
69
70
// https://html.spec.whatwg.org/multipage/tables.html#dom-tr-cells
71
JS::NonnullGCPtr<DOM::HTMLCollection> HTMLTableRowElement::cells() const
72
0
{
73
    // The cells attribute must return an HTMLCollection rooted at this tr element,
74
    // whose filter matches only td and th elements that are children of the tr element.
75
0
    if (!m_cells) {
76
0
        m_cells = DOM::HTMLCollection::create(const_cast<HTMLTableRowElement&>(*this), DOM::HTMLCollection::Scope::Children, [](Element const& element) {
77
0
            return is<HTMLTableCellElement>(element);
78
0
        });
79
0
    }
80
0
    return *m_cells;
81
0
}
82
83
// https://html.spec.whatwg.org/multipage/tables.html#dom-tr-rowindex
84
int HTMLTableRowElement::row_index() const
85
0
{
86
    // The rowIndex attribute must, if this element has a parent table element,
87
    // or a parent tbody, thead, or tfoot element and a grandparent table element,
88
    // return the index of this tr element in that table element's rows collection.
89
    // If there is no such table element, then the attribute must return −1.
90
0
    auto rows_collection = [&]() -> JS::GCPtr<DOM::HTMLCollection> {
91
0
        if (!parent())
92
0
            return nullptr;
93
0
        if (is<HTMLTableElement>(*parent()))
94
0
            return const_cast<HTMLTableElement&>(static_cast<HTMLTableElement const&>(*parent())).rows();
95
0
        if (is<HTMLTableSectionElement>(*parent()) && parent()->parent() && is<HTMLTableElement>(*parent()->parent()))
96
0
            return const_cast<HTMLTableElement&>(static_cast<HTMLTableElement const&>(*parent()->parent())).rows();
97
0
        return nullptr;
98
0
    }();
99
0
    if (!rows_collection)
100
0
        return -1;
101
0
    auto rows = rows_collection->collect_matching_elements();
102
0
    for (size_t i = 0; i < rows.size(); ++i) {
103
0
        if (rows[i] == this)
104
0
            return i;
105
0
    }
106
0
    return -1;
107
0
}
108
109
int HTMLTableRowElement::section_row_index() const
110
0
{
111
    // The sectionRowIndex attribute must, if this element has a parent table, tbody, thead, or tfoot element,
112
    // return the index of the tr element in the parent element's rows collection
113
    // (for tables, that's HTMLTableElement's rows collection; for table sections, that's HTMLTableSectionElement's rows collection).
114
    // If there is no such parent element, then the attribute must return −1.
115
0
    auto rows_collection = [&]() -> JS::GCPtr<DOM::HTMLCollection> {
116
0
        if (!parent())
117
0
            return nullptr;
118
0
        if (is<HTMLTableElement>(*parent()))
119
0
            return const_cast<HTMLTableElement&>(static_cast<HTMLTableElement const&>(*parent())).rows();
120
0
        if (is<HTMLTableSectionElement>(*parent()))
121
0
            return static_cast<HTMLTableSectionElement const&>(*parent()).rows();
122
0
        return nullptr;
123
0
    }();
124
0
    if (!rows_collection)
125
0
        return -1;
126
0
    auto rows = rows_collection->collect_matching_elements();
127
0
    for (size_t i = 0; i < rows.size(); ++i) {
128
0
        if (rows[i] == this)
129
0
            return i;
130
0
    }
131
0
    return -1;
132
0
}
133
134
// https://html.spec.whatwg.org/multipage/tables.html#dom-tr-insertcell
135
WebIDL::ExceptionOr<JS::NonnullGCPtr<HTMLTableCellElement>> HTMLTableRowElement::insert_cell(i32 index)
136
0
{
137
0
    auto cells_collection = cells();
138
0
    auto cells_collection_size = static_cast<i32>(cells_collection->length());
139
140
    // 1. If index is less than −1 or greater than the number of elements in the cells collection, then throw an "IndexSizeError" DOMException.
141
0
    if (index < -1 || index > cells_collection_size)
142
0
        return WebIDL::IndexSizeError::create(realm(), "Index is negative or greater than the number of cells"_string);
143
144
    // 2. Let table cell be the result of creating an element given this tr element's node document, td, and the HTML namespace.
145
0
    auto& table_cell = static_cast<HTMLTableCellElement&>(*TRY(DOM::create_element(document(), HTML::TagNames::td, Namespace::HTML)));
146
147
    // 3. If index is equal to −1 or equal to the number of items in cells collection, then append table cell to this tr element.
148
0
    if (index == -1 || index == cells_collection_size)
149
0
        TRY(append_child(table_cell));
150
151
    // 4. Otherwise, insert table cell as a child of this tr element, immediately before the indexth td or th element in the cells collection.
152
0
    else
153
0
        insert_before(table_cell, cells_collection->item(index));
154
155
    // 5. Return table cell.
156
0
    return JS::NonnullGCPtr(table_cell);
157
0
}
158
159
// https://html.spec.whatwg.org/multipage/tables.html#dom-tr-deletecell
160
WebIDL::ExceptionOr<void> HTMLTableRowElement::delete_cell(i32 index)
161
0
{
162
0
    auto cells_collection = cells();
163
0
    auto cells_collection_size = static_cast<i32>(cells_collection->length());
164
165
    // 1. If index is less than −1 or greater than or equal to the number of elements in the cells collection, then throw an "IndexSizeError" DOMException.
166
0
    if (index < -1 || index >= cells_collection_size)
167
0
        return WebIDL::IndexSizeError::create(realm(), "Index is negative or greater than or equal to the number of cells"_string);
168
169
    // 2. If index is −1, then remove the last element in the cells collection from its parent, or do nothing if the cells collection is empty.
170
0
    if (index == -1) {
171
0
        if (cells_collection_size > 0)
172
0
            cells_collection->item(cells_collection_size - 1)->remove();
173
0
    }
174
175
    // 3. Otherwise, remove the indexth element in the cells collection from its parent.
176
0
    else {
177
0
        cells_collection->item(index)->remove();
178
0
    }
179
180
0
    return {};
181
0
}
182
183
}