Serenity Operating System
at master 625 lines 34 kB view raw
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}