mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-24 09:52:31 +00:00
LibWeb: Map logical aliases at cascade time
Previously we would incorrectly map these in `CSSStyleProperties::convert_declarations_to_specified_order`, aside from being too early (as it meant we didn't maintain them as distinct from their physical counterparts in CSSStyleProperties), this meant that we didn't yet have the required context to map them correctly. We now map them as part of the cascade process. To compute the mapping context we do a cascade without mapping, and extract the relevant properties (writing-direction and direction).
This commit is contained in:
parent
4e87f85458
commit
cfc8d3031b
Notes:
github-actions[bot]
2025-06-23 14:20:40 +00:00
Author: https://github.com/Calme1709
Commit: cfc8d3031b
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/5138
Reviewed-by: https://github.com/AtkinsSJ ✅
14 changed files with 848 additions and 141 deletions
|
@ -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)` |
|
| `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<CSSStyleValue const> property_initial_value(PropertyID)` |
|
| `initial` | Yes | | String. The property's initial value if it is not specified. | `NonnullRefPtr<CSSStyleValue const> property_initial_value(PropertyID)` |
|
||||||
| `legacy-alias-for` | No | Nothing | String. The name of a property this is an alias for. See below. | |
|
| `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<PropertyID> longhands_for_shorthand(PropertyID)`<br/>`Vector<PropertyID> expanded_longhands_for_shorthand(PropertyID)`<br/>`Vector<PropertyID> shorthands_for_longhand(PropertyID)`|
|
| `longhands` | No | `[]` | Array of strings. If this is a shorthand, these are the property names that it expands out into. | `Vector<PropertyID> longhands_for_shorthand(PropertyID)`<br/>`Vector<PropertyID> expanded_longhands_for_shorthand(PropertyID)`<br/>`Vector<PropertyID> 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)` |
|
| `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<ValueType> property_resolves_percentages_relative_to(PropertyID)` |
|
| `percentages-resolve-to` | No | Nothing | String. What type percentages get resolved to. eg, for `width` percentages are resolved to `length` values. | `Optional<ValueType> property_resolves_percentages_relative_to(PropertyID)` |
|
||||||
|
|
|
@ -627,71 +627,6 @@ void StyleComputer::for_each_property_expanding_shorthands(PropertyID property_i
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto map_logical_property_to_real_property = [](PropertyID property_id) -> Optional<PropertyID> {
|
|
||||||
// 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()) {
|
if (value.is_shorthand()) {
|
||||||
auto& shorthand_value = value.as_shorthand();
|
auto& shorthand_value = value.as_shorthand();
|
||||||
auto& properties = shorthand_value.sub_properties();
|
auto& properties = shorthand_value.sub_properties();
|
||||||
|
@ -902,46 +837,6 @@ void StyleComputer::for_each_property_expanding_shorthands(PropertyID property_i
|
||||||
return;
|
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 (property_id == CSS::PropertyID::Transition) {
|
||||||
if (value.to_keyword() == Keyword::None) {
|
if (value.to_keyword() == Keyword::None) {
|
||||||
// Handle `none` as a shorthand for `all 0s ease 0s`.
|
// 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);
|
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(
|
void StyleComputer::cascade_declarations(
|
||||||
CascadedProperties& cascaded_properties,
|
CascadedProperties& cascaded_properties,
|
||||||
DOM::Element& element,
|
DOM::Element& element,
|
||||||
|
@ -1000,7 +965,8 @@ void StyleComputer::cascade_declarations(
|
||||||
Vector<MatchingRule const*> const& matching_rules,
|
Vector<MatchingRule const*> const& matching_rules,
|
||||||
CascadeOrigin cascade_origin,
|
CascadeOrigin cascade_origin,
|
||||||
Important important,
|
Important important,
|
||||||
Optional<FlyString> layer_name) const
|
Optional<FlyString> layer_name,
|
||||||
|
Optional<LogicalAliasMappingContext> logical_alias_mapping_context) const
|
||||||
{
|
{
|
||||||
auto seen_properties = MUST(Bitmap::create(to_underlying(last_property_id) + 1, false));
|
auto seen_properties = MUST(Bitmap::create(to_underlying(last_property_id) + 1, false));
|
||||||
auto cascade_style_declaration = [&](CSSStyleProperties const& declaration) {
|
auto cascade_style_declaration = [&](CSSStyleProperties const& declaration) {
|
||||||
|
@ -1044,12 +1010,22 @@ void StyleComputer::cascade_declarations(
|
||||||
return;
|
return;
|
||||||
seen_properties.set(to_underlying(longhand_id), true);
|
seen_properties.set(to_underlying(longhand_id), true);
|
||||||
|
|
||||||
if (longhand_value.is_revert()) {
|
PropertyID physical_property_id;
|
||||||
cascaded_properties.revert_property(longhand_id, important, cascade_origin);
|
|
||||||
} else if (longhand_value.is_revert_layer()) {
|
if (property_is_logical_alias(longhand_id)) {
|
||||||
cascaded_properties.revert_layer_property(longhand_id, important, layer_name);
|
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 {
|
} 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<CSS::
|
||||||
HashMap<PropertyID, PropertyID> longhands_set_by_property_id;
|
HashMap<PropertyID, PropertyID> 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_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
|
// https://drafts.csswg.org/web-animations-1/#ref-for-computed-keyframes
|
||||||
auto is_property_preferred = [&](PropertyID a, PropertyID b) {
|
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:
|
// 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, Optional<CSS::
|
||||||
return number_of_expanded_shorthands_a < number_of_expanded_shorthands_b;
|
return number_of_expanded_shorthands_a < number_of_expanded_shorthands_b;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: 3. Physical properties override logical properties.
|
auto property_a_is_logical_alias = property_is_logical_alias_including_shorthands(a);
|
||||||
|
auto property_b_is_logical_alias = property_is_logical_alias_including_shorthands(b);
|
||||||
|
|
||||||
|
// 3. Physical properties override logical properties.
|
||||||
|
if (property_a_is_logical_alias != property_b_is_logical_alias)
|
||||||
|
return !property_a_is_logical_alias;
|
||||||
|
|
||||||
// 4. For shorthand properties with an equal number of longhand components, properties whose IDL name (see
|
// 4. For shorthand properties with an equal number of longhand components, properties whose IDL name (see
|
||||||
// the CSS property to IDL attribute algorithm [CSSOM]) appears earlier when sorted in ascending order
|
// the CSS property to IDL attribute algorithm [CSSOM]) appears earlier when sorted in ascending order
|
||||||
|
@ -1208,19 +1197,20 @@ void StyleComputer::collect_animation_into(DOM::Element& element, Optional<CSS::
|
||||||
style_value = Parser::Parser::resolve_unresolved_style_value(Parser::ParsingParams { element.document() }, element, pseudo_element, property_id, style_value->as_unresolved());
|
style_value = Parser::Parser::resolve_unresolved_style_value(Parser::ParsingParams { element.document() }, element, pseudo_element, property_id, style_value->as_unresolved());
|
||||||
|
|
||||||
for_each_property_expanding_shorthands(property_id, *style_value, [&](PropertyID longhand_id, CSSStyleValue const& longhand_value) {
|
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
|
// 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;
|
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
|
// 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;
|
return;
|
||||||
|
|
||||||
longhands_set_by_property_id.set(longhand_id, property_id);
|
longhands_set_by_property_id.set(physical_longhand_id, property_id);
|
||||||
property_is_set_by_use_initial.set(longhand_id_bitmap_index, is_use_initial);
|
property_is_set_by_use_initial.set(physical_longhand_id_bitmap_index, is_use_initial);
|
||||||
result.set(longhand_id, { longhand_value });
|
result.set(physical_longhand_id, { longhand_value });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -1632,7 +1622,7 @@ void StyleComputer::start_needed_transitions(ComputedProperties const& previous_
|
||||||
|
|
||||||
// https://www.w3.org/TR/css-cascade/#cascading
|
// https://www.w3.org/TR/css-cascade/#cascading
|
||||||
// https://drafts.csswg.org/css-cascade-5/#layering
|
// https://drafts.csswg.org/css-cascade-5/#layering
|
||||||
GC::Ref<CascadedProperties> StyleComputer::compute_cascaded_values(DOM::Element& element, Optional<CSS::PseudoElement> pseudo_element, bool& did_match_any_pseudo_element_rules, PseudoClassBitmap& attempted_pseudo_class_matches, ComputeStyleMode mode) const
|
GC::Ref<CascadedProperties> StyleComputer::compute_cascaded_values(DOM::Element& element, Optional<CSS::PseudoElement> pseudo_element, bool& did_match_any_pseudo_element_rules, PseudoClassBitmap& attempted_pseudo_class_matches, ComputeStyleMode mode, Optional<LogicalAliasMappingContext> logical_alias_mapping_context) const
|
||||||
{
|
{
|
||||||
auto cascaded_properties = m_document->heap().allocate<CascadedProperties>();
|
auto cascaded_properties = m_document->heap().allocate<CascadedProperties>();
|
||||||
|
|
||||||
|
@ -1676,10 +1666,10 @@ GC::Ref<CascadedProperties> StyleComputer::compute_cascaded_values(DOM::Element&
|
||||||
// Then we apply the declarations from the matched rules in cascade order:
|
// Then we apply the declarations from the matched rules in cascade order:
|
||||||
|
|
||||||
// Normal user agent declarations
|
// 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
|
// 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
|
// Author presentational hints
|
||||||
// The spec calls this a special "Author presentational hint origin":
|
// The spec calls this a special "Author presentational hint origin":
|
||||||
|
@ -1702,23 +1692,23 @@ GC::Ref<CascadedProperties> StyleComputer::compute_cascaded_values(DOM::Element&
|
||||||
|
|
||||||
// Normal author declarations, ordered by @layer, with un-@layer-ed rules last
|
// Normal author declarations, ordered by @layer, with un-@layer-ed rules last
|
||||||
for (auto const& layer : matching_rule_set.author_rules) {
|
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.
|
// 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()) {
|
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
|
// 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
|
// 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]
|
// Transition declarations [css-transitions-1]
|
||||||
// Note that we have to do these after finishing computing the style,
|
// 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;
|
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<CSS::PseudoElement> 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
|
Gfx::Font const& StyleComputer::initial_font() const
|
||||||
{
|
{
|
||||||
// FIXME: This is not correct.
|
// FIXME: This is not correct.
|
||||||
|
@ -2493,8 +2515,8 @@ GC::Ptr<ComputedProperties> StyleComputer::compute_style_impl(DOM::Element& elem
|
||||||
// 1. Perform the cascade. This produces the "specified style"
|
// 1. Perform the cascade. This produces the "specified style"
|
||||||
bool did_match_any_pseudo_element_rules = false;
|
bool did_match_any_pseudo_element_rules = false;
|
||||||
PseudoClassBitmap attempted_pseudo_class_matches;
|
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);
|
element.set_cascaded_properties(pseudo_element, cascaded_properties);
|
||||||
|
|
||||||
if (mode == ComputeStyleMode::CreatePseudoElementStyleIfNeeded) {
|
if (mode == ComputeStyleMode::CreatePseudoElementStyleIfNeeded) {
|
||||||
|
@ -2631,6 +2653,7 @@ GC::Ref<ComputedProperties> StyleComputer::compute_properties(DOM::Element& elem
|
||||||
if (property_id == PropertyID::FontSize && !value && new_font_size)
|
if (property_id == PropertyID::FontSize && !value && new_font_size)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
// FIXME: Logical properties should inherit from their parent's equivalent unmapped logical property.
|
||||||
if ((!value && is_inherited_property(property_id))
|
if ((!value && is_inherited_property(property_id))
|
||||||
|| (value && value->is_inherit())) {
|
|| (value && value->is_inherit())) {
|
||||||
if (auto inheritance_parent = element_to_inherit_style_from(&element, pseudo_element)) {
|
if (auto inheritance_parent = element_to_inherit_style_from(&element, pseudo_element)) {
|
||||||
|
|
|
@ -131,6 +131,13 @@ public:
|
||||||
static void for_each_property_expanding_shorthands(PropertyID, CSSStyleValue const&, Function<void(PropertyID, CSSStyleValue const&)> const& set_longhand_property);
|
static void for_each_property_expanding_shorthands(PropertyID, CSSStyleValue const&, Function<void(PropertyID, CSSStyleValue const&)> const& set_longhand_property);
|
||||||
static NonnullRefPtr<CSSStyleValue const> get_inherit_value(CSS::PropertyID, DOM::Element const*, Optional<CSS::PseudoElement> = {});
|
static NonnullRefPtr<CSSStyleValue const> get_inherit_value(CSS::PropertyID, DOM::Element const*, Optional<CSS::PseudoElement> = {});
|
||||||
|
|
||||||
|
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<String> user_agent_style_sheet_source(StringView name);
|
static Optional<String> user_agent_style_sheet_source(StringView name);
|
||||||
|
|
||||||
explicit StyleComputer(DOM::Document&);
|
explicit StyleComputer(DOM::Document&);
|
||||||
|
@ -201,8 +208,9 @@ private:
|
||||||
|
|
||||||
struct MatchingFontCandidate;
|
struct MatchingFontCandidate;
|
||||||
|
|
||||||
|
LogicalAliasMappingContext compute_logical_alias_mapping_context(DOM::Element&, Optional<CSS::PseudoElement>, ComputeStyleMode) const;
|
||||||
[[nodiscard]] GC::Ptr<ComputedProperties> compute_style_impl(DOM::Element&, Optional<CSS::PseudoElement>, ComputeStyleMode) const;
|
[[nodiscard]] GC::Ptr<ComputedProperties> compute_style_impl(DOM::Element&, Optional<CSS::PseudoElement>, ComputeStyleMode) const;
|
||||||
[[nodiscard]] GC::Ref<CascadedProperties> compute_cascaded_values(DOM::Element&, Optional<CSS::PseudoElement>, bool& did_match_any_pseudo_element_rules, PseudoClassBitmap& attempted_pseudo_class_matches, ComputeStyleMode) const;
|
[[nodiscard]] GC::Ref<CascadedProperties> compute_cascaded_values(DOM::Element&, Optional<CSS::PseudoElement>, bool& did_match_any_pseudo_element_rules, PseudoClassBitmap& attempted_pseudo_class_matches, ComputeStyleMode, Optional<LogicalAliasMappingContext>) const;
|
||||||
static RefPtr<Gfx::FontCascadeList const> find_matching_font_weight_ascending(Vector<MatchingFontCandidate> const& candidates, int target_weight, float font_size_in_pt, bool inclusive);
|
static RefPtr<Gfx::FontCascadeList const> find_matching_font_weight_ascending(Vector<MatchingFontCandidate> const& candidates, int target_weight, float font_size_in_pt, bool inclusive);
|
||||||
static RefPtr<Gfx::FontCascadeList const> find_matching_font_weight_descending(Vector<MatchingFontCandidate> const& candidates, int target_weight, float font_size_in_pt, bool inclusive);
|
static RefPtr<Gfx::FontCascadeList const> find_matching_font_weight_descending(Vector<MatchingFontCandidate> const& candidates, int target_weight, float font_size_in_pt, bool inclusive);
|
||||||
RefPtr<Gfx::FontCascadeList const> font_matching_algorithm(FlyString const& family_name, int weight, int slope, float font_size_in_pt) const;
|
RefPtr<Gfx::FontCascadeList const> font_matching_algorithm(FlyString const& family_name, int weight, int slope, float font_size_in_pt) const;
|
||||||
|
@ -242,7 +250,8 @@ private:
|
||||||
Vector<MatchingRule const*> const&,
|
Vector<MatchingRule const*> const&,
|
||||||
CascadeOrigin,
|
CascadeOrigin,
|
||||||
Important,
|
Important,
|
||||||
Optional<FlyString> layer_name) const;
|
Optional<FlyString> layer_name,
|
||||||
|
Optional<LogicalAliasMappingContext>) const;
|
||||||
|
|
||||||
void build_rule_cache();
|
void build_rule_cache();
|
||||||
void build_rule_cache_if_needed() const;
|
void build_rule_cache_if_needed() const;
|
||||||
|
|
|
@ -132,7 +132,7 @@ void populate_all_property_longhands(JsonObject& properties)
|
||||||
VERIFY(all_entry.has_value());
|
VERIFY(all_entry.has_value());
|
||||||
|
|
||||||
properties.for_each_member([&](auto name, auto 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;
|
return;
|
||||||
|
|
||||||
MUST(all_entry->get_array("longhands"sv)->append(JsonValue { name }));
|
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_has_quirk(PropertyID, Quirk);
|
||||||
|
|
||||||
|
bool property_is_logical_alias(PropertyID);
|
||||||
|
|
||||||
} // namespace Web::CSS
|
} // namespace Web::CSS
|
||||||
|
|
||||||
namespace AK {
|
namespace AK {
|
||||||
|
@ -1275,6 +1277,33 @@ Vector<PropertyID> shorthands_for_longhand(PropertyID property_id)
|
||||||
return { };
|
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"~~~(
|
generator.append(R"~~~(
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
Harness status: OK
|
Harness status: OK
|
||||||
|
|
||||||
Found 206 tests
|
Found 236 tests
|
||||||
|
|
||||||
202 Pass
|
218 Pass
|
||||||
4 Fail
|
18 Fail
|
||||||
Pass accent-color
|
Pass accent-color
|
||||||
Pass border-collapse
|
Pass border-collapse
|
||||||
Pass border-spacing
|
Pass border-spacing
|
||||||
|
@ -90,11 +90,24 @@ Pass background-position-x
|
||||||
Pass background-position-y
|
Pass background-position-y
|
||||||
Pass background-repeat
|
Pass background-repeat
|
||||||
Pass background-size
|
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-color
|
||||||
Pass border-bottom-left-radius
|
Pass border-bottom-left-radius
|
||||||
Pass border-bottom-right-radius
|
Pass border-bottom-right-radius
|
||||||
Pass border-bottom-style
|
Pass border-bottom-style
|
||||||
Pass border-bottom-width
|
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-color
|
||||||
Pass border-left-style
|
Pass border-left-style
|
||||||
Pass border-left-width
|
Pass border-left-width
|
||||||
|
@ -142,20 +155,33 @@ Pass grid-template-areas
|
||||||
Pass grid-template-columns
|
Pass grid-template-columns
|
||||||
Pass grid-template-rows
|
Pass grid-template-rows
|
||||||
Fail height
|
Fail height
|
||||||
|
Fail inline-size
|
||||||
|
Pass inset-block-end
|
||||||
|
Pass inset-block-start
|
||||||
|
Pass inset-inline-end
|
||||||
|
Pass inset-inline-start
|
||||||
Pass isolation
|
Pass isolation
|
||||||
Pass justify-content
|
Pass justify-content
|
||||||
Pass justify-items
|
Pass justify-items
|
||||||
Pass justify-self
|
Pass justify-self
|
||||||
Pass left
|
Pass left
|
||||||
|
Pass margin-block-end
|
||||||
|
Pass margin-block-start
|
||||||
Pass margin-bottom
|
Pass margin-bottom
|
||||||
|
Pass margin-inline-end
|
||||||
|
Pass margin-inline-start
|
||||||
Pass margin-left
|
Pass margin-left
|
||||||
Pass margin-right
|
Pass margin-right
|
||||||
Pass margin-top
|
Pass margin-top
|
||||||
Pass mask-image
|
Pass mask-image
|
||||||
Pass mask-type
|
Pass mask-type
|
||||||
|
Fail max-block-size
|
||||||
Pass max-height
|
Pass max-height
|
||||||
|
Fail max-inline-size
|
||||||
Pass max-width
|
Pass max-width
|
||||||
|
Fail min-block-size
|
||||||
Pass min-height
|
Pass min-height
|
||||||
|
Fail min-inline-size
|
||||||
Pass min-width
|
Pass min-width
|
||||||
Pass mix-blend-mode
|
Pass mix-blend-mode
|
||||||
Pass object-fit
|
Pass object-fit
|
||||||
|
@ -168,7 +194,11 @@ Pass outline-style
|
||||||
Pass outline-width
|
Pass outline-width
|
||||||
Pass overflow-x
|
Pass overflow-x
|
||||||
Pass overflow-y
|
Pass overflow-y
|
||||||
|
Pass padding-block-end
|
||||||
|
Pass padding-block-start
|
||||||
Pass padding-bottom
|
Pass padding-bottom
|
||||||
|
Pass padding-inline-end
|
||||||
|
Pass padding-inline-start
|
||||||
Pass padding-left
|
Pass padding-left
|
||||||
Pass padding-right
|
Pass padding-right
|
||||||
Pass padding-top
|
Pass padding-top
|
||||||
|
|
|
@ -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; '.
|
|
@ -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; '.
|
|
@ -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; '.
|
|
@ -2,13 +2,12 @@ Harness status: OK
|
||||||
|
|
||||||
Found 11 tests
|
Found 11 tests
|
||||||
|
|
||||||
7 Pass
|
11 Pass
|
||||||
4 Fail
|
Pass All properties can serialize 'initial'
|
||||||
Fail All properties can serialize 'initial'
|
|
||||||
Pass All properties (except 'all') can serialize their initial value (computed)
|
Pass All properties (except 'all') can serialize their initial value (computed)
|
||||||
Fail All properties (except 'all') can serialize their initial value (specified)
|
Pass All properties (except 'all') can serialize their initial value (specified)
|
||||||
Fail All shorthands can serialize their longhands set to 'initial'
|
Pass 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 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 'initial'
|
||||||
Pass All aliases can serialize target property set to its initial value
|
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
|
Pass Can't serialize shorthand when longhands are set to different css-wide keywords
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>CSS Logical Properties: Flow-Relative Offsets</title>
|
||||||
|
<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com" />
|
||||||
|
<link rel="help" href="https://drafts.csswg.org/css-logical/#inset-properties">
|
||||||
|
<meta name="assert" content="This test checks the interaction of the flow-relative inset-* properties with the physical ones in different writing modes." />
|
||||||
|
<script src="../../resources/testharness.js"></script>
|
||||||
|
<script src="../../resources/testharnessreport.js"></script>
|
||||||
|
|
||||||
|
<div id="log"></div>
|
||||||
|
|
||||||
|
<script type="module">
|
||||||
|
import {runTests, createBoxPropertyGroup} from "./resources/test-box-properties.js";
|
||||||
|
runTests(createBoxPropertyGroup("inset-*", {
|
||||||
|
type: "length",
|
||||||
|
prerequisites: {"position": "relative"},
|
||||||
|
}));
|
||||||
|
</script>
|
|
@ -0,0 +1,15 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>CSS Logical Properties: Flow-Relative Margins</title>
|
||||||
|
<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com" />
|
||||||
|
<link rel="help" href="https://drafts.csswg.org/css-logical/#margin-properties">
|
||||||
|
<meta name="assert" content="This test checks the interaction of the flow-relative margin-* properties with the physical ones in different writing modes." />
|
||||||
|
<script src="../../resources/testharness.js"></script>
|
||||||
|
<script src="../../resources/testharnessreport.js"></script>
|
||||||
|
|
||||||
|
<div id="log"></div>
|
||||||
|
|
||||||
|
<script type="module">
|
||||||
|
import {runTests, createBoxPropertyGroup} from "./resources/test-box-properties.js";
|
||||||
|
runTests(createBoxPropertyGroup("margin-*", {type: "length"}));
|
||||||
|
</script>
|
|
@ -0,0 +1,15 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>CSS Logical Properties: Flow-Relative Padding</title>
|
||||||
|
<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com" />
|
||||||
|
<link rel="help" href="https://drafts.csswg.org/css-logical/#padding-properties">
|
||||||
|
<meta name="assert" content="This test checks the interaction of the flow-relative padding-* properties with the physical ones in different writing modes." />
|
||||||
|
<script src="../../resources/testharness.js"></script>
|
||||||
|
<script src="../../resources/testharnessreport.js"></script>
|
||||||
|
|
||||||
|
<div id="log"></div>
|
||||||
|
|
||||||
|
<script type="module">
|
||||||
|
import {runTests, createBoxPropertyGroup} from "./resources/test-box-properties.js";
|
||||||
|
runTests(createBoxPropertyGroup("padding-*", {type: "length"}));
|
||||||
|
</script>
|
|
@ -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}'.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,122 @@
|
||||||
|
const sheet = document.head.appendChild(document.createElement("style"));
|
||||||
|
|
||||||
|
// Specify size for outer <div> to avoid unconstrained-size warnings
|
||||||
|
// when writing-mode of the inner test <div> 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;
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue