|
31 | 31 | #include <LibWeb/CSS/StyleValues/LengthStyleValue.h> |
32 | 32 | #include <LibWeb/CSS/StyleValues/NumberStyleValue.h> |
33 | 33 | #include <LibWeb/CSS/StyleValues/PercentageStyleValue.h> |
| 34 | +#include <LibWeb/CSS/StyleValues/RandomValueSharingStyleValue.h> |
34 | 35 | #include <LibWeb/CSS/StyleValues/ResolutionStyleValue.h> |
35 | 36 | #include <LibWeb/CSS/StyleValues/TimeStyleValue.h> |
36 | 37 |
|
@@ -284,6 +285,11 @@ static String serialize_a_math_function(CalculationNode const& fn, CalculationCo |
284 | 285 | } |
285 | 286 | } |
286 | 287 |
|
| 288 | + // AD-HOC: We serialize random() directly since it has abnormal children (e.g. m_random_value_sharing which is not a |
| 289 | + // calculation node). |
| 290 | + if (fn.type() == CalculationNode::Type::Random) |
| 291 | + return as<RandomCalculationNode>(fn).to_string(context, serialization_mode); |
| 292 | + |
287 | 293 | // 3. If the calculation tree’s root node is a numeric value, or a calc-operator node, let s be a string initially |
288 | 294 | // containing "calc(". |
289 | 295 | // Otherwise, let s be a string initially containing the name of the root node, lowercased (such as "sin" or |
@@ -572,6 +578,8 @@ StringView CalculationNode::name() const |
572 | 578 | return "log"sv; |
573 | 579 | case Type::Exp: |
574 | 580 | return "exp"sv; |
| 581 | + case Type::Random: |
| 582 | + return "random"sv; |
575 | 583 | case Type::Round: |
576 | 584 | return "round"sv; |
577 | 585 | case Type::Mod: |
@@ -2434,6 +2442,134 @@ bool ModCalculationNode::equals(CalculationNode const& other) const |
2434 | 2442 | && m_y->equals(*static_cast<ModCalculationNode const&>(other).m_y); |
2435 | 2443 | } |
2436 | 2444 |
|
| 2445 | +NonnullRefPtr<RandomCalculationNode const> RandomCalculationNode::create(NonnullRefPtr<RandomValueSharingStyleValue const> random_value_sharing, NonnullRefPtr<CalculationNode const> minimum, NonnullRefPtr<CalculationNode const> maximum) |
| 2446 | +{ |
| 2447 | + Optional<NumericType> numeric_type = add_the_types(*minimum, *maximum); |
| 2448 | + |
| 2449 | + return adopt_ref(*new (nothrow) RandomCalculationNode(move(random_value_sharing), move(minimum), move(maximum), move(numeric_type))); |
| 2450 | +} |
| 2451 | + |
| 2452 | +RandomCalculationNode::RandomCalculationNode(NonnullRefPtr<RandomValueSharingStyleValue const> random_value_sharing, NonnullRefPtr<CalculationNode const> minimum, NonnullRefPtr<CalculationNode const> maximum, Optional<NumericType> numeric_type) |
| 2453 | + : CalculationNode(Type::Random, move(numeric_type)) |
| 2454 | + , m_random_value_sharing(move(random_value_sharing)) |
| 2455 | + , m_minimum(move(minimum)) |
| 2456 | + , m_maximum(move(maximum)) |
| 2457 | +{ |
| 2458 | +} |
| 2459 | + |
| 2460 | +RandomCalculationNode::~RandomCalculationNode() = default; |
| 2461 | + |
| 2462 | +bool RandomCalculationNode::contains_percentage() const |
| 2463 | +{ |
| 2464 | + return m_minimum->contains_percentage() || m_maximum->contains_percentage(); |
| 2465 | +} |
| 2466 | + |
| 2467 | +NonnullRefPtr<CalculationNode const> RandomCalculationNode::with_simplified_children(CalculationContext const& context, CalculationResolutionContext const& resolution_context) const |
| 2468 | +{ |
| 2469 | + ValueComparingRefPtr<RandomValueSharingStyleValue const> simplified_random_value_sharing; |
| 2470 | + |
| 2471 | + // When we are in the absolutization process we should absolutize m_random_value_sharing |
| 2472 | + if (resolution_context.length_resolution_context.has_value()) { |
| 2473 | + ComputationContext computation_context { |
| 2474 | + .length_resolution_context = resolution_context.length_resolution_context.value(), |
| 2475 | + .abstract_element = resolution_context.abstract_element |
| 2476 | + }; |
| 2477 | + |
| 2478 | + simplified_random_value_sharing = m_random_value_sharing->absolutized(computation_context)->as_random_value_sharing(); |
| 2479 | + } else { |
| 2480 | + simplified_random_value_sharing = m_random_value_sharing; |
| 2481 | + } |
| 2482 | + |
| 2483 | + ValueComparingNonnullRefPtr<CalculationNode const> simplified_minimum = simplify_a_calculation_tree(m_minimum, context, resolution_context); |
| 2484 | + ValueComparingNonnullRefPtr<CalculationNode const> simplified_maximum = simplify_a_calculation_tree(m_maximum, context, resolution_context); |
| 2485 | + |
| 2486 | + if (simplified_random_value_sharing == m_random_value_sharing && simplified_minimum == m_minimum && simplified_maximum == m_maximum) |
| 2487 | + return *this; |
| 2488 | + |
| 2489 | + return RandomCalculationNode::create(simplified_random_value_sharing.release_nonnull(), move(simplified_minimum), move(simplified_maximum)); |
| 2490 | +} |
| 2491 | + |
| 2492 | +// https://drafts.csswg.org/css-values-5/#random-evaluation |
| 2493 | +Optional<CalculatedStyleValue::CalculationResult> RandomCalculationNode::run_operation_if_possible(CalculationContext const& context, CalculationResolutionContext const& resolution_context) const |
| 2494 | +{ |
| 2495 | + // NB: We don't want to resolve this before computation time even if it's possible |
| 2496 | + if (!resolution_context.abstract_element.has_value() && !resolution_context.length_resolution_context.has_value() && resolution_context.percentage_basis.has<Empty>()) |
| 2497 | + return {}; |
| 2498 | + |
| 2499 | + auto random_base_value = m_random_value_sharing->random_base_value(); |
| 2500 | + |
| 2501 | + auto minimum = try_get_value_with_canonical_unit(m_minimum, context, resolution_context); |
| 2502 | + auto maximum = try_get_value_with_canonical_unit(m_maximum, context, resolution_context); |
| 2503 | + |
| 2504 | + if (!minimum.has_value() || !maximum.has_value()) |
| 2505 | + return {}; |
| 2506 | + |
| 2507 | + auto minimum_value = minimum->value(); |
| 2508 | + auto maximum_value = maximum->value(); |
| 2509 | + |
| 2510 | + // https://drafts.csswg.org/css-values-5/#random-infinities |
| 2511 | + // If the maximum value is less than the minimum value, it behaves as if it’s equal to the minimum value. |
| 2512 | + if (maximum_value < minimum_value) |
| 2513 | + maximum_value = minimum_value; |
| 2514 | + |
| 2515 | + // https://drafts.csswg.org/css-values-5/#random-infinities |
| 2516 | + // In random(A, B), if A is infinite, the result is infinite. |
| 2517 | + if (isinf(minimum_value)) |
| 2518 | + return CalculatedStyleValue::CalculationResult { AK::Infinity<double>, numeric_type() }; |
| 2519 | + |
| 2520 | + // If A is finite, but the difference between A and B is either infinite or large enough to be treated as infinite |
| 2521 | + // in the user agent, the result is NaN. |
| 2522 | + if (isinf(maximum_value)) |
| 2523 | + return CalculatedStyleValue::CalculationResult { AK::NaN<double>, numeric_type() }; |
| 2524 | + |
| 2525 | + // Note: As usual for math functions, if any argument calculation is NaN, the result is NaN. |
| 2526 | + if (isnan(minimum_value) || isnan(maximum_value)) |
| 2527 | + return CalculatedStyleValue::CalculationResult { AK::NaN<double>, numeric_type() }; |
| 2528 | + |
| 2529 | + // Given a random function with a random base value R, the value of the function is: |
| 2530 | + // - for a random() function with min and max, but no step |
| 2531 | + // Return min + R * (max - min) |
| 2532 | + return CalculatedStyleValue::CalculationResult { |
| 2533 | + minimum_value + (random_base_value * (maximum_value - minimum_value)), |
| 2534 | + numeric_type() |
| 2535 | + }; |
| 2536 | +} |
| 2537 | + |
| 2538 | +String RandomCalculationNode::to_string(CalculationContext const& context, SerializationMode serialization_mode) const |
| 2539 | +{ |
| 2540 | + StringBuilder builder; |
| 2541 | + |
| 2542 | + builder.append("random("sv); |
| 2543 | + builder.appendff("{}, ", m_random_value_sharing->to_string(serialization_mode)); |
| 2544 | + builder.appendff("{}, ", serialize_a_calculation_tree(m_minimum, context, serialization_mode)); |
| 2545 | + builder.appendff("{})", serialize_a_calculation_tree(m_maximum, context, serialization_mode)); |
| 2546 | + |
| 2547 | + return builder.to_string_without_validation(); |
| 2548 | +} |
| 2549 | + |
| 2550 | +void RandomCalculationNode::dump(StringBuilder& builder, int indent) const |
| 2551 | +{ |
| 2552 | + builder.appendff("{: >{}}RANDOM:\n", "", indent); |
| 2553 | + builder.appendff("{}\n", m_random_value_sharing->to_string(SerializationMode::Normal)); |
| 2554 | + m_minimum->dump(builder, indent + 2); |
| 2555 | + m_maximum->dump(builder, indent + 2); |
| 2556 | +} |
| 2557 | + |
| 2558 | +bool RandomCalculationNode::equals(CalculationNode const& other) const |
| 2559 | +{ |
| 2560 | + if (this == &other) |
| 2561 | + return true; |
| 2562 | + |
| 2563 | + if (type() != other.type()) |
| 2564 | + return false; |
| 2565 | + |
| 2566 | + auto const& other_random = as<RandomCalculationNode>(other); |
| 2567 | + |
| 2568 | + return m_random_value_sharing == other_random.m_random_value_sharing |
| 2569 | + && m_minimum == other_random.m_minimum |
| 2570 | + && m_maximum == other_random.m_maximum; |
| 2571 | +} |
| 2572 | + |
2437 | 2573 | NonnullRefPtr<RemCalculationNode const> RemCalculationNode::create(NonnullRefPtr<CalculationNode const> x, NonnullRefPtr<CalculationNode const> y) |
2438 | 2574 | { |
2439 | 2575 | // https://www.w3.org/TR/css-values-4/#determine-the-type-of-a-calculation |
|
0 commit comments