Serenity Operating System
at master 2335 lines 94 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * Copyright (c) 2021-2023, Sam Atkins <atkinssj@serenityos.org> 4 * Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org> 5 * Copyright (c) 2022-2023, MacDue <macdue@dueutil.tech> 6 * 7 * SPDX-License-Identifier: BSD-2-Clause 8 */ 9 10#include <AK/ByteBuffer.h> 11#include <LibGfx/Palette.h> 12#include <LibWeb/CSS/Serialize.h> 13#include <LibWeb/CSS/StyleValue.h> 14#include <LibWeb/DOM/Document.h> 15#include <LibWeb/HTML/BrowsingContext.h> 16#include <LibWeb/Loader/LoadRequest.h> 17#include <LibWeb/Loader/ResourceLoader.h> 18#include <LibWeb/Page/Page.h> 19#include <LibWeb/Painting/GradientPainting.h> 20#include <LibWeb/Platform/Timer.h> 21 22namespace Web::CSS { 23 24StyleValue::StyleValue(Type type) 25 : m_type(type) 26{ 27} 28 29AbstractImageStyleValue const& StyleValue::as_abstract_image() const 30{ 31 VERIFY(is_abstract_image()); 32 return static_cast<AbstractImageStyleValue const&>(*this); 33} 34 35AngleStyleValue const& StyleValue::as_angle() const 36{ 37 VERIFY(is_angle()); 38 return static_cast<AngleStyleValue const&>(*this); 39} 40 41BackgroundStyleValue const& StyleValue::as_background() const 42{ 43 VERIFY(is_background()); 44 return static_cast<BackgroundStyleValue const&>(*this); 45} 46 47BackgroundRepeatStyleValue const& StyleValue::as_background_repeat() const 48{ 49 VERIFY(is_background_repeat()); 50 return static_cast<BackgroundRepeatStyleValue const&>(*this); 51} 52 53BackgroundSizeStyleValue const& StyleValue::as_background_size() const 54{ 55 VERIFY(is_background_size()); 56 return static_cast<BackgroundSizeStyleValue const&>(*this); 57} 58 59BorderStyleValue const& StyleValue::as_border() const 60{ 61 VERIFY(is_border()); 62 return static_cast<BorderStyleValue const&>(*this); 63} 64 65BorderRadiusStyleValue const& StyleValue::as_border_radius() const 66{ 67 VERIFY(is_border_radius()); 68 return static_cast<BorderRadiusStyleValue const&>(*this); 69} 70 71BorderRadiusShorthandStyleValue const& StyleValue::as_border_radius_shorthand() const 72{ 73 VERIFY(is_border_radius_shorthand()); 74 return static_cast<BorderRadiusShorthandStyleValue const&>(*this); 75} 76 77ShadowStyleValue const& StyleValue::as_shadow() const 78{ 79 VERIFY(is_shadow()); 80 return static_cast<ShadowStyleValue const&>(*this); 81} 82 83CalculatedStyleValue const& StyleValue::as_calculated() const 84{ 85 VERIFY(is_calculated()); 86 return static_cast<CalculatedStyleValue const&>(*this); 87} 88 89ColorStyleValue const& StyleValue::as_color() const 90{ 91 VERIFY(is_color()); 92 return static_cast<ColorStyleValue const&>(*this); 93} 94 95ConicGradientStyleValue const& StyleValue::as_conic_gradient() const 96{ 97 VERIFY(is_conic_gradient()); 98 return static_cast<ConicGradientStyleValue const&>(*this); 99} 100 101ContentStyleValue const& StyleValue::as_content() const 102{ 103 VERIFY(is_content()); 104 return static_cast<ContentStyleValue const&>(*this); 105} 106 107FilterValueListStyleValue const& StyleValue::as_filter_value_list() const 108{ 109 VERIFY(is_filter_value_list()); 110 return static_cast<FilterValueListStyleValue const&>(*this); 111} 112 113FlexStyleValue const& StyleValue::as_flex() const 114{ 115 VERIFY(is_flex()); 116 return static_cast<FlexStyleValue const&>(*this); 117} 118 119FlexFlowStyleValue const& StyleValue::as_flex_flow() const 120{ 121 VERIFY(is_flex_flow()); 122 return static_cast<FlexFlowStyleValue const&>(*this); 123} 124 125FontStyleValue const& StyleValue::as_font() const 126{ 127 VERIFY(is_font()); 128 return static_cast<FontStyleValue const&>(*this); 129} 130 131FrequencyStyleValue const& StyleValue::as_frequency() const 132{ 133 VERIFY(is_frequency()); 134 return static_cast<FrequencyStyleValue const&>(*this); 135} 136 137GridTrackPlacementShorthandStyleValue const& StyleValue::as_grid_track_placement_shorthand() const 138{ 139 VERIFY(is_grid_track_placement_shorthand()); 140 return static_cast<GridTrackPlacementShorthandStyleValue const&>(*this); 141} 142 143GridAreaShorthandStyleValue const& StyleValue::as_grid_area_shorthand() const 144{ 145 VERIFY(is_grid_area_shorthand()); 146 return static_cast<GridAreaShorthandStyleValue const&>(*this); 147} 148 149GridTemplateAreaStyleValue const& StyleValue::as_grid_template_area() const 150{ 151 VERIFY(is_grid_template_area()); 152 return static_cast<GridTemplateAreaStyleValue const&>(*this); 153} 154 155GridTrackPlacementStyleValue const& StyleValue::as_grid_track_placement() const 156{ 157 VERIFY(is_grid_track_placement()); 158 return static_cast<GridTrackPlacementStyleValue const&>(*this); 159} 160 161IdentifierStyleValue const& StyleValue::as_identifier() const 162{ 163 VERIFY(is_identifier()); 164 return static_cast<IdentifierStyleValue const&>(*this); 165} 166 167ImageStyleValue const& StyleValue::as_image() const 168{ 169 VERIFY(is_image()); 170 return static_cast<ImageStyleValue const&>(*this); 171} 172 173InheritStyleValue const& StyleValue::as_inherit() const 174{ 175 VERIFY(is_inherit()); 176 return static_cast<InheritStyleValue const&>(*this); 177} 178 179InitialStyleValue const& StyleValue::as_initial() const 180{ 181 VERIFY(is_initial()); 182 return static_cast<InitialStyleValue const&>(*this); 183} 184 185LengthStyleValue const& StyleValue::as_length() const 186{ 187 VERIFY(is_length()); 188 return static_cast<LengthStyleValue const&>(*this); 189} 190 191GridTrackSizeStyleValue const& StyleValue::as_grid_track_size_list() const 192{ 193 VERIFY(is_grid_track_size_list()); 194 return static_cast<GridTrackSizeStyleValue const&>(*this); 195} 196 197LinearGradientStyleValue const& StyleValue::as_linear_gradient() const 198{ 199 VERIFY(is_linear_gradient()); 200 return static_cast<LinearGradientStyleValue const&>(*this); 201} 202 203ListStyleStyleValue const& StyleValue::as_list_style() const 204{ 205 VERIFY(is_list_style()); 206 return static_cast<ListStyleStyleValue const&>(*this); 207} 208 209NumericStyleValue const& StyleValue::as_numeric() const 210{ 211 VERIFY(is_numeric()); 212 return static_cast<NumericStyleValue const&>(*this); 213} 214 215OverflowStyleValue const& StyleValue::as_overflow() const 216{ 217 VERIFY(is_overflow()); 218 return static_cast<OverflowStyleValue const&>(*this); 219} 220 221PercentageStyleValue const& StyleValue::as_percentage() const 222{ 223 VERIFY(is_percentage()); 224 return static_cast<PercentageStyleValue const&>(*this); 225} 226 227PositionStyleValue const& StyleValue::as_position() const 228{ 229 VERIFY(is_position()); 230 return static_cast<PositionStyleValue const&>(*this); 231} 232 233RadialGradientStyleValue const& StyleValue::as_radial_gradient() const 234{ 235 VERIFY(is_radial_gradient()); 236 return static_cast<RadialGradientStyleValue const&>(*this); 237} 238 239RectStyleValue const& StyleValue::as_rect() const 240{ 241 VERIFY(is_rect()); 242 return static_cast<RectStyleValue const&>(*this); 243} 244 245ResolutionStyleValue const& StyleValue::as_resolution() const 246{ 247 VERIFY(is_resolution()); 248 return static_cast<ResolutionStyleValue const&>(*this); 249} 250 251StringStyleValue const& StyleValue::as_string() const 252{ 253 VERIFY(is_string()); 254 return static_cast<StringStyleValue const&>(*this); 255} 256 257TextDecorationStyleValue const& StyleValue::as_text_decoration() const 258{ 259 VERIFY(is_text_decoration()); 260 return static_cast<TextDecorationStyleValue const&>(*this); 261} 262 263TimeStyleValue const& StyleValue::as_time() const 264{ 265 VERIFY(is_time()); 266 return static_cast<TimeStyleValue const&>(*this); 267} 268 269TransformationStyleValue const& StyleValue::as_transformation() const 270{ 271 VERIFY(is_transformation()); 272 return static_cast<TransformationStyleValue const&>(*this); 273} 274 275UnresolvedStyleValue const& StyleValue::as_unresolved() const 276{ 277 VERIFY(is_unresolved()); 278 return static_cast<UnresolvedStyleValue const&>(*this); 279} 280 281UnsetStyleValue const& StyleValue::as_unset() const 282{ 283 VERIFY(is_unset()); 284 return static_cast<UnsetStyleValue const&>(*this); 285} 286 287StyleValueList const& StyleValue::as_value_list() const 288{ 289 VERIFY(is_value_list()); 290 return static_cast<StyleValueList const&>(*this); 291} 292 293BackgroundStyleValue::BackgroundStyleValue( 294 ValueComparingNonnullRefPtr<StyleValue const> color, 295 ValueComparingNonnullRefPtr<StyleValue const> image, 296 ValueComparingNonnullRefPtr<StyleValue const> position, 297 ValueComparingNonnullRefPtr<StyleValue const> size, 298 ValueComparingNonnullRefPtr<StyleValue const> repeat, 299 ValueComparingNonnullRefPtr<StyleValue const> attachment, 300 ValueComparingNonnullRefPtr<StyleValue const> origin, 301 ValueComparingNonnullRefPtr<StyleValue const> clip) 302 : StyleValueWithDefaultOperators(Type::Background) 303 , m_properties { 304 .color = move(color), 305 .image = move(image), 306 .position = move(position), 307 .size = move(size), 308 .repeat = move(repeat), 309 .attachment = move(attachment), 310 .origin = move(origin), 311 .clip = move(clip), 312 .layer_count = 0 313 } 314{ 315 auto layer_count = [](auto style_value) -> size_t { 316 if (style_value->is_value_list()) 317 return style_value->as_value_list().size(); 318 else 319 return 1; 320 }; 321 322 m_properties.layer_count = max(layer_count(m_properties.image), layer_count(m_properties.position)); 323 m_properties.layer_count = max(m_properties.layer_count, layer_count(m_properties.size)); 324 m_properties.layer_count = max(m_properties.layer_count, layer_count(m_properties.repeat)); 325 m_properties.layer_count = max(m_properties.layer_count, layer_count(m_properties.attachment)); 326 m_properties.layer_count = max(m_properties.layer_count, layer_count(m_properties.origin)); 327 m_properties.layer_count = max(m_properties.layer_count, layer_count(m_properties.clip)); 328 329 VERIFY(!m_properties.color->is_value_list()); 330} 331 332ErrorOr<String> BackgroundStyleValue::to_string() const 333{ 334 if (m_properties.layer_count == 1) { 335 return String::formatted("{} {} {} {} {} {} {} {}", TRY(m_properties.color->to_string()), TRY(m_properties.image->to_string()), TRY(m_properties.position->to_string()), TRY(m_properties.size->to_string()), TRY(m_properties.repeat->to_string()), TRY(m_properties.attachment->to_string()), TRY(m_properties.origin->to_string()), TRY(m_properties.clip->to_string())); 336 } 337 338 auto get_layer_value_string = [](ValueComparingNonnullRefPtr<StyleValue const> const& style_value, size_t index) { 339 if (style_value->is_value_list()) 340 return style_value->as_value_list().value_at(index, true)->to_string(); 341 return style_value->to_string(); 342 }; 343 344 StringBuilder builder; 345 for (size_t i = 0; i < m_properties.layer_count; i++) { 346 if (i) 347 TRY(builder.try_append(", "sv)); 348 if (i == m_properties.layer_count - 1) 349 TRY(builder.try_appendff("{} ", TRY(m_properties.color->to_string()))); 350 TRY(builder.try_appendff("{} {} {} {} {} {} {}", TRY(get_layer_value_string(m_properties.image, i)), TRY(get_layer_value_string(m_properties.position, i)), TRY(get_layer_value_string(m_properties.size, i)), TRY(get_layer_value_string(m_properties.repeat, i)), TRY(get_layer_value_string(m_properties.attachment, i)), TRY(get_layer_value_string(m_properties.origin, i)), TRY(get_layer_value_string(m_properties.clip, i)))); 351 } 352 353 return builder.to_string(); 354} 355 356ErrorOr<String> BackgroundRepeatStyleValue::to_string() const 357{ 358 return String::formatted("{} {}", CSS::to_string(m_properties.repeat_x), CSS::to_string(m_properties.repeat_y)); 359} 360 361ErrorOr<String> BackgroundSizeStyleValue::to_string() const 362{ 363 return String::formatted("{} {}", TRY(m_properties.size_x.to_string()), TRY(m_properties.size_y.to_string())); 364} 365 366ErrorOr<String> BorderStyleValue::to_string() const 367{ 368 return String::formatted("{} {} {}", TRY(m_properties.border_width->to_string()), TRY(m_properties.border_style->to_string()), TRY(m_properties.border_color->to_string())); 369} 370 371ErrorOr<String> BorderRadiusStyleValue::to_string() const 372{ 373 if (m_properties.horizontal_radius == m_properties.vertical_radius) 374 return m_properties.horizontal_radius.to_string(); 375 return String::formatted("{} / {}", TRY(m_properties.horizontal_radius.to_string()), TRY(m_properties.vertical_radius.to_string())); 376} 377 378ErrorOr<String> BorderRadiusShorthandStyleValue::to_string() const 379{ 380 return String::formatted("{} {} {} {} / {} {} {} {}", TRY(m_properties.top_left->horizontal_radius().to_string()), TRY(m_properties.top_right->horizontal_radius().to_string()), TRY(m_properties.bottom_right->horizontal_radius().to_string()), TRY(m_properties.bottom_left->horizontal_radius().to_string()), TRY(m_properties.top_left->vertical_radius().to_string()), TRY(m_properties.top_right->vertical_radius().to_string()), TRY(m_properties.bottom_right->vertical_radius().to_string()), TRY(m_properties.bottom_left->vertical_radius().to_string())); 381} 382 383void CalculatedStyleValue::CalculationResult::add(CalculationResult const& other, Layout::Node const* layout_node, PercentageBasis const& percentage_basis) 384{ 385 add_or_subtract_internal(SumOperation::Add, other, layout_node, percentage_basis); 386} 387 388void CalculatedStyleValue::CalculationResult::subtract(CalculationResult const& other, Layout::Node const* layout_node, PercentageBasis const& percentage_basis) 389{ 390 add_or_subtract_internal(SumOperation::Subtract, other, layout_node, percentage_basis); 391} 392 393void CalculatedStyleValue::CalculationResult::add_or_subtract_internal(SumOperation op, CalculationResult const& other, Layout::Node const* layout_node, PercentageBasis const& percentage_basis) 394{ 395 // We know from validation when resolving the type, that "both sides have the same type, or that one side is a <number> and the other is an <integer>". 396 // Though, having the same type may mean that one side is a <dimension> and the other a <percentage>. 397 // Note: This is almost identical to ::add() 398 399 m_value.visit( 400 [&](Number const& number) { 401 auto other_number = other.m_value.get<Number>(); 402 if (op == SumOperation::Add) { 403 m_value = number + other_number; 404 } else { 405 m_value = number - other_number; 406 } 407 }, 408 [&](Angle const& angle) { 409 auto this_degrees = angle.to_degrees(); 410 if (other.m_value.has<Angle>()) { 411 auto other_degrees = other.m_value.get<Angle>().to_degrees(); 412 if (op == SumOperation::Add) 413 m_value = Angle::make_degrees(this_degrees + other_degrees); 414 else 415 m_value = Angle::make_degrees(this_degrees - other_degrees); 416 } else { 417 VERIFY(percentage_basis.has<Angle>()); 418 419 auto other_degrees = percentage_basis.get<Angle>().percentage_of(other.m_value.get<Percentage>()).to_degrees(); 420 if (op == SumOperation::Add) 421 m_value = Angle::make_degrees(this_degrees + other_degrees); 422 else 423 m_value = Angle::make_degrees(this_degrees - other_degrees); 424 } 425 }, 426 [&](Frequency const& frequency) { 427 auto this_hertz = frequency.to_hertz(); 428 if (other.m_value.has<Frequency>()) { 429 auto other_hertz = other.m_value.get<Frequency>().to_hertz(); 430 if (op == SumOperation::Add) 431 m_value = Frequency::make_hertz(this_hertz + other_hertz); 432 else 433 m_value = Frequency::make_hertz(this_hertz - other_hertz); 434 } else { 435 VERIFY(percentage_basis.has<Frequency>()); 436 437 auto other_hertz = percentage_basis.get<Frequency>().percentage_of(other.m_value.get<Percentage>()).to_hertz(); 438 if (op == SumOperation::Add) 439 m_value = Frequency::make_hertz(this_hertz + other_hertz); 440 else 441 m_value = Frequency::make_hertz(this_hertz - other_hertz); 442 } 443 }, 444 [&](Length const& length) { 445 auto this_px = length.to_px(*layout_node); 446 if (other.m_value.has<Length>()) { 447 auto other_px = other.m_value.get<Length>().to_px(*layout_node); 448 if (op == SumOperation::Add) 449 m_value = Length::make_px(this_px + other_px); 450 else 451 m_value = Length::make_px(this_px - other_px); 452 } else { 453 VERIFY(percentage_basis.has<Length>()); 454 455 auto other_px = percentage_basis.get<Length>().percentage_of(other.m_value.get<Percentage>()).to_px(*layout_node); 456 if (op == SumOperation::Add) 457 m_value = Length::make_px(this_px + other_px); 458 else 459 m_value = Length::make_px(this_px - other_px); 460 } 461 }, 462 [&](Time const& time) { 463 auto this_seconds = time.to_seconds(); 464 if (other.m_value.has<Time>()) { 465 auto other_seconds = other.m_value.get<Time>().to_seconds(); 466 if (op == SumOperation::Add) 467 m_value = Time::make_seconds(this_seconds + other_seconds); 468 else 469 m_value = Time::make_seconds(this_seconds - other_seconds); 470 } else { 471 VERIFY(percentage_basis.has<Time>()); 472 473 auto other_seconds = percentage_basis.get<Time>().percentage_of(other.m_value.get<Percentage>()).to_seconds(); 474 if (op == SumOperation::Add) 475 m_value = Time::make_seconds(this_seconds + other_seconds); 476 else 477 m_value = Time::make_seconds(this_seconds - other_seconds); 478 } 479 }, 480 [&](Percentage const& percentage) { 481 if (other.m_value.has<Percentage>()) { 482 if (op == SumOperation::Add) 483 m_value = Percentage { percentage.value() + other.m_value.get<Percentage>().value() }; 484 else 485 m_value = Percentage { percentage.value() - other.m_value.get<Percentage>().value() }; 486 return; 487 } 488 489 // Other side isn't a percentage, so the easiest way to handle it without duplicating all the logic, is just to swap `this` and `other`. 490 CalculationResult new_value = other; 491 if (op == SumOperation::Add) { 492 new_value.add(*this, layout_node, percentage_basis); 493 } else { 494 // Turn 'this - other' into '-other + this', as 'A + B == B + A', but 'A - B != B - A' 495 new_value.multiply_by({ Number { Number::Type::Integer, -1.0f } }, layout_node); 496 new_value.add(*this, layout_node, percentage_basis); 497 } 498 499 *this = new_value; 500 }); 501} 502 503void CalculatedStyleValue::CalculationResult::multiply_by(CalculationResult const& other, Layout::Node const* layout_node) 504{ 505 // We know from validation when resolving the type, that at least one side must be a <number> or <integer>. 506 // Both of these are represented as a float. 507 VERIFY(m_value.has<Number>() || other.m_value.has<Number>()); 508 bool other_is_number = other.m_value.has<Number>(); 509 510 m_value.visit( 511 [&](Number const& number) { 512 if (other_is_number) { 513 m_value = number * other.m_value.get<Number>(); 514 } else { 515 // Avoid duplicating all the logic by swapping `this` and `other`. 516 CalculationResult new_value = other; 517 new_value.multiply_by(*this, layout_node); 518 *this = new_value; 519 } 520 }, 521 [&](Angle const& angle) { 522 m_value = Angle::make_degrees(angle.to_degrees() * other.m_value.get<Number>().value()); 523 }, 524 [&](Frequency const& frequency) { 525 m_value = Frequency::make_hertz(frequency.to_hertz() * other.m_value.get<Number>().value()); 526 }, 527 [&](Length const& length) { 528 VERIFY(layout_node); 529 m_value = Length::make_px(length.to_px(*layout_node) * other.m_value.get<Number>().value()); 530 }, 531 [&](Time const& time) { 532 m_value = Time::make_seconds(time.to_seconds() * other.m_value.get<Number>().value()); 533 }, 534 [&](Percentage const& percentage) { 535 m_value = Percentage { percentage.value() * other.m_value.get<Number>().value() }; 536 }); 537} 538 539void CalculatedStyleValue::CalculationResult::divide_by(CalculationResult const& other, Layout::Node const* layout_node) 540{ 541 // We know from validation when resolving the type, that `other` must be a <number> or <integer>. 542 // Both of these are represented as a Number. 543 auto denominator = other.m_value.get<Number>().value(); 544 // FIXME: Dividing by 0 is invalid, and should be caught during parsing. 545 VERIFY(denominator != 0.0f); 546 547 m_value.visit( 548 [&](Number const& number) { 549 m_value = Number { 550 Number::Type::Number, 551 number.value() / denominator 552 }; 553 }, 554 [&](Angle const& angle) { 555 m_value = Angle::make_degrees(angle.to_degrees() / denominator); 556 }, 557 [&](Frequency const& frequency) { 558 m_value = Frequency::make_hertz(frequency.to_hertz() / denominator); 559 }, 560 [&](Length const& length) { 561 VERIFY(layout_node); 562 m_value = Length::make_px(length.to_px(*layout_node) / denominator); 563 }, 564 [&](Time const& time) { 565 m_value = Time::make_seconds(time.to_seconds() / denominator); 566 }, 567 [&](Percentage const& percentage) { 568 m_value = Percentage { percentage.value() / denominator }; 569 }); 570} 571 572ErrorOr<String> CalculatedStyleValue::to_string() const 573{ 574 return String::formatted("calc({})", TRY(m_expression->to_string())); 575} 576 577bool CalculatedStyleValue::equals(StyleValue const& other) const 578{ 579 if (type() != other.type()) 580 return false; 581 // This is a case where comparing the strings actually makes sense. 582 return to_string().release_value_but_fixme_should_propagate_errors() == other.to_string().release_value_but_fixme_should_propagate_errors(); 583} 584 585ErrorOr<String> CalculatedStyleValue::CalcNumberValue::to_string() const 586{ 587 return value.visit( 588 [](Number const& number) -> ErrorOr<String> { return String::number(number.value()); }, 589 [](NonnullOwnPtr<CalcNumberSum> const& sum) -> ErrorOr<String> { return String::formatted("({})", TRY(sum->to_string())); }); 590} 591 592ErrorOr<String> CalculatedStyleValue::CalcValue::to_string() const 593{ 594 return value.visit( 595 [](Number const& number) -> ErrorOr<String> { return String::number(number.value()); }, 596 [](NonnullOwnPtr<CalcSum> const& sum) -> ErrorOr<String> { return String::formatted("({})", TRY(sum->to_string())); }, 597 [](auto const& v) -> ErrorOr<String> { return v.to_string(); }); 598} 599 600ErrorOr<String> CalculatedStyleValue::CalcSum::to_string() const 601{ 602 StringBuilder builder; 603 TRY(builder.try_append(TRY(first_calc_product->to_string()))); 604 for (auto const& item : zero_or_more_additional_calc_products) 605 TRY(builder.try_append(TRY(item->to_string()))); 606 return builder.to_string(); 607} 608 609ErrorOr<String> CalculatedStyleValue::CalcNumberSum::to_string() const 610{ 611 StringBuilder builder; 612 TRY(builder.try_append(TRY(first_calc_number_product->to_string()))); 613 for (auto const& item : zero_or_more_additional_calc_number_products) 614 TRY(builder.try_append(TRY(item->to_string()))); 615 return builder.to_string(); 616} 617 618ErrorOr<String> CalculatedStyleValue::CalcProduct::to_string() const 619{ 620 StringBuilder builder; 621 TRY(builder.try_append(TRY(first_calc_value.to_string()))); 622 for (auto const& item : zero_or_more_additional_calc_values) 623 TRY(builder.try_append(TRY(item->to_string()))); 624 return builder.to_string(); 625} 626 627ErrorOr<String> CalculatedStyleValue::CalcSumPartWithOperator::to_string() const 628{ 629 return String::formatted(" {} {}", op == SumOperation::Add ? "+"sv : "-"sv, TRY(value->to_string())); 630} 631 632ErrorOr<String> CalculatedStyleValue::CalcProductPartWithOperator::to_string() const 633{ 634 auto value_string = TRY(value.visit( 635 [](CalcValue const& v) { return v.to_string(); }, 636 [](CalcNumberValue const& v) { return v.to_string(); })); 637 return String::formatted(" {} {}", op == ProductOperation::Multiply ? "*"sv : "/"sv, value_string); 638} 639 640ErrorOr<String> CalculatedStyleValue::CalcNumberProduct::to_string() const 641{ 642 StringBuilder builder; 643 TRY(builder.try_append(TRY(first_calc_number_value.to_string()))); 644 for (auto const& item : zero_or_more_additional_calc_number_values) 645 TRY(builder.try_append(TRY(item->to_string()))); 646 return builder.to_string(); 647} 648 649ErrorOr<String> CalculatedStyleValue::CalcNumberProductPartWithOperator::to_string() const 650{ 651 return String::formatted(" {} {}", op == ProductOperation::Multiply ? "*"sv : "/"sv, TRY(value.to_string())); 652} 653 654ErrorOr<String> CalculatedStyleValue::CalcNumberSumPartWithOperator::to_string() const 655{ 656 return String::formatted(" {} {}", op == SumOperation::Add ? "+"sv : "-"sv, TRY(value->to_string())); 657} 658 659Optional<Angle> CalculatedStyleValue::resolve_angle() const 660{ 661 auto result = m_expression->resolve(nullptr, {}); 662 663 if (result.value().has<Angle>()) 664 return result.value().get<Angle>(); 665 return {}; 666} 667 668Optional<Angle> CalculatedStyleValue::resolve_angle_percentage(Angle const& percentage_basis) const 669{ 670 auto result = m_expression->resolve(nullptr, percentage_basis); 671 672 return result.value().visit( 673 [&](Angle const& angle) -> Optional<Angle> { 674 return angle; 675 }, 676 [&](Percentage const& percentage) -> Optional<Angle> { 677 return percentage_basis.percentage_of(percentage); 678 }, 679 [&](auto const&) -> Optional<Angle> { 680 return {}; 681 }); 682} 683 684Optional<Frequency> CalculatedStyleValue::resolve_frequency() const 685{ 686 auto result = m_expression->resolve(nullptr, {}); 687 688 if (result.value().has<Frequency>()) 689 return result.value().get<Frequency>(); 690 return {}; 691} 692 693Optional<Frequency> CalculatedStyleValue::resolve_frequency_percentage(Frequency const& percentage_basis) const 694{ 695 auto result = m_expression->resolve(nullptr, percentage_basis); 696 697 return result.value().visit( 698 [&](Frequency const& frequency) -> Optional<Frequency> { 699 return frequency; 700 }, 701 [&](Percentage const& percentage) -> Optional<Frequency> { 702 return percentage_basis.percentage_of(percentage); 703 }, 704 [&](auto const&) -> Optional<Frequency> { 705 return {}; 706 }); 707} 708 709Optional<Length> CalculatedStyleValue::resolve_length(Layout::Node const& layout_node) const 710{ 711 auto result = m_expression->resolve(&layout_node, {}); 712 713 if (result.value().has<Length>()) 714 return result.value().get<Length>(); 715 return {}; 716} 717 718Optional<Length> CalculatedStyleValue::resolve_length_percentage(Layout::Node const& layout_node, Length const& percentage_basis) const 719{ 720 auto result = m_expression->resolve(&layout_node, percentage_basis); 721 722 return result.value().visit( 723 [&](Length const& length) -> Optional<Length> { 724 return length; 725 }, 726 [&](Percentage const& percentage) -> Optional<Length> { 727 return percentage_basis.percentage_of(percentage); 728 }, 729 [&](auto const&) -> Optional<Length> { 730 return {}; 731 }); 732} 733 734Optional<Percentage> CalculatedStyleValue::resolve_percentage() const 735{ 736 auto result = m_expression->resolve(nullptr, {}); 737 if (result.value().has<Percentage>()) 738 return result.value().get<Percentage>(); 739 return {}; 740} 741 742Optional<Time> CalculatedStyleValue::resolve_time() const 743{ 744 auto result = m_expression->resolve(nullptr, {}); 745 746 if (result.value().has<Time>()) 747 return result.value().get<Time>(); 748 return {}; 749} 750 751Optional<Time> CalculatedStyleValue::resolve_time_percentage(Time const& percentage_basis) const 752{ 753 auto result = m_expression->resolve(nullptr, percentage_basis); 754 755 return result.value().visit( 756 [&](Time const& time) -> Optional<Time> { 757 return time; 758 }, 759 [&](auto const&) -> Optional<Time> { 760 return {}; 761 }); 762} 763 764Optional<float> CalculatedStyleValue::resolve_number() 765{ 766 auto result = m_expression->resolve(nullptr, {}); 767 if (result.value().has<Number>()) 768 return result.value().get<Number>().value(); 769 return {}; 770} 771 772Optional<i64> CalculatedStyleValue::resolve_integer() 773{ 774 auto result = m_expression->resolve(nullptr, {}); 775 if (result.value().has<Number>()) 776 return result.value().get<Number>().integer_value(); 777 return {}; 778} 779 780static bool is_number(CalculatedStyleValue::ResolvedType type) 781{ 782 return type == CalculatedStyleValue::ResolvedType::Number || type == CalculatedStyleValue::ResolvedType::Integer; 783} 784 785static bool is_dimension(CalculatedStyleValue::ResolvedType type) 786{ 787 return type != CalculatedStyleValue::ResolvedType::Number 788 && type != CalculatedStyleValue::ResolvedType::Integer 789 && type != CalculatedStyleValue::ResolvedType::Percentage; 790} 791 792template<typename SumWithOperator> 793static Optional<CalculatedStyleValue::ResolvedType> resolve_sum_type(CalculatedStyleValue::ResolvedType first_type, Vector<NonnullOwnPtr<SumWithOperator>> const& zero_or_more_additional_products) 794{ 795 auto type = first_type; 796 797 for (auto const& product : zero_or_more_additional_products) { 798 auto maybe_product_type = product->resolved_type(); 799 if (!maybe_product_type.has_value()) 800 return {}; 801 auto product_type = maybe_product_type.value(); 802 803 // At + or -, check that both sides have the same type, or that one side is a <number> and the other is an <integer>. 804 // If both sides are the same type, resolve to that type. 805 if (product_type == type) 806 continue; 807 808 // If one side is a <number> and the other is an <integer>, resolve to <number>. 809 if (is_number(type) && is_number(product_type)) { 810 type = CalculatedStyleValue::ResolvedType::Number; 811 continue; 812 } 813 814 // FIXME: calc() handles <percentage> by allowing them to pretend to be whatever <dimension> type is allowed at this location. 815 // Since we can't easily check what that type is, we just allow <percentage> to combine with any other <dimension> type. 816 if (type == CalculatedStyleValue::ResolvedType::Percentage && is_dimension(product_type)) { 817 type = product_type; 818 continue; 819 } 820 if (is_dimension(type) && product_type == CalculatedStyleValue::ResolvedType::Percentage) 821 continue; 822 823 return {}; 824 } 825 return type; 826} 827 828Optional<CalculatedStyleValue::ResolvedType> CalculatedStyleValue::CalcSum::resolved_type() const 829{ 830 auto maybe_type = first_calc_product->resolved_type(); 831 if (!maybe_type.has_value()) 832 return {}; 833 auto type = maybe_type.value(); 834 return resolve_sum_type(type, zero_or_more_additional_calc_products); 835} 836 837// https://www.w3.org/TR/CSS2/visufx.html#value-def-shape 838Gfx::FloatRect EdgeRect::resolved(Layout::Node const& layout_node, Gfx::FloatRect border_box) const 839{ 840 // In CSS 2.1, the only valid <shape> value is: rect(<top>, <right>, <bottom>, <left>) where 841 // <top> and <bottom> specify offsets from the top border edge of the box, and <right>, and 842 // <left> specify offsets from the left border edge of the box. 843 844 // The value 'auto' means that a given edge of the clipping region will be the same as the edge 845 // of the element's generated border box (i.e., 'auto' means the same as '0' for <top> and 846 // <left>, the same as the used value of the height plus the sum of vertical padding and border 847 // widths for <bottom>, and the same as the used value of the width plus the sum of the 848 // horizontal padding and border widths for <right>, such that four 'auto' values result in the 849 // clipping region being the same as the element's border box). 850 auto left = border_box.left() + (left_edge.is_auto() ? 0 : left_edge.to_px(layout_node)).value(); 851 auto top = border_box.top() + (top_edge.is_auto() ? 0 : top_edge.to_px(layout_node)).value(); 852 auto right = border_box.left() + (right_edge.is_auto() ? border_box.width() : right_edge.to_px(layout_node)).value(); 853 auto bottom = border_box.top() + (bottom_edge.is_auto() ? border_box.height() : bottom_edge.to_px(layout_node)).value(); 854 return Gfx::FloatRect { 855 left, 856 top, 857 right - left, 858 bottom - top 859 }; 860} 861 862Optional<CalculatedStyleValue::ResolvedType> CalculatedStyleValue::CalcNumberSum::resolved_type() const 863{ 864 auto maybe_type = first_calc_number_product->resolved_type(); 865 if (!maybe_type.has_value()) 866 return {}; 867 auto type = maybe_type.value(); 868 return resolve_sum_type(type, zero_or_more_additional_calc_number_products); 869} 870 871template<typename ProductWithOperator> 872static Optional<CalculatedStyleValue::ResolvedType> resolve_product_type(CalculatedStyleValue::ResolvedType first_type, Vector<NonnullOwnPtr<ProductWithOperator>> const& zero_or_more_additional_values) 873{ 874 auto type = first_type; 875 876 for (auto const& value : zero_or_more_additional_values) { 877 auto maybe_value_type = value->resolved_type(); 878 if (!maybe_value_type.has_value()) 879 return {}; 880 auto value_type = maybe_value_type.value(); 881 882 if (value->op == CalculatedStyleValue::ProductOperation::Multiply) { 883 // At *, check that at least one side is <number>. 884 if (!(is_number(type) || is_number(value_type))) 885 return {}; 886 // If both sides are <integer>, resolve to <integer>. 887 if (type == CalculatedStyleValue::ResolvedType::Integer && value_type == CalculatedStyleValue::ResolvedType::Integer) { 888 type = CalculatedStyleValue::ResolvedType::Integer; 889 } else { 890 // Otherwise, resolve to the type of the other side. 891 if (is_number(type)) 892 type = value_type; 893 } 894 895 continue; 896 } else { 897 VERIFY(value->op == CalculatedStyleValue::ProductOperation::Divide); 898 // At /, check that the right side is <number>. 899 if (!is_number(value_type)) 900 return {}; 901 // If the left side is <integer>, resolve to <number>. 902 if (type == CalculatedStyleValue::ResolvedType::Integer) { 903 type = CalculatedStyleValue::ResolvedType::Number; 904 } else { 905 // Otherwise, resolve to the type of the left side. 906 } 907 908 // FIXME: Division by zero makes the whole calc() expression invalid. 909 } 910 } 911 return type; 912} 913 914Optional<CalculatedStyleValue::ResolvedType> CalculatedStyleValue::CalcProduct::resolved_type() const 915{ 916 auto maybe_type = first_calc_value.resolved_type(); 917 if (!maybe_type.has_value()) 918 return {}; 919 auto type = maybe_type.value(); 920 return resolve_product_type(type, zero_or_more_additional_calc_values); 921} 922 923Optional<CalculatedStyleValue::ResolvedType> CalculatedStyleValue::CalcSumPartWithOperator::resolved_type() const 924{ 925 return value->resolved_type(); 926} 927 928Optional<CalculatedStyleValue::ResolvedType> CalculatedStyleValue::CalcNumberProduct::resolved_type() const 929{ 930 auto maybe_type = first_calc_number_value.resolved_type(); 931 if (!maybe_type.has_value()) 932 return {}; 933 auto type = maybe_type.value(); 934 return resolve_product_type(type, zero_or_more_additional_calc_number_values); 935} 936 937Optional<CalculatedStyleValue::ResolvedType> CalculatedStyleValue::CalcNumberProductPartWithOperator::resolved_type() const 938{ 939 return value.resolved_type(); 940} 941 942Optional<CalculatedStyleValue::ResolvedType> CalculatedStyleValue::CalcNumberSumPartWithOperator::resolved_type() const 943{ 944 return value->resolved_type(); 945} 946 947Optional<CalculatedStyleValue::ResolvedType> CalculatedStyleValue::CalcProductPartWithOperator::resolved_type() const 948{ 949 return value.visit( 950 [](CalcValue const& calc_value) { 951 return calc_value.resolved_type(); 952 }, 953 [](CalcNumberValue const& calc_number_value) { 954 return calc_number_value.resolved_type(); 955 }); 956} 957 958Optional<CalculatedStyleValue::ResolvedType> CalculatedStyleValue::CalcValue::resolved_type() const 959{ 960 return value.visit( 961 [](Number const& number) -> Optional<CalculatedStyleValue::ResolvedType> { 962 return { number.is_integer() ? ResolvedType::Integer : ResolvedType::Number }; 963 }, 964 [](Angle const&) -> Optional<CalculatedStyleValue::ResolvedType> { return { ResolvedType::Angle }; }, 965 [](Frequency const&) -> Optional<CalculatedStyleValue::ResolvedType> { return { ResolvedType::Frequency }; }, 966 [](Length const&) -> Optional<CalculatedStyleValue::ResolvedType> { return { ResolvedType::Length }; }, 967 [](Percentage const&) -> Optional<CalculatedStyleValue::ResolvedType> { return { ResolvedType::Percentage }; }, 968 [](Time const&) -> Optional<CalculatedStyleValue::ResolvedType> { return { ResolvedType::Time }; }, 969 [](NonnullOwnPtr<CalcSum> const& sum) { return sum->resolved_type(); }); 970} 971 972Optional<CalculatedStyleValue::ResolvedType> CalculatedStyleValue::CalcNumberValue::resolved_type() const 973{ 974 return value.visit( 975 [](Number const& number) -> Optional<CalculatedStyleValue::ResolvedType> { 976 return { number.is_integer() ? ResolvedType::Integer : ResolvedType::Number }; 977 }, 978 [](NonnullOwnPtr<CalcNumberSum> const& sum) { return sum->resolved_type(); }); 979} 980 981CalculatedStyleValue::CalculationResult CalculatedStyleValue::CalcNumberValue::resolve(Layout::Node const* layout_node, PercentageBasis const& percentage_basis) const 982{ 983 return value.visit( 984 [&](Number const& number) -> CalculatedStyleValue::CalculationResult { 985 return CalculatedStyleValue::CalculationResult { number }; 986 }, 987 [&](NonnullOwnPtr<CalcNumberSum> const& sum) -> CalculatedStyleValue::CalculationResult { 988 return sum->resolve(layout_node, percentage_basis); 989 }); 990} 991 992CalculatedStyleValue::CalculationResult CalculatedStyleValue::CalcValue::resolve(Layout::Node const* layout_node, PercentageBasis const& percentage_basis) const 993{ 994 return value.visit( 995 [&](NonnullOwnPtr<CalcSum> const& sum) -> CalculatedStyleValue::CalculationResult { 996 return sum->resolve(layout_node, percentage_basis); 997 }, 998 [&](auto const& v) -> CalculatedStyleValue::CalculationResult { 999 return CalculatedStyleValue::CalculationResult { v }; 1000 }); 1001} 1002 1003CalculatedStyleValue::CalculationResult CalculatedStyleValue::CalcSum::resolve(Layout::Node const* layout_node, PercentageBasis const& percentage_basis) const 1004{ 1005 auto value = first_calc_product->resolve(layout_node, percentage_basis); 1006 1007 for (auto& additional_product : zero_or_more_additional_calc_products) { 1008 auto additional_value = additional_product->resolve(layout_node, percentage_basis); 1009 1010 if (additional_product->op == CalculatedStyleValue::SumOperation::Add) 1011 value.add(additional_value, layout_node, percentage_basis); 1012 else if (additional_product->op == CalculatedStyleValue::SumOperation::Subtract) 1013 value.subtract(additional_value, layout_node, percentage_basis); 1014 else 1015 VERIFY_NOT_REACHED(); 1016 } 1017 1018 return value; 1019} 1020 1021CalculatedStyleValue::CalculationResult CalculatedStyleValue::CalcNumberSum::resolve(Layout::Node const* layout_node, PercentageBasis const& percentage_basis) const 1022{ 1023 auto value = first_calc_number_product->resolve(layout_node, percentage_basis); 1024 1025 for (auto& additional_product : zero_or_more_additional_calc_number_products) { 1026 auto additional_value = additional_product->resolve(layout_node, percentage_basis); 1027 1028 if (additional_product->op == CSS::CalculatedStyleValue::SumOperation::Add) 1029 value.add(additional_value, layout_node, percentage_basis); 1030 else if (additional_product->op == CalculatedStyleValue::SumOperation::Subtract) 1031 value.subtract(additional_value, layout_node, percentage_basis); 1032 else 1033 VERIFY_NOT_REACHED(); 1034 } 1035 1036 return value; 1037} 1038 1039CalculatedStyleValue::CalculationResult CalculatedStyleValue::CalcProduct::resolve(Layout::Node const* layout_node, PercentageBasis const& percentage_basis) const 1040{ 1041 auto value = first_calc_value.resolve(layout_node, percentage_basis); 1042 1043 for (auto& additional_value : zero_or_more_additional_calc_values) { 1044 additional_value->value.visit( 1045 [&](CalculatedStyleValue::CalcValue const& calc_value) { 1046 VERIFY(additional_value->op == CalculatedStyleValue::ProductOperation::Multiply); 1047 auto resolved_value = calc_value.resolve(layout_node, percentage_basis); 1048 value.multiply_by(resolved_value, layout_node); 1049 }, 1050 [&](CalculatedStyleValue::CalcNumberValue const& calc_number_value) { 1051 VERIFY(additional_value->op == CalculatedStyleValue::ProductOperation::Divide); 1052 auto resolved_calc_number_value = calc_number_value.resolve(layout_node, percentage_basis); 1053 // FIXME: Checking for division by 0 should happen during parsing. 1054 VERIFY(resolved_calc_number_value.value().get<Number>().value() != 0.0f); 1055 value.divide_by(resolved_calc_number_value, layout_node); 1056 }); 1057 } 1058 1059 return value; 1060} 1061 1062CalculatedStyleValue::CalculationResult CalculatedStyleValue::CalcNumberProduct::resolve(Layout::Node const* layout_node, PercentageBasis const& percentage_basis) const 1063{ 1064 auto value = first_calc_number_value.resolve(layout_node, percentage_basis); 1065 1066 for (auto& additional_number_value : zero_or_more_additional_calc_number_values) { 1067 auto additional_value = additional_number_value->resolve(layout_node, percentage_basis); 1068 1069 if (additional_number_value->op == CalculatedStyleValue::ProductOperation::Multiply) 1070 value.multiply_by(additional_value, layout_node); 1071 else if (additional_number_value->op == CalculatedStyleValue::ProductOperation::Divide) 1072 value.divide_by(additional_value, layout_node); 1073 else 1074 VERIFY_NOT_REACHED(); 1075 } 1076 1077 return value; 1078} 1079 1080CalculatedStyleValue::CalculationResult CalculatedStyleValue::CalcProductPartWithOperator::resolve(Layout::Node const* layout_node, PercentageBasis const& percentage_basis) const 1081{ 1082 return value.visit( 1083 [&](CalcValue const& calc_value) { 1084 return calc_value.resolve(layout_node, percentage_basis); 1085 }, 1086 [&](CalcNumberValue const& calc_number_value) { 1087 return calc_number_value.resolve(layout_node, percentage_basis); 1088 }); 1089} 1090 1091CalculatedStyleValue::CalculationResult CalculatedStyleValue::CalcSumPartWithOperator::resolve(Layout::Node const* layout_node, PercentageBasis const& percentage_basis) const 1092{ 1093 return value->resolve(layout_node, percentage_basis); 1094} 1095 1096CalculatedStyleValue::CalculationResult CalculatedStyleValue::CalcNumberProductPartWithOperator::resolve(Layout::Node const* layout_node, PercentageBasis const& percentage_basis) const 1097{ 1098 return value.resolve(layout_node, percentage_basis); 1099} 1100 1101CalculatedStyleValue::CalculationResult CalculatedStyleValue::CalcNumberSumPartWithOperator::resolve(Layout::Node const* layout_node, PercentageBasis const& percentage_basis) const 1102{ 1103 return value->resolve(layout_node, percentage_basis); 1104} 1105 1106ErrorOr<String> ColorStyleValue::to_string() const 1107{ 1108 return serialize_a_srgb_value(m_color); 1109} 1110 1111ErrorOr<String> ContentStyleValue::to_string() const 1112{ 1113 if (has_alt_text()) 1114 return String::formatted("{} / {}", TRY(m_properties.content->to_string()), TRY(m_properties.alt_text->to_string())); 1115 return m_properties.content->to_string(); 1116} 1117 1118float Filter::Blur::resolved_radius(Layout::Node const& node) const 1119{ 1120 // Default value when omitted is 0px. 1121 auto sigma = 0; 1122 if (radius.has_value()) 1123 sigma = radius->resolved(node).to_px(node).value(); 1124 // Note: The radius/sigma of the blur needs to be doubled for LibGfx's blur functions. 1125 return sigma * 2; 1126} 1127 1128Filter::DropShadow::Resolved Filter::DropShadow::resolved(Layout::Node const& node) const 1129{ 1130 // The default value for omitted values is missing length values set to 0 1131 // and the missing used color is taken from the color property. 1132 return Resolved { 1133 offset_x.resolved(node).to_px(node).value(), 1134 offset_y.resolved(node).to_px(node).value(), 1135 radius.has_value() ? radius->resolved(node).to_px(node).value() : 0.0f, 1136 color.has_value() ? *color : node.computed_values().color() 1137 }; 1138} 1139 1140float Filter::HueRotate::angle_degrees() const 1141{ 1142 // Default value when omitted is 0deg. 1143 if (!angle.has_value()) 1144 return 0.0f; 1145 return angle->visit([&](Angle const& angle) { return angle.to_degrees(); }, [&](auto) { return 0.0f; }); 1146} 1147 1148float Filter::Color::resolved_amount() const 1149{ 1150 if (amount.has_value()) { 1151 if (amount->is_percentage()) 1152 return amount->percentage().as_fraction(); 1153 return amount->number().value(); 1154 } 1155 // All color filters (brightness, sepia, etc) have a default amount of 1. 1156 return 1.0f; 1157} 1158 1159ErrorOr<String> FilterValueListStyleValue::to_string() const 1160{ 1161 StringBuilder builder {}; 1162 bool first = true; 1163 for (auto& filter_function : filter_value_list()) { 1164 if (!first) 1165 TRY(builder.try_append(' ')); 1166 TRY(filter_function.visit( 1167 [&](Filter::Blur const& blur) -> ErrorOr<void> { 1168 TRY(builder.try_append("blur("sv)); 1169 if (blur.radius.has_value()) 1170 TRY(builder.try_append(TRY(blur.radius->to_string()))); 1171 return {}; 1172 }, 1173 [&](Filter::DropShadow const& drop_shadow) -> ErrorOr<void> { 1174 TRY(builder.try_appendff("drop-shadow({} {}"sv, 1175 drop_shadow.offset_x, drop_shadow.offset_y)); 1176 if (drop_shadow.radius.has_value()) 1177 TRY(builder.try_appendff(" {}", TRY(drop_shadow.radius->to_string()))); 1178 if (drop_shadow.color.has_value()) { 1179 TRY(builder.try_append(' ')); 1180 TRY(serialize_a_srgb_value(builder, *drop_shadow.color)); 1181 } 1182 return {}; 1183 }, 1184 [&](Filter::HueRotate const& hue_rotate) -> ErrorOr<void> { 1185 TRY(builder.try_append("hue-rotate("sv)); 1186 if (hue_rotate.angle.has_value()) { 1187 TRY(hue_rotate.angle->visit( 1188 [&](Angle const& angle) -> ErrorOr<void> { 1189 return builder.try_append(TRY(angle.to_string())); 1190 }, 1191 [&](auto&) -> ErrorOr<void> { 1192 return builder.try_append('0'); 1193 })); 1194 } 1195 return {}; 1196 }, 1197 [&](Filter::Color const& color) -> ErrorOr<void> { 1198 TRY(builder.try_appendff("{}(", 1199 [&] { 1200 switch (color.operation) { 1201 case Filter::Color::Operation::Brightness: 1202 return "brightness"sv; 1203 case Filter::Color::Operation::Contrast: 1204 return "contrast"sv; 1205 case Filter::Color::Operation::Grayscale: 1206 return "grayscale"sv; 1207 case Filter::Color::Operation::Invert: 1208 return "invert"sv; 1209 case Filter::Color::Operation::Opacity: 1210 return "opacity"sv; 1211 case Filter::Color::Operation::Saturate: 1212 return "saturate"sv; 1213 case Filter::Color::Operation::Sepia: 1214 return "sepia"sv; 1215 default: 1216 VERIFY_NOT_REACHED(); 1217 } 1218 }())); 1219 if (color.amount.has_value()) 1220 TRY(builder.try_append(TRY(color.amount->to_string()))); 1221 return {}; 1222 })); 1223 TRY(builder.try_append(')')); 1224 first = false; 1225 } 1226 return builder.to_string(); 1227} 1228 1229ErrorOr<String> FlexStyleValue::to_string() const 1230{ 1231 return String::formatted("{} {} {}", TRY(m_properties.grow->to_string()), TRY(m_properties.shrink->to_string()), TRY(m_properties.basis->to_string())); 1232} 1233 1234ErrorOr<String> FlexFlowStyleValue::to_string() const 1235{ 1236 return String::formatted("{} {}", TRY(m_properties.flex_direction->to_string()), TRY(m_properties.flex_wrap->to_string())); 1237} 1238 1239ErrorOr<String> FontStyleValue::to_string() const 1240{ 1241 return String::formatted("{} {} {} / {} {}", TRY(m_properties.font_style->to_string()), TRY(m_properties.font_weight->to_string()), TRY(m_properties.font_size->to_string()), TRY(m_properties.line_height->to_string()), TRY(m_properties.font_families->to_string())); 1242} 1243 1244ErrorOr<String> GridTrackPlacementShorthandStyleValue::to_string() const 1245{ 1246 if (m_properties.end->grid_track_placement().is_auto()) 1247 return String::formatted("{}", TRY(m_properties.start->grid_track_placement().to_string())); 1248 return String::formatted("{} / {}", TRY(m_properties.start->grid_track_placement().to_string()), TRY(m_properties.end->grid_track_placement().to_string())); 1249} 1250 1251ErrorOr<String> GridAreaShorthandStyleValue::to_string() const 1252{ 1253 StringBuilder builder; 1254 if (!m_properties.row_start->as_grid_track_placement().grid_track_placement().is_auto()) 1255 TRY(builder.try_appendff("{}", TRY(m_properties.row_start->as_grid_track_placement().grid_track_placement().to_string()))); 1256 if (!m_properties.column_start->as_grid_track_placement().grid_track_placement().is_auto()) 1257 TRY(builder.try_appendff(" / {}", TRY(m_properties.column_start->as_grid_track_placement().grid_track_placement().to_string()))); 1258 if (!m_properties.row_end->as_grid_track_placement().grid_track_placement().is_auto()) 1259 TRY(builder.try_appendff(" / {}", TRY(m_properties.row_end->as_grid_track_placement().grid_track_placement().to_string()))); 1260 if (!m_properties.column_end->as_grid_track_placement().grid_track_placement().is_auto()) 1261 TRY(builder.try_appendff(" / {}", TRY(m_properties.column_end->as_grid_track_placement().grid_track_placement().to_string()))); 1262 return builder.to_string(); 1263} 1264 1265ErrorOr<String> GridTrackPlacementStyleValue::to_string() const 1266{ 1267 return m_grid_track_placement.to_string(); 1268} 1269 1270ErrorOr<String> GridTemplateAreaStyleValue::to_string() const 1271{ 1272 StringBuilder builder; 1273 for (size_t y = 0; y < m_grid_template_area.size(); ++y) { 1274 for (size_t x = 0; x < m_grid_template_area[y].size(); ++x) { 1275 TRY(builder.try_appendff("{}", m_grid_template_area[y][x])); 1276 if (x < m_grid_template_area[y].size() - 1) 1277 TRY(builder.try_append(" "sv)); 1278 } 1279 if (y < m_grid_template_area.size() - 1) 1280 TRY(builder.try_append(", "sv)); 1281 } 1282 return builder.to_string(); 1283} 1284 1285ErrorOr<String> GridTrackSizeStyleValue::to_string() const 1286{ 1287 return m_grid_track_size_list.to_string(); 1288} 1289 1290ErrorOr<String> IdentifierStyleValue::to_string() const 1291{ 1292 return String::from_utf8(CSS::string_from_value_id(m_id)); 1293} 1294 1295bool IdentifierStyleValue::has_color() const 1296{ 1297 switch (m_id) { 1298 case ValueID::Currentcolor: 1299 case ValueID::LibwebLink: 1300 case ValueID::LibwebPaletteActiveLink: 1301 case ValueID::LibwebPaletteActiveWindowBorder1: 1302 case ValueID::LibwebPaletteActiveWindowBorder2: 1303 case ValueID::LibwebPaletteActiveWindowTitle: 1304 case ValueID::LibwebPaletteBase: 1305 case ValueID::LibwebPaletteBaseText: 1306 case ValueID::LibwebPaletteButton: 1307 case ValueID::LibwebPaletteButtonText: 1308 case ValueID::LibwebPaletteDesktopBackground: 1309 case ValueID::LibwebPaletteFocusOutline: 1310 case ValueID::LibwebPaletteHighlightWindowBorder1: 1311 case ValueID::LibwebPaletteHighlightWindowBorder2: 1312 case ValueID::LibwebPaletteHighlightWindowTitle: 1313 case ValueID::LibwebPaletteHoverHighlight: 1314 case ValueID::LibwebPaletteInactiveSelection: 1315 case ValueID::LibwebPaletteInactiveSelectionText: 1316 case ValueID::LibwebPaletteInactiveWindowBorder1: 1317 case ValueID::LibwebPaletteInactiveWindowBorder2: 1318 case ValueID::LibwebPaletteInactiveWindowTitle: 1319 case ValueID::LibwebPaletteLink: 1320 case ValueID::LibwebPaletteMenuBase: 1321 case ValueID::LibwebPaletteMenuBaseText: 1322 case ValueID::LibwebPaletteMenuSelection: 1323 case ValueID::LibwebPaletteMenuSelectionText: 1324 case ValueID::LibwebPaletteMenuStripe: 1325 case ValueID::LibwebPaletteMovingWindowBorder1: 1326 case ValueID::LibwebPaletteMovingWindowBorder2: 1327 case ValueID::LibwebPaletteMovingWindowTitle: 1328 case ValueID::LibwebPaletteRubberBandBorder: 1329 case ValueID::LibwebPaletteRubberBandFill: 1330 case ValueID::LibwebPaletteRuler: 1331 case ValueID::LibwebPaletteRulerActiveText: 1332 case ValueID::LibwebPaletteRulerBorder: 1333 case ValueID::LibwebPaletteRulerInactiveText: 1334 case ValueID::LibwebPaletteSelection: 1335 case ValueID::LibwebPaletteSelectionText: 1336 case ValueID::LibwebPaletteSyntaxComment: 1337 case ValueID::LibwebPaletteSyntaxControlKeyword: 1338 case ValueID::LibwebPaletteSyntaxIdentifier: 1339 case ValueID::LibwebPaletteSyntaxKeyword: 1340 case ValueID::LibwebPaletteSyntaxNumber: 1341 case ValueID::LibwebPaletteSyntaxOperator: 1342 case ValueID::LibwebPaletteSyntaxPreprocessorStatement: 1343 case ValueID::LibwebPaletteSyntaxPreprocessorValue: 1344 case ValueID::LibwebPaletteSyntaxPunctuation: 1345 case ValueID::LibwebPaletteSyntaxString: 1346 case ValueID::LibwebPaletteSyntaxType: 1347 case ValueID::LibwebPaletteTextCursor: 1348 case ValueID::LibwebPaletteThreedHighlight: 1349 case ValueID::LibwebPaletteThreedShadow1: 1350 case ValueID::LibwebPaletteThreedShadow2: 1351 case ValueID::LibwebPaletteVisitedLink: 1352 case ValueID::LibwebPaletteWindow: 1353 case ValueID::LibwebPaletteWindowText: 1354 return true; 1355 default: 1356 return false; 1357 } 1358} 1359 1360Color IdentifierStyleValue::to_color(Layout::NodeWithStyle const& node) const 1361{ 1362 if (id() == CSS::ValueID::Currentcolor) { 1363 if (!node.has_style()) 1364 return Color::Black; 1365 return node.computed_values().color(); 1366 } 1367 1368 auto& document = node.document(); 1369 if (id() == CSS::ValueID::LibwebLink) 1370 return document.link_color(); 1371 1372 if (!document.page()) 1373 return {}; 1374 1375 auto palette = document.page()->palette(); 1376 switch (id()) { 1377 case CSS::ValueID::LibwebPaletteDesktopBackground: 1378 return palette.color(ColorRole::DesktopBackground); 1379 case CSS::ValueID::LibwebPaletteActiveWindowBorder1: 1380 return palette.color(ColorRole::ActiveWindowBorder1); 1381 case CSS::ValueID::LibwebPaletteActiveWindowBorder2: 1382 return palette.color(ColorRole::ActiveWindowBorder2); 1383 case CSS::ValueID::LibwebPaletteActiveWindowTitle: 1384 return palette.color(ColorRole::ActiveWindowTitle); 1385 case CSS::ValueID::LibwebPaletteInactiveWindowBorder1: 1386 return palette.color(ColorRole::InactiveWindowBorder1); 1387 case CSS::ValueID::LibwebPaletteInactiveWindowBorder2: 1388 return palette.color(ColorRole::InactiveWindowBorder2); 1389 case CSS::ValueID::LibwebPaletteInactiveWindowTitle: 1390 return palette.color(ColorRole::InactiveWindowTitle); 1391 case CSS::ValueID::LibwebPaletteMovingWindowBorder1: 1392 return palette.color(ColorRole::MovingWindowBorder1); 1393 case CSS::ValueID::LibwebPaletteMovingWindowBorder2: 1394 return palette.color(ColorRole::MovingWindowBorder2); 1395 case CSS::ValueID::LibwebPaletteMovingWindowTitle: 1396 return palette.color(ColorRole::MovingWindowTitle); 1397 case CSS::ValueID::LibwebPaletteHighlightWindowBorder1: 1398 return palette.color(ColorRole::HighlightWindowBorder1); 1399 case CSS::ValueID::LibwebPaletteHighlightWindowBorder2: 1400 return palette.color(ColorRole::HighlightWindowBorder2); 1401 case CSS::ValueID::LibwebPaletteHighlightWindowTitle: 1402 return palette.color(ColorRole::HighlightWindowTitle); 1403 case CSS::ValueID::LibwebPaletteMenuStripe: 1404 return palette.color(ColorRole::MenuStripe); 1405 case CSS::ValueID::LibwebPaletteMenuBase: 1406 return palette.color(ColorRole::MenuBase); 1407 case CSS::ValueID::LibwebPaletteMenuBaseText: 1408 return palette.color(ColorRole::MenuBaseText); 1409 case CSS::ValueID::LibwebPaletteMenuSelection: 1410 return palette.color(ColorRole::MenuSelection); 1411 case CSS::ValueID::LibwebPaletteMenuSelectionText: 1412 return palette.color(ColorRole::MenuSelectionText); 1413 case CSS::ValueID::LibwebPaletteWindow: 1414 return palette.color(ColorRole::Window); 1415 case CSS::ValueID::LibwebPaletteWindowText: 1416 return palette.color(ColorRole::WindowText); 1417 case CSS::ValueID::LibwebPaletteButton: 1418 return palette.color(ColorRole::Button); 1419 case CSS::ValueID::LibwebPaletteButtonText: 1420 return palette.color(ColorRole::ButtonText); 1421 case CSS::ValueID::LibwebPaletteBase: 1422 return palette.color(ColorRole::Base); 1423 case CSS::ValueID::LibwebPaletteBaseText: 1424 return palette.color(ColorRole::BaseText); 1425 case CSS::ValueID::LibwebPaletteThreedHighlight: 1426 return palette.color(ColorRole::ThreedHighlight); 1427 case CSS::ValueID::LibwebPaletteThreedShadow1: 1428 return palette.color(ColorRole::ThreedShadow1); 1429 case CSS::ValueID::LibwebPaletteThreedShadow2: 1430 return palette.color(ColorRole::ThreedShadow2); 1431 case CSS::ValueID::LibwebPaletteHoverHighlight: 1432 return palette.color(ColorRole::HoverHighlight); 1433 case CSS::ValueID::LibwebPaletteSelection: 1434 return palette.color(ColorRole::Selection); 1435 case CSS::ValueID::LibwebPaletteSelectionText: 1436 return palette.color(ColorRole::SelectionText); 1437 case CSS::ValueID::LibwebPaletteInactiveSelection: 1438 return palette.color(ColorRole::InactiveSelection); 1439 case CSS::ValueID::LibwebPaletteInactiveSelectionText: 1440 return palette.color(ColorRole::InactiveSelectionText); 1441 case CSS::ValueID::LibwebPaletteRubberBandFill: 1442 return palette.color(ColorRole::RubberBandFill); 1443 case CSS::ValueID::LibwebPaletteRubberBandBorder: 1444 return palette.color(ColorRole::RubberBandBorder); 1445 case CSS::ValueID::LibwebPaletteLink: 1446 return palette.color(ColorRole::Link); 1447 case CSS::ValueID::LibwebPaletteActiveLink: 1448 return palette.color(ColorRole::ActiveLink); 1449 case CSS::ValueID::LibwebPaletteVisitedLink: 1450 return palette.color(ColorRole::VisitedLink); 1451 case CSS::ValueID::LibwebPaletteRuler: 1452 return palette.color(ColorRole::Ruler); 1453 case CSS::ValueID::LibwebPaletteRulerBorder: 1454 return palette.color(ColorRole::RulerBorder); 1455 case CSS::ValueID::LibwebPaletteRulerActiveText: 1456 return palette.color(ColorRole::RulerActiveText); 1457 case CSS::ValueID::LibwebPaletteRulerInactiveText: 1458 return palette.color(ColorRole::RulerInactiveText); 1459 case CSS::ValueID::LibwebPaletteTextCursor: 1460 return palette.color(ColorRole::TextCursor); 1461 case CSS::ValueID::LibwebPaletteFocusOutline: 1462 return palette.color(ColorRole::FocusOutline); 1463 case CSS::ValueID::LibwebPaletteSyntaxComment: 1464 return palette.color(ColorRole::SyntaxComment); 1465 case CSS::ValueID::LibwebPaletteSyntaxNumber: 1466 return palette.color(ColorRole::SyntaxNumber); 1467 case CSS::ValueID::LibwebPaletteSyntaxString: 1468 return palette.color(ColorRole::SyntaxString); 1469 case CSS::ValueID::LibwebPaletteSyntaxType: 1470 return palette.color(ColorRole::SyntaxType); 1471 case CSS::ValueID::LibwebPaletteSyntaxPunctuation: 1472 return palette.color(ColorRole::SyntaxPunctuation); 1473 case CSS::ValueID::LibwebPaletteSyntaxOperator: 1474 return palette.color(ColorRole::SyntaxOperator); 1475 case CSS::ValueID::LibwebPaletteSyntaxKeyword: 1476 return palette.color(ColorRole::SyntaxKeyword); 1477 case CSS::ValueID::LibwebPaletteSyntaxControlKeyword: 1478 return palette.color(ColorRole::SyntaxControlKeyword); 1479 case CSS::ValueID::LibwebPaletteSyntaxIdentifier: 1480 return palette.color(ColorRole::SyntaxIdentifier); 1481 case CSS::ValueID::LibwebPaletteSyntaxPreprocessorStatement: 1482 return palette.color(ColorRole::SyntaxPreprocessorStatement); 1483 case CSS::ValueID::LibwebPaletteSyntaxPreprocessorValue: 1484 return palette.color(ColorRole::SyntaxPreprocessorValue); 1485 default: 1486 return {}; 1487 } 1488} 1489 1490ImageStyleValue::ImageStyleValue(AK::URL const& url) 1491 : AbstractImageStyleValue(Type::Image) 1492 , m_url(url) 1493{ 1494} 1495 1496void ImageStyleValue::load_any_resources(DOM::Document& document) 1497{ 1498 if (resource()) 1499 return; 1500 1501 m_document = &document; 1502 auto request = LoadRequest::create_for_url_on_page(m_url, document.page()); 1503 set_resource(ResourceLoader::the().load_resource(Resource::Type::Image, request)); 1504} 1505 1506void ImageStyleValue::resource_did_load() 1507{ 1508 if (!m_document) 1509 return; 1510 // FIXME: Do less than a full repaint if possible? 1511 if (m_document && m_document->browsing_context()) 1512 m_document->browsing_context()->set_needs_display(); 1513 1514 if (resource()->is_animated() && resource()->frame_count() > 1) { 1515 m_timer = Platform::Timer::create(); 1516 m_timer->set_interval(resource()->frame_duration(0)); 1517 m_timer->on_timeout = [this] { animate(); }; 1518 m_timer->start(); 1519 } 1520} 1521 1522void ImageStyleValue::animate() 1523{ 1524 m_current_frame_index = (m_current_frame_index + 1) % resource()->frame_count(); 1525 auto current_frame_duration = resource()->frame_duration(m_current_frame_index); 1526 1527 if (current_frame_duration != m_timer->interval()) 1528 m_timer->restart(current_frame_duration); 1529 1530 if (m_current_frame_index == resource()->frame_count() - 1) { 1531 ++m_loops_completed; 1532 if (m_loops_completed > 0 && m_loops_completed == resource()->loop_count()) 1533 m_timer->stop(); 1534 } 1535 1536 if (on_animate) 1537 on_animate(); 1538} 1539 1540Gfx::Bitmap const* ImageStyleValue::bitmap(size_t frame_index) const 1541{ 1542 if (!resource()) 1543 return nullptr; 1544 return resource()->bitmap(frame_index); 1545} 1546 1547ErrorOr<String> ImageStyleValue::to_string() const 1548{ 1549 return serialize_a_url(m_url.to_deprecated_string()); 1550} 1551 1552bool ImageStyleValue::equals(StyleValue const& other) const 1553{ 1554 if (type() != other.type()) 1555 return false; 1556 return m_url == other.as_image().m_url; 1557} 1558 1559Optional<CSSPixels> ImageStyleValue::natural_width() const 1560{ 1561 if (auto* b = bitmap(0); b != nullptr) 1562 return b->width(); 1563 return {}; 1564} 1565 1566Optional<CSSPixels> ImageStyleValue::natural_height() const 1567{ 1568 if (auto* b = bitmap(0); b != nullptr) 1569 return b->height(); 1570 return {}; 1571} 1572 1573void ImageStyleValue::paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering image_rendering) const 1574{ 1575 if (auto* b = bitmap(m_current_frame_index); b != nullptr) 1576 context.painter().draw_scaled_bitmap(dest_rect.to_type<int>(), *b, bitmap(0)->rect(), 1.0f, to_gfx_scaling_mode(image_rendering)); 1577} 1578 1579static ErrorOr<void> serialize_color_stop_list(StringBuilder& builder, auto const& color_stop_list) 1580{ 1581 bool first = true; 1582 for (auto const& element : color_stop_list) { 1583 if (!first) 1584 TRY(builder.try_append(", "sv)); 1585 1586 if (element.transition_hint.has_value()) 1587 TRY(builder.try_appendff("{}, "sv, TRY(element.transition_hint->value.to_string()))); 1588 1589 TRY(serialize_a_srgb_value(builder, element.color_stop.color)); 1590 for (auto position : Array { &element.color_stop.position, &element.color_stop.second_position }) { 1591 if (position->has_value()) 1592 TRY(builder.try_appendff(" {}"sv, TRY((*position)->to_string()))); 1593 } 1594 first = false; 1595 } 1596 return {}; 1597} 1598 1599ErrorOr<String> LinearGradientStyleValue::to_string() const 1600{ 1601 StringBuilder builder; 1602 auto side_or_corner_to_string = [](SideOrCorner value) { 1603 switch (value) { 1604 case SideOrCorner::Top: 1605 return "top"sv; 1606 case SideOrCorner::Bottom: 1607 return "bottom"sv; 1608 case SideOrCorner::Left: 1609 return "left"sv; 1610 case SideOrCorner::Right: 1611 return "right"sv; 1612 case SideOrCorner::TopLeft: 1613 return "top left"sv; 1614 case SideOrCorner::TopRight: 1615 return "top right"sv; 1616 case SideOrCorner::BottomLeft: 1617 return "bottom left"sv; 1618 case SideOrCorner::BottomRight: 1619 return "bottom right"sv; 1620 default: 1621 VERIFY_NOT_REACHED(); 1622 } 1623 }; 1624 1625 if (m_properties.gradient_type == GradientType::WebKit) 1626 TRY(builder.try_append("-webkit-"sv)); 1627 if (is_repeating()) 1628 TRY(builder.try_append("repeating-"sv)); 1629 TRY(builder.try_append("linear-gradient("sv)); 1630 TRY(m_properties.direction.visit( 1631 [&](SideOrCorner side_or_corner) -> ErrorOr<void> { 1632 return builder.try_appendff("{}{}, "sv, m_properties.gradient_type == GradientType::Standard ? "to "sv : ""sv, side_or_corner_to_string(side_or_corner)); 1633 }, 1634 [&](Angle const& angle) -> ErrorOr<void> { 1635 return builder.try_appendff("{}, "sv, TRY(angle.to_string())); 1636 })); 1637 1638 TRY(serialize_color_stop_list(builder, m_properties.color_stop_list)); 1639 TRY(builder.try_append(")"sv)); 1640 return builder.to_string(); 1641} 1642 1643bool LinearGradientStyleValue::equals(StyleValue const& other_) const 1644{ 1645 if (type() != other_.type()) 1646 return false; 1647 auto& other = other_.as_linear_gradient(); 1648 return m_properties == other.m_properties; 1649} 1650 1651float LinearGradientStyleValue::angle_degrees(CSSPixelSize gradient_size) const 1652{ 1653 auto corner_angle_degrees = [&] { 1654 return static_cast<float>(atan2(gradient_size.height().value(), gradient_size.width().value())) * 180 / AK::Pi<float>; 1655 }; 1656 return m_properties.direction.visit( 1657 [&](SideOrCorner side_or_corner) { 1658 auto angle = [&] { 1659 switch (side_or_corner) { 1660 case SideOrCorner::Top: 1661 return 0.0f; 1662 case SideOrCorner::Bottom: 1663 return 180.0f; 1664 case SideOrCorner::Left: 1665 return 270.0f; 1666 case SideOrCorner::Right: 1667 return 90.0f; 1668 case SideOrCorner::TopRight: 1669 return corner_angle_degrees(); 1670 case SideOrCorner::BottomLeft: 1671 return corner_angle_degrees() + 180.0f; 1672 case SideOrCorner::TopLeft: 1673 return -corner_angle_degrees(); 1674 case SideOrCorner::BottomRight: 1675 return -(corner_angle_degrees() + 180.0f); 1676 default: 1677 VERIFY_NOT_REACHED(); 1678 } 1679 }(); 1680 // Note: For unknowable reasons the angles are opposite on the -webkit- version 1681 if (m_properties.gradient_type == GradientType::WebKit) 1682 return angle + 180.0f; 1683 return angle; 1684 }, 1685 [&](Angle const& angle) { 1686 return angle.to_degrees(); 1687 }); 1688} 1689 1690void LinearGradientStyleValue::resolve_for_size(Layout::Node const& node, CSSPixelSize size) const 1691{ 1692 if (m_resolved.has_value() && m_resolved->size == size) 1693 return; 1694 m_resolved = ResolvedData { Painting::resolve_linear_gradient_data(node, size, *this), size }; 1695} 1696 1697void LinearGradientStyleValue::paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering) const 1698{ 1699 VERIFY(m_resolved.has_value()); 1700 Painting::paint_linear_gradient(context, dest_rect, m_resolved->data); 1701} 1702 1703CSSPixelPoint PositionValue::resolved(Layout::Node const& node, CSSPixelRect const& rect) const 1704{ 1705 // Note: A preset + a none default x/y_relative_to is impossible in the syntax (and makes little sense) 1706 CSSPixels x = horizontal_position.visit( 1707 [&](HorizontalPreset preset) -> CSSPixels { 1708 return rect.width() * [&] { 1709 switch (preset) { 1710 case HorizontalPreset::Left: 1711 return 0.0f; 1712 case HorizontalPreset::Center: 1713 return 0.5f; 1714 case HorizontalPreset::Right: 1715 return 1.0f; 1716 default: 1717 VERIFY_NOT_REACHED(); 1718 } 1719 }(); 1720 }, 1721 [&](LengthPercentage length_percentage) -> CSSPixels { 1722 return length_percentage.resolved(node, Length::make_px(rect.width())).to_px(node); 1723 }); 1724 CSSPixels y = vertical_position.visit( 1725 [&](VerticalPreset preset) -> CSSPixels { 1726 return rect.height() * [&] { 1727 switch (preset) { 1728 case VerticalPreset::Top: 1729 return 0.0f; 1730 case VerticalPreset::Center: 1731 return 0.5f; 1732 case VerticalPreset::Bottom: 1733 return 1.0f; 1734 default: 1735 VERIFY_NOT_REACHED(); 1736 } 1737 }(); 1738 }, 1739 [&](LengthPercentage length_percentage) -> CSSPixels { 1740 return length_percentage.resolved(node, Length::make_px(rect.height())).to_px(node); 1741 }); 1742 if (x_relative_to == HorizontalEdge::Right) 1743 x = rect.width() - x; 1744 if (y_relative_to == VerticalEdge::Bottom) 1745 y = rect.height() - y; 1746 return CSSPixelPoint { rect.x() + x, rect.y() + y }; 1747} 1748 1749ErrorOr<void> PositionValue::serialize(StringBuilder& builder) const 1750{ 1751 // Note: This means our serialization with simplify any with explicit edges that are just `top left`. 1752 bool has_relative_edges = x_relative_to == HorizontalEdge::Right || y_relative_to == VerticalEdge::Bottom; 1753 if (has_relative_edges) 1754 TRY(builder.try_append(x_relative_to == HorizontalEdge::Left ? "left "sv : "right "sv)); 1755 TRY(horizontal_position.visit( 1756 [&](HorizontalPreset preset) -> ErrorOr<void> { 1757 return builder.try_append([&] { 1758 switch (preset) { 1759 case HorizontalPreset::Left: 1760 return "left"sv; 1761 case HorizontalPreset::Center: 1762 return "center"sv; 1763 case HorizontalPreset::Right: 1764 return "right"sv; 1765 default: 1766 VERIFY_NOT_REACHED(); 1767 } 1768 }()); 1769 }, 1770 [&](LengthPercentage length_percentage) -> ErrorOr<void> { 1771 return builder.try_appendff(TRY(length_percentage.to_string())); 1772 })); 1773 TRY(builder.try_append(' ')); 1774 if (has_relative_edges) 1775 TRY(builder.try_append(y_relative_to == VerticalEdge::Top ? "top "sv : "bottom "sv)); 1776 TRY(vertical_position.visit( 1777 [&](VerticalPreset preset) -> ErrorOr<void> { 1778 return builder.try_append([&] { 1779 switch (preset) { 1780 case VerticalPreset::Top: 1781 return "top"sv; 1782 case VerticalPreset::Center: 1783 return "center"sv; 1784 case VerticalPreset::Bottom: 1785 return "bottom"sv; 1786 default: 1787 VERIFY_NOT_REACHED(); 1788 } 1789 }()); 1790 }, 1791 [&](LengthPercentage length_percentage) -> ErrorOr<void> { 1792 return builder.try_append(TRY(length_percentage.to_string())); 1793 })); 1794 return {}; 1795} 1796 1797ErrorOr<String> RadialGradientStyleValue::to_string() const 1798{ 1799 StringBuilder builder; 1800 if (is_repeating()) 1801 TRY(builder.try_append("repeating-"sv)); 1802 TRY(builder.try_appendff("radial-gradient({} "sv, 1803 m_properties.ending_shape == EndingShape::Circle ? "circle"sv : "ellipse"sv)); 1804 1805 TRY(m_properties.size.visit( 1806 [&](Extent extent) -> ErrorOr<void> { 1807 return builder.try_append([&] { 1808 switch (extent) { 1809 case Extent::ClosestCorner: 1810 return "closest-corner"sv; 1811 case Extent::ClosestSide: 1812 return "closest-side"sv; 1813 case Extent::FarthestCorner: 1814 return "farthest-corner"sv; 1815 case Extent::FarthestSide: 1816 return "farthest-side"sv; 1817 default: 1818 VERIFY_NOT_REACHED(); 1819 } 1820 }()); 1821 }, 1822 [&](CircleSize const& circle_size) -> ErrorOr<void> { 1823 return builder.try_append(TRY(circle_size.radius.to_string())); 1824 }, 1825 [&](EllipseSize const& ellipse_size) -> ErrorOr<void> { 1826 return builder.try_appendff("{} {}", TRY(ellipse_size.radius_a.to_string()), TRY(ellipse_size.radius_b.to_string())); 1827 })); 1828 1829 if (m_properties.position != PositionValue::center()) { 1830 TRY(builder.try_appendff(" at "sv)); 1831 TRY(m_properties.position.serialize(builder)); 1832 } 1833 1834 TRY(builder.try_append(", "sv)); 1835 TRY(serialize_color_stop_list(builder, m_properties.color_stop_list)); 1836 TRY(builder.try_append(')')); 1837 return builder.to_string(); 1838} 1839 1840Gfx::FloatSize RadialGradientStyleValue::resolve_size(Layout::Node const& node, Gfx::FloatPoint center, Gfx::FloatRect const& size) const 1841{ 1842 auto const side_shape = [&](auto distance_function) { 1843 auto const distance_from = [&](float v, float a, float b, auto distance_function) { 1844 return distance_function(fabs(a - v), fabs(b - v)); 1845 }; 1846 auto x_dist = distance_from(center.x(), size.left(), size.right(), distance_function); 1847 auto y_dist = distance_from(center.y(), size.top(), size.bottom(), distance_function); 1848 if (m_properties.ending_shape == EndingShape::Circle) { 1849 auto dist = distance_function(x_dist, y_dist); 1850 return Gfx::FloatSize { dist, dist }; 1851 } else { 1852 return Gfx::FloatSize { x_dist, y_dist }; 1853 } 1854 }; 1855 1856 auto const closest_side_shape = [&] { 1857 return side_shape(AK::min<float>); 1858 }; 1859 1860 auto const farthest_side_shape = [&] { 1861 return side_shape(AK::max<float>); 1862 }; 1863 1864 auto const corner_distance = [&](auto distance_compare, Gfx::FloatPoint& corner) { 1865 auto top_left_distance = size.top_left().distance_from(center); 1866 auto top_right_distance = size.top_right().distance_from(center); 1867 auto bottom_right_distance = size.bottom_right().distance_from(center); 1868 auto bottom_left_distance = size.bottom_left().distance_from(center); 1869 auto distance = top_left_distance; 1870 if (distance_compare(top_right_distance, distance)) { 1871 corner = size.top_right(); 1872 distance = top_right_distance; 1873 } 1874 if (distance_compare(bottom_right_distance, distance)) { 1875 corner = size.top_right(); 1876 distance = bottom_right_distance; 1877 } 1878 if (distance_compare(bottom_left_distance, distance)) { 1879 corner = size.top_right(); 1880 distance = bottom_left_distance; 1881 } 1882 return distance; 1883 }; 1884 1885 auto const closest_corner_distance = [&](Gfx::FloatPoint& corner) { 1886 return corner_distance([](float a, float b) { return a < b; }, corner); 1887 }; 1888 1889 auto const farthest_corner_distance = [&](Gfx::FloatPoint& corner) { 1890 return corner_distance([](float a, float b) { return a > b; }, corner); 1891 }; 1892 1893 auto const corner_shape = [&](auto corner_distance, auto get_shape) { 1894 Gfx::FloatPoint corner {}; 1895 auto distance = corner_distance(corner); 1896 if (m_properties.ending_shape == EndingShape::Ellipse) { 1897 auto shape = get_shape(); 1898 auto aspect_ratio = shape.width() / shape.height(); 1899 auto p = corner - center; 1900 auto radius_a = AK::sqrt(p.y() * p.y() * aspect_ratio * aspect_ratio + p.x() * p.x()); 1901 auto radius_b = radius_a / aspect_ratio; 1902 return Gfx::FloatSize { radius_a, radius_b }; 1903 } 1904 return Gfx::FloatSize { distance, distance }; 1905 }; 1906 1907 // https://w3c.github.io/csswg-drafts/css-images/#radial-gradient-syntax 1908 auto resolved_size = m_properties.size.visit( 1909 [&](Extent extent) { 1910 switch (extent) { 1911 case Extent::ClosestSide: 1912 // The ending shape is sized so that it exactly meets the side of the gradient box closest to the gradient’s center. 1913 // If the shape is an ellipse, it exactly meets the closest side in each dimension. 1914 return closest_side_shape(); 1915 case Extent::ClosestCorner: 1916 // The ending shape is sized so that it passes through the corner of the gradient box closest to the gradient’s center. 1917 // If the shape is an ellipse, the ending shape is given the same aspect-ratio it would have if closest-side were specified 1918 return corner_shape(closest_corner_distance, closest_side_shape); 1919 case Extent::FarthestCorner: 1920 // Same as closest-corner, except the ending shape is sized based on the farthest corner. 1921 // If the shape is an ellipse, the ending shape is given the same aspect ratio it would have if farthest-side were specified. 1922 return corner_shape(farthest_corner_distance, farthest_side_shape); 1923 case Extent::FarthestSide: 1924 // Same as closest-side, except the ending shape is sized based on the farthest side(s). 1925 return farthest_side_shape(); 1926 default: 1927 VERIFY_NOT_REACHED(); 1928 } 1929 }, 1930 [&](CircleSize const& circle_size) { 1931 auto radius = circle_size.radius.to_px(node); 1932 return Gfx::FloatSize { radius, radius }; 1933 }, 1934 [&](EllipseSize const& ellipse_size) { 1935 auto radius_a = ellipse_size.radius_a.resolved(node, CSS::Length::make_px(size.width())).to_px(node); 1936 auto radius_b = ellipse_size.radius_b.resolved(node, CSS::Length::make_px(size.height())).to_px(node); 1937 return Gfx::FloatSize { radius_a, radius_b }; 1938 }); 1939 1940 // Handle degenerate cases 1941 // https://w3c.github.io/csswg-drafts/css-images/#degenerate-radials 1942 1943 constexpr auto arbitrary_small_number = 1e-10; 1944 constexpr auto arbitrary_large_number = 1e10; 1945 1946 // If the ending shape is a circle with zero radius: 1947 if (m_properties.ending_shape == EndingShape::Circle && resolved_size.is_empty()) { 1948 // Render as if the ending shape was a circle whose radius was an arbitrary very small number greater than zero. 1949 // This will make the gradient continue to look like a circle. 1950 return Gfx::FloatSize { arbitrary_small_number, arbitrary_small_number }; 1951 } 1952 // If the ending shape has zero width (regardless of the height): 1953 if (resolved_size.width() <= 0) { 1954 // Render as if the ending shape was an ellipse whose height was an arbitrary very large number 1955 // and whose width was an arbitrary very small number greater than zero. 1956 // This will make the gradient look similar to a horizontal linear gradient that is mirrored across the center of the ellipse. 1957 // It also means that all color-stop positions specified with a percentage resolve to 0px. 1958 return Gfx::FloatSize { arbitrary_small_number, arbitrary_large_number }; 1959 } 1960 // Otherwise, if the ending shape has zero height: 1961 if (resolved_size.height() <= 0) { 1962 // Render as if the ending shape was an ellipse whose width was an arbitrary very large number and whose height 1963 // was an arbitrary very small number greater than zero. This will make the gradient look like a solid-color image equal 1964 // to the color of the last color-stop, or equal to the average color of the gradient if it’s repeating. 1965 return Gfx::FloatSize { arbitrary_large_number, arbitrary_small_number }; 1966 } 1967 return resolved_size; 1968} 1969 1970void RadialGradientStyleValue::resolve_for_size(Layout::Node const& node, CSSPixelSize paint_size) const 1971{ 1972 CSSPixelRect gradient_box { { 0, 0 }, paint_size }; 1973 auto center = m_properties.position.resolved(node, gradient_box).to_type<float>(); 1974 auto gradient_size = resolve_size(node, center, gradient_box.to_type<float>()); 1975 if (m_resolved.has_value() && m_resolved->gradient_size == gradient_size) 1976 return; 1977 m_resolved = ResolvedData { 1978 Painting::resolve_radial_gradient_data(node, gradient_size.to_type<CSSPixels>(), *this), 1979 gradient_size, 1980 center, 1981 }; 1982} 1983 1984bool RadialGradientStyleValue::equals(StyleValue const& other) const 1985{ 1986 if (type() != other.type()) 1987 return false; 1988 auto& other_gradient = other.as_radial_gradient(); 1989 return m_properties == other_gradient.m_properties; 1990} 1991 1992void RadialGradientStyleValue::paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering) const 1993{ 1994 VERIFY(m_resolved.has_value()); 1995 Painting::paint_radial_gradient(context, dest_rect, m_resolved->data, 1996 context.rounded_device_point(m_resolved->center.to_type<CSSPixels>()), 1997 context.rounded_device_size(m_resolved->gradient_size.to_type<CSSPixels>())); 1998} 1999 2000ErrorOr<String> ConicGradientStyleValue::to_string() const 2001{ 2002 StringBuilder builder; 2003 if (is_repeating()) 2004 TRY(builder.try_append("repeating-"sv)); 2005 TRY(builder.try_append("conic-gradient("sv)); 2006 bool has_from_angle = false; 2007 bool has_at_position = false; 2008 if ((has_from_angle = m_properties.from_angle.to_degrees() != 0)) 2009 TRY(builder.try_appendff("from {}", TRY(m_properties.from_angle.to_string()))); 2010 if ((has_at_position = m_properties.position != PositionValue::center())) { 2011 if (has_from_angle) 2012 TRY(builder.try_append(' ')); 2013 TRY(builder.try_appendff("at "sv)); 2014 TRY(m_properties.position.serialize(builder)); 2015 } 2016 if (has_from_angle || has_at_position) 2017 TRY(builder.try_append(", "sv)); 2018 TRY(serialize_color_stop_list(builder, m_properties.color_stop_list)); 2019 TRY(builder.try_append(')')); 2020 return builder.to_string(); 2021} 2022 2023void ConicGradientStyleValue::resolve_for_size(Layout::Node const& node, CSSPixelSize size) const 2024{ 2025 if (!m_resolved.has_value()) 2026 m_resolved = ResolvedData { Painting::resolve_conic_gradient_data(node, *this), {} }; 2027 m_resolved->position = m_properties.position.resolved(node, CSSPixelRect { { 0, 0 }, size }); 2028} 2029 2030void ConicGradientStyleValue::paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering) const 2031{ 2032 VERIFY(m_resolved.has_value()); 2033 Painting::paint_conic_gradient(context, dest_rect, m_resolved->data, context.rounded_device_point(m_resolved->position)); 2034} 2035 2036bool ConicGradientStyleValue::equals(StyleValue const& other) const 2037{ 2038 if (type() != other.type()) 2039 return false; 2040 auto& other_gradient = other.as_conic_gradient(); 2041 return m_properties == other_gradient.m_properties; 2042} 2043 2044float ConicGradientStyleValue::angle_degrees() const 2045{ 2046 return m_properties.from_angle.to_degrees(); 2047} 2048 2049ErrorOr<String> ListStyleStyleValue::to_string() const 2050{ 2051 return String::formatted("{} {} {}", TRY(m_properties.position->to_string()), TRY(m_properties.image->to_string()), TRY(m_properties.style_type->to_string())); 2052} 2053 2054ErrorOr<String> NumericStyleValue::to_string() const 2055{ 2056 return m_value.visit( 2057 [](auto value) { 2058 return String::formatted("{}", value); 2059 }); 2060} 2061 2062ErrorOr<String> OverflowStyleValue::to_string() const 2063{ 2064 return String::formatted("{} {}", TRY(m_properties.overflow_x->to_string()), TRY(m_properties.overflow_y->to_string())); 2065} 2066 2067ErrorOr<String> PercentageStyleValue::to_string() const 2068{ 2069 return m_percentage.to_string(); 2070} 2071 2072ErrorOr<String> PositionStyleValue::to_string() const 2073{ 2074 auto to_string = [](PositionEdge edge) { 2075 switch (edge) { 2076 case PositionEdge::Left: 2077 return "left"; 2078 case PositionEdge::Right: 2079 return "right"; 2080 case PositionEdge::Top: 2081 return "top"; 2082 case PositionEdge::Bottom: 2083 return "bottom"; 2084 } 2085 VERIFY_NOT_REACHED(); 2086 }; 2087 2088 return String::formatted("{} {} {} {}", to_string(m_properties.edge_x), TRY(m_properties.offset_x.to_string()), to_string(m_properties.edge_y), TRY(m_properties.offset_y.to_string())); 2089} 2090 2091ErrorOr<String> RectStyleValue::to_string() const 2092{ 2093 return String::formatted("rect({} {} {} {})", m_rect.top_edge, m_rect.right_edge, m_rect.bottom_edge, m_rect.left_edge); 2094} 2095 2096ErrorOr<String> ShadowStyleValue::to_string() const 2097{ 2098 StringBuilder builder; 2099 TRY(builder.try_appendff("{} {} {} {} {}", m_properties.color.to_deprecated_string(), TRY(m_properties.offset_x.to_string()), TRY(m_properties.offset_y.to_string()), TRY(m_properties.blur_radius.to_string()), TRY(m_properties.spread_distance.to_string()))); 2100 if (m_properties.placement == ShadowPlacement::Inner) 2101 TRY(builder.try_append(" inset"sv)); 2102 return builder.to_string(); 2103} 2104 2105ErrorOr<String> TextDecorationStyleValue::to_string() const 2106{ 2107 return String::formatted("{} {} {} {}", TRY(m_properties.line->to_string()), TRY(m_properties.thickness->to_string()), TRY(m_properties.style->to_string()), TRY(m_properties.color->to_string())); 2108} 2109 2110ErrorOr<String> TransformationStyleValue::to_string() const 2111{ 2112 StringBuilder builder; 2113 TRY(builder.try_append(CSS::to_string(m_properties.transform_function))); 2114 TRY(builder.try_append('(')); 2115 for (size_t i = 0; i < m_properties.values.size(); ++i) { 2116 TRY(builder.try_append(TRY(m_properties.values[i]->to_string()))); 2117 if (i != m_properties.values.size() - 1) 2118 TRY(builder.try_append(", "sv)); 2119 } 2120 TRY(builder.try_append(')')); 2121 2122 return builder.to_string(); 2123} 2124 2125bool TransformationStyleValue::Properties::operator==(Properties const& other) const 2126{ 2127 return transform_function == other.transform_function && values.span() == other.values.span(); 2128} 2129 2130ErrorOr<String> UnresolvedStyleValue::to_string() const 2131{ 2132 StringBuilder builder; 2133 for (auto& value : m_values) 2134 TRY(builder.try_append(TRY(value.to_string()))); 2135 return builder.to_string(); 2136} 2137 2138bool UnresolvedStyleValue::equals(StyleValue const& other) const 2139{ 2140 if (type() != other.type()) 2141 return false; 2142 // This is a case where comparing the strings actually makes sense. 2143 return to_string().release_value_but_fixme_should_propagate_errors() == other.to_string().release_value_but_fixme_should_propagate_errors(); 2144} 2145 2146bool StyleValueList::Properties::operator==(Properties const& other) const 2147{ 2148 return separator == other.separator && values.span() == other.values.span(); 2149} 2150 2151ErrorOr<String> StyleValueList::to_string() const 2152{ 2153 auto separator = ""sv; 2154 switch (m_properties.separator) { 2155 case Separator::Space: 2156 separator = " "sv; 2157 break; 2158 case Separator::Comma: 2159 separator = ", "sv; 2160 break; 2161 default: 2162 VERIFY_NOT_REACHED(); 2163 } 2164 2165 StringBuilder builder; 2166 for (size_t i = 0; i < m_properties.values.size(); ++i) { 2167 TRY(builder.try_append(TRY(m_properties.values[i]->to_string()))); 2168 if (i != m_properties.values.size() - 1) 2169 TRY(builder.try_append(separator)); 2170 } 2171 return builder.to_string(); 2172} 2173 2174ValueComparingNonnullRefPtr<ColorStyleValue> ColorStyleValue::create(Color color) 2175{ 2176 if (color.value() == 0) { 2177 static auto transparent = adopt_ref(*new ColorStyleValue(color)); 2178 return transparent; 2179 } 2180 2181 if (color == Color::from_rgb(0x000000)) { 2182 static auto black = adopt_ref(*new ColorStyleValue(color)); 2183 return black; 2184 } 2185 2186 if (color == Color::from_rgb(0xffffff)) { 2187 static auto white = adopt_ref(*new ColorStyleValue(color)); 2188 return white; 2189 } 2190 2191 return adopt_ref(*new ColorStyleValue(color)); 2192} 2193 2194ValueComparingNonnullRefPtr<GridTemplateAreaStyleValue> GridTemplateAreaStyleValue::create(Vector<Vector<String>> grid_template_area) 2195{ 2196 return adopt_ref(*new GridTemplateAreaStyleValue(grid_template_area)); 2197} 2198 2199ValueComparingNonnullRefPtr<GridTrackPlacementStyleValue> GridTrackPlacementStyleValue::create(CSS::GridTrackPlacement grid_track_placement) 2200{ 2201 return adopt_ref(*new GridTrackPlacementStyleValue(grid_track_placement)); 2202} 2203 2204ValueComparingNonnullRefPtr<GridTrackSizeStyleValue> GridTrackSizeStyleValue::create(CSS::GridTrackSizeList grid_track_size_list) 2205{ 2206 return adopt_ref(*new GridTrackSizeStyleValue(grid_track_size_list)); 2207} 2208 2209ValueComparingNonnullRefPtr<GridTrackSizeStyleValue> GridTrackSizeStyleValue::make_auto() 2210{ 2211 return adopt_ref(*new GridTrackSizeStyleValue(CSS::GridTrackSizeList())); 2212} 2213 2214ValueComparingNonnullRefPtr<RectStyleValue> RectStyleValue::create(EdgeRect rect) 2215{ 2216 return adopt_ref(*new RectStyleValue(rect)); 2217} 2218 2219ValueComparingNonnullRefPtr<LengthStyleValue> LengthStyleValue::create(Length const& length) 2220{ 2221 if (length.is_auto()) { 2222 static auto value = adopt_ref(*new LengthStyleValue(CSS::Length::make_auto())); 2223 return value; 2224 } 2225 if (length.is_px()) { 2226 if (length.raw_value() == 0) { 2227 static auto value = adopt_ref(*new LengthStyleValue(CSS::Length::make_px(0))); 2228 return value; 2229 } 2230 if (length.raw_value() == 1) { 2231 static auto value = adopt_ref(*new LengthStyleValue(CSS::Length::make_px(1))); 2232 return value; 2233 } 2234 } 2235 return adopt_ref(*new LengthStyleValue(length)); 2236} 2237 2238static Optional<CSS::Length> absolutized_length(CSS::Length const& length, CSSPixelRect const& viewport_rect, Gfx::FontPixelMetrics const& font_metrics, CSSPixels font_size, CSSPixels root_font_size) 2239{ 2240 if (length.is_px()) 2241 return {}; 2242 if (length.is_absolute() || length.is_relative()) { 2243 auto px = length.to_px(viewport_rect, font_metrics, font_size, root_font_size); 2244 return CSS::Length::make_px(px); 2245 } 2246 return {}; 2247} 2248 2249ValueComparingNonnullRefPtr<StyleValue const> StyleValue::absolutized(CSSPixelRect const&, Gfx::FontPixelMetrics const&, CSSPixels, CSSPixels) const 2250{ 2251 return *this; 2252} 2253 2254ValueComparingNonnullRefPtr<StyleValue const> LengthStyleValue::absolutized(CSSPixelRect const& viewport_rect, Gfx::FontPixelMetrics const& font_metrics, CSSPixels font_size, CSSPixels root_font_size) const 2255{ 2256 if (auto length = absolutized_length(m_length, viewport_rect, font_metrics, font_size, root_font_size); length.has_value()) 2257 return LengthStyleValue::create(length.release_value()); 2258 return *this; 2259} 2260 2261ValueComparingNonnullRefPtr<StyleValue const> ShadowStyleValue::absolutized(CSSPixelRect const& viewport_rect, Gfx::FontPixelMetrics const& font_metrics, CSSPixels font_size, CSSPixels root_font_size) const 2262{ 2263 auto absolutized_offset_x = absolutized_length(m_properties.offset_x, viewport_rect, font_metrics, font_size, root_font_size).value_or(m_properties.offset_x); 2264 auto absolutized_offset_y = absolutized_length(m_properties.offset_y, viewport_rect, font_metrics, font_size, root_font_size).value_or(m_properties.offset_y); 2265 auto absolutized_blur_radius = absolutized_length(m_properties.blur_radius, viewport_rect, font_metrics, font_size, root_font_size).value_or(m_properties.blur_radius); 2266 auto absolutized_spread_distance = absolutized_length(m_properties.spread_distance, viewport_rect, font_metrics, font_size, root_font_size).value_or(m_properties.spread_distance); 2267 return ShadowStyleValue::create(m_properties.color, absolutized_offset_x, absolutized_offset_y, absolutized_blur_radius, absolutized_spread_distance, m_properties.placement); 2268} 2269 2270ValueComparingNonnullRefPtr<StyleValue const> BorderRadiusStyleValue::absolutized(CSSPixelRect const& viewport_rect, Gfx::FontPixelMetrics const& font_metrics, CSSPixels font_size, CSSPixels root_font_size) const 2271{ 2272 if (m_properties.horizontal_radius.is_percentage() && m_properties.vertical_radius.is_percentage()) 2273 return *this; 2274 auto absolutized_horizontal_radius = m_properties.horizontal_radius; 2275 auto absolutized_vertical_radius = m_properties.vertical_radius; 2276 if (!m_properties.horizontal_radius.is_percentage()) 2277 absolutized_horizontal_radius = absolutized_length(m_properties.horizontal_radius.length(), viewport_rect, font_metrics, font_size, root_font_size).value_or(m_properties.horizontal_radius.length()); 2278 if (!m_properties.vertical_radius.is_percentage()) 2279 absolutized_vertical_radius = absolutized_length(m_properties.vertical_radius.length(), viewport_rect, font_metrics, font_size, root_font_size).value_or(m_properties.vertical_radius.length()); 2280 return BorderRadiusStyleValue::create(absolutized_horizontal_radius, absolutized_vertical_radius); 2281} 2282 2283bool CalculatedStyleValue::contains_percentage() const 2284{ 2285 return m_expression->contains_percentage(); 2286} 2287 2288bool CalculatedStyleValue::CalcSum::contains_percentage() const 2289{ 2290 if (first_calc_product->contains_percentage()) 2291 return true; 2292 for (auto& part : zero_or_more_additional_calc_products) { 2293 if (part->contains_percentage()) 2294 return true; 2295 } 2296 return false; 2297} 2298 2299bool CalculatedStyleValue::CalcSumPartWithOperator::contains_percentage() const 2300{ 2301 return value->contains_percentage(); 2302} 2303 2304bool CalculatedStyleValue::CalcProduct::contains_percentage() const 2305{ 2306 if (first_calc_value.contains_percentage()) 2307 return true; 2308 for (auto& part : zero_or_more_additional_calc_values) { 2309 if (part->contains_percentage()) 2310 return true; 2311 } 2312 return false; 2313} 2314 2315bool CalculatedStyleValue::CalcProductPartWithOperator::contains_percentage() const 2316{ 2317 return value.visit( 2318 [](CalcValue const& value) { return value.contains_percentage(); }, 2319 [](CalcNumberValue const&) { return false; }); 2320} 2321 2322bool CalculatedStyleValue::CalcValue::contains_percentage() const 2323{ 2324 return value.visit( 2325 [](Percentage const&) { return true; }, 2326 [](NonnullOwnPtr<CalcSum> const& sum) { return sum->contains_percentage(); }, 2327 [](auto const&) { return false; }); 2328} 2329 2330bool calculated_style_value_contains_percentage(CalculatedStyleValue const& value) 2331{ 2332 return value.contains_percentage(); 2333} 2334 2335}