Serenity Operating System
1/*
2 * Copyright (c) 2021-2023, Andreas Kling <kling@serenityos.org>
3 * Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org>
4 * Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
5 *
6 * SPDX-License-Identifier: BSD-2-Clause
7 */
8
9#include <AK/Debug.h>
10#include <AK/Format.h>
11#include <AK/NonnullRefPtr.h>
12#include <LibWeb/CSS/Enums.h>
13#include <LibWeb/CSS/ResolvedCSSStyleDeclaration.h>
14#include <LibWeb/CSS/StyleComputer.h>
15#include <LibWeb/DOM/Document.h>
16#include <LibWeb/DOM/Element.h>
17#include <LibWeb/Layout/Viewport.h>
18#include <LibWeb/Painting/PaintableBox.h>
19#include <LibWeb/Painting/StackingContext.h>
20
21namespace Web::CSS {
22
23WebIDL::ExceptionOr<JS::NonnullGCPtr<ResolvedCSSStyleDeclaration>> ResolvedCSSStyleDeclaration::create(DOM::Element& element)
24{
25 return MUST_OR_THROW_OOM(element.realm().heap().allocate<ResolvedCSSStyleDeclaration>(element.realm(), element));
26}
27
28ResolvedCSSStyleDeclaration::ResolvedCSSStyleDeclaration(DOM::Element& element)
29 : CSSStyleDeclaration(element.realm())
30 , m_element(element)
31{
32}
33
34void ResolvedCSSStyleDeclaration::visit_edges(Cell::Visitor& visitor)
35{
36 Base::visit_edges(visitor);
37 visitor.visit(m_element.ptr());
38}
39
40size_t ResolvedCSSStyleDeclaration::length() const
41{
42 return 0;
43}
44
45DeprecatedString ResolvedCSSStyleDeclaration::item(size_t index) const
46{
47 (void)index;
48 return {};
49}
50
51static RefPtr<StyleValue> style_value_for_display(CSS::Display display)
52{
53 if (display.is_none())
54 return IdentifierStyleValue::create(CSS::ValueID::None);
55
56 if (display.is_outside_and_inside()) {
57 StyleValueVector values;
58 switch (display.outside()) {
59 case CSS::Display::Outside::Inline:
60 values.append(IdentifierStyleValue::create(CSS::ValueID::Inline));
61 break;
62 case CSS::Display::Outside::Block:
63 values.append(IdentifierStyleValue::create(CSS::ValueID::Block));
64 break;
65 case CSS::Display::Outside::RunIn:
66 values.append(IdentifierStyleValue::create(CSS::ValueID::RunIn));
67 break;
68 }
69 switch (display.inside()) {
70 case CSS::Display::Inside::Flow:
71 values.append(IdentifierStyleValue::create(CSS::ValueID::Flow));
72 break;
73 case CSS::Display::Inside::FlowRoot:
74 values.append(IdentifierStyleValue::create(CSS::ValueID::FlowRoot));
75 break;
76 case CSS::Display::Inside::Table:
77 values.append(IdentifierStyleValue::create(CSS::ValueID::Table));
78 break;
79 case CSS::Display::Inside::Flex:
80 values.append(IdentifierStyleValue::create(CSS::ValueID::Flex));
81 break;
82 case CSS::Display::Inside::Grid:
83 values.append(IdentifierStyleValue::create(CSS::ValueID::Grid));
84 break;
85 case CSS::Display::Inside::Ruby:
86 values.append(IdentifierStyleValue::create(CSS::ValueID::Ruby));
87 break;
88 }
89
90 return StyleValueList::create(move(values), StyleValueList::Separator::Space);
91 }
92
93 if (display.is_internal()) {
94 switch (display.internal()) {
95 case CSS::Display::Internal::TableRowGroup:
96 return IdentifierStyleValue::create(CSS::ValueID::TableRowGroup);
97 case CSS::Display::Internal::TableHeaderGroup:
98 return IdentifierStyleValue::create(CSS::ValueID::TableHeaderGroup);
99 case CSS::Display::Internal::TableFooterGroup:
100 return IdentifierStyleValue::create(CSS::ValueID::TableFooterGroup);
101 case CSS::Display::Internal::TableRow:
102 return IdentifierStyleValue::create(CSS::ValueID::TableRow);
103 case CSS::Display::Internal::TableCell:
104 return IdentifierStyleValue::create(CSS::ValueID::TableCell);
105 case CSS::Display::Internal::TableColumnGroup:
106 return IdentifierStyleValue::create(CSS::ValueID::TableColumnGroup);
107 case CSS::Display::Internal::TableColumn:
108 return IdentifierStyleValue::create(CSS::ValueID::TableColumn);
109 case CSS::Display::Internal::TableCaption:
110 return IdentifierStyleValue::create(CSS::ValueID::TableCaption);
111 case CSS::Display::Internal::RubyBase:
112 return IdentifierStyleValue::create(CSS::ValueID::RubyBase);
113 case CSS::Display::Internal::RubyText:
114 return IdentifierStyleValue::create(CSS::ValueID::RubyText);
115 case CSS::Display::Internal::RubyBaseContainer:
116 return IdentifierStyleValue::create(CSS::ValueID::RubyBaseContainer);
117 case CSS::Display::Internal::RubyTextContainer:
118 return IdentifierStyleValue::create(CSS::ValueID::RubyTextContainer);
119 }
120 }
121
122 TODO();
123}
124
125static NonnullRefPtr<StyleValue const> value_or_default(Optional<StyleProperty> property, NonnullRefPtr<StyleValue> default_style)
126{
127 if (property.has_value())
128 return property.value().value;
129 return default_style;
130}
131
132static NonnullRefPtr<StyleValue const> style_value_for_length_percentage(LengthPercentage const& length_percentage)
133{
134 if (length_percentage.is_percentage())
135 return PercentageStyleValue::create(length_percentage.percentage());
136 if (length_percentage.is_length())
137 return LengthStyleValue::create(length_percentage.length());
138 return length_percentage.calculated();
139}
140
141static NonnullRefPtr<StyleValue const> style_value_for_size(CSS::Size const& size)
142{
143 if (size.is_none())
144 return IdentifierStyleValue::create(ValueID::None);
145 if (size.is_percentage())
146 return PercentageStyleValue::create(size.percentage());
147 if (size.is_length())
148 return LengthStyleValue::create(size.length());
149 if (size.is_auto())
150 return IdentifierStyleValue::create(ValueID::Auto);
151 if (size.is_min_content())
152 return IdentifierStyleValue::create(ValueID::MinContent);
153 if (size.is_max_content())
154 return IdentifierStyleValue::create(ValueID::MaxContent);
155 // FIXME: Support fit-content(<length>)
156 TODO();
157}
158
159RefPtr<StyleValue const> ResolvedCSSStyleDeclaration::style_value_for_property(Layout::NodeWithStyle const& layout_node, PropertyID property_id) const
160{
161 switch (property_id) {
162 case CSS::PropertyID::Background: {
163 auto maybe_background_color = property(CSS::PropertyID::BackgroundColor);
164 auto maybe_background_image = property(CSS::PropertyID::BackgroundImage);
165 auto maybe_background_position = property(CSS::PropertyID::BackgroundPosition);
166 auto maybe_background_size = property(CSS::PropertyID::BackgroundSize);
167 auto maybe_background_repeat = property(CSS::PropertyID::BackgroundRepeat);
168 auto maybe_background_attachment = property(CSS::PropertyID::BackgroundAttachment);
169 auto maybe_background_origin = property(CSS::PropertyID::BackgroundOrigin);
170 auto maybe_background_clip = property(CSS::PropertyID::BackgroundClip);
171
172 return BackgroundStyleValue::create(
173 value_or_default(maybe_background_color, InitialStyleValue::the()),
174 value_or_default(maybe_background_image, IdentifierStyleValue::create(CSS::ValueID::None)),
175 value_or_default(maybe_background_position, PositionStyleValue::create(PositionEdge::Left, Length::make_px(0), PositionEdge::Top, Length::make_px(0))),
176 value_or_default(maybe_background_size, IdentifierStyleValue::create(CSS::ValueID::Auto)),
177 value_or_default(maybe_background_repeat, BackgroundRepeatStyleValue::create(CSS::Repeat::Repeat, CSS::Repeat::Repeat)),
178 value_or_default(maybe_background_attachment, IdentifierStyleValue::create(CSS::ValueID::Scroll)),
179 value_or_default(maybe_background_origin, IdentifierStyleValue::create(CSS::ValueID::PaddingBox)),
180 value_or_default(maybe_background_clip, IdentifierStyleValue::create(CSS::ValueID::BorderBox)));
181 }
182 case PropertyID::BackgroundColor:
183 return ColorStyleValue::create(layout_node.computed_values().background_color());
184 case CSS::PropertyID::BorderBottom: {
185 auto border = layout_node.computed_values().border_bottom();
186 auto width = LengthStyleValue::create(Length::make_px(border.width));
187 auto style = IdentifierStyleValue::create(to_value_id(border.line_style));
188 auto color = ColorStyleValue::create(border.color);
189 return BorderStyleValue::create(width, style, color);
190 }
191 case CSS::PropertyID::BorderBottomColor:
192 return ColorStyleValue::create(layout_node.computed_values().border_bottom().color);
193 case CSS::PropertyID::BorderBottomLeftRadius: {
194 auto const& border_radius = layout_node.computed_values().border_bottom_left_radius();
195 return BorderRadiusStyleValue::create(border_radius.horizontal_radius, border_radius.vertical_radius);
196 }
197 case CSS::PropertyID::BorderBottomRightRadius: {
198 auto const& border_radius = layout_node.computed_values().border_bottom_right_radius();
199 return BorderRadiusStyleValue::create(border_radius.horizontal_radius, border_radius.vertical_radius);
200 }
201 case CSS::PropertyID::BorderBottomStyle:
202 return IdentifierStyleValue::create(to_value_id(layout_node.computed_values().border_bottom().line_style));
203 case CSS::PropertyID::BorderBottomWidth:
204 return LengthStyleValue::create(Length::make_px(layout_node.computed_values().border_bottom().width));
205 case CSS::PropertyID::BorderCollapse:
206 return IdentifierStyleValue::create(to_value_id(layout_node.computed_values().border_collapse()));
207 case CSS::PropertyID::BorderLeft: {
208 auto border = layout_node.computed_values().border_left();
209 auto width = LengthStyleValue::create(Length::make_px(border.width));
210 auto style = IdentifierStyleValue::create(to_value_id(border.line_style));
211 auto color = ColorStyleValue::create(border.color);
212 return BorderStyleValue::create(width, style, color);
213 }
214 case CSS::PropertyID::BorderLeftColor:
215 return ColorStyleValue::create(layout_node.computed_values().border_left().color);
216 case CSS::PropertyID::BorderLeftStyle:
217 return IdentifierStyleValue::create(to_value_id(layout_node.computed_values().border_left().line_style));
218 case CSS::PropertyID::BorderLeftWidth:
219 return LengthStyleValue::create(Length::make_px(layout_node.computed_values().border_left().width));
220 case CSS::PropertyID::BorderRadius: {
221 auto maybe_top_left_radius = property(CSS::PropertyID::BorderTopLeftRadius);
222 auto maybe_top_right_radius = property(CSS::PropertyID::BorderTopRightRadius);
223 auto maybe_bottom_left_radius = property(CSS::PropertyID::BorderBottomLeftRadius);
224 auto maybe_bottom_right_radius = property(CSS::PropertyID::BorderBottomRightRadius);
225 RefPtr<BorderRadiusStyleValue const> top_left_radius, top_right_radius, bottom_left_radius, bottom_right_radius;
226 if (maybe_top_left_radius.has_value()) {
227 VERIFY(maybe_top_left_radius.value().value->is_border_radius());
228 top_left_radius = maybe_top_left_radius.value().value->as_border_radius();
229 }
230 if (maybe_top_right_radius.has_value()) {
231 VERIFY(maybe_top_right_radius.value().value->is_border_radius());
232 top_right_radius = maybe_top_right_radius.value().value->as_border_radius();
233 }
234 if (maybe_bottom_left_radius.has_value()) {
235 VERIFY(maybe_bottom_left_radius.value().value->is_border_radius());
236 bottom_left_radius = maybe_bottom_left_radius.value().value->as_border_radius();
237 }
238 if (maybe_bottom_right_radius.has_value()) {
239 VERIFY(maybe_bottom_right_radius.value().value->is_border_radius());
240 bottom_right_radius = maybe_bottom_right_radius.value().value->as_border_radius();
241 }
242
243 return BorderRadiusShorthandStyleValue::create(top_left_radius.release_nonnull(), top_right_radius.release_nonnull(), bottom_right_radius.release_nonnull(), bottom_left_radius.release_nonnull());
244 }
245 case CSS::PropertyID::BorderRight: {
246 auto border = layout_node.computed_values().border_right();
247 auto width = LengthStyleValue::create(Length::make_px(border.width));
248 auto style = IdentifierStyleValue::create(to_value_id(border.line_style));
249 auto color = ColorStyleValue::create(border.color);
250 return BorderStyleValue::create(width, style, color);
251 }
252 case CSS::PropertyID::BorderRightColor:
253 return ColorStyleValue::create(layout_node.computed_values().border_right().color);
254 case CSS::PropertyID::BorderRightStyle:
255 return IdentifierStyleValue::create(to_value_id(layout_node.computed_values().border_right().line_style));
256 case CSS::PropertyID::BorderRightWidth:
257 return LengthStyleValue::create(Length::make_px(layout_node.computed_values().border_right().width));
258 case CSS::PropertyID::BorderTop: {
259 auto border = layout_node.computed_values().border_top();
260 auto width = LengthStyleValue::create(Length::make_px(border.width));
261 auto style = IdentifierStyleValue::create(to_value_id(border.line_style));
262 auto color = ColorStyleValue::create(border.color);
263 return BorderStyleValue::create(width, style, color);
264 }
265 case CSS::PropertyID::BorderTopColor:
266 return ColorStyleValue::create(layout_node.computed_values().border_top().color);
267 case CSS::PropertyID::BorderTopLeftRadius: {
268 auto const& border_radius = layout_node.computed_values().border_top_left_radius();
269 return BorderRadiusStyleValue::create(border_radius.horizontal_radius, border_radius.vertical_radius);
270 }
271 case CSS::PropertyID::BorderTopRightRadius: {
272 auto const& border_radius = layout_node.computed_values().border_top_right_radius();
273 return BorderRadiusStyleValue::create(border_radius.horizontal_radius, border_radius.vertical_radius);
274 }
275 case CSS::PropertyID::BorderTopStyle:
276 return IdentifierStyleValue::create(to_value_id(layout_node.computed_values().border_top().line_style));
277 case CSS::PropertyID::BorderTopWidth:
278 return LengthStyleValue::create(Length::make_px(layout_node.computed_values().border_top().width));
279 case CSS::PropertyID::BoxShadow: {
280 auto box_shadow_layers = layout_node.computed_values().box_shadow();
281 if (box_shadow_layers.is_empty())
282 return {};
283
284 auto make_box_shadow_style_value = [](ShadowData const& data) {
285 return ShadowStyleValue::create(data.color, data.offset_x, data.offset_y, data.blur_radius, data.spread_distance, data.placement);
286 };
287
288 if (box_shadow_layers.size() == 1)
289 return make_box_shadow_style_value(box_shadow_layers.first());
290
291 StyleValueVector box_shadow;
292 box_shadow.ensure_capacity(box_shadow_layers.size());
293 for (auto const& layer : box_shadow_layers)
294 box_shadow.append(make_box_shadow_style_value(layer));
295 return StyleValueList::create(move(box_shadow), StyleValueList::Separator::Comma);
296 }
297 case CSS::PropertyID::BoxSizing:
298 return IdentifierStyleValue::create(to_value_id(layout_node.computed_values().box_sizing()));
299 case CSS::PropertyID::Bottom:
300 return style_value_for_length_percentage(layout_node.computed_values().inset().bottom());
301 case CSS::PropertyID::Clear:
302 return IdentifierStyleValue::create(to_value_id(layout_node.computed_values().clear()));
303 case CSS::PropertyID::Clip:
304 return RectStyleValue::create(layout_node.computed_values().clip().to_rect());
305 case CSS::PropertyID::Color:
306 return ColorStyleValue::create(layout_node.computed_values().color());
307 case CSS::PropertyID::ColumnGap:
308 return style_value_for_size(layout_node.computed_values().column_gap());
309 case CSS::PropertyID::Cursor:
310 return IdentifierStyleValue::create(to_value_id(layout_node.computed_values().cursor()));
311 case CSS::PropertyID::Display:
312 return style_value_for_display(layout_node.display());
313 case CSS::PropertyID::FlexBasis: {
314 switch (layout_node.computed_values().flex_basis().type) {
315 case FlexBasis::Content:
316 return IdentifierStyleValue::create(CSS::ValueID::Content);
317 case FlexBasis::LengthPercentage:
318 return style_value_for_length_percentage(*layout_node.computed_values().flex_basis().length_percentage);
319 case FlexBasis::Auto:
320 return IdentifierStyleValue::create(CSS::ValueID::Auto);
321 default:
322 VERIFY_NOT_REACHED();
323 }
324 break;
325 case CSS::PropertyID::FlexDirection:
326 return IdentifierStyleValue::create(to_value_id(layout_node.computed_values().flex_direction()));
327 case CSS::PropertyID::FlexGrow:
328 return NumericStyleValue::create_float(layout_node.computed_values().flex_grow());
329 case CSS::PropertyID::FlexShrink:
330 return NumericStyleValue::create_float(layout_node.computed_values().flex_shrink());
331 case CSS::PropertyID::FlexWrap:
332 return IdentifierStyleValue::create(to_value_id(layout_node.computed_values().flex_wrap()));
333 case CSS::PropertyID::Float:
334 return IdentifierStyleValue::create(to_value_id(layout_node.computed_values().float_()));
335 case CSS::PropertyID::GridArea: {
336 auto maybe_grid_row_start = property(CSS::PropertyID::GridRowStart);
337 auto maybe_grid_column_start = property(CSS::PropertyID::GridColumnStart);
338 auto maybe_grid_row_end = property(CSS::PropertyID::GridRowEnd);
339 auto maybe_grid_column_end = property(CSS::PropertyID::GridColumnEnd);
340 RefPtr<GridTrackPlacementStyleValue const> grid_row_start, grid_column_start, grid_row_end, grid_column_end;
341 if (maybe_grid_row_start.has_value()) {
342 VERIFY(maybe_grid_row_start.value().value->is_grid_track_placement());
343 grid_row_start = maybe_grid_row_start.value().value->as_grid_track_placement();
344 }
345 if (maybe_grid_column_start.has_value()) {
346 VERIFY(maybe_grid_column_start.value().value->is_grid_track_placement());
347 grid_column_start = maybe_grid_column_start.value().value->as_grid_track_placement();
348 }
349 if (maybe_grid_row_end.has_value()) {
350 VERIFY(maybe_grid_row_end.value().value->is_grid_track_placement());
351 grid_row_end = maybe_grid_row_end.value().value->as_grid_track_placement();
352 }
353 if (maybe_grid_column_end.has_value()) {
354 VERIFY(maybe_grid_column_end.value().value->is_grid_track_placement());
355 grid_column_end = maybe_grid_column_end.value().value->as_grid_track_placement();
356 }
357 return GridAreaShorthandStyleValue::create(
358 grid_row_start.release_nonnull(),
359 grid_column_start.release_nonnull(),
360 grid_row_end.release_nonnull(),
361 grid_column_end.release_nonnull());
362 }
363 case CSS::PropertyID::GridColumn: {
364 auto maybe_grid_column_end = property(CSS::PropertyID::GridColumnEnd);
365 auto maybe_grid_column_start = property(CSS::PropertyID::GridColumnStart);
366 RefPtr<GridTrackPlacementStyleValue const> grid_column_start, grid_column_end;
367 if (maybe_grid_column_end.has_value()) {
368 VERIFY(maybe_grid_column_end.value().value->is_grid_track_placement());
369 grid_column_end = maybe_grid_column_end.value().value->as_grid_track_placement();
370 }
371 if (maybe_grid_column_start.has_value()) {
372 VERIFY(maybe_grid_column_start.value().value->is_grid_track_placement());
373 grid_column_start = maybe_grid_column_start.value().value->as_grid_track_placement();
374 }
375 return GridTrackPlacementShorthandStyleValue::create(grid_column_end.release_nonnull(), grid_column_start.release_nonnull());
376 }
377 case CSS::PropertyID::GridColumnEnd:
378 return GridTrackPlacementStyleValue::create(layout_node.computed_values().grid_column_end());
379 case CSS::PropertyID::GridColumnStart:
380 return GridTrackPlacementStyleValue::create(layout_node.computed_values().grid_column_start());
381 case CSS::PropertyID::GridRow: {
382 auto maybe_grid_row_end = property(CSS::PropertyID::GridRowEnd);
383 auto maybe_grid_row_start = property(CSS::PropertyID::GridRowStart);
384 RefPtr<GridTrackPlacementStyleValue const> grid_row_start, grid_row_end;
385 if (maybe_grid_row_end.has_value()) {
386 VERIFY(maybe_grid_row_end.value().value->is_grid_track_placement());
387 grid_row_end = maybe_grid_row_end.value().value->as_grid_track_placement();
388 }
389 if (maybe_grid_row_start.has_value()) {
390 VERIFY(maybe_grid_row_start.value().value->is_grid_track_placement());
391 grid_row_start = maybe_grid_row_start.value().value->as_grid_track_placement();
392 }
393 return GridTrackPlacementShorthandStyleValue::create(grid_row_end.release_nonnull(), grid_row_start.release_nonnull());
394 }
395 case CSS::PropertyID::GridRowEnd:
396 return GridTrackPlacementStyleValue::create(layout_node.computed_values().grid_row_end());
397 case CSS::PropertyID::GridRowStart:
398 return GridTrackPlacementStyleValue::create(layout_node.computed_values().grid_row_start());
399 case CSS::PropertyID::GridTemplateColumns:
400 return GridTrackSizeStyleValue::create(layout_node.computed_values().grid_template_columns());
401 case CSS::PropertyID::GridTemplateRows:
402 return GridTrackSizeStyleValue::create(layout_node.computed_values().grid_template_rows());
403 case CSS::PropertyID::Height:
404 return style_value_for_size(layout_node.computed_values().height());
405 case CSS::PropertyID::ImageRendering:
406 return IdentifierStyleValue::create(to_value_id(layout_node.computed_values().image_rendering()));
407 case CSS::PropertyID::JustifyContent:
408 return IdentifierStyleValue::create(to_value_id(layout_node.computed_values().justify_content()));
409 case CSS::PropertyID::Left:
410 return style_value_for_length_percentage(layout_node.computed_values().inset().left());
411 case CSS::PropertyID::ListStyleType:
412 return IdentifierStyleValue::create(to_value_id(layout_node.computed_values().list_style_type()));
413 case CSS::PropertyID::Margin: {
414 auto margin = layout_node.computed_values().margin();
415 auto values = StyleValueVector {};
416 values.append(style_value_for_length_percentage(margin.top()));
417 values.append(style_value_for_length_percentage(margin.right()));
418 values.append(style_value_for_length_percentage(margin.bottom()));
419 values.append(style_value_for_length_percentage(margin.left()));
420 return StyleValueList::create(move(values), StyleValueList::Separator::Space);
421 }
422 case CSS::PropertyID::MarginBottom:
423 return style_value_for_length_percentage(layout_node.computed_values().margin().bottom());
424 case CSS::PropertyID::MarginLeft:
425 return style_value_for_length_percentage(layout_node.computed_values().margin().left());
426 case CSS::PropertyID::MarginRight:
427 return style_value_for_length_percentage(layout_node.computed_values().margin().right());
428 case CSS::PropertyID::MarginTop:
429 return style_value_for_length_percentage(layout_node.computed_values().margin().top());
430 case CSS::PropertyID::MaxHeight:
431 return style_value_for_size(layout_node.computed_values().max_height());
432 case CSS::PropertyID::MaxWidth:
433 return style_value_for_size(layout_node.computed_values().max_width());
434 case CSS::PropertyID::MinHeight:
435 return style_value_for_size(layout_node.computed_values().min_height());
436 case CSS::PropertyID::MinWidth:
437 return style_value_for_size(layout_node.computed_values().min_width());
438 case CSS::PropertyID::Opacity:
439 return NumericStyleValue::create_float(layout_node.computed_values().opacity());
440 case CSS::PropertyID::Order:
441 return NumericStyleValue::create_integer(layout_node.computed_values().order());
442 case CSS::PropertyID::OverflowX:
443 return IdentifierStyleValue::create(to_value_id(layout_node.computed_values().overflow_x()));
444 case CSS::PropertyID::OverflowY:
445 return IdentifierStyleValue::create(to_value_id(layout_node.computed_values().overflow_y()));
446 case CSS::PropertyID::Padding: {
447 auto padding = layout_node.computed_values().padding();
448 auto values = StyleValueVector {};
449 values.append(style_value_for_length_percentage(padding.top()));
450 values.append(style_value_for_length_percentage(padding.right()));
451 values.append(style_value_for_length_percentage(padding.bottom()));
452 values.append(style_value_for_length_percentage(padding.left()));
453 return StyleValueList::create(move(values), StyleValueList::Separator::Space);
454 }
455 case CSS::PropertyID::PaddingBottom:
456 case CSS::PropertyID::PaddingLeft:
457 return style_value_for_length_percentage(layout_node.computed_values().padding().left());
458 return style_value_for_length_percentage(layout_node.computed_values().padding().bottom());
459 case CSS::PropertyID::PaddingRight:
460 return style_value_for_length_percentage(layout_node.computed_values().padding().right());
461 case CSS::PropertyID::PaddingTop:
462 return style_value_for_length_percentage(layout_node.computed_values().padding().top());
463 case CSS::PropertyID::Position:
464 return IdentifierStyleValue::create(to_value_id(layout_node.computed_values().position()));
465 case CSS::PropertyID::Right:
466 return style_value_for_length_percentage(layout_node.computed_values().inset().right());
467 case CSS::PropertyID::RowGap:
468 return style_value_for_size(layout_node.computed_values().row_gap());
469 case CSS::PropertyID::TextAlign:
470 return IdentifierStyleValue::create(to_value_id(layout_node.computed_values().text_align()));
471 case CSS::PropertyID::TextDecorationLine: {
472 auto text_decoration_lines = layout_node.computed_values().text_decoration_line();
473 if (text_decoration_lines.is_empty())
474 return IdentifierStyleValue::create(ValueID::None);
475 StyleValueVector style_values;
476 for (auto const& line : text_decoration_lines) {
477 style_values.append(IdentifierStyleValue::create(to_value_id(line)));
478 }
479 return StyleValueList::create(move(style_values), StyleValueList::Separator::Space);
480 }
481 case CSS::PropertyID::TextDecorationStyle:
482 return IdentifierStyleValue::create(to_value_id(layout_node.computed_values().text_decoration_style()));
483 case CSS::PropertyID::TextTransform:
484 return IdentifierStyleValue::create(to_value_id(layout_node.computed_values().text_transform()));
485 case CSS::PropertyID::Top:
486 return style_value_for_length_percentage(layout_node.computed_values().inset().top());
487 case CSS::PropertyID::Transform: {
488 // NOTE: The computed value for `transform` serializes as a single `matrix(...)` value, instead of
489 // the original list of transform functions. So, we produce a StyleValue for that.
490 // https://www.w3.org/TR/css-transforms-1/#serialization-of-the-computed-value
491 auto transformations = layout_node.computed_values().transformations();
492 if (transformations.is_empty())
493 return IdentifierStyleValue::create(ValueID::None);
494
495 // The transform matrix is held by the StackingContext, so we need to make sure we have one first.
496 auto const* viewport = layout_node.document().layout_node();
497 VERIFY(viewport);
498 const_cast<Layout::Viewport&>(*viewport).build_stacking_context_tree_if_needed();
499
500 VERIFY(layout_node.paintable());
501 auto const& paintable_box = verify_cast<Painting::PaintableBox const>(layout_node.paintable());
502 VERIFY(paintable_box->stacking_context());
503
504 // FIXME: This needs to serialize to matrix3d if the transformation matrix is a 3D matrix.
505 // https://w3c.github.io/csswg-drafts/css-transforms-2/#serialization-of-the-computed-value
506 auto affine_matrix = paintable_box->stacking_context()->affine_transform_matrix();
507
508 StyleValueVector parameters;
509 parameters.ensure_capacity(6);
510 parameters.append(NumericStyleValue::create_float(affine_matrix.a()));
511 parameters.append(NumericStyleValue::create_float(affine_matrix.b()));
512 parameters.append(NumericStyleValue::create_float(affine_matrix.c()));
513 parameters.append(NumericStyleValue::create_float(affine_matrix.d()));
514 parameters.append(NumericStyleValue::create_float(affine_matrix.e()));
515 parameters.append(NumericStyleValue::create_float(affine_matrix.f()));
516
517 NonnullRefPtr<StyleValue> matrix_function = TransformationStyleValue::create(TransformFunction::Matrix, move(parameters));
518 // Elsewhere we always store the transform property's value as a StyleValueList of TransformationStyleValues,
519 // so this is just for consistency.
520 StyleValueVector matrix_functions;
521 matrix_functions.append(matrix_function);
522 return StyleValueList::create(move(matrix_functions), StyleValueList::Separator::Space);
523 }
524 case CSS::PropertyID::VerticalAlign:
525 if (auto const* length_percentage = layout_node.computed_values().vertical_align().get_pointer<CSS::LengthPercentage>()) {
526 if (length_percentage->is_length())
527 return LengthStyleValue::create(length_percentage->length());
528 if (length_percentage->is_percentage())
529 return PercentageStyleValue::create(length_percentage->percentage());
530 VERIFY_NOT_REACHED();
531 }
532 return IdentifierStyleValue::create(to_value_id(layout_node.computed_values().vertical_align().get<CSS::VerticalAlign>()));
533 case CSS::PropertyID::WhiteSpace:
534 return IdentifierStyleValue::create(to_value_id(layout_node.computed_values().white_space()));
535 case CSS::PropertyID::Width:
536 return style_value_for_size(layout_node.computed_values().width());
537 case CSS::PropertyID::ZIndex: {
538 auto maybe_z_index = layout_node.computed_values().z_index();
539 if (!maybe_z_index.has_value())
540 return {};
541 return NumericStyleValue::create_integer(maybe_z_index.release_value());
542 }
543 case CSS::PropertyID::Invalid:
544 return IdentifierStyleValue::create(CSS::ValueID::Invalid);
545 case CSS::PropertyID::Custom:
546 dbgln_if(LIBWEB_CSS_DEBUG, "Computed style for custom properties was requested (?)");
547 return {};
548 default:
549 dbgln_if(LIBWEB_CSS_DEBUG, "FIXME: Computed style for the '{}' property was requested", string_from_property_id(property_id));
550 return {};
551 }
552 }
553}
554
555Optional<StyleProperty> ResolvedCSSStyleDeclaration::property(PropertyID property_id) const
556{
557 if (CSS::property_affects_layout(property_id)) {
558 const_cast<DOM::Document&>(m_element->document()).update_layout();
559 } else {
560 // FIXME: If we had a way to update style for a single element, this would be a good place to use it.
561 const_cast<DOM::Document&>(m_element->document()).update_style();
562 }
563
564 if (!m_element->layout_node()) {
565 auto style_or_error = m_element->document().style_computer().compute_style(const_cast<DOM::Element&>(*m_element));
566 if (style_or_error.is_error()) {
567 dbgln("ResolvedCSSStyleDeclaration::property style computer failed");
568 return {};
569 }
570 auto style = style_or_error.release_value();
571
572 // FIXME: This is a stopgap until we implement shorthand -> longhand conversion.
573 auto value = style->maybe_null_property(property_id);
574 if (!value) {
575 dbgln("FIXME: ResolvedCSSStyleDeclaration::property(property_id=0x{:x}) No value for property ID in newly computed style case.", to_underlying(property_id));
576 return {};
577 }
578 return StyleProperty {
579 .property_id = property_id,
580 .value = value.release_nonnull(),
581 };
582 }
583
584 auto& layout_node = *m_element->layout_node();
585 auto value = style_value_for_property(layout_node, property_id);
586 if (!value)
587 return {};
588 return StyleProperty {
589 .property_id = property_id,
590 .value = value.release_nonnull(),
591 };
592}
593
594// https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-setproperty
595WebIDL::ExceptionOr<void> ResolvedCSSStyleDeclaration::set_property(PropertyID, StringView, StringView)
596{
597 // 1. If the computed flag is set, then throw a NoModificationAllowedError exception.
598 return WebIDL::NoModificationAllowedError::create(realm(), "Cannot modify properties in result of getComputedStyle()");
599}
600
601// https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-removeproperty
602WebIDL::ExceptionOr<DeprecatedString> ResolvedCSSStyleDeclaration::remove_property(PropertyID)
603{
604 // 1. If the computed flag is set, then throw a NoModificationAllowedError exception.
605 return WebIDL::NoModificationAllowedError::create(realm(), "Cannot remove properties from result of getComputedStyle()");
606}
607
608DeprecatedString ResolvedCSSStyleDeclaration::serialized() const
609{
610 // https://www.w3.org/TR/cssom/#dom-cssstyledeclaration-csstext
611 // If the computed flag is set, then return the empty string.
612
613 // NOTE: ResolvedCSSStyleDeclaration is something you would only get from window.getComputedStyle(),
614 // which returns what the spec calls "resolved style". The "computed flag" is always set here.
615 return DeprecatedString::empty();
616}
617
618// https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-csstext
619WebIDL::ExceptionOr<void> ResolvedCSSStyleDeclaration::set_css_text(StringView)
620{
621 // 1. If the computed flag is set, then throw a NoModificationAllowedError exception.
622 return WebIDL::NoModificationAllowedError::create(realm(), "Cannot modify properties in result of getComputedStyle()");
623}
624
625}