@@ -1471,6 +1471,9 @@ void native_document::layout_children(dom_node& parent)
14711471 size_t row_span{1 };
14721472 size_t column_span{1 };
14731473 };
1474+ const auto inherited_subgrid_columns = parent_grid.subgrid_columns
1475+ && parent.has_grid_layout ()
1476+ && !parent.grid_layout ().column_widths .empty ();
14741477 auto effective_columns = parent_grid.template_columns ;
14751478 if (effective_columns.empty () && implicit_column_grid) {
14761479 const auto implicit_count = std::max<size_t >(
@@ -1490,9 +1493,11 @@ void native_document::layout_children(dom_node& parent)
14901493 parent_grid.auto_columns [index % parent_grid.auto_columns .size ()]);
14911494 }
14921495 }
1493- const auto column_count = std::max<size_t >(
1494- implicit_column_grid ? 1U : 2U ,
1495- effective_columns.size ());
1496+ const auto column_count = inherited_subgrid_columns
1497+ ? parent.grid_layout ().column_widths .size ()
1498+ : std::max<size_t >(
1499+ implicit_column_grid ? 1U : 2U ,
1500+ effective_columns.size ());
14961501 const auto parse_line = [](std::string_view value) -> std::optional<int32_t > {
14971502 const auto first = value.find_first_not_of (" \t\r\n " );
14981503 if (first == std::string_view::npos) return std::nullopt ;
@@ -1619,56 +1624,63 @@ void native_document::layout_children(dom_node& parent)
16191624 }
16201625 }
16211626
1622- const auto column_gap = resolve_length (
1623- parent, parent.style .column_gap , content.width , 0 );
1627+ const auto column_gap = inherited_subgrid_columns
1628+ ? parent.grid_layout ().column_gap
1629+ : resolve_length (parent, parent.style .column_gap , content.width , 0 );
16241630 const auto row_gap = resolve_length (
16251631 parent, parent.style .row_gap , content.height , 0 );
1626- std::vector<float > column_widths (column_count, 0 .0F );
1632+ std::vector<float > column_widths = inherited_subgrid_columns
1633+ ? parent.grid_layout ().column_widths
1634+ : std::vector<float >(column_count, 0 .0F );
16271635 auto fractional_weight = 0 .0F ;
1628- for (size_t column = 0 ; column < effective_columns.size (); ++column) {
1629- const auto & track = effective_columns[column];
1630- if (track.kind == grid_track::sizing::fixed) {
1631- column_widths[column] = resolve_length (
1632- parent, track.maximum , content.width , 0 );
1633- } else if (track.kind == grid_track::sizing::minmax) {
1634- column_widths[column] = resolve_length (
1635- parent, track.minimum , content.width , 0 );
1636- if (track.fraction <= 0 && is_specified (track.maximum )) {
1637- column_widths[column] = std::max (
1638- column_widths[column],
1639- resolve_length (parent, track.maximum , content.width , 0 ));
1636+ if (!inherited_subgrid_columns) {
1637+ for (size_t column = 0 ; column < effective_columns.size (); ++column) {
1638+ const auto & track = effective_columns[column];
1639+ if (track.kind == grid_track::sizing::fixed) {
1640+ column_widths[column] = resolve_length (
1641+ parent, track.maximum , content.width , 0 );
1642+ } else if (track.kind == grid_track::sizing::minmax) {
1643+ column_widths[column] = resolve_length (
1644+ parent, track.minimum , content.width , 0 );
1645+ if (track.fraction <= 0 && is_specified (track.maximum )) {
1646+ column_widths[column] = std::max (
1647+ column_widths[column],
1648+ resolve_length (parent, track.maximum , content.width , 0 ));
1649+ }
16401650 }
1651+ fractional_weight += track.fraction ;
1652+ }
1653+ for (const auto & item : items) {
1654+ if (item.column_span != 1U ) continue ;
1655+ const auto authored_track = item.column < effective_columns.size ()
1656+ ? &effective_columns[item.column ] : nullptr ;
1657+ if (authored_track != nullptr
1658+ && authored_track->kind != grid_track::sizing::automatic) continue ;
1659+ const auto margins = resolve_length (
1660+ *item.node , item.node ->style .margin_left , content.width , 0 )
1661+ + resolve_length (
1662+ *item.node , item.node ->style .margin_right , content.width , 0 );
1663+ column_widths[item.column ] = std::max (
1664+ column_widths[item.column ],
1665+ intrinsic_size (*item.node , true , content.width ) + margins);
16411666 }
1642- fractional_weight += track.fraction ;
1643- }
1644- for (const auto & item : items) {
1645- if (item.column_span != 1U ) continue ;
1646- const auto authored_track = item.column < effective_columns.size ()
1647- ? &effective_columns[item.column ] : nullptr ;
1648- if (authored_track != nullptr
1649- && authored_track->kind != grid_track::sizing::automatic) continue ;
1650- const auto margins = resolve_length (
1651- *item.node , item.node ->style .margin_left , content.width , 0 )
1652- + resolve_length (
1653- *item.node , item.node ->style .margin_right , content.width , 0 );
1654- column_widths[item.column ] = std::max (
1655- column_widths[item.column ],
1656- intrinsic_size (*item.node , true , content.width ) + margins);
16571667 }
16581668 const auto total_gap = column_gap * static_cast <float >(column_count - 1U );
1659- const auto committed_width = std::accumulate (
1660- column_widths.begin (), column_widths.end (), total_gap);
1661- const auto fractional_space = std::max (0 .0F , content.width - committed_width);
1662- if (fractional_weight > 0 ) {
1663- for (size_t column = 0 ; column < effective_columns.size (); ++column) {
1664- const auto & track = effective_columns[column];
1665- if (track.fraction <= 0 ) continue ;
1666- column_widths[column] = std::max (
1667- column_widths[column],
1668- fractional_space * track.fraction / fractional_weight);
1669+ if (!inherited_subgrid_columns) {
1670+ const auto committed_width = std::accumulate (
1671+ column_widths.begin (), column_widths.end (), total_gap);
1672+ const auto fractional_space = std::max (0 .0F , content.width - committed_width);
1673+ if (fractional_weight > 0 ) {
1674+ for (size_t column = 0 ; column < effective_columns.size (); ++column) {
1675+ const auto & track = effective_columns[column];
1676+ if (track.fraction <= 0 ) continue ;
1677+ column_widths[column] = std::max (
1678+ column_widths[column],
1679+ fractional_space * track.fraction / fractional_weight);
1680+ }
1681+ } else if (committed_width < content.width && !column_widths.empty ()) {
1682+ column_widths.back () += content.width - committed_width;
16691683 }
1670- } else if (committed_width < content.width && !column_widths.empty ()) {
1671- column_widths.back () += content.width - committed_width;
16721684 }
16731685
16741686 const auto row_count = std::max<size_t >(
@@ -1772,6 +1784,16 @@ void native_document::layout_children(dom_node& parent)
17721784 auto height = is_specified (child->style .height )
17731785 ? outer_authored_size (*child, false , available_height)
17741786 : std::max (0 .0F , available_height - margin_top - margin_bottom);
1787+ if (child->style .grid ().subgrid_columns ) {
1788+ auto & inherited = child->mutable_grid_layout ();
1789+ inherited.column_widths .assign (
1790+ column_widths.begin () + static_cast <std::ptrdiff_t >(item.column ),
1791+ column_widths.begin () + static_cast <std::ptrdiff_t >(
1792+ item.column + item.column_span ));
1793+ inherited.column_gap = column_gap;
1794+ } else {
1795+ child->clear_grid_layout ();
1796+ }
17751797 layout_child (*child, parent.layout , {
17761798 column_offsets[item.column ] + margin_left,
17771799 row_offsets[item.row ] + margin_top,
@@ -2723,10 +2745,21 @@ void native_document::layout_children(dom_node& parent)
27232745 const auto propagates_document_overflow =
27242746 (&parent == body_ && child->tag == " html" )
27252747 || (parent.tag == " html" && child->tag == " body" );
2726- const auto flow_width = propagates_document_overflow
2748+ // A scroll container includes descendant overflow that reaches it
2749+ // through an overflow-visible child. The child's principal box still
2750+ // participates in normal flow at its own used size: descendant
2751+ // overflow must enlarge scrollWidth/scrollHeight without moving the
2752+ // next sibling or making an auto-height ancestor grow. TradingView's
2753+ // settings panes use exactly this shape: overflow:auto on the pane,
2754+ // then a height:100% wrapper whose visible descendants are taller.
2755+ const auto propagates_child_overflow_x = propagates_document_overflow
2756+ || child->style .overflow_x == overflow_mode::visible;
2757+ const auto propagates_child_overflow_y = propagates_document_overflow
2758+ || child->style .overflow_y == overflow_mode::visible;
2759+ const auto scrollable_flow_width = propagates_child_overflow_x
27272760 ? std::max (child->layout .width , child->scroll_content_width )
27282761 : child->layout .width ;
2729- const auto flow_height = propagates_document_overflow
2762+ const auto scrollable_flow_height = propagates_child_overflow_y
27302763 ? std::max (child->layout .height , child->scroll_content_height )
27312764 : child->layout .height ;
27322765 // Scroll translation is applied to `positioned`; `assigned` remains in
@@ -2735,13 +2768,13 @@ void native_document::layout_children(dom_node& parent)
27352768 // or scrollHeight grow on every relayout and creates an endless range.
27362769 maximum_flow_right = std::max (
27372770 maximum_flow_right,
2738- assigned.x + flow_width );
2771+ assigned.x + scrollable_flow_width );
27392772 maximum_flow_bottom = std::max (
27402773 maximum_flow_bottom,
2741- assigned.y + flow_height );
2774+ assigned.y + scrollable_flow_height );
27422775 if (!horizontal) {
27432776 cursor = assigned.y - content.y
2744- + flow_height
2777+ + child-> layout . height
27452778 + vertical_margin_bottom
27462779 + justify_gap;
27472780 }
@@ -2923,7 +2956,9 @@ void native_document::layout_children(dom_node& parent)
29232956 }
29242957 }
29252958 parent.scroll_content_width = horizontal
2926- ? std::max (content.width , cursor)
2959+ ? std::max (
2960+ content.width ,
2961+ std::max (cursor, maximum_flow_right - content.x ))
29272962 : std::max (content.width , maximum_flow_right - content.x );
29282963 parent.scroll_content_height = horizontal
29292964 ? std::max (
@@ -2933,7 +2968,11 @@ void native_document::layout_children(dom_node& parent)
29332968 : maximum_flow_bottom - content.y )
29342969 : std::max (
29352970 content.height ,
2936- std::max (cursor, maximum_float_bottom - content.y ));
2971+ std::max (
2972+ cursor,
2973+ std::max (
2974+ maximum_flow_bottom - content.y ,
2975+ maximum_float_bottom - content.y )));
29372976 // Blink determines the exposed scroll dimensions from pixel-snapped
29382977 // layout sizes. A fractional descendant edge that remains inside the same
29392978 // snapped CSS pixel as the scrollport therefore creates no usable scroll
@@ -3552,6 +3591,13 @@ void native_document::layout_child(
35523591 std::max (0 .0F , assigned.width ),
35533592 std::max (0 .0F , assigned.height )};
35543593 const auto * parent = composed_parent (child);
3594+ const auto inherits_parent_grid_columns = child.style .grid ().subgrid_columns
3595+ && parent != nullptr
3596+ && is_grid_container (parent->style .display )
3597+ && (parent->style .grid ().two_columns
3598+ || (parent->style .grid ().auto_flow_column
3599+ && !parent->style .grid ().auto_columns .empty ()));
3600+ if (!inherits_parent_grid_columns) child.clear_grid_layout ();
35553601 child.used_height_is_definite = assigned_height_is_definite
35563602 || (is_specified (child.style .height )
35573603 && (child.style .height .unit != length_unit::percent
0 commit comments