/src/serenity/Userland/Libraries/LibWeb/HTML/HTMLTableCellElement.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2020-2022, Andreas Kling <kling@serenityos.org> |
3 | | * Copyright (c) 2024, Jamie Mansfield <jmansfield@cadixdev.org> |
4 | | * |
5 | | * SPDX-License-Identifier: BSD-2-Clause |
6 | | */ |
7 | | |
8 | | #include <LibWeb/Bindings/HTMLTableCellElementPrototype.h> |
9 | | #include <LibWeb/Bindings/Intrinsics.h> |
10 | | #include <LibWeb/CSS/Parser/Parser.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/CSS/StyleValues/LengthStyleValue.h> |
16 | | #include <LibWeb/DOM/Document.h> |
17 | | #include <LibWeb/DOM/HTMLCollection.h> |
18 | | #include <LibWeb/HTML/HTMLTableCellElement.h> |
19 | | #include <LibWeb/HTML/HTMLTableElement.h> |
20 | | #include <LibWeb/HTML/Numbers.h> |
21 | | #include <LibWeb/HTML/Parser/HTMLParser.h> |
22 | | |
23 | | namespace Web::HTML { |
24 | | |
25 | | JS_DEFINE_ALLOCATOR(HTMLTableCellElement); |
26 | | |
27 | | HTMLTableCellElement::HTMLTableCellElement(DOM::Document& document, DOM::QualifiedName qualified_name) |
28 | 0 | : HTMLElement(document, move(qualified_name)) |
29 | 0 | { |
30 | 0 | } |
31 | | |
32 | 0 | HTMLTableCellElement::~HTMLTableCellElement() = default; |
33 | | |
34 | | void HTMLTableCellElement::initialize(JS::Realm& realm) |
35 | 0 | { |
36 | 0 | Base::initialize(realm); |
37 | 0 | WEB_SET_PROTOTYPE_FOR_INTERFACE(HTMLTableCellElement); |
38 | 0 | } |
39 | | |
40 | | void HTMLTableCellElement::apply_presentational_hints(CSS::StyleProperties& style) const |
41 | 0 | { |
42 | 0 | for_each_attribute([&](auto& name, auto& value) { |
43 | 0 | if (name == HTML::AttributeNames::bgcolor) { |
44 | | // https://html.spec.whatwg.org/multipage/rendering.html#tables-2:rules-for-parsing-a-legacy-colour-value |
45 | 0 | auto color = parse_legacy_color_value(value); |
46 | 0 | if (color.has_value()) |
47 | 0 | style.set_property(CSS::PropertyID::BackgroundColor, CSS::CSSColorValue::create_from_color(color.value())); |
48 | 0 | return; |
49 | 0 | } |
50 | 0 | if (name == HTML::AttributeNames::valign) { |
51 | 0 | if (auto parsed_value = parse_css_value(CSS::Parser::ParsingContext { document() }, value, CSS::PropertyID::VerticalAlign)) |
52 | 0 | style.set_property(CSS::PropertyID::VerticalAlign, parsed_value.release_nonnull()); |
53 | 0 | return; |
54 | 0 | } |
55 | 0 | if (name == HTML::AttributeNames::align) { |
56 | 0 | if (value.equals_ignoring_ascii_case("center"sv) || value.equals_ignoring_ascii_case("middle"sv)) { |
57 | 0 | style.set_property(CSS::PropertyID::TextAlign, CSS::CSSKeywordValue::create(CSS::Keyword::LibwebCenter)); |
58 | 0 | } else if (value.equals_ignoring_ascii_case("left"sv)) { |
59 | 0 | style.set_property(CSS::PropertyID::TextAlign, CSS::CSSKeywordValue::create(CSS::Keyword::LibwebLeft)); |
60 | 0 | } else if (value.equals_ignoring_ascii_case("right"sv)) { |
61 | 0 | style.set_property(CSS::PropertyID::TextAlign, CSS::CSSKeywordValue::create(CSS::Keyword::LibwebRight)); |
62 | 0 | } else { |
63 | 0 | if (auto parsed_value = parse_css_value(CSS::Parser::ParsingContext { document() }, value, CSS::PropertyID::TextAlign)) |
64 | 0 | style.set_property(CSS::PropertyID::TextAlign, parsed_value.release_nonnull()); |
65 | 0 | } |
66 | 0 | return; |
67 | 0 | } |
68 | 0 | if (name == HTML::AttributeNames::width) { |
69 | 0 | if (auto parsed_value = parse_nonzero_dimension_value(value)) |
70 | 0 | style.set_property(CSS::PropertyID::Width, parsed_value.release_nonnull()); |
71 | 0 | return; |
72 | 0 | } else if (name == HTML::AttributeNames::height) { |
73 | 0 | if (auto parsed_value = parse_nonzero_dimension_value(value)) |
74 | 0 | style.set_property(CSS::PropertyID::Height, parsed_value.release_nonnull()); |
75 | 0 | return; |
76 | 0 | } else if (name == HTML::AttributeNames::background) { |
77 | 0 | if (auto parsed_value = document().parse_url(value); parsed_value.is_valid()) |
78 | 0 | style.set_property(CSS::PropertyID::BackgroundImage, CSS::ImageStyleValue::create(parsed_value)); |
79 | 0 | return; |
80 | 0 | } |
81 | 0 | }); |
82 | |
|
83 | 0 | auto const table_element = first_ancestor_of_type<HTMLTableElement>(); |
84 | 0 | if (!table_element) |
85 | 0 | return; |
86 | | |
87 | 0 | if (auto padding = table_element->padding()) { |
88 | 0 | style.set_property(CSS::PropertyID::PaddingTop, CSS::LengthStyleValue::create(CSS::Length::make_px(padding))); |
89 | 0 | style.set_property(CSS::PropertyID::PaddingBottom, CSS::LengthStyleValue::create(CSS::Length::make_px(padding))); |
90 | 0 | style.set_property(CSS::PropertyID::PaddingLeft, CSS::LengthStyleValue::create(CSS::Length::make_px(padding))); |
91 | 0 | style.set_property(CSS::PropertyID::PaddingRight, CSS::LengthStyleValue::create(CSS::Length::make_px(padding))); |
92 | 0 | } |
93 | |
|
94 | 0 | auto border = table_element->border(); |
95 | |
|
96 | 0 | if (!border) |
97 | 0 | return; |
98 | 0 | auto apply_border_style = [&](CSS::PropertyID style_property, CSS::PropertyID width_property, CSS::PropertyID color_property) { |
99 | 0 | style.set_property(style_property, CSS::CSSKeywordValue::create(CSS::Keyword::Inset)); |
100 | 0 | style.set_property(width_property, CSS::LengthStyleValue::create(CSS::Length::make_px(1))); |
101 | 0 | style.set_property(color_property, table_element->computed_css_values()->property(color_property)); |
102 | 0 | }; |
103 | 0 | apply_border_style(CSS::PropertyID::BorderLeftStyle, CSS::PropertyID::BorderLeftWidth, CSS::PropertyID::BorderLeftColor); |
104 | 0 | apply_border_style(CSS::PropertyID::BorderTopStyle, CSS::PropertyID::BorderTopWidth, CSS::PropertyID::BorderTopColor); |
105 | 0 | apply_border_style(CSS::PropertyID::BorderRightStyle, CSS::PropertyID::BorderRightWidth, CSS::PropertyID::BorderRightColor); |
106 | 0 | apply_border_style(CSS::PropertyID::BorderBottomStyle, CSS::PropertyID::BorderBottomWidth, CSS::PropertyID::BorderBottomColor); |
107 | 0 | } |
108 | | |
109 | | // This implements step 8 in the spec here: |
110 | | // https://html.spec.whatwg.org/multipage/tables.html#algorithm-for-processing-rows |
111 | | unsigned int HTMLTableCellElement::col_span() const |
112 | 0 | { |
113 | 0 | auto optional_value = Web::HTML::parse_non_negative_integer(get_attribute_value(HTML::AttributeNames::colspan)); |
114 | | |
115 | | // If parsing that value failed, or returned zero, or if the attribute is absent, then let colspan be 1, instead. |
116 | 0 | if (!optional_value.has_value() || optional_value.value() == 0) { |
117 | 0 | return 1; |
118 | 0 | } |
119 | | |
120 | 0 | auto value = optional_value.value(); |
121 | | |
122 | | // If colspan is greater than 1000, let it be 1000 instead. |
123 | 0 | if (value > 1000) { |
124 | 0 | return 1000; |
125 | 0 | } |
126 | | |
127 | 0 | return value; |
128 | 0 | } |
129 | | |
130 | | WebIDL::ExceptionOr<void> HTMLTableCellElement::set_col_span(unsigned int value) |
131 | 0 | { |
132 | 0 | return set_attribute(HTML::AttributeNames::colspan, String::number(value)); |
133 | 0 | } |
134 | | |
135 | | // This implements step 9 in the spec here: |
136 | | // https://html.spec.whatwg.org/multipage/tables.html#algorithm-for-processing-rows |
137 | | unsigned int HTMLTableCellElement::row_span() const |
138 | 0 | { |
139 | | // If parsing that value failed or if the attribute is absent, then let rowspan be 1, instead. |
140 | 0 | auto value = Web::HTML::parse_non_negative_integer(get_attribute_value(HTML::AttributeNames::rowspan)).value_or(1); |
141 | | |
142 | | // If rowspan is greater than 65534, let it be 65534 instead. |
143 | 0 | if (value > 65534) { |
144 | 0 | return 65534; |
145 | 0 | } |
146 | | |
147 | 0 | return value; |
148 | 0 | } |
149 | | |
150 | | WebIDL::ExceptionOr<void> HTMLTableCellElement::set_row_span(unsigned int value) |
151 | 0 | { |
152 | 0 | return set_attribute(HTML::AttributeNames::rowspan, String::number(value)); |
153 | 0 | } |
154 | | |
155 | | // https://html.spec.whatwg.org/multipage/tables.html#dom-tdth-cellindex |
156 | | WebIDL::Long HTMLTableCellElement::cell_index() const |
157 | 0 | { |
158 | | // The cellIndex IDL attribute must, if the element has a parent tr element, return the index of the cell's |
159 | | // element in the parent element's cells collection. If there is no such parent element, then the attribute |
160 | | // must return −1. |
161 | 0 | auto const* parent = first_ancestor_of_type<HTMLTableRowElement>(); |
162 | 0 | if (!parent) |
163 | 0 | return -1; |
164 | | |
165 | 0 | auto rows = parent->cells()->collect_matching_elements(); |
166 | 0 | for (size_t i = 0; i < rows.size(); ++i) { |
167 | 0 | if (rows[i] == this) |
168 | 0 | return i; |
169 | 0 | } |
170 | 0 | return -1; |
171 | 0 | } |
172 | | |
173 | | Optional<ARIA::Role> HTMLTableCellElement::default_role() const |
174 | 0 | { |
175 | | // TODO: For td: |
176 | | // role=cell if the ancestor table element is exposed as a role=table |
177 | | // role=gridcell if the ancestor table element is exposed as a role=grid or treegrid |
178 | | // No corresponding role if the ancestor table element is not exposed as a role=table, grid or treegrid |
179 | | // For th: |
180 | | // role=columnheader, rowheader or cell if the ancestor table element is exposed as a role=table |
181 | | // role=columnheader, rowheader or gridcell if the ancestor table element is exposed as a role=grid or treegrid |
182 | | // No corresponding role if the ancestor table element is not exposed as a role=table, grid or treegrid |
183 | | // https://www.w3.org/TR/html-aria/#el-td |
184 | | // https://www.w3.org/TR/html-aria/#el-th |
185 | 0 | return {}; |
186 | 0 | } |
187 | | |
188 | | } |