LibWeb: Parse grid track placements closer to spec

This brings parsing of grid-row-* and grid-column-* properties (and
their associated shorthands) more inline with spec.

Changes:
- Only set omitted properties for combined-value shorthands (e.g.
  `grid-row: a` rather than `grid-row: a / b`) if the single value is
  `<custom-ident>`.

- `[ [ <integer [-∞,-1]> | <integer [1,∞]> ] && <custom-ident>? ]`:
  - Properly resolve `calc`s for `<integer>` that rely on compute-time
    information.

- `[ span && [ <integer [1,∞]> || <custom-ident> ] ]`
  - Allow `calc`s for `<integer>`
  - Allow `<custom-ident>`

There is still work to be done to properly use these parsed values.

Gains us 46 WPT tests.
This commit is contained in:
Callum Law 2025-07-08 14:31:40 +12:00 committed by Tim Ledbetter
parent a424a06d45
commit 36e2d25efa
Notes: github-actions[bot] 2025-07-08 16:27:31 +00:00
12 changed files with 153 additions and 171 deletions

View file

@ -19,15 +19,21 @@ String GridTrackPlacement::to_string() const
},
[&](AreaOrLine const& area_or_line) {
if (area_or_line.line_number.has_value() && area_or_line.name.has_value()) {
builder.appendff("{} {}", *area_or_line.line_number, *area_or_line.name);
builder.appendff("{} {}", area_or_line.line_number->to_string(), *area_or_line.name);
} else if (area_or_line.line_number.has_value()) {
builder.appendff("{}", *area_or_line.line_number);
builder.appendff("{}", area_or_line.line_number->to_string());
} else if (area_or_line.name.has_value()) {
builder.appendff("{}", *area_or_line.name);
}
},
[&](Span const& span) {
builder.appendff("span {}", span.value);
builder.append("span"sv);
if (!span.name.has_value() || span.value.is_calculated() || span.value.value() != 1)
builder.appendff(" {}", span.value.to_string());
if (span.name.has_value())
builder.appendff(" {}", span.name.value());
});
return MUST(builder.to_string());
}

View file

@ -8,6 +8,7 @@
#pragma once
#include <AK/String.h>
#include <LibWeb/CSS/CalculatedOr.h>
namespace Web::CSS {
@ -18,14 +19,14 @@ public:
return GridTrackPlacement();
}
static GridTrackPlacement make_line(Optional<int> line_number, Optional<String> name)
static GridTrackPlacement make_line(Optional<IntegerOrCalculated> line_number, Optional<String> name)
{
return GridTrackPlacement(AreaOrLine { .line_number = line_number, .name = name });
}
static GridTrackPlacement make_span(int value)
static GridTrackPlacement make_span(IntegerOrCalculated value, Optional<String> name)
{
return GridTrackPlacement(Span { .value = value });
return GridTrackPlacement(Span { .value = value, .name = name });
}
bool is_auto() const { return m_value.has<Auto>(); }
@ -35,6 +36,8 @@ public:
bool is_auto_positioned() const { return is_auto() || is_span(); }
bool is_positioned() const { return !is_auto_positioned(); }
bool is_custom_ident() const { return is_area_or_line() && !m_value.get<AreaOrLine>().line_number.has_value(); }
bool has_identifier() const
{
return is_area_or_line() && m_value.get<AreaOrLine>().name.has_value();
@ -47,8 +50,8 @@ public:
String identifier() const { return *m_value.get<AreaOrLine>().name; }
int line_number() const { return *m_value.get<AreaOrLine>().line_number; }
int span() const { return m_value.get<Span>().value; }
IntegerOrCalculated line_number() const { return *m_value.get<AreaOrLine>().line_number; }
IntegerOrCalculated span() const { return m_value.get<Span>().value; }
String to_string() const;
@ -60,13 +63,14 @@ private:
};
struct AreaOrLine {
Optional<int> line_number;
Optional<IntegerOrCalculated> line_number;
Optional<String> name;
bool operator==(AreaOrLine const& other) const = default;
};
struct Span {
int value;
IntegerOrCalculated value;
Optional<String> name;
bool operator==(Span const& other) const = default;
};

View file

@ -4528,7 +4528,7 @@ RefPtr<CSSStyleValue const> Parser::parse_grid_track_placement_shorthand_value(P
auto parsed_start_value = parse_grid_track_placement(track_start_placement_token_stream);
if (parsed_start_value && track_end_placement_tokens.is_empty()) {
transaction.commit();
if (parsed_start_value->grid_track_placement().has_identifier()) {
if (parsed_start_value->grid_track_placement().is_custom_ident()) {
auto custom_ident = parsed_start_value.release_nonnull();
return ShorthandStyleValue::create(property_id, { start_property, end_property }, { custom_ident, custom_ident });
}
@ -4721,21 +4721,21 @@ RefPtr<CSSStyleValue const> Parser::parse_grid_area_shorthand_value(TokenStream<
// that value. Otherwise, it is set to auto.
if (column_start_style_value)
column_start = column_start_style_value.release_nonnull()->as_grid_track_placement().grid_track_placement();
else
else if (row_start.is_custom_ident())
column_start = row_start;
// When grid-row-end is omitted, if grid-row-start is a <custom-ident>, grid-row-end is set to that
// <custom-ident>; otherwise, it is set to auto.
if (row_end_style_value)
row_end = row_end_style_value.release_nonnull()->as_grid_track_placement().grid_track_placement();
else
else if (row_start.is_custom_ident())
row_end = row_start;
// When grid-column-end is omitted, if grid-column-start is a <custom-ident>, grid-column-end is set to
// that <custom-ident>; otherwise, it is set to auto.
if (column_end_style_value)
column_end = column_end_style_value.release_nonnull()->as_grid_track_placement().grid_track_placement();
else
else if (column_start.is_custom_ident())
column_end = column_start;
transaction.commit();

View file

@ -3647,108 +3647,72 @@ GridTrackSizeList Parser::parse_explicit_track_list(TokenStream<ComponentValue>&
RefPtr<GridTrackPlacementStyleValue const> Parser::parse_grid_track_placement(TokenStream<ComponentValue>& tokens)
{
// FIXME: This shouldn't be needed. Right now, the below code returns a CSSStyleValue even if no tokens are consumed!
if (!tokens.has_next_token())
return nullptr;
if (tokens.remaining_token_count() > 3)
return nullptr;
// https://www.w3.org/TR/css-grid-2/#line-placement
// Line-based Placement: the grid-row-start, grid-column-start, grid-row-end, and grid-column-end properties
// <grid-line> =
// auto |
// <custom-ident> |
// [ <integer> && <custom-ident>? ] |
// [ span && [ <integer> || <custom-ident> ] ]
auto is_valid_integer = [](auto& token) -> bool {
// An <integer> value of zero makes the declaration invalid.
if (token.is(Token::Type::Number) && token.token().number().is_integer() && token.token().number_value() != 0)
return true;
return false;
};
auto parse_custom_ident = [this](auto& tokens) {
// The <custom-ident> additionally excludes the keywords span and auto.
return parse_custom_ident_value(tokens, { { "span"sv, "auto"sv } });
};
// [ [ <integer [-∞,-1]> | <integer [1,∞]> ] && <custom-ident>? ] |
// [ span && [ <integer [1,∞]> || <custom-ident> ] ]
bool is_span = false;
Optional<String> parsed_custom_ident;
Optional<IntegerOrCalculated> parsed_integer;
auto transaction = tokens.begin_transaction();
// FIXME: Handle the single-token case inside the loop instead, so that we can more easily call this from
// `parse_grid_area_shorthand_value()` using a single TokenStream.
if (tokens.remaining_token_count() == 1) {
if (auto custom_ident = parse_custom_ident(tokens)) {
transaction.commit();
return GridTrackPlacementStyleValue::create(GridTrackPlacement::make_line({}, custom_ident->custom_ident().to_string()));
}
auto const& token = tokens.consume_a_token();
if (auto maybe_calculated = parse_calculated_value(token)) {
if (maybe_calculated->is_number()) {
transaction.commit();
return GridTrackPlacementStyleValue::create(GridTrackPlacement::make_line(static_cast<int>(maybe_calculated->as_number().number()), {}));
}
if (maybe_calculated->is_calculated() && maybe_calculated->as_calculated().resolves_to_number()) {
transaction.commit();
return GridTrackPlacementStyleValue::create(GridTrackPlacement::make_line(static_cast<int>(maybe_calculated->as_calculated().resolve_integer({}).value()), {}));
}
}
if (token.is_ident("auto"sv)) {
if (tokens.remaining_token_count() == 1 && tokens.next_token().is_ident("auto"sv)) {
tokens.discard_a_token();
transaction.commit();
return GridTrackPlacementStyleValue::create(GridTrackPlacement::make_auto());
}
if (is_valid_integer(token)) {
transaction.commit();
return GridTrackPlacementStyleValue::create(GridTrackPlacement::make_line(static_cast<int>(token.token().number_value()), {}));
}
return nullptr;
}
auto span_value = false;
auto span_or_position_value = 0;
String identifier_value;
while (tokens.has_next_token()) {
auto const& token = tokens.next_token();
if (token.is_ident("auto"sv))
return nullptr;
if (token.is_ident("span"sv)) {
if (span_value)
return nullptr;
tokens.discard_a_token(); // span
if (tokens.has_next_token() && ((span_or_position_value != 0 && identifier_value.is_empty()) || (span_or_position_value == 0 && !identifier_value.is_empty())))
return nullptr;
span_value = true;
continue;
}
if (is_valid_integer(token)) {
if (span_or_position_value != 0)
return nullptr;
span_or_position_value = static_cast<int>(tokens.consume_a_token().token().to_integer());
continue;
}
if (auto custom_ident = parse_custom_ident(tokens)) {
if (!identifier_value.is_empty())
return nullptr;
identifier_value = custom_ident->custom_ident().to_string();
continue;
}
break;
}
if (tokens.has_next_token())
if (tokens.next_token().is_ident("span"sv)) {
if (is_span)
return nullptr;
// Negative integers or zero are invalid.
if (span_value && span_or_position_value < 1)
tokens.discard_a_token();
// NOTE: "span" must not appear in between <custom-ident> and <integer>
if (tokens.has_next_token() && (parsed_custom_ident.has_value() || parsed_integer.has_value()))
return nullptr;
// If the <integer> is omitted, it defaults to 1.
if (span_or_position_value == 0)
span_or_position_value = 1;
is_span = true;
continue;
}
if (auto maybe_parsed_custom_ident = parse_custom_ident(tokens, { { "auto"sv } }); maybe_parsed_custom_ident.has_value()) {
if (parsed_custom_ident.has_value())
return nullptr;
parsed_custom_ident = maybe_parsed_custom_ident->to_string();
continue;
}
if (auto maybe_parsed_integer = parse_integer(tokens); maybe_parsed_integer.has_value()) {
if (parsed_integer.has_value())
return nullptr;
parsed_integer = maybe_parsed_integer;
continue;
}
return nullptr;
}
transaction.commit();
if (!identifier_value.is_empty())
return GridTrackPlacementStyleValue::create(GridTrackPlacement::make_line(span_or_position_value, identifier_value));
return GridTrackPlacementStyleValue::create(GridTrackPlacement::make_span(span_or_position_value));
// <custom-ident>
// [ [ <integer [-∞,-1]> | <integer [1,∞]> ] && <custom-ident>? ]
if (!is_span && (parsed_integer.has_value() || parsed_custom_ident.has_value()) && (!parsed_integer.has_value() || parsed_integer.value().is_calculated() || parsed_integer.value().value() != 0))
return GridTrackPlacementStyleValue::create(GridTrackPlacement::make_line(parsed_integer, parsed_custom_ident));
// [ span && [ <integer [1,∞]> || <custom-ident> ] ]
if (is_span && (parsed_integer.has_value() || parsed_custom_ident.has_value()) && (!parsed_integer.has_value() || parsed_integer.value().is_calculated() || parsed_integer.value().value() > 0))
// If the <integer> is omitted, it defaults to 1.
return GridTrackPlacementStyleValue::create(GridTrackPlacement::make_span(parsed_integer.value_or(1), parsed_custom_ident));
return nullptr;
}
RefPtr<CSSStyleValue const> Parser::parse_calculated_value(ComponentValue const& component_value)

View file

@ -282,16 +282,21 @@ GridFormattingContext::PlacementPosition GridFormattingContext::resolve_grid_pos
auto const& placement_start = dimension == GridDimension::Row ? computed_values.grid_row_start() : computed_values.grid_column_start();
auto const& placement_end = dimension == GridDimension::Row ? computed_values.grid_row_end() : computed_values.grid_column_end();
CSS::CalculationResolutionContext resolution_context { .length_resolution_context = CSS::Length::ResolutionContext::for_layout_node(child_box) };
Optional<i64> placement_start_line_number = placement_start.has_line_number() ? placement_start.line_number().resolved(resolution_context) : Optional<i64> {};
Optional<i64> placement_end_line_number = placement_end.has_line_number() ? placement_end.line_number().resolved(resolution_context) : Optional<i64> {};
PlacementPosition result;
if (placement_start.has_line_number() && placement_start.line_number() > 0)
result.start = placement_start.line_number() - 1;
else if (placement_start.has_line_number()) {
if (placement_start_line_number.has_value() && placement_start_line_number.value() > 0)
result.start = placement_start_line_number.value() - 1;
else if (placement_start_line_number.has_value()) {
auto explicit_line_count = dimension == GridDimension::Row ? m_explicit_rows_line_count : m_explicit_columns_line_count;
result.start = explicit_line_count + placement_start.line_number();
result.start = explicit_line_count + placement_start_line_number.value();
}
if (placement_end.has_line_number())
result.end = placement_end.line_number() - 1;
if (placement_end_line_number.has_value())
result.end = *placement_end_line_number - 1;
if (result.end < 0) {
if (dimension == GridDimension::Row)
@ -300,10 +305,13 @@ GridFormattingContext::PlacementPosition GridFormattingContext::resolve_grid_pos
result.end = m_occupation_grid.column_count() + result.end + 2;
}
// FIXME: If a name is given as a <custom-ident>, only lines with that name are counted. If not enough lines with
// that name exist, all implicit grid lines on the side of the explicit grid corresponding to the search
// direction are assumed to have that name for the purpose of counting this span.
if (placement_end.is_span())
result.span = placement_end.span();
result.span = placement_end.span().resolved(resolution_context).value();
if (placement_start.is_span()) {
result.span = placement_start.span();
result.span = placement_start.span().resolved(resolution_context).value();
result.start = result.end - result.span;
// FIXME: Remove me once have implemented spans overflowing into negative indexes, e.g., grid-row: span 2 / 1
if (result.start < 0)
@ -312,7 +320,7 @@ GridFormattingContext::PlacementPosition GridFormattingContext::resolve_grid_pos
if (placement_end.has_identifier()) {
auto area_end_line_name = MUST(String::formatted("{}-end", placement_end.identifier()));
auto line_number = placement_end.has_line_number() ? placement_end.line_number() : 1;
auto line_number = placement_end_line_number.value_or(1);
if (auto area_end_line_index = get_nth_line_index_by_line_name(dimension, area_end_line_name, line_number); area_end_line_index.has_value()) {
result.end = area_end_line_index.value();
} else if (auto line_name_index = get_nth_line_index_by_line_name(dimension, placement_end.identifier(), line_number); line_name_index.has_value()) {
@ -325,7 +333,7 @@ GridFormattingContext::PlacementPosition GridFormattingContext::resolve_grid_pos
if (placement_start.has_identifier()) {
auto area_start_line_name = MUST(String::formatted("{}-start", placement_start.identifier()));
auto line_number = placement_start.has_line_number() ? placement_start.line_number() : 1;
auto line_number = placement_start_line_number.value_or(1);
if (auto area_start_line_index = get_nth_line_index_by_line_name(dimension, area_start_line_name, line_number); area_start_line_index.has_value()) {
result.start = area_start_line_index.value();
} else if (auto line_name_index = get_nth_line_index_by_line_name(dimension, placement_start.identifier(), line_number); line_name_index.has_value()) {
@ -357,7 +365,7 @@ GridFormattingContext::PlacementPosition GridFormattingContext::resolve_grid_pos
// If the placement contains two spans, remove the one contributed by the end grid-placement
// property.
if (placement_start.is_span() && placement_end.is_span())
result.span = placement_start.span();
result.span = placement_start.span().resolved(resolution_context).value();
return result;
}
@ -389,7 +397,7 @@ void GridFormattingContext::place_item_with_row_position(Box const& child_box)
auto const& grid_column_start = child_box.computed_values().grid_column_start();
int column_start = 0;
size_t column_span = grid_column_start.is_span() ? grid_column_start.span() : 1;
size_t column_span = grid_column_start.is_span() ? grid_column_start.span().resolved({ .length_resolution_context = CSS::Length::ResolutionContext::for_layout_node(child_box) }).value() : 1;
bool found_available_column = false;
for (size_t column_index = column_start; column_index < m_occupation_grid.column_count(); column_index++) {
@ -419,7 +427,7 @@ void GridFormattingContext::place_item_with_column_position(Box const& child_box
size_t column_span = placement_position.span;
auto const& grid_row_start = child_box.computed_values().grid_row_start();
size_t row_span = grid_row_start.is_span() ? grid_row_start.span() : 1;
size_t row_span = grid_row_start.is_span() ? grid_row_start.span().resolved({ .length_resolution_context = CSS::Length::ResolutionContext::for_layout_node(child_box) }).value() : 1;
// 4.1.1.1. Set the column position of the cursor to the grid item's column-start line. If this is
// less than the previous column position of the cursor, increment the row position by 1.
@ -484,18 +492,20 @@ void GridFormattingContext::place_item_with_no_declared_position(Box const& chil
auto const& grid_column_start = computed_values.grid_column_start();
auto const& grid_column_end = computed_values.grid_column_end();
CSS::CalculationResolutionContext resolution_context { .length_resolution_context = CSS::Length::ResolutionContext::for_layout_node(child_box) };
auto column_start = 0;
size_t column_span = 1;
if (grid_column_start.is_span())
column_span = grid_column_start.span();
column_span = grid_column_start.span().resolved(resolution_context).value();
else if (grid_column_end.is_span())
column_span = grid_column_end.span();
column_span = grid_column_end.span().resolved(resolution_context).value();
auto row_start = 0;
size_t row_span = 1;
if (grid_row_start.is_span())
row_span = grid_row_start.span();
row_span = grid_row_start.span().resolved(resolution_context).value();
else if (grid_row_end.is_span())
row_span = grid_row_end.span();
row_span = grid_row_end.span().resolved(resolution_context).value();
auto const& auto_flow = grid_container().computed_values().grid_auto_flow();
auto dimension = auto_flow.row ? GridDimension::Column : GridDimension::Row;
@ -1483,11 +1493,13 @@ void GridFormattingContext::place_grid_items()
auto const& grid_column_start = child_box->computed_values().grid_column_start();
auto const& grid_column_end = child_box->computed_values().grid_column_end();
CSS::CalculationResolutionContext resolution_context { .length_resolution_context = CSS::Length::ResolutionContext::for_layout_node(child_box) };
int column_span = 1;
if (grid_column_start.is_span())
column_span = grid_column_start.span();
column_span = grid_column_start.span().resolved(resolution_context).value();
else if (grid_column_end.is_span())
column_span = grid_column_end.span();
column_span = grid_column_end.span().resolved(resolution_context).value();
if (column_span - 1 > m_occupation_grid.max_column_index())
m_occupation_grid.set_max_column_index(column_span - 1);

View file

@ -2,8 +2,8 @@ Harness status: OK
Found 33 tests
18 Pass
15 Fail
24 Pass
9 Fail
Pass Property grid-area value 'auto / auto / auto / auto'
Pass Property grid-row value 'auto / auto'
Pass Property grid-column-end value 'auto'
@ -22,18 +22,18 @@ Pass Property grid-column-end value '5 π_'
Fail Property grid-row-start value 'calc(1.1) -a-'
Fail Property grid-row-start value 'calc(10) -a-'
Fail Property grid-row-start value 'calc(10 + (sign(2cqw - 10px) * 5)) -a-'
Fail Property grid-area value 'span 2 i / auto / auto / auto'
Pass Property grid-area value 'span 2 i / auto / auto / auto'
Pass Property grid-row value 'span 2 / auto'
Fail Property grid-column-start value 'span 1 i'
Pass Property grid-column-start value 'span 1 i'
Pass Property grid-row-start value 'span 1'
Fail Property grid-row-end value 'span 2 i'
Pass Property grid-row-end value 'span 2 i'
Pass Property grid-column-end value 'span 2'
Fail Property grid-row-start value 'span i'
Fail Property grid-row value 'span i / auto'
Pass Property grid-row-start value 'span i'
Pass Property grid-row value 'span i / auto'
Fail Property grid-area value 'auto / i / auto / i'
Fail Property grid-area value 'auto / i / 2 j'
Fail Property grid-area value 'auto / i / 2 j / span 3 k'
Pass Property grid-row value 'auto / i'
Fail Property grid-column value '2 j / span 3 k'
Pass Property grid-column value '2 j / span 3 k'
Fail Property grid-column-end value '\31st'
Fail Property grid-column-end value '\31 st'

View file

@ -2,8 +2,8 @@ Harness status: OK
Found 53 tests
46 Pass
7 Fail
48 Pass
5 Fail
Pass e.style['grid-area'] = "auto" should set grid-column-end
Pass e.style['grid-area'] = "auto" should set grid-column-start
Pass e.style['grid-area'] = "auto" should set grid-row-end
@ -54,6 +54,6 @@ Pass e.style['grid-column'] = "5 span" should not set unrelated longhands
Pass e.style['grid-column'] = "1 / two" should set grid-column-end
Pass e.style['grid-column'] = "1 / two" should set grid-column-start
Pass e.style['grid-column'] = "1 / two" should not set unrelated longhands
Fail e.style['grid-column'] = "span 1 two / four 3 span" should set grid-column-end
Fail e.style['grid-column'] = "span 1 two / four 3 span" should set grid-column-start
Pass e.style['grid-column'] = "span 1 two / four 3 span" should set grid-column-end
Pass e.style['grid-column'] = "span 1 two / four 3 span" should set grid-column-start
Pass e.style['grid-column'] = "span 1 two / four 3 span" should not set unrelated longhands

View file

@ -2,8 +2,8 @@ Harness status: OK
Found 57 tests
30 Pass
27 Fail
40 Pass
17 Fail
Pass e.style['grid-area'] = "auto" should set the property value
Pass e.style['grid-area'] = "auto / auto" should set the property value
Pass e.style['grid-area'] = "auto / auto / auto" should set the property value
@ -19,7 +19,7 @@ Pass e.style['grid-row'] = "i / i" should set the property value
Pass e.style['grid-row-start'] = "AZ" should set the property value
Pass e.style['grid-column-start'] = "-_π" should set the property value
Pass e.style['grid-row-end'] = "_9" should set the property value
Fail e.style['grid-area'] = "1" should set the property value
Pass e.style['grid-area'] = "1" should set the property value
Fail e.style['grid-area'] = "+90 -a-" should set the property value
Pass e.style['grid-row'] = "az 2" should set the property value
Pass e.style['grid-column'] = "9" should set the property value
@ -31,20 +31,20 @@ Pass e.style['grid-column-start'] = "+90" should set the property value
Pass e.style['grid-column-start'] = "Z -44" should set the property value
Pass e.style['grid-row-end'] = "1 -πA" should set the property value
Pass e.style['grid-column-end'] = "π_ +5" should set the property value
Fail e.style['grid-area'] = "span 2 i" should set the property value
Fail e.style['grid-area'] = "i 2 SpAn" should set the property value
Fail e.style['grid-area'] = "span 1 i" should set the property value
Pass e.style['grid-area'] = "span 2 i" should set the property value
Pass e.style['grid-area'] = "i 2 SpAn" should set the property value
Pass e.style['grid-area'] = "span 1 i" should set the property value
Pass e.style['grid-row'] = "span 2" should set the property value
Fail e.style['grid-column'] = "i SpAn" should set the property value
Fail e.style['grid-row-start'] = "span i" should set the property value
Fail e.style['grid-column-start'] = "SpAn i 2" should set the property value
Fail e.style['grid-row-end'] = "2 i span" should set the property value
Pass e.style['grid-column'] = "i SpAn" should set the property value
Pass e.style['grid-row-start'] = "span i" should set the property value
Pass e.style['grid-column-start'] = "SpAn i 2" should set the property value
Pass e.style['grid-row-end'] = "2 i span" should set the property value
Pass e.style['grid-column-end'] = "2 SpAn" should set the property value
Fail e.style['grid-area'] = "auto / i" should set the property value
Fail e.style['grid-area'] = "auto / i / auto / i" should set the property value
Fail e.style['grid-area'] = "auto / i / auto / 2 i" should set the property value
Fail e.style['grid-area'] = "1 / i / auto / i" should set the property value
Fail e.style['grid-area'] = "1 / auto / auto / auto" should set the property value
Pass e.style['grid-area'] = "1 / auto / auto / auto" should set the property value
Fail e.style['grid-area'] = "1 / auto / i / auto" should set the property value
Pass e.style['grid-area'] = "1 / j / i / k" should set the property value
Fail e.style['grid-area'] = "1 / auto / 2 / auto" should set the property value
@ -57,7 +57,7 @@ Pass e.style['grid-row'] = "auto / i" should set the property value
Fail e.style['grid-row'] = "i / auto" should set the property value
Pass e.style['grid-row'] = "2 i / auto" should set the property value
Pass e.style['grid-row'] = "1 / auto" should set the property value
Fail e.style['grid-column'] = "2 j / span 3 k" should set the property value
Pass e.style['grid-column'] = "2 j / span 3 k" should set the property value
Fail e.style['grid-column-end'] = "\\31st" should set the property value
Fail e.style['grid-column-end'] = "\\31 st" should set the property value
Fail e.style['grid-column'] = "\\31st / \\31 st" should set the property value

View file

@ -2,8 +2,7 @@ Harness status: OK
Found 16 tests
12 Pass
4 Fail
16 Pass
Pass Property grid-column value 'auto / auto'
Pass Property grid-column value 'auto'
Pass Property grid-column value '10 / auto'
@ -16,7 +15,7 @@ Pass Property grid-column value 'span 2 / auto'
Pass Property grid-column value 'span 2'
Pass Property grid-column value '2 first / auto'
Pass Property grid-column value '2 first'
Fail Property grid-column value 'span first / auto'
Fail Property grid-column value 'span first'
Fail Property grid-column value 'span 2 first / auto'
Fail Property grid-column value 'span 2 first'
Pass Property grid-column value 'span first / auto'
Pass Property grid-column value 'span first'
Pass Property grid-column value 'span 2 first / auto'
Pass Property grid-column value 'span 2 first'

View file

@ -2,8 +2,7 @@ Harness status: OK
Found 48 tests
38 Pass
10 Fail
48 Pass
Pass e.style['grid-column'] = "auto / auto" should set grid-column-end
Pass e.style['grid-column'] = "auto / auto" should set grid-column-start
Pass e.style['grid-column'] = "auto / auto" should not set unrelated longhands
@ -31,20 +30,20 @@ Pass e.style['grid-column'] = "span 2" should not set unrelated longhands
Pass e.style['grid-column'] = "3 last / auto" should set grid-column-end
Pass e.style['grid-column'] = "3 last / auto" should set grid-column-start
Pass e.style['grid-column'] = "3 last / auto" should not set unrelated longhands
Fail e.style['grid-column'] = "3 last" should set grid-column-end
Pass e.style['grid-column'] = "3 last" should set grid-column-end
Pass e.style['grid-column'] = "3 last" should set grid-column-start
Pass e.style['grid-column'] = "3 last" should not set unrelated longhands
Fail e.style['grid-column'] = "span first / auto" should set grid-column-end
Fail e.style['grid-column'] = "span first / auto" should set grid-column-start
Fail e.style['grid-column'] = "span first / auto" should not set unrelated longhands
Fail e.style['grid-column'] = "span first" should set grid-column-end
Fail e.style['grid-column'] = "span first" should set grid-column-start
Fail e.style['grid-column'] = "span first" should not set unrelated longhands
Pass e.style['grid-column'] = "span first / auto" should set grid-column-end
Pass e.style['grid-column'] = "span first / auto" should set grid-column-start
Pass e.style['grid-column'] = "span first / auto" should not set unrelated longhands
Pass e.style['grid-column'] = "span first" should set grid-column-end
Pass e.style['grid-column'] = "span first" should set grid-column-start
Pass e.style['grid-column'] = "span first" should not set unrelated longhands
Pass e.style['grid-column'] = "span 2 first / auto" should set grid-column-end
Fail e.style['grid-column'] = "span 2 first / auto" should set grid-column-start
Pass e.style['grid-column'] = "span 2 first / auto" should set grid-column-start
Pass e.style['grid-column'] = "span 2 first / auto" should not set unrelated longhands
Fail e.style['grid-column'] = "span 2 first" should set grid-column-end
Fail e.style['grid-column'] = "span 2 first" should set grid-column-start
Pass e.style['grid-column'] = "span 2 first" should set grid-column-end
Pass e.style['grid-column'] = "span 2 first" should set grid-column-start
Pass e.style['grid-column'] = "span 2 first" should not set unrelated longhands
Pass e.style['grid-column'] = "last / last" should set grid-column-end
Pass e.style['grid-column'] = "last / last" should set grid-column-start

View file

@ -2,8 +2,7 @@ Harness status: OK
Found 15 tests
11 Pass
4 Fail
15 Pass
Pass Property grid-row value 'auto / auto'
Pass Property grid-row value 'auto'
Pass Property grid-row value '10 / auto'
@ -14,8 +13,8 @@ Pass Property grid-row value 'span 2 / auto'
Pass Property grid-row value 'span 2'
Pass Property grid-row value '3 last / auto'
Pass Property grid-row value '3 last'
Fail Property grid-row value 'span first / auto'
Fail Property grid-row value 'span first'
Fail Property grid-row value 'span 2 first / auto'
Fail Property grid-row value 'span 2 first'
Pass Property grid-row value 'span first / auto'
Pass Property grid-row value 'span first'
Pass Property grid-row value 'span 2 first / auto'
Pass Property grid-row value 'span 2 first'
Pass Property grid-row value 'last / last'

View file

@ -2,8 +2,7 @@ Harness status: OK
Found 48 tests
38 Pass
10 Fail
48 Pass
Pass e.style['grid-row'] = "auto / auto" should set grid-row-end
Pass e.style['grid-row'] = "auto / auto" should set grid-row-start
Pass e.style['grid-row'] = "auto / auto" should not set unrelated longhands
@ -31,20 +30,20 @@ Pass e.style['grid-row'] = "span 2" should not set unrelated longhands
Pass e.style['grid-row'] = "3 last / auto" should set grid-row-end
Pass e.style['grid-row'] = "3 last / auto" should set grid-row-start
Pass e.style['grid-row'] = "3 last / auto" should not set unrelated longhands
Fail e.style['grid-row'] = "3 last" should set grid-row-end
Pass e.style['grid-row'] = "3 last" should set grid-row-end
Pass e.style['grid-row'] = "3 last" should set grid-row-start
Pass e.style['grid-row'] = "3 last" should not set unrelated longhands
Fail e.style['grid-row'] = "span first / auto" should set grid-row-end
Fail e.style['grid-row'] = "span first / auto" should set grid-row-start
Fail e.style['grid-row'] = "span first / auto" should not set unrelated longhands
Fail e.style['grid-row'] = "span first" should set grid-row-end
Fail e.style['grid-row'] = "span first" should set grid-row-start
Fail e.style['grid-row'] = "span first" should not set unrelated longhands
Pass e.style['grid-row'] = "span first / auto" should set grid-row-end
Pass e.style['grid-row'] = "span first / auto" should set grid-row-start
Pass e.style['grid-row'] = "span first / auto" should not set unrelated longhands
Pass e.style['grid-row'] = "span first" should set grid-row-end
Pass e.style['grid-row'] = "span first" should set grid-row-start
Pass e.style['grid-row'] = "span first" should not set unrelated longhands
Pass e.style['grid-row'] = "span 2 first / auto" should set grid-row-end
Fail e.style['grid-row'] = "span 2 first / auto" should set grid-row-start
Pass e.style['grid-row'] = "span 2 first / auto" should set grid-row-start
Pass e.style['grid-row'] = "span 2 first / auto" should not set unrelated longhands
Fail e.style['grid-row'] = "span 2 first" should set grid-row-end
Fail e.style['grid-row'] = "span 2 first" should set grid-row-start
Pass e.style['grid-row'] = "span 2 first" should set grid-row-end
Pass e.style['grid-row'] = "span 2 first" should set grid-row-start
Pass e.style['grid-row'] = "span 2 first" should not set unrelated longhands
Pass e.style['grid-row'] = "last / last" should set grid-row-end
Pass e.style['grid-row'] = "last / last" should set grid-row-start