diff --git a/Documentation/CSSGeneratedFiles.md b/Documentation/CSSGeneratedFiles.md index c7c9860c93b..ab8b164a129 100644 --- a/Documentation/CSSGeneratedFiles.md +++ b/Documentation/CSSGeneratedFiles.md @@ -27,7 +27,7 @@ Each property will have some set of these fields on it: | `inherited` | Yes | | Boolean. Whether the property is inherited by its child elements. | `bool is_inherited_property(PropertyID)` | | `initial` | Yes | | String. The property's initial value if it is not specified. | `NonnullRefPtr property_initial_value(PropertyID)` | | `legacy-alias-for` | No | Nothing | String. The name of a property this is an alias for. See below. | | -| `logical-alias-for` | No | Nothing | Array of strings. The name of a property this is an alias for. See below. | | +| `logical-alias-for` | No | Nothing | Array of strings. The name of a property this is an alias for. See below. | `bool property_is_logical_alias(PropertyID);` | | `longhands` | No | `[]` | Array of strings. If this is a shorthand, these are the property names that it expands out into. | `Vector longhands_for_shorthand(PropertyID)`
`Vector expanded_longhands_for_shorthand(PropertyID)`
`Vector shorthands_for_longhand(PropertyID)`| | `max-values` | No | `1` | Integer. How many values can be parsed for this property. eg, `margin` can have up to 4 values. | `size_t property_maximum_value_count(PropertyID)` | | `percentages-resolve-to` | No | Nothing | String. What type percentages get resolved to. eg, for `width` percentages are resolved to `length` values. | `Optional property_resolves_percentages_relative_to(PropertyID)` | diff --git a/Libraries/LibWeb/CSS/StyleComputer.cpp b/Libraries/LibWeb/CSS/StyleComputer.cpp index 231f938b58c..b46cebe3ca4 100644 --- a/Libraries/LibWeb/CSS/StyleComputer.cpp +++ b/Libraries/LibWeb/CSS/StyleComputer.cpp @@ -627,71 +627,6 @@ void StyleComputer::for_each_property_expanding_shorthands(PropertyID property_i return; } - auto map_logical_property_to_real_property = [](PropertyID property_id) -> Optional { - // FIXME: Honor writing-mode, direction and text-orientation. - switch (property_id) { - case PropertyID::BlockSize: - return PropertyID::Height; - case PropertyID::BorderBlockEndColor: - return PropertyID::BorderBottomColor; - case PropertyID::BorderBlockEndStyle: - return PropertyID::BorderBottomStyle; - case PropertyID::BorderBlockEndWidth: - return PropertyID::BorderBottomWidth; - case PropertyID::BorderBlockStartColor: - return PropertyID::BorderTopColor; - case PropertyID::BorderBlockStartStyle: - return PropertyID::BorderTopStyle; - case PropertyID::BorderBlockStartWidth: - return PropertyID::BorderTopWidth; - case PropertyID::BorderInlineStartColor: - return PropertyID::BorderLeftColor; - case PropertyID::BorderInlineStartStyle: - return PropertyID::BorderLeftStyle; - case PropertyID::BorderInlineStartWidth: - return PropertyID::BorderLeftWidth; - case PropertyID::BorderInlineEndColor: - return PropertyID::BorderRightColor; - case PropertyID::BorderInlineEndStyle: - return PropertyID::BorderRightStyle; - case PropertyID::BorderInlineEndWidth: - return PropertyID::BorderRightWidth; - case PropertyID::MarginBlockStart: - return PropertyID::MarginTop; - case PropertyID::MarginBlockEnd: - return PropertyID::MarginBottom; - case PropertyID::MarginInlineStart: - return PropertyID::MarginLeft; - case PropertyID::MarginInlineEnd: - return PropertyID::MarginRight; - case PropertyID::PaddingBlockStart: - return PropertyID::PaddingTop; - case PropertyID::PaddingBlockEnd: - return PropertyID::PaddingBottom; - case PropertyID::PaddingInlineStart: - return PropertyID::PaddingLeft; - case PropertyID::PaddingInlineEnd: - return PropertyID::PaddingRight; - case PropertyID::InlineSize: - return PropertyID::Width; - case PropertyID::InsetBlockStart: - return PropertyID::Top; - case PropertyID::InsetBlockEnd: - return PropertyID::Bottom; - case PropertyID::InsetInlineStart: - return PropertyID::Left; - case PropertyID::InsetInlineEnd: - return PropertyID::Right; - default: - return {}; - } - }; - - if (auto real_property_id = map_logical_property_to_real_property(property_id); real_property_id.has_value()) { - for_each_property_expanding_shorthands(real_property_id.value(), value, set_longhand_property); - return; - } - if (value.is_shorthand()) { auto& shorthand_value = value.as_shorthand(); auto& properties = shorthand_value.sub_properties(); @@ -902,46 +837,6 @@ void StyleComputer::for_each_property_expanding_shorthands(PropertyID property_i return; } - if (property_id == CSS::PropertyID::MaxInlineSize || property_id == CSS::PropertyID::MinInlineSize) { - // FIXME: Use writing-mode to determine if we should set width or height. - bool is_horizontal = true; - - if (is_horizontal) { - if (property_id == CSS::PropertyID::MaxInlineSize) { - set_longhand_property(CSS::PropertyID::MaxWidth, value); - } else { - set_longhand_property(CSS::PropertyID::MinWidth, value); - } - } else { - if (property_id == CSS::PropertyID::MaxInlineSize) { - set_longhand_property(CSS::PropertyID::MaxHeight, value); - } else { - set_longhand_property(CSS::PropertyID::MinHeight, value); - } - } - return; - } - - if (property_id == CSS::PropertyID::MaxBlockSize || property_id == CSS::PropertyID::MinBlockSize) { - // FIXME: Use writing-mode to determine if we should set width or height. - bool is_horizontal = true; - - if (is_horizontal) { - if (property_id == CSS::PropertyID::MaxBlockSize) { - set_longhand_property(CSS::PropertyID::MaxHeight, value); - } else { - set_longhand_property(CSS::PropertyID::MinHeight, value); - } - } else { - if (property_id == CSS::PropertyID::MaxBlockSize) { - set_longhand_property(CSS::PropertyID::MaxWidth, value); - } else { - set_longhand_property(CSS::PropertyID::MinWidth, value); - } - } - return; - } - if (property_id == CSS::PropertyID::Transition) { if (value.to_keyword() == Keyword::None) { // Handle `none` as a shorthand for `all 0s ease 0s`. @@ -993,6 +888,76 @@ void StyleComputer::for_each_property_expanding_shorthands(PropertyID property_i set_longhand_property(property_id, value); } +PropertyID StyleComputer::map_logical_alias_to_physical_property_id(PropertyID property_id, LogicalAliasMappingContext) +{ + // FIXME: Honor writing-mode, direction and text-orientation. + switch (property_id) { + case PropertyID::BlockSize: + return PropertyID::Height; + case PropertyID::BorderBlockEndColor: + return PropertyID::BorderBottomColor; + case PropertyID::BorderBlockEndStyle: + return PropertyID::BorderBottomStyle; + case PropertyID::BorderBlockEndWidth: + return PropertyID::BorderBottomWidth; + case PropertyID::BorderBlockStartColor: + return PropertyID::BorderTopColor; + case PropertyID::BorderBlockStartStyle: + return PropertyID::BorderTopStyle; + case PropertyID::BorderBlockStartWidth: + return PropertyID::BorderTopWidth; + case PropertyID::BorderInlineStartColor: + return PropertyID::BorderLeftColor; + case PropertyID::BorderInlineStartStyle: + return PropertyID::BorderLeftStyle; + case PropertyID::BorderInlineStartWidth: + return PropertyID::BorderLeftWidth; + case PropertyID::BorderInlineEndColor: + return PropertyID::BorderRightColor; + case PropertyID::BorderInlineEndStyle: + return PropertyID::BorderRightStyle; + case PropertyID::BorderInlineEndWidth: + return PropertyID::BorderRightWidth; + case PropertyID::MarginBlockStart: + return PropertyID::MarginTop; + case PropertyID::MarginBlockEnd: + return PropertyID::MarginBottom; + case PropertyID::MarginInlineStart: + return PropertyID::MarginLeft; + case PropertyID::MarginInlineEnd: + return PropertyID::MarginRight; + case PropertyID::MaxBlockSize: + return PropertyID::MaxHeight; + case PropertyID::MaxInlineSize: + return PropertyID::MaxWidth; + case PropertyID::MinBlockSize: + return PropertyID::MinHeight; + case PropertyID::MinInlineSize: + return PropertyID::MinWidth; + case PropertyID::PaddingBlockStart: + return PropertyID::PaddingTop; + case PropertyID::PaddingBlockEnd: + return PropertyID::PaddingBottom; + case PropertyID::PaddingInlineStart: + return PropertyID::PaddingLeft; + case PropertyID::PaddingInlineEnd: + return PropertyID::PaddingRight; + case PropertyID::InlineSize: + return PropertyID::Width; + case PropertyID::InsetBlockStart: + return PropertyID::Top; + case PropertyID::InsetBlockEnd: + return PropertyID::Bottom; + case PropertyID::InsetInlineStart: + return PropertyID::Left; + case PropertyID::InsetInlineEnd: + return PropertyID::Right; + default: + VERIFY(!property_is_logical_alias(property_id)); + return property_id; + } +} + void StyleComputer::cascade_declarations( CascadedProperties& cascaded_properties, DOM::Element& element, @@ -1000,7 +965,8 @@ void StyleComputer::cascade_declarations( Vector const& matching_rules, CascadeOrigin cascade_origin, Important important, - Optional layer_name) const + Optional layer_name, + Optional logical_alias_mapping_context) const { auto seen_properties = MUST(Bitmap::create(to_underlying(last_property_id) + 1, false)); auto cascade_style_declaration = [&](CSSStyleProperties const& declaration) { @@ -1044,12 +1010,22 @@ void StyleComputer::cascade_declarations( return; seen_properties.set(to_underlying(longhand_id), true); - if (longhand_value.is_revert()) { - cascaded_properties.revert_property(longhand_id, important, cascade_origin); - } else if (longhand_value.is_revert_layer()) { - cascaded_properties.revert_layer_property(longhand_id, important, layer_name); + PropertyID physical_property_id; + + if (property_is_logical_alias(longhand_id)) { + if (!logical_alias_mapping_context.has_value()) + return; + physical_property_id = map_logical_alias_to_physical_property_id(longhand_id, logical_alias_mapping_context.value()); } else { - cascaded_properties.set_property(longhand_id, longhand_value, important, cascade_origin, layer_name, declaration); + physical_property_id = longhand_id; + } + + if (longhand_value.is_revert()) { + cascaded_properties.revert_property(physical_property_id, important, cascade_origin); + } else if (longhand_value.is_revert_layer()) { + cascaded_properties.revert_layer_property(physical_property_id, important, layer_name); + } else { + cascaded_properties.set_property(physical_property_id, longhand_value, important, cascade_origin, layer_name, declaration); } }); } @@ -1152,6 +1128,14 @@ void StyleComputer::collect_animation_into(DOM::Element& element, Optional longhands_set_by_property_id; auto property_is_set_by_use_initial = MUST(Bitmap::create(to_underlying(last_longhand_property_id) - to_underlying(first_longhand_property_id) + 1, false)); + auto property_is_logical_alias_including_shorthands = [&](PropertyID property_id) { + if (property_is_shorthand(property_id)) + // NOTE: All expanded longhands for a logical alias shorthand are logical aliases so we only need to check the first one. + return property_is_logical_alias(expanded_longhands_for_shorthand(property_id)[0]); + + return property_is_logical_alias(property_id); + }; + // https://drafts.csswg.org/web-animations-1/#ref-for-computed-keyframes auto is_property_preferred = [&](PropertyID a, PropertyID b) { // If conflicts arise when expanding shorthand properties or replacing logical properties with physical properties, apply the following rules in order until the conflict is resolved: @@ -1168,7 +1152,12 @@ void StyleComputer::collect_animation_into(DOM::Element& element, Optionalas_unresolved()); for_each_property_expanding_shorthands(property_id, *style_value, [&](PropertyID longhand_id, CSSStyleValue const& longhand_value) { - auto longhand_id_bitmap_index = to_underlying(longhand_id) - to_underlying(first_longhand_property_id); + auto physical_longhand_id = map_logical_alias_to_physical_property_id(longhand_id, LogicalAliasMappingContext { computed_properties.writing_mode(), computed_properties.direction() }); + auto physical_longhand_id_bitmap_index = to_underlying(physical_longhand_id) - to_underlying(first_longhand_property_id); // Don't overwrite values if this is the result of a UseInitial - if (result.contains(longhand_id) && result.get(longhand_id) != nullptr && is_use_initial) + if (result.contains(physical_longhand_id) && result.get(physical_longhand_id) != nullptr && is_use_initial) return; // Don't overwrite unless the value was originally set by a UseInitial or this property is preferred over the one that set it originally - if (result.contains(longhand_id) && result.get(longhand_id) != nullptr && !property_is_set_by_use_initial.get(longhand_id_bitmap_index) && !is_property_preferred(property_id, longhands_set_by_property_id.get(longhand_id).value())) + if (result.contains(physical_longhand_id) && result.get(physical_longhand_id) != nullptr && !property_is_set_by_use_initial.get(physical_longhand_id_bitmap_index) && !is_property_preferred(property_id, longhands_set_by_property_id.get(physical_longhand_id).value())) return; - longhands_set_by_property_id.set(longhand_id, property_id); - property_is_set_by_use_initial.set(longhand_id_bitmap_index, is_use_initial); - result.set(longhand_id, { longhand_value }); + longhands_set_by_property_id.set(physical_longhand_id, property_id); + property_is_set_by_use_initial.set(physical_longhand_id_bitmap_index, is_use_initial); + result.set(physical_longhand_id, { longhand_value }); }); } return result; @@ -1632,7 +1622,7 @@ void StyleComputer::start_needed_transitions(ComputedProperties const& previous_ // https://www.w3.org/TR/css-cascade/#cascading // https://drafts.csswg.org/css-cascade-5/#layering -GC::Ref StyleComputer::compute_cascaded_values(DOM::Element& element, Optional pseudo_element, bool& did_match_any_pseudo_element_rules, PseudoClassBitmap& attempted_pseudo_class_matches, ComputeStyleMode mode) const +GC::Ref StyleComputer::compute_cascaded_values(DOM::Element& element, Optional pseudo_element, bool& did_match_any_pseudo_element_rules, PseudoClassBitmap& attempted_pseudo_class_matches, ComputeStyleMode mode, Optional logical_alias_mapping_context) const { auto cascaded_properties = m_document->heap().allocate(); @@ -1676,10 +1666,10 @@ GC::Ref StyleComputer::compute_cascaded_values(DOM::Element& // Then we apply the declarations from the matched rules in cascade order: // Normal user agent declarations - cascade_declarations(*cascaded_properties, element, pseudo_element, matching_rule_set.user_agent_rules, CascadeOrigin::UserAgent, Important::No, {}); + cascade_declarations(*cascaded_properties, element, pseudo_element, matching_rule_set.user_agent_rules, CascadeOrigin::UserAgent, Important::No, {}, logical_alias_mapping_context); // Normal user declarations - cascade_declarations(*cascaded_properties, element, pseudo_element, matching_rule_set.user_rules, CascadeOrigin::User, Important::No, {}); + cascade_declarations(*cascaded_properties, element, pseudo_element, matching_rule_set.user_rules, CascadeOrigin::User, Important::No, {}, logical_alias_mapping_context); // Author presentational hints // The spec calls this a special "Author presentational hint origin": @@ -1702,23 +1692,23 @@ GC::Ref StyleComputer::compute_cascaded_values(DOM::Element& // Normal author declarations, ordered by @layer, with un-@layer-ed rules last for (auto const& layer : matching_rule_set.author_rules) { - cascade_declarations(cascaded_properties, element, pseudo_element, layer.rules, CascadeOrigin::Author, Important::No, layer.qualified_layer_name); + cascade_declarations(cascaded_properties, element, pseudo_element, layer.rules, CascadeOrigin::Author, Important::No, layer.qualified_layer_name, logical_alias_mapping_context); } // Important author declarations, with un-@layer-ed rules first, followed by each @layer in reverse order. for (auto const& layer : matching_rule_set.author_rules.in_reverse()) { - cascade_declarations(cascaded_properties, element, pseudo_element, layer.rules, CascadeOrigin::Author, Important::Yes, {}); + cascade_declarations(cascaded_properties, element, pseudo_element, layer.rules, CascadeOrigin::Author, Important::Yes, {}, logical_alias_mapping_context); } // Important user declarations - cascade_declarations(cascaded_properties, element, pseudo_element, matching_rule_set.user_rules, CascadeOrigin::User, Important::Yes, {}); + cascade_declarations(cascaded_properties, element, pseudo_element, matching_rule_set.user_rules, CascadeOrigin::User, Important::Yes, {}, logical_alias_mapping_context); // Important user agent declarations - cascade_declarations(cascaded_properties, element, pseudo_element, matching_rule_set.user_agent_rules, CascadeOrigin::UserAgent, Important::Yes, {}); + cascade_declarations(cascaded_properties, element, pseudo_element, matching_rule_set.user_agent_rules, CascadeOrigin::UserAgent, Important::Yes, {}, logical_alias_mapping_context); // Transition declarations [css-transitions-1] // Note that we have to do these after finishing computing the style, - // so they're not done here, but as the final step in compute_style_impl() + // so they're not done here, but as the final step in compute_properties() return cascaded_properties; } @@ -2216,6 +2206,38 @@ void StyleComputer::compute_font(ComputedProperties& style, DOM::Element const* } } +StyleComputer::LogicalAliasMappingContext StyleComputer::compute_logical_alias_mapping_context(DOM::Element& element, Optional pseudo_element, ComputeStyleMode mode) const +{ + auto normalize_value = [&](auto property_id, auto value) { + if (!value || value->is_inherit() || value->is_unset()) { + if (auto const* inheritance_parent = element_to_inherit_style_from(&element, pseudo_element)) { + value = inheritance_parent->computed_properties()->property(property_id); + } else { + value = property_initial_value(property_id); + } + } + + if (value->is_initial()) + value = property_initial_value(property_id); + + return value; + }; + + bool did_match_any_pseudo_element_rules = false; + PseudoClassBitmap attempted_pseudo_class_matches; + + // FIXME: Ideally we wouldn't run the whole cascade just for these few properties. + auto cascaded_properties = compute_cascaded_values(element, pseudo_element, did_match_any_pseudo_element_rules, attempted_pseudo_class_matches, mode, {}); + + auto writing_mode = normalize_value(PropertyID::WritingMode, cascaded_properties->property(PropertyID::WritingMode)); + auto direction = normalize_value(PropertyID::Direction, cascaded_properties->property(PropertyID::Direction)); + + return LogicalAliasMappingContext { + .writing_mode = keyword_to_writing_mode(writing_mode->to_keyword()).release_value(), + .direction = keyword_to_direction(direction->to_keyword()).release_value() + }; +} + Gfx::Font const& StyleComputer::initial_font() const { // FIXME: This is not correct. @@ -2493,8 +2515,8 @@ GC::Ptr StyleComputer::compute_style_impl(DOM::Element& elem // 1. Perform the cascade. This produces the "specified style" bool did_match_any_pseudo_element_rules = false; PseudoClassBitmap attempted_pseudo_class_matches; - auto cascaded_properties = compute_cascaded_values(element, pseudo_element, did_match_any_pseudo_element_rules, attempted_pseudo_class_matches, mode); - + auto logical_alias_mapping_context = compute_logical_alias_mapping_context(element, pseudo_element, mode); + auto cascaded_properties = compute_cascaded_values(element, pseudo_element, did_match_any_pseudo_element_rules, attempted_pseudo_class_matches, mode, logical_alias_mapping_context); element.set_cascaded_properties(pseudo_element, cascaded_properties); if (mode == ComputeStyleMode::CreatePseudoElementStyleIfNeeded) { @@ -2631,6 +2653,7 @@ GC::Ref StyleComputer::compute_properties(DOM::Element& elem if (property_id == PropertyID::FontSize && !value && new_font_size) continue; + // FIXME: Logical properties should inherit from their parent's equivalent unmapped logical property. if ((!value && is_inherited_property(property_id)) || (value && value->is_inherit())) { if (auto inheritance_parent = element_to_inherit_style_from(&element, pseudo_element)) { diff --git a/Libraries/LibWeb/CSS/StyleComputer.h b/Libraries/LibWeb/CSS/StyleComputer.h index 7f95e8c1d20..0df8ee14ba6 100644 --- a/Libraries/LibWeb/CSS/StyleComputer.h +++ b/Libraries/LibWeb/CSS/StyleComputer.h @@ -131,6 +131,13 @@ public: static void for_each_property_expanding_shorthands(PropertyID, CSSStyleValue const&, Function const& set_longhand_property); static NonnullRefPtr get_inherit_value(CSS::PropertyID, DOM::Element const*, Optional = {}); + struct LogicalAliasMappingContext { + CSS::WritingMode writing_mode; + CSS::Direction direction; + // TODO: text-orientation + }; + static PropertyID map_logical_alias_to_physical_property_id(PropertyID, LogicalAliasMappingContext); + static Optional user_agent_style_sheet_source(StringView name); explicit StyleComputer(DOM::Document&); @@ -201,8 +208,9 @@ private: struct MatchingFontCandidate; + LogicalAliasMappingContext compute_logical_alias_mapping_context(DOM::Element&, Optional, ComputeStyleMode) const; [[nodiscard]] GC::Ptr compute_style_impl(DOM::Element&, Optional, ComputeStyleMode) const; - [[nodiscard]] GC::Ref compute_cascaded_values(DOM::Element&, Optional, bool& did_match_any_pseudo_element_rules, PseudoClassBitmap& attempted_pseudo_class_matches, ComputeStyleMode) const; + [[nodiscard]] GC::Ref compute_cascaded_values(DOM::Element&, Optional, bool& did_match_any_pseudo_element_rules, PseudoClassBitmap& attempted_pseudo_class_matches, ComputeStyleMode, Optional) const; static RefPtr find_matching_font_weight_ascending(Vector const& candidates, int target_weight, float font_size_in_pt, bool inclusive); static RefPtr find_matching_font_weight_descending(Vector const& candidates, int target_weight, float font_size_in_pt, bool inclusive); RefPtr font_matching_algorithm(FlyString const& family_name, int weight, int slope, float font_size_in_pt) const; @@ -242,7 +250,8 @@ private: Vector const&, CascadeOrigin, Important, - Optional layer_name) const; + Optional layer_name, + Optional) const; void build_rule_cache(); void build_rule_cache_if_needed() const; diff --git a/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSPropertyID.cpp b/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSPropertyID.cpp index 9c5a573b5e6..3f1caf5bd43 100644 --- a/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSPropertyID.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSPropertyID.cpp @@ -132,7 +132,7 @@ void populate_all_property_longhands(JsonObject& properties) VERIFY(all_entry.has_value()); properties.for_each_member([&](auto name, auto value) { - if (value.as_object().has_array("longhands"sv) || value.as_object().has_array("logical-alias-for"sv) || value.as_object().has_string("legacy-alias-for"sv) || name == "direction" || name == "unicode-bidi") + if (value.as_object().has_array("longhands"sv) || value.as_object().has_string("legacy-alias-for"sv) || name == "direction" || name == "unicode-bidi") return; MUST(all_entry->get_array("longhands"sv)->append(JsonValue { name })); @@ -310,6 +310,8 @@ enum class Quirk { }; bool property_has_quirk(PropertyID, Quirk); +bool property_is_logical_alias(PropertyID); + } // namespace Web::CSS namespace AK { @@ -1275,6 +1277,33 @@ Vector shorthands_for_longhand(PropertyID property_id) return { }; } } +)~~~"); + + generator.append(R"~~~( +bool property_is_logical_alias(PropertyID property_id) +{ + switch(property_id) { +)~~~"); + + properties.for_each_member([&](auto& name, auto& value) { + if (is_legacy_alias(value.as_object())) + return; + + if (value.as_object().has("logical-alias-for"sv)) { + auto property_generator = generator.fork(); + property_generator.set("name:titlecase", title_casify(name)); + property_generator.append(R"~~~( + case PropertyID::@name:titlecase@: +)~~~"); + } + }); + + generator.append(R"~~~( + return true; + default: + return false; + } +} )~~~"); generator.append(R"~~~( diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-cascade/all-prop-revert-layer.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-cascade/all-prop-revert-layer.txt index e464a08754b..eb1d2bc0b1f 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/css/css-cascade/all-prop-revert-layer.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-cascade/all-prop-revert-layer.txt @@ -1,9 +1,9 @@ Harness status: OK -Found 206 tests +Found 236 tests -202 Pass -4 Fail +218 Pass +18 Fail Pass accent-color Pass border-collapse Pass border-spacing @@ -90,11 +90,24 @@ Pass background-position-x Pass background-position-y Pass background-repeat Pass background-size +Fail block-size +Pass border-block-end-color +Fail border-block-end-style +Fail border-block-end-width +Pass border-block-start-color +Fail border-block-start-style +Fail border-block-start-width Pass border-bottom-color Pass border-bottom-left-radius Pass border-bottom-right-radius Pass border-bottom-style Pass border-bottom-width +Pass border-inline-end-color +Fail border-inline-end-style +Fail border-inline-end-width +Pass border-inline-start-color +Fail border-inline-start-style +Fail border-inline-start-width Pass border-left-color Pass border-left-style Pass border-left-width @@ -142,20 +155,33 @@ Pass grid-template-areas Pass grid-template-columns Pass grid-template-rows Fail height +Fail inline-size +Pass inset-block-end +Pass inset-block-start +Pass inset-inline-end +Pass inset-inline-start Pass isolation Pass justify-content Pass justify-items Pass justify-self Pass left +Pass margin-block-end +Pass margin-block-start Pass margin-bottom +Pass margin-inline-end +Pass margin-inline-start Pass margin-left Pass margin-right Pass margin-top Pass mask-image Pass mask-type +Fail max-block-size Pass max-height +Fail max-inline-size Pass max-width +Fail min-block-size Pass min-height +Fail min-inline-size Pass min-width Pass mix-blend-mode Pass object-fit @@ -168,7 +194,11 @@ Pass outline-style Pass outline-width Pass overflow-x Pass overflow-y +Pass padding-block-end +Pass padding-block-start Pass padding-bottom +Pass padding-inline-end +Pass padding-inline-start Pass padding-left Pass padding-right Pass padding-top diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-logical/logical-box-inset.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-logical/logical-box-inset.txt new file mode 100644 index 00000000000..544f440bcdd --- /dev/null +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-logical/logical-box-inset.txt @@ -0,0 +1,50 @@ +Harness status: OK + +Found 44 tests + +8 Pass +36 Fail +Pass Test that logical inset-* properties are supported. +Pass Test that inset-inline shorthand sets longhands and serializes correctly. +Pass Test that inset-block shorthand sets longhands and serializes correctly. +Pass Test that inset shorthand sets longhands and serializes correctly. +Pass Test that logical inset-* properties share computed values with their physical associates, with 'writing-mode: horizontal-tb; direction: ltr; '. +Pass Test that inset-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: horizontal-tb; direction: ltr; '. +Pass Test that inset-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: ltr; '. +Pass Test that inset-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: ltr; '. +Fail Test that logical inset-* properties share computed values with their physical associates, with 'writing-mode: horizontal-tb; direction: rtl; '. +Fail Test that inset-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: horizontal-tb; direction: rtl; '. +Fail Test that inset-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: rtl; '. +Fail Test that inset-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: rtl; '. +Fail Test that logical inset-* properties share computed values with their physical associates, with 'writing-mode: vertical-rl; direction: rtl; '. +Fail Test that inset-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-rl; direction: rtl; '. +Fail Test that inset-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: rtl; '. +Fail Test that inset-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: rtl; '. +Fail Test that logical inset-* properties share computed values with their physical associates, with 'writing-mode: sideways-rl; direction: rtl; '. +Fail Test that inset-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-rl; direction: rtl; '. +Fail Test that inset-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: rtl; '. +Fail Test that inset-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: rtl; '. +Fail Test that logical inset-* properties share computed values with their physical associates, with 'writing-mode: vertical-rl; direction: ltr; '. +Fail Test that inset-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-rl; direction: ltr; '. +Fail Test that inset-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: ltr; '. +Fail Test that inset-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: ltr; '. +Fail Test that logical inset-* properties share computed values with their physical associates, with 'writing-mode: sideways-rl; direction: ltr; '. +Fail Test that inset-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-rl; direction: ltr; '. +Fail Test that inset-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: ltr; '. +Fail Test that inset-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: ltr; '. +Fail Test that logical inset-* properties share computed values with their physical associates, with 'writing-mode: vertical-lr; direction: rtl; '. +Fail Test that inset-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-lr; direction: rtl; '. +Fail Test that inset-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: rtl; '. +Fail Test that inset-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: rtl; '. +Fail Test that logical inset-* properties share computed values with their physical associates, with 'writing-mode: sideways-lr; direction: ltr; '. +Fail Test that inset-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-lr; direction: ltr; '. +Fail Test that inset-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: ltr; '. +Fail Test that inset-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: ltr; '. +Fail Test that logical inset-* properties share computed values with their physical associates, with 'writing-mode: vertical-lr; direction: ltr; '. +Fail Test that inset-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-lr; direction: ltr; '. +Fail Test that inset-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: ltr; '. +Fail Test that inset-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: ltr; '. +Fail Test that logical inset-* properties share computed values with their physical associates, with 'writing-mode: sideways-lr; direction: rtl; '. +Fail Test that inset-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-lr; direction: rtl; '. +Fail Test that inset-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: rtl; '. +Fail Test that inset-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: rtl; '. \ No newline at end of file diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-logical/logical-box-margin.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-logical/logical-box-margin.txt new file mode 100644 index 00000000000..10b95274c20 --- /dev/null +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-logical/logical-box-margin.txt @@ -0,0 +1,50 @@ +Harness status: OK + +Found 44 tests + +8 Pass +36 Fail +Pass Test that logical margin-* properties are supported. +Pass Test that margin-inline shorthand sets longhands and serializes correctly. +Pass Test that margin-block shorthand sets longhands and serializes correctly. +Pass Test that margin shorthand sets longhands and serializes correctly. +Pass Test that logical margin-* properties share computed values with their physical associates, with 'writing-mode: horizontal-tb; direction: ltr; '. +Pass Test that margin-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: horizontal-tb; direction: ltr; '. +Pass Test that margin-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: ltr; '. +Pass Test that margin-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: ltr; '. +Fail Test that logical margin-* properties share computed values with their physical associates, with 'writing-mode: horizontal-tb; direction: rtl; '. +Fail Test that margin-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: horizontal-tb; direction: rtl; '. +Fail Test that margin-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: rtl; '. +Fail Test that margin-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: rtl; '. +Fail Test that logical margin-* properties share computed values with their physical associates, with 'writing-mode: vertical-rl; direction: rtl; '. +Fail Test that margin-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-rl; direction: rtl; '. +Fail Test that margin-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: rtl; '. +Fail Test that margin-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: rtl; '. +Fail Test that logical margin-* properties share computed values with their physical associates, with 'writing-mode: sideways-rl; direction: rtl; '. +Fail Test that margin-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-rl; direction: rtl; '. +Fail Test that margin-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: rtl; '. +Fail Test that margin-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: rtl; '. +Fail Test that logical margin-* properties share computed values with their physical associates, with 'writing-mode: vertical-rl; direction: ltr; '. +Fail Test that margin-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-rl; direction: ltr; '. +Fail Test that margin-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: ltr; '. +Fail Test that margin-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: ltr; '. +Fail Test that logical margin-* properties share computed values with their physical associates, with 'writing-mode: sideways-rl; direction: ltr; '. +Fail Test that margin-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-rl; direction: ltr; '. +Fail Test that margin-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: ltr; '. +Fail Test that margin-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: ltr; '. +Fail Test that logical margin-* properties share computed values with their physical associates, with 'writing-mode: vertical-lr; direction: rtl; '. +Fail Test that margin-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-lr; direction: rtl; '. +Fail Test that margin-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: rtl; '. +Fail Test that margin-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: rtl; '. +Fail Test that logical margin-* properties share computed values with their physical associates, with 'writing-mode: sideways-lr; direction: ltr; '. +Fail Test that margin-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-lr; direction: ltr; '. +Fail Test that margin-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: ltr; '. +Fail Test that margin-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: ltr; '. +Fail Test that logical margin-* properties share computed values with their physical associates, with 'writing-mode: vertical-lr; direction: ltr; '. +Fail Test that margin-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-lr; direction: ltr; '. +Fail Test that margin-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: ltr; '. +Fail Test that margin-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: ltr; '. +Fail Test that logical margin-* properties share computed values with their physical associates, with 'writing-mode: sideways-lr; direction: rtl; '. +Fail Test that margin-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-lr; direction: rtl; '. +Fail Test that margin-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: rtl; '. +Fail Test that margin-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: rtl; '. \ No newline at end of file diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-logical/logical-box-padding.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-logical/logical-box-padding.txt new file mode 100644 index 00000000000..95d83bf1fb2 --- /dev/null +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-logical/logical-box-padding.txt @@ -0,0 +1,50 @@ +Harness status: OK + +Found 44 tests + +8 Pass +36 Fail +Pass Test that logical padding-* properties are supported. +Pass Test that padding-inline shorthand sets longhands and serializes correctly. +Pass Test that padding-block shorthand sets longhands and serializes correctly. +Pass Test that padding shorthand sets longhands and serializes correctly. +Pass Test that logical padding-* properties share computed values with their physical associates, with 'writing-mode: horizontal-tb; direction: ltr; '. +Pass Test that padding-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: horizontal-tb; direction: ltr; '. +Pass Test that padding-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: ltr; '. +Pass Test that padding-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: ltr; '. +Fail Test that logical padding-* properties share computed values with their physical associates, with 'writing-mode: horizontal-tb; direction: rtl; '. +Fail Test that padding-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: horizontal-tb; direction: rtl; '. +Fail Test that padding-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: rtl; '. +Fail Test that padding-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: rtl; '. +Fail Test that logical padding-* properties share computed values with their physical associates, with 'writing-mode: vertical-rl; direction: rtl; '. +Fail Test that padding-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-rl; direction: rtl; '. +Fail Test that padding-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: rtl; '. +Fail Test that padding-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: rtl; '. +Fail Test that logical padding-* properties share computed values with their physical associates, with 'writing-mode: sideways-rl; direction: rtl; '. +Fail Test that padding-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-rl; direction: rtl; '. +Fail Test that padding-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: rtl; '. +Fail Test that padding-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: rtl; '. +Fail Test that logical padding-* properties share computed values with their physical associates, with 'writing-mode: vertical-rl; direction: ltr; '. +Fail Test that padding-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-rl; direction: ltr; '. +Fail Test that padding-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: ltr; '. +Fail Test that padding-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-rl; direction: ltr; '. +Fail Test that logical padding-* properties share computed values with their physical associates, with 'writing-mode: sideways-rl; direction: ltr; '. +Fail Test that padding-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-rl; direction: ltr; '. +Fail Test that padding-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: ltr; '. +Fail Test that padding-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-rl; direction: ltr; '. +Fail Test that logical padding-* properties share computed values with their physical associates, with 'writing-mode: vertical-lr; direction: rtl; '. +Fail Test that padding-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-lr; direction: rtl; '. +Fail Test that padding-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: rtl; '. +Fail Test that padding-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: rtl; '. +Fail Test that logical padding-* properties share computed values with their physical associates, with 'writing-mode: sideways-lr; direction: ltr; '. +Fail Test that padding-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-lr; direction: ltr; '. +Fail Test that padding-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: ltr; '. +Fail Test that padding-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: ltr; '. +Fail Test that logical padding-* properties share computed values with their physical associates, with 'writing-mode: vertical-lr; direction: ltr; '. +Fail Test that padding-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: vertical-lr; direction: ltr; '. +Fail Test that padding-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: ltr; '. +Fail Test that padding-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: vertical-lr; direction: ltr; '. +Fail Test that logical padding-* properties share computed values with their physical associates, with 'writing-mode: sideways-lr; direction: rtl; '. +Fail Test that padding-* shorthands set the computed value of both logical and physical longhands, with 'writing-mode: sideways-lr; direction: rtl; '. +Fail Test that padding-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: rtl; '. +Fail Test that padding-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: sideways-lr; direction: rtl; '. \ No newline at end of file diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/cssom/cssom-getPropertyValue-common-checks.txt b/Tests/LibWeb/Text/expected/wpt-import/css/cssom/cssom-getPropertyValue-common-checks.txt index d77de0b684b..563b3ce346b 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/css/cssom/cssom-getPropertyValue-common-checks.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/css/cssom/cssom-getPropertyValue-common-checks.txt @@ -2,13 +2,12 @@ Harness status: OK Found 11 tests -7 Pass -4 Fail -Fail All properties can serialize 'initial' +11 Pass +Pass All properties can serialize 'initial' Pass All properties (except 'all') can serialize their initial value (computed) -Fail All properties (except 'all') can serialize their initial value (specified) -Fail All shorthands can serialize their longhands set to 'initial' -Fail All shorthands (except 'all') can serialize their longhands set to their initial value +Pass All properties (except 'all') can serialize their initial value (specified) +Pass All shorthands can serialize their longhands set to 'initial' +Pass All shorthands (except 'all') can serialize their longhands set to their initial value Pass All aliases can serialize target property set to 'initial' Pass All aliases can serialize target property set to its initial value Pass Can't serialize shorthand when longhands are set to different css-wide keywords diff --git a/Tests/LibWeb/Text/input/wpt-import/css/css-logical/logical-box-inset.html b/Tests/LibWeb/Text/input/wpt-import/css/css-logical/logical-box-inset.html new file mode 100644 index 00000000000..3866d61a4ae --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/css/css-logical/logical-box-inset.html @@ -0,0 +1,18 @@ + + +CSS Logical Properties: Flow-Relative Offsets + + + + + + +
+ + diff --git a/Tests/LibWeb/Text/input/wpt-import/css/css-logical/logical-box-margin.html b/Tests/LibWeb/Text/input/wpt-import/css/css-logical/logical-box-margin.html new file mode 100644 index 00000000000..93f05bf26c8 --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/css/css-logical/logical-box-margin.html @@ -0,0 +1,15 @@ + + +CSS Logical Properties: Flow-Relative Margins + + + + + + +
+ + diff --git a/Tests/LibWeb/Text/input/wpt-import/css/css-logical/logical-box-padding.html b/Tests/LibWeb/Text/input/wpt-import/css/css-logical/logical-box-padding.html new file mode 100644 index 00000000000..49c0cab88b1 --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/css/css-logical/logical-box-padding.html @@ -0,0 +1,15 @@ + + +CSS Logical Properties: Flow-Relative Padding + + + + + + +
+ + diff --git a/Tests/LibWeb/Text/input/wpt-import/css/css-logical/resources/test-box-properties.js b/Tests/LibWeb/Text/input/wpt-import/css/css-logical/resources/test-box-properties.js new file mode 100644 index 00000000000..6c65c4024af --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/css/css-logical/resources/test-box-properties.js @@ -0,0 +1,297 @@ +import { + testElement, + writingModes, + testCSSValues, + testComputedValues, + makeDeclaration +} from "./test-shared.js"; + +// Values to use while testing +const testValues = { + "length": ["1px", "2px", "3px", "4px", "5px"], + "color": ["rgb(1, 1, 1)", "rgb(2, 2, 2)", "rgb(3, 3, 3)", "rgb(4, 4, 4)", "rgb(5, 5, 5)"], + "border-style": ["solid", "dashed", "dotted", "double", "groove"], +}; + +/** + * Creates a group of physical and logical box properties, such as + * + * { physical: { + * left: "margin-left", right: "margin-right", + * top: "margin-top", bottom: "margin-bottom", + * }, logical: { + * inlineStart: "margin-inline-start", inlineEnd: "margin-inline-end", + * blockStart: "margin-block-start", blockEnd: "margin-block-end", + * }, shorthands: { + * "margin": ["margin-top", "margin-right", "margin-bottom", "margin-left"], + * "margin-inline": ["margin-inline-start", "margin-inline-end"], + * "margin-block": ["margin-block-start", "margin-block-end"], + * }, type: ["length"], prerequisites: "...", property: "margin-*" } + * + * @param {string} property + * A string representing the property names, like "margin-*". + * @param {Object} descriptor + * @param {string|string[]} descriptor.type + * Describes the kind of values accepted by the property, like "length". + * Must be a key or a collection of keys from the `testValues` object. + * @param {Object={}} descriptor.prerequisites + * Represents property declarations that are needed by `property` to work. + * For example, border-width properties require a border style. + */ +export function createBoxPropertyGroup(property, descriptor) { + const logical = {}; + const physical = {}; + const shorthands = {}; + for (const axis of ["inline", "block"]) { + const shorthand = property.replace("*", axis); + const longhands = []; + shorthands[shorthand] = longhands; + for (const side of ["start", "end"]) { + const logicalSide = axis + "-" + side; + const camelCase = logicalSide.replace(/-(.)/g, (match, $1) => $1.toUpperCase()); + const longhand = property.replace("*", logicalSide); + logical[camelCase] = longhand; + longhands.push(longhand); + } + } + const isInset = property === "inset-*"; + let prerequisites = ""; + for (const physicalSide of ["left", "right", "top", "bottom"]) { + physical[physicalSide] = isInset ? physicalSide : property.replace("*", physicalSide); + prerequisites += makeDeclaration(descriptor.prerequisites, physicalSide); + } + shorthands[property.replace("-*", "")] = + ["top", "right", "bottom", "left"].map(physicalSide => physical[physicalSide]); + const type = [].concat(descriptor.type); + return { logical, physical, shorthands, type, prerequisites, property }; +} + +/** + * Creates a group physical and logical box-corner properties. + * + * @param {string} property + * A string representing the property names, like "border-*-radius". + * @param {Object} descriptor + * @param {string|string[]} descriptor.type + * Describes the kind of values accepted by the property, like "length". + * Must be a key or a collection of keys from the `testValues` object. + * @param {Object={}} descriptor.prerequisites + * Represents property declarations that are needed by `property` to work. + * For example, border-width properties require a border style. + */ +export function createCornerPropertyGroup(property, descriptor) { + const logical = {}; + const physical = {}; + const shorthands = {}; + for (const logicalCorner of ["start-start", "start-end", "end-start", "end-end"]) { + const prop = property.replace("*", logicalCorner); + const [block_side, inline_side] = logicalCorner.split("-"); + const b = "block" + block_side.charAt(0).toUpperCase() + block_side.slice(1); + const i = "inline" + inline_side.charAt(0).toUpperCase() + inline_side.slice(1); + const index = b + "-" + i; // e.g. "blockStart-inlineEnd" + logical[index] = prop; + } + let prerequisites = ""; + for (const physicalCorner of ["top-left", "top-right", "bottom-left", "bottom-right"]) { + const prop = property.replace("*", physicalCorner); + physical[physicalCorner] = prop; + prerequisites += makeDeclaration(descriptor.prerequisites, physicalCorner); + } + const type = [].concat(descriptor.type); + return { logical, physical, shorthands, type, prerequisites, property }; +} + +/** + * Creates a group of physical and logical sizing properties. + * + * @param {string} prefix + * One of "", "max-" or "min-". + */ +export function createSizingPropertyGroup(prefix) { + return { + logical: { + inline: `${prefix}inline-size`, + block: `${prefix}block-size`, + }, + physical: { + horizontal: `${prefix}width`, + vertical: `${prefix}height`, + }, + type: ["length"], + prerequisites: makeDeclaration({ display: "block" }), + property: (prefix ? prefix.slice(0, -1) + " " : "") + "sizing", + }; +} + +/** + * Tests a grup of logical and physical properties in different writing modes. + * + * @param {Object} group + * An object returned by createBoxPropertyGroup or createSizingPropertyGroup. + */ +export function runTests(group) { + const values = testValues[group.type[0]].map(function (_, i) { + return group.type.map(type => testValues[type][i]).join(" "); + }); + const logicals = Object.values(group.logical); + const physicals = Object.values(group.physical); + const shorthands = group.shorthands ? Object.entries(group.shorthands) : null; + const is_corner = group.property == "border-*-radius"; + + test(function () { + const expected = []; + for (const [i, logicalProp] of logicals.entries()) { + testElement.style.setProperty(logicalProp, values[i]); + expected.push([logicalProp, values[i]]); + } + testCSSValues("logical properties in inline style", testElement.style, expected); + }, `Test that logical ${group.property} properties are supported.`); + testElement.style.cssText = ""; + + const shorthandValues = {}; + for (const [shorthand, longhands] of shorthands || []) { + let valueArray; + if (group.type.length > 1) { + valueArray = [values[0]]; + } else { + valueArray = testValues[group.type].slice(0, longhands.length); + } + shorthandValues[shorthand] = valueArray; + const value = valueArray.join(" "); + const expected = [[shorthand, value]]; + for (let [i, longhand] of longhands.entries()) { + expected.push([longhand, valueArray[group.type.length > 1 ? 0 : i]]); + } + test(function () { + testElement.style.setProperty(shorthand, value); + testCSSValues("shorthand in inline style", testElement.style, expected); + const stylesheet = `.test { ${group.prerequisites} }`; + testComputedValues("shorthand in computed style", stylesheet, expected); + }, `Test that ${shorthand} shorthand sets longhands and serializes correctly.`); + testElement.style.cssText = ""; + } + + for (const writingMode of writingModes) { + for (const style of writingMode.styles) { + const writingModeDecl = makeDeclaration(style); + + const associated = {}; + for (const [logicalSide, logicalProp] of Object.entries(group.logical)) { + let physicalProp; + if (is_corner) { + const [block_side, inline_side] = logicalSide.split("-"); + const physicalSide1 = writingMode[block_side]; + const physicalSide2 = writingMode[inline_side]; + let physicalCorner; + // mirror "left-top" to "top-left" etc + if (["top", "bottom"].includes(physicalSide1)) { + physicalCorner = physicalSide1 + "-" + physicalSide2; + } else { + physicalCorner = physicalSide2 + "-" + physicalSide1; + } + physicalProp = group.physical[physicalCorner]; + } else { + physicalProp = group.physical[writingMode[logicalSide]]; + } + associated[logicalProp] = physicalProp; + associated[physicalProp] = logicalProp; + } + + // Test that logical properties are converted to their physical + // equivalent correctly when all in the group are present on a single + // declaration, with no overwriting of previous properties and + // no physical properties present. We put the writing mode properties + // on a separate declaration to test that the computed values of these + // properties are used, rather than those on the same declaration. + test(function () { + let decl = group.prerequisites; + const expected = []; + for (const [i, logicalProp] of logicals.entries()) { + decl += `${logicalProp}: ${values[i]}; `; + expected.push([logicalProp, values[i]]); + expected.push([associated[logicalProp], values[i]]); + } + testComputedValues("logical properties on one declaration, writing " + + `mode properties on another, '${writingModeDecl}'`, + `.test { ${writingModeDecl} } .test { ${decl} }`, + expected); + }, `Test that logical ${group.property} properties share computed values ` + + `with their physical associates, with '${writingModeDecl}'.`); + + // Test logical shorthand properties. + if (shorthands) { + test(function () { + for (const [shorthand, longhands] of shorthands) { + let valueArray = shorthandValues[shorthand]; + const decl = group.prerequisites + `${shorthand}: ${valueArray.join(" ")}; `; + const expected = []; + for (let [i, longhand] of longhands.entries()) { + const longhandValue = valueArray[group.type.length > 1 ? 0 : i]; + expected.push([longhand, longhandValue]); + expected.push([associated[longhand], longhandValue]); + } + testComputedValues("shorthand properties on one declaration, writing " + + `mode properties on another, '${writingModeDecl}'`, + `.test { ${writingModeDecl} } .test { ${decl} }`, + expected); + } + }, `Test that ${group.property} shorthands set the computed value of both ` + + `logical and physical longhands, with '${writingModeDecl}'.`); + } + + // Test that logical and physical properties are cascaded together, + // honoring their relative order on a single declaration + // (a) with a single logical property after the physical ones + // (b) with a single physical property after the logical ones + test(function () { + for (const lastIsLogical of [true, false]) { + const lasts = lastIsLogical ? logicals : physicals; + const others = lastIsLogical ? physicals : logicals; + for (const lastProp of lasts) { + let decl = writingModeDecl + group.prerequisites; + const expected = []; + for (const [i, prop] of others.entries()) { + decl += `${prop}: ${values[i]}; `; + const valueIdx = associated[prop] === lastProp ? others.length : i; + expected.push([prop, values[valueIdx]]); + expected.push([associated[prop], values[valueIdx]]); + } + decl += `${lastProp}: ${values[others.length]}; `; + testComputedValues(`'${lastProp}' last on single declaration, '${writingModeDecl}'`, + `.test { ${decl} }`, + expected); + } + } + }, `Test that ${group.property} properties honor order of appearance when both ` + + `logical and physical associates are declared, with '${writingModeDecl}'.`); + + // Test that logical and physical properties are cascaded properly when + // on different declarations + // (a) with a logical property in the high specificity rule + // (b) with a physical property in the high specificity rule + test(function () { + for (const highIsLogical of [true, false]) { + let lowDecl = writingModeDecl + group.prerequisites; + const high = highIsLogical ? logicals : physicals; + const others = highIsLogical ? physicals : logicals; + for (const [i, prop] of others.entries()) { + lowDecl += `${prop}: ${values[i]}; `; + } + for (const highProp of high) { + const highDecl = `${highProp}: ${values[others.length]}; `; + const expected = []; + for (const [i, prop] of others.entries()) { + const valueIdx = associated[prop] === highProp ? others.length : i; + expected.push([prop, values[valueIdx]]); + expected.push([associated[prop], values[valueIdx]]); + } + testComputedValues(`'${highProp}', two declarations, '${writingModeDecl}'`, + `#test { ${highDecl} } .test { ${lowDecl} }`, + expected); + } + } + }, `Test that ${group.property} properties honor selector specificty when both ` + + `logical and physical associates are declared, with '${writingModeDecl}'.`); + } + } +} diff --git a/Tests/LibWeb/Text/input/wpt-import/css/css-logical/resources/test-shared.js b/Tests/LibWeb/Text/input/wpt-import/css/css-logical/resources/test-shared.js new file mode 100644 index 00000000000..e744f25477a --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/css/css-logical/resources/test-shared.js @@ -0,0 +1,122 @@ +const sheet = document.head.appendChild(document.createElement("style")); + +// Specify size for outer
to avoid unconstrained-size warnings +// when writing-mode of the inner test
is vertical-* +const wrapper = document.body.appendChild(document.createElement("div")); +wrapper.style.cssText = "width:100px; height: 100px;"; +export const testElement = wrapper.appendChild(document.createElement("div")); +testElement.id = testElement.className = "test"; + +// Six unique overall writing modes for property-mapping purposes. +export const writingModes = [ + { + styles: [ + { "writing-mode": "horizontal-tb", "direction": "ltr" }, + ], + blockStart: "top", blockEnd: "bottom", inlineStart: "left", inlineEnd: "right", + over: "top", under: "bottom", lineLeft: "left", lineRight: "right", + block: "vertical", inline: "horizontal" + }, + { + styles: [ + { "writing-mode": "horizontal-tb", "direction": "rtl" }, + ], + blockStart: "top", blockEnd: "bottom", inlineStart: "right", inlineEnd: "left", + over: "top", under: "bottom", lineLeft: "left", lineRight: "right", + block: "vertical", inline: "horizontal" + }, + { + styles: [ + { "writing-mode": "vertical-rl", "direction": "rtl" }, + { "writing-mode": "sideways-rl", "direction": "rtl" }, + ], + blockStart: "right", blockEnd: "left", inlineStart: "bottom", inlineEnd: "top", + over: "right", under: "left", lineLeft: "top", lineRight: "bottom", + block: "horizontal", inline: "vertical" + }, + { + styles: [ + { "writing-mode": "vertical-rl", "direction": "ltr" }, + { "writing-mode": "sideways-rl", "direction": "ltr" }, + ], + blockStart: "right", blockEnd: "left", inlineStart: "top", inlineEnd: "bottom", + over: "right", under: "left", lineLeft: "top", lineRight: "bottom", + block: "horizontal", inline: "vertical" + }, + { + styles: [ + { "writing-mode": "vertical-lr", "direction": "rtl" }, + ], + blockStart: "left", blockEnd: "right", inlineStart: "bottom", inlineEnd: "top", + over: "right", under: "left", lineLeft: "top", lineRight: "bottom", + block: "horizontal", inline: "vertical" + }, + { + styles: [ + { "writing-mode": "sideways-lr", "direction": "ltr" }, + ], + blockStart: "left", blockEnd: "right", inlineStart: "bottom", inlineEnd: "top", + over: "left", under: "right", lineLeft: "bottom", lineRight: "top", + block: "horizontal", inline: "vertical" + }, + { + styles: [ + { "writing-mode": "vertical-lr", "direction": "ltr" }, + ], + blockStart: "left", blockEnd: "right", inlineStart: "top", inlineEnd: "bottom", + over: "right", under: "left", lineLeft: "top", lineRight: "bottom", + block: "horizontal", inline: "vertical" + }, + { + styles: [ + { "writing-mode": "sideways-lr", "direction": "rtl" }, + ], + blockStart: "left", blockEnd: "right", inlineStart: "top", inlineEnd: "bottom", + over: "left", under: "right", lineLeft: "bottom", lineRight: "top", + block: "horizontal", inline: "vertical" + }, +]; + +// Check if logical properties work well in WebKit non-standard +// '-webkit-writing-mode: horizontal-bt' mode +if (CSS.supports("-webkit-writing-mode", "horizontal-bt")) { + writingModes.push( + { + styles: [ + { "-webkit-writing-mode": "horizontal-bt", "direction": "ltr" }, + ], + blockStart: "bottom", blockEnd: "top", inlineStart: "left", inlineEnd: "right", + over: "top", under: "bottom", lineLeft: "left", lineRight: "right", + block: "vertical", inline: "horizontal" + }, + { + styles: [ + { "-webkit-writing-mode": "horizontal-bt", "direction": "rtl" }, + ], + blockStart: "bottom", blockEnd: "top", inlineStart: "right", inlineEnd: "left", + over: "top", under: "bottom", lineLeft: "left", lineRight: "right", + block: "vertical", inline: "horizontal" + }, + ) +} + +export function testCSSValues(testName, style, expectedValues) { + for (const [property, value] of expectedValues) { + assert_equals(style.getPropertyValue(property), value, `${testName}, ${property}`); + } +} + +export function testComputedValues(testName, rules, expectedValues) { + sheet.textContent = rules; + const cs = getComputedStyle(testElement); + testCSSValues(testName, cs, expectedValues); + sheet.textContent = ""; +} + +export function makeDeclaration(object = {}, replacement = "*") { + let decl = ""; + for (const [property, value] of Object.entries(object)) { + decl += `${property.replace("*", replacement)}: ${value}; `; + } + return decl; +}