Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1)

Side by Side Diff: third_party/WebKit/Source/core/layout/LayoutTableSection.cpp

Issue 2584143003: Repeat footers in paginated context (Closed)
Patch Set: bug 656232 Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright (C) 1997 Martin Jones (mjones@kde.org) 2 * Copyright (C) 1997 Martin Jones (mjones@kde.org)
3 * (C) 1997 Torben Weis (weis@kde.org) 3 * (C) 1997 Torben Weis (weis@kde.org)
4 * (C) 1998 Waldo Bastian (bastian@kde.org) 4 * (C) 1998 Waldo Bastian (bastian@kde.org)
5 * (C) 1999 Lars Knoll (knoll@kde.org) 5 * (C) 1999 Lars Knoll (knoll@kde.org)
6 * (C) 1999 Antti Koivisto (koivisto@kde.org) 6 * (C) 1999 Antti Koivisto (koivisto@kde.org)
7 * Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009, 2010, 2013 Apple Inc. 7 * Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009, 2010, 2013 Apple Inc.
8 * All rights reserved. 8 * All rights reserved.
9 * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com) 9 * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
10 * 10 *
(...skipping 1214 matching lines...) Expand 10 before | Expand all | Expand 10 after
1225 } 1225 }
1226 1226
1227 DCHECK(!NeedsLayout()); 1227 DCHECK(!NeedsLayout());
1228 1228
1229 SetLogicalHeight(LayoutUnit(row_pos_[total_rows])); 1229 SetLogicalHeight(LayoutUnit(row_pos_[total_rows]));
1230 1230
1231 ComputeOverflowFromCells(total_rows, Table()->NumEffectiveColumns()); 1231 ComputeOverflowFromCells(total_rows, Table()->NumEffectiveColumns());
1232 } 1232 }
1233 1233
1234 int LayoutTableSection::PaginationStrutForRow(LayoutTableRow* row, 1234 int LayoutTableSection::PaginationStrutForRow(LayoutTableRow* row,
1235 LayoutUnit logical_offset) const { 1235 LayoutUnit logical_offset,
1236 bool& can_repeat_footer) const {
mstensho (USE GERRIT) 2017/04/24 11:57:16 I think calculating can_repeat_footer should be om
1236 DCHECK(row); 1237 DCHECK(row);
1237 if (row->GetPaginationBreakability() == kAllowAnyBreaks) 1238 if (row->GetPaginationBreakability() == kAllowAnyBreaks)
1238 return 0; 1239 return 0;
1239 LayoutUnit page_logical_height = PageLogicalHeightForOffset(logical_offset); 1240 LayoutUnit page_logical_height = PageLogicalHeightForOffset(logical_offset);
1240 if (!page_logical_height) 1241 if (!page_logical_height)
1241 return 0; 1242 return 0;
1242 // If the row is too tall for the page don't insert a strut. 1243 // If the row is too tall for the page don't insert a strut.
1243 LayoutUnit row_logical_height = row->LogicalHeight(); 1244 LayoutUnit row_logical_height = row->LogicalHeight();
1244 if (row_logical_height > page_logical_height) 1245 if (row_logical_height > page_logical_height)
1245 return 0; 1246 return 0;
1246 1247
1248 LayoutTableSection* footer = Table()->Footer();
1249 bool footer_wants_to_repeat =
1250 footer && footer != this &&
1251 footer->GetPaginationBreakability() != kAllowAnyBreaks &&
1252 Table()->RowOffsetFromRepeatingHeader() && row->RowIndex();
mstensho (USE GERRIT) 2017/04/24 11:57:16 RowOffsetFromRepeatingHeader() suggests that we de
1253 if (footer_wants_to_repeat) {
1254 if (row_logical_height + footer->LogicalHeight() <= page_logical_height) {
mstensho (USE GERRIT) 2017/04/24 11:57:16 ... however, if there *is* a repeating header, we
1255 row_logical_height += footer->LogicalHeight();
1256 }
1257 }
1258
1247 LayoutUnit remaining_logical_height = PageRemainingLogicalHeightForOffset( 1259 LayoutUnit remaining_logical_height = PageRemainingLogicalHeightForOffset(
1248 logical_offset, LayoutBlock::kAssociateWithLatterPage); 1260 logical_offset, LayoutBlock::kAssociateWithLatterPage);
1249 if (remaining_logical_height >= row_logical_height) 1261 if (remaining_logical_height >= row_logical_height)
1250 return 0; // It fits fine where it is. No need to break. 1262 return 0; // It fits fine where it is. No need to break.
1251 LayoutUnit pagination_strut = CalculatePaginationStrutToFitContent( 1263 LayoutUnit pagination_strut = CalculatePaginationStrutToFitContent(
1252 logical_offset, remaining_logical_height, row_logical_height); 1264 logical_offset, remaining_logical_height, row_logical_height);
1253 if (pagination_strut == remaining_logical_height && 1265 if (pagination_strut == remaining_logical_height &&
1254 remaining_logical_height == page_logical_height) { 1266 remaining_logical_height == page_logical_height) {
1255 // Don't break if we were at the top of a page, and we failed to fit the 1267 // Don't break if we were at the top of a page, and we failed to fit the
1256 // content completely. No point in leaving a page completely blank. 1268 // content completely. No point in leaving a page completely blank.
1257 return 0; 1269 return 0;
1258 } 1270 }
1271
1272 if (footer && remaining_logical_height >=
1273 (footer->LogicalHeight() - Table()->VBorderSpacing()))
1274 can_repeat_footer = true;
1259 // Table layout parts only work on integers, so we have to round. Round up, to 1275 // Table layout parts only work on integers, so we have to round. Round up, to
1260 // make sure that no fraction ever gets left behind in the previous 1276 // make sure that no fraction ever gets left behind in the previous
1261 // fragmentainer. 1277 // fragmentainer.
1262 return pagination_strut.Ceil(); 1278 return pagination_strut.Ceil();
1263 } 1279 }
1264 1280
1265 void LayoutTableSection::ComputeOverflowFromCells() { 1281 void LayoutTableSection::ComputeOverflowFromCells() {
1266 unsigned total_rows = grid_.size(); 1282 unsigned total_rows = grid_.size();
1267 unsigned n_eff_cols = Table()->NumEffectiveColumns(); 1283 unsigned n_eff_cols = Table()->NumEffectiveColumns();
1268 ComputeOverflowFromCells(total_rows, n_eff_cols); 1284 ComputeOverflowFromCells(total_rows, n_eff_cols);
(...skipping 775 matching lines...) Expand 10 before | Expand all | Expand 10 after
2044 MinimumValueForLength(grid_[row_index].logical_height, LayoutUnit()); 2060 MinimumValueForLength(grid_[row_index].logical_height, LayoutUnit());
2045 logical_height = std::max(logical_height, specified_logical_height.ToInt()); 2061 logical_height = std::max(logical_height, specified_logical_height.ToInt());
2046 } 2062 }
2047 return logical_height; 2063 return logical_height;
2048 } 2064 }
2049 2065
2050 void LayoutTableSection::AdjustRowForPagination(LayoutTableRow& row_object, 2066 void LayoutTableSection::AdjustRowForPagination(LayoutTableRow& row_object,
2051 SubtreeLayoutScope& layouter) { 2067 SubtreeLayoutScope& layouter) {
2052 row_object.SetPaginationStrut(LayoutUnit()); 2068 row_object.SetPaginationStrut(LayoutUnit());
2053 row_object.SetLogicalHeight(LayoutUnit(LogicalHeightForRow(row_object))); 2069 row_object.SetLogicalHeight(LayoutUnit(LogicalHeightForRow(row_object)));
2054 int pagination_strut = 2070 bool can_repeat_footer = false;
2055 PaginationStrutForRow(&row_object, row_object.LogicalTop()); 2071 int pagination_strut = PaginationStrutForRow(
2072 &row_object, row_object.LogicalTop(), can_repeat_footer);
2056 bool row_is_at_top_of_column = false; 2073 bool row_is_at_top_of_column = false;
2057 LayoutUnit offset_from_top_of_page; 2074 LayoutUnit offset_from_top_of_page;
2058 if (!pagination_strut) { 2075 if (!pagination_strut) {
2059 LayoutUnit page_logical_height = 2076 LayoutUnit page_logical_height =
2060 PageLogicalHeightForOffset(row_object.LogicalTop()); 2077 PageLogicalHeightForOffset(row_object.LogicalTop());
2061 if (page_logical_height && Table()->Header() && 2078 if (page_logical_height && Table()->Header() &&
2062 Table()->RowOffsetFromRepeatingHeader()) { 2079 Table()->RowOffsetFromRepeatingHeader()) {
2063 offset_from_top_of_page = 2080 offset_from_top_of_page =
2064 page_logical_height - 2081 page_logical_height -
2065 PageRemainingLogicalHeightForOffset(row_object.LogicalTop(), 2082 PageRemainingLogicalHeightForOffset(row_object.LogicalTop(),
(...skipping 13 matching lines...) Expand all
2079 2096
2080 LayoutTableSection* header = Table()->Header(); 2097 LayoutTableSection* header = Table()->Header();
2081 if (row_object.IsFirstRowInSectionAfterHeader()) 2098 if (row_object.IsFirstRowInSectionAfterHeader())
2082 Table()->SetRowOffsetFromRepeatingHeader(LayoutUnit()); 2099 Table()->SetRowOffsetFromRepeatingHeader(LayoutUnit());
2083 // Border spacing from the previous row has pushed this row just past the top 2100 // Border spacing from the previous row has pushed this row just past the top
2084 // of the page, so we must reposition it to the top of the page and avoid any 2101 // of the page, so we must reposition it to the top of the page and avoid any
2085 // repeating header. 2102 // repeating header.
2086 if (row_is_at_top_of_column && offset_from_top_of_page) 2103 if (row_is_at_top_of_column && offset_from_top_of_page)
2087 pagination_strut -= offset_from_top_of_page.ToInt(); 2104 pagination_strut -= offset_from_top_of_page.ToInt();
2088 2105
2106 LayoutTableSection* footer = Table()->Footer();
2107 if (footer && footer != this && header != this &&
2108 footer->GetPaginationBreakability() != kAllowAnyBreaks &&
2109 Table()->RowOffsetFromRepeatingHeader() && can_repeat_footer) {
mstensho (USE GERRIT) 2017/04/24 11:57:16 Check can_repeat_footer first.
2110 unsigned page = FloorToInt((LogicalTop() + row_object.LogicalTop()) /
2111 PageLogicalHeightForOffset(LayoutUnit()));
mstensho (USE GERRIT) 2017/04/24 11:57:16 PageLogicalHeightForOffset() may be 0 (initial col
2112 Table()->SetPositionOfRepeatingFooterGroupOnPage(
2113 page, LogicalTop() + row_object.LogicalTop());
mstensho (USE GERRIT) 2017/04/24 11:57:16 Then again, do we really need to allow individual
2114 }
2115
2089 // If we have a header group we will paint it at the top of each page, 2116 // If we have a header group we will paint it at the top of each page,
2090 // move the rows down to accomodate it. 2117 // move the rows down to accomodate it.
2091 if (header && header != this) 2118 if (header && header != this)
2092 pagination_strut += Table()->RowOffsetFromRepeatingHeader().ToInt(); 2119 pagination_strut += Table()->RowOffsetFromRepeatingHeader().ToInt();
2093 row_object.SetPaginationStrut(LayoutUnit(pagination_strut)); 2120 row_object.SetPaginationStrut(LayoutUnit(pagination_strut));
2094 2121
2095 // We have inserted a pagination strut before the row. Adjust the logical top 2122 // We have inserted a pagination strut before the row. Adjust the logical top
2096 // and re-lay out. We no longer want to break inside the row, but rather 2123 // and re-lay out. We no longer want to break inside the row, but rather
2097 // *before* it. From the previous layout pass, there are most likely 2124 // *before* it. From the previous layout pass, there are most likely
2098 // pagination struts inside some cell in this row that we need to get rid of. 2125 // pagination struts inside some cell in this row that we need to get rid of.
2099 row_object.SetLogicalTop(row_object.LogicalTop() + pagination_strut); 2126 row_object.SetLogicalTop(row_object.LogicalTop() + pagination_strut);
2100 layouter.SetChildNeedsLayout(&row_object); 2127 layouter.SetChildNeedsLayout(&row_object);
2101 row_object.LayoutIfNeeded(); 2128 row_object.LayoutIfNeeded();
2102 2129
2103 // It's very likely that re-laying out (and nuking pagination struts inside 2130 // It's very likely that re-laying out (and nuking pagination struts inside
2104 // cells) gave us a new height. 2131 // cells) gave us a new height.
2105 row_object.SetLogicalHeight(LayoutUnit(LogicalHeightForRow(row_object))); 2132 row_object.SetLogicalHeight(LayoutUnit(LogicalHeightForRow(row_object)));
2106 } 2133 }
2107 2134
2135 bool LayoutTableSection::IsRepeatingFooterGroup() const {
2136 if (GetPaginationBreakability() == LayoutBox::kAllowAnyBreaks)
mstensho (USE GERRIT) 2017/04/24 11:57:16 LayoutBox:: scope unnecessary.
2137 return false;
2138 // TODO(rhogan): Should we paint a header repeatedly if it's self-painting?
mstensho (USE GERRIT) 2017/04/24 11:57:16 I don't think it's currently possible to have a se
2139 if (HasSelfPaintingLayer())
2140 return false;
2141 LayoutUnit page_height = Table()->PageLogicalHeightForOffset(LayoutUnit());
mstensho (USE GERRIT) 2017/04/24 11:57:16 If "page" height isn't uniform (e.g. a multicol co
2142 if (!page_height)
2143 return false;
2144 if (LogicalHeight() > page_height)
2145 return false;
2146 // If the first row of the section after the header group doesn't fit on the
2147 // page, then don't repeat the header on each page.
2148 // See https://drafts.csswg.org/css-tables-3/#repeated-headers
2149 if (Table()->Header() && !Table()->Header()->IsRepeatingHeaderGroup())
2150 return false;
2151 return true;
2152 }
2153
2108 bool LayoutTableSection::IsRepeatingHeaderGroup() const { 2154 bool LayoutTableSection::IsRepeatingHeaderGroup() const {
2109 if (GetPaginationBreakability() == LayoutBox::kAllowAnyBreaks) 2155 if (GetPaginationBreakability() == LayoutBox::kAllowAnyBreaks)
2110 return false; 2156 return false;
2111 // TODO(rhogan): Should we paint a header repeatedly if it's self-painting? 2157 // TODO(rhogan): Should we paint a header repeatedly if it's self-painting?
2112 if (HasSelfPaintingLayer()) 2158 if (HasSelfPaintingLayer())
2113 return false; 2159 return false;
2114 LayoutUnit page_height = Table()->PageLogicalHeightForOffset(LayoutUnit()); 2160 LayoutUnit page_height = Table()->PageLogicalHeightForOffset(LayoutUnit());
2115 if (!page_height) 2161 if (!page_height)
2116 return false; 2162 return false;
2117 2163
(...skipping 14 matching lines...) Expand all
2132 2178
2133 return true; 2179 return true;
2134 } 2180 }
2135 2181
2136 bool LayoutTableSection::MapToVisualRectInAncestorSpaceInternal( 2182 bool LayoutTableSection::MapToVisualRectInAncestorSpaceInternal(
2137 const LayoutBoxModelObject* ancestor, 2183 const LayoutBoxModelObject* ancestor,
2138 TransformState& transform_state, 2184 TransformState& transform_state,
2139 VisualRectFlags flags) const { 2185 VisualRectFlags flags) const {
2140 if (ancestor == this) 2186 if (ancestor == this)
2141 return true; 2187 return true;
2188
2189 if (Table()->Footer() == this && IsRepeatingFooterGroup()) {
2190 transform_state.Flatten();
2191 FloatRect rect = transform_state.LastPlanarQuad().BoundingBox();
2192 rect.SetHeight(Table()->LogicalHeight());
mstensho (USE GERRIT) 2017/04/24 11:57:16 Introducing a lot of duplicated code here (shared
2193 transform_state.SetQuad(FloatQuad(rect));
2194 bool mapped = Table()->MapToVisualRectInAncestorSpaceInternal(
mstensho (USE GERRIT) 2017/04/24 11:57:16 We here skip the table section, ignoring its posit
2195 ancestor, transform_state, flags);
2196 return mapped;
2197 }
2198
2142 // Repeating table headers are painted once per fragmentation page/column. 2199 // Repeating table headers are painted once per fragmentation page/column.
2143 // This does not go through the regular fragmentation machinery, so we need 2200 // This does not go through the regular fragmentation machinery, so we need
2144 // special code to expand the invalidation rect to contain all positions of 2201 // special code to expand the invalidation rect to contain all positions of
2145 // the header in all columns. 2202 // the header in all columns.
2146 // Note that this is in flow thread coordinates, not visual coordinates. The 2203 // Note that this is in flow thread coordinates, not visual coordinates. The
2147 // enclosing LayoutFlowThread will convert to visual coordinates. 2204 // enclosing LayoutFlowThread will convert to visual coordinates.
2148 if (Table()->Header() == this && IsRepeatingHeaderGroup()) { 2205 if (Table()->Header() == this && IsRepeatingHeaderGroup()) {
2149 transform_state.Flatten(); 2206 transform_state.Flatten();
2150 FloatRect rect = transform_state.LastPlanarQuad().BoundingBox(); 2207 FloatRect rect = transform_state.LastPlanarQuad().BoundingBox();
2151 rect.SetHeight(Table()->LogicalHeight()); 2208 rect.SetHeight(Table()->LogicalHeight());
2152 transform_state.SetQuad(FloatQuad(rect)); 2209 transform_state.SetQuad(FloatQuad(rect));
2153 } 2210 }
2154 return LayoutTableBoxComponent::MapToVisualRectInAncestorSpaceInternal( 2211 return LayoutTableBoxComponent::MapToVisualRectInAncestorSpaceInternal(
2155 ancestor, transform_state, flags); 2212 ancestor, transform_state, flags);
2156 } 2213 }
2157 2214
2158 bool LayoutTableSection::PaintedOutputOfObjectHasNoEffectRegardlessOfSize() 2215 bool LayoutTableSection::PaintedOutputOfObjectHasNoEffectRegardlessOfSize()
2159 const { 2216 const {
2160 // LayoutTableSection paints background from columns. 2217 // LayoutTableSection paints background from columns.
2161 if (Table()->HasColElements()) 2218 if (Table()->HasColElements())
2162 return false; 2219 return false;
2163 return LayoutTableBoxComponent:: 2220 return LayoutTableBoxComponent::
2164 PaintedOutputOfObjectHasNoEffectRegardlessOfSize(); 2221 PaintedOutputOfObjectHasNoEffectRegardlessOfSize();
2165 } 2222 }
2166 2223
2167 } // namespace blink 2224 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698