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

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, 7 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 1236 matching lines...) Expand 10 before | Expand all | Expand 10 after
1247 if (row->GetPaginationBreakability() == kAllowAnyBreaks) 1247 if (row->GetPaginationBreakability() == kAllowAnyBreaks)
1248 return 0; 1248 return 0;
1249 LayoutUnit page_logical_height = PageLogicalHeightForOffset(logical_offset); 1249 LayoutUnit page_logical_height = PageLogicalHeightForOffset(logical_offset);
1250 if (!page_logical_height) 1250 if (!page_logical_height)
1251 return 0; 1251 return 0;
1252 // If the row is too tall for the page don't insert a strut. 1252 // If the row is too tall for the page don't insert a strut.
1253 LayoutUnit row_logical_height = row->LogicalHeight(); 1253 LayoutUnit row_logical_height = row->LogicalHeight();
1254 if (row_logical_height > page_logical_height) 1254 if (row_logical_height > page_logical_height)
1255 return 0; 1255 return 0;
1256 1256
1257 LayoutTableSection* footer = Table()->Footer();
mstensho (USE GERRIT) 2017/05/08 13:55:59 This one can be const, I guess?
rhogan 2017/05/09 19:57:16 Done.
1258 bool footer_wants_to_repeat =
mstensho (USE GERRIT) 2017/05/08 13:55:59 make_room_for_repeating_footer?
rhogan 2017/05/09 19:57:16 Done.
1259 footer && footer != this &&
1260 footer->IsRepeatingFooterGroup(const_cast<LayoutTableSection*>(this)) &&
mstensho (USE GERRIT) 2017/05/08 13:55:59 Ugh. But is there really any reason not to let the
rhogan 2017/05/09 19:57:16 Done.
1261 row->RowIndex();
1262 if (footer_wants_to_repeat)
1263 row_logical_height += footer->LogicalHeight();
1264
1257 LayoutUnit remaining_logical_height = PageRemainingLogicalHeightForOffset( 1265 LayoutUnit remaining_logical_height = PageRemainingLogicalHeightForOffset(
1258 logical_offset, LayoutBlock::kAssociateWithLatterPage); 1266 logical_offset, LayoutBlock::kAssociateWithLatterPage);
1259 if (remaining_logical_height >= row_logical_height) 1267 if (remaining_logical_height >= row_logical_height)
1260 return 0; // It fits fine where it is. No need to break. 1268 return 0; // It fits fine where it is. No need to break.
1261 LayoutUnit pagination_strut = CalculatePaginationStrutToFitContent( 1269 LayoutUnit pagination_strut = CalculatePaginationStrutToFitContent(
1262 logical_offset, remaining_logical_height, row_logical_height); 1270 logical_offset, remaining_logical_height, row_logical_height);
1263 if (pagination_strut == remaining_logical_height && 1271 if (pagination_strut == remaining_logical_height &&
1264 remaining_logical_height == page_logical_height) { 1272 remaining_logical_height == page_logical_height) {
1265 // Don't break if we were at the top of a page, and we failed to fit the 1273 // Don't break if we were at the top of a page, and we failed to fit the
1266 // content completely. No point in leaving a page completely blank. 1274 // content completely. No point in leaving a page completely blank.
1267 return 0; 1275 return 0;
1268 } 1276 }
1277
mstensho (USE GERRIT) 2017/05/08 13:55:59 Back out this?
rhogan 2017/05/09 19:57:16 Done.
1269 // Table layout parts only work on integers, so we have to round. Round up, to 1278 // Table layout parts only work on integers, so we have to round. Round up, to
1270 // make sure that no fraction ever gets left behind in the previous 1279 // make sure that no fraction ever gets left behind in the previous
1271 // fragmentainer. 1280 // fragmentainer.
1272 return pagination_strut.Ceil(); 1281 return pagination_strut.Ceil();
1273 } 1282 }
1274 1283
1275 void LayoutTableSection::ComputeOverflowFromCells() { 1284 void LayoutTableSection::ComputeOverflowFromCells() {
1276 unsigned total_rows = grid_.size(); 1285 unsigned total_rows = grid_.size();
1277 unsigned n_eff_cols = Table()->NumEffectiveColumns(); 1286 unsigned n_eff_cols = Table()->NumEffectiveColumns();
1278 ComputeOverflowFromCells(total_rows, n_eff_cols); 1287 ComputeOverflowFromCells(total_rows, n_eff_cols);
(...skipping 777 matching lines...) Expand 10 before | Expand all | Expand 10 after
2056 } 2065 }
2057 return logical_height; 2066 return logical_height;
2058 } 2067 }
2059 2068
2060 void LayoutTableSection::AdjustRowForPagination(LayoutTableRow& row_object, 2069 void LayoutTableSection::AdjustRowForPagination(LayoutTableRow& row_object,
2061 SubtreeLayoutScope& layouter) { 2070 SubtreeLayoutScope& layouter) {
2062 row_object.SetPaginationStrut(LayoutUnit()); 2071 row_object.SetPaginationStrut(LayoutUnit());
2063 row_object.SetLogicalHeight(LayoutUnit(LogicalHeightForRow(row_object))); 2072 row_object.SetLogicalHeight(LayoutUnit(LogicalHeightForRow(row_object)));
2064 int pagination_strut = 2073 int pagination_strut =
2065 PaginationStrutForRow(&row_object, row_object.LogicalTop()); 2074 PaginationStrutForRow(&row_object, row_object.LogicalTop());
2075 LayoutTableSection* footer = Table()->Footer();
mstensho (USE GERRIT) 2017/05/08 13:55:59 Can you push it a few lines down, next to where it
rhogan 2017/05/09 19:57:16 Done.
2066 bool row_is_at_top_of_column = false; 2076 bool row_is_at_top_of_column = false;
2067 LayoutUnit offset_from_top_of_page; 2077 LayoutUnit offset_from_top_of_page;
2068 if (!pagination_strut) { 2078 if (!pagination_strut) {
2069 LayoutUnit page_logical_height = 2079 LayoutUnit page_logical_height =
2070 PageLogicalHeightForOffset(row_object.LogicalTop()); 2080 PageLogicalHeightForOffset(row_object.LogicalTop());
2071 if (page_logical_height && Table()->Header() && Table()->Header() != this && 2081 if (page_logical_height && Table()->Header() && Table()->Header() != this &&
2072 Table()->RowOffsetFromRepeatingHeader()) { 2082 Table()->RowOffsetFromRepeatingHeader()) {
2073 offset_from_top_of_page = 2083 offset_from_top_of_page =
2074 page_logical_height - 2084 page_logical_height -
2075 PageRemainingLogicalHeightForOffset(row_object.LogicalTop(), 2085 PageRemainingLogicalHeightForOffset(row_object.LogicalTop(),
(...skipping 13 matching lines...) Expand all
2089 2099
2090 LayoutTableSection* header = Table()->Header(); 2100 LayoutTableSection* header = Table()->Header();
2091 if (row_object.IsFirstRowInSectionAfterHeader()) 2101 if (row_object.IsFirstRowInSectionAfterHeader())
2092 Table()->SetRowOffsetFromRepeatingHeader(LayoutUnit()); 2102 Table()->SetRowOffsetFromRepeatingHeader(LayoutUnit());
2093 // Border spacing from the previous row has pushed this row just past the top 2103 // Border spacing from the previous row has pushed this row just past the top
2094 // of the page, so we must reposition it to the top of the page and avoid any 2104 // of the page, so we must reposition it to the top of the page and avoid any
2095 // repeating header. 2105 // repeating header.
2096 if (row_is_at_top_of_column && offset_from_top_of_page) 2106 if (row_is_at_top_of_column && offset_from_top_of_page)
2097 pagination_strut -= offset_from_top_of_page.ToInt(); 2107 pagination_strut -= offset_from_top_of_page.ToInt();
2098 2108
2109 if (footer && footer != this && header != this &&
mstensho (USE GERRIT) 2017/05/08 13:55:59 If the header isn't repeating, there should be no
rhogan 2017/05/09 19:57:16 Done.
2110 footer->IsRepeatingFooterGroup(this)) {
2111 DCHECK(PageLogicalHeightForOffset(LayoutUnit()));
2112 LayoutUnit offset_from_top = LogicalTop() + row_object.LogicalTop();
2113 unsigned page =
2114 FloorToInt(offset_from_top / PageLogicalHeightForOffset(LayoutUnit()));
mstensho (USE GERRIT) 2017/05/08 13:55:59 To cope with non-uniform page heights, you should
rhogan 2017/05/09 19:57:15 Done.
2115 LayoutUnit remaining_logical_height = PageRemainingLogicalHeightForOffset(
2116 row_object.LogicalTop(), LayoutBlock::kAssociateWithLatterPage);
mstensho (USE GERRIT) 2017/05/08 13:55:59 LayoutBlock:: scope should be unnecessary. This on
rhogan 2017/05/09 19:57:16 Done.
2117 if (remaining_logical_height >=
2118 (footer->LogicalHeight() - Table()->VBorderSpacing()))
2119 Table()->SetPositionOfRepeatingFooterGroupOnPage(page, offset_from_top);
2120 }
2121
2099 // If we have a header group we will paint it at the top of each page, 2122 // If we have a header group we will paint it at the top of each page,
2100 // move the rows down to accomodate it. 2123 // move the rows down to accomodate it.
2101 if (header && header != this) 2124 if (header && header != this)
2102 pagination_strut += Table()->RowOffsetFromRepeatingHeader().ToInt(); 2125 pagination_strut += Table()->RowOffsetFromRepeatingHeader().ToInt();
2103 row_object.SetPaginationStrut(LayoutUnit(pagination_strut)); 2126 row_object.SetPaginationStrut(LayoutUnit(pagination_strut));
2104 2127
2105 // We have inserted a pagination strut before the row. Adjust the logical top 2128 // We have inserted a pagination strut before the row. Adjust the logical top
2106 // and re-lay out. We no longer want to break inside the row, but rather 2129 // and re-lay out. We no longer want to break inside the row, but rather
2107 // *before* it. From the previous layout pass, there are most likely 2130 // *before* it. From the previous layout pass, there are most likely
2108 // pagination struts inside some cell in this row that we need to get rid of. 2131 // pagination struts inside some cell in this row that we need to get rid of.
2109 row_object.SetLogicalTop(row_object.LogicalTop() + pagination_strut); 2132 row_object.SetLogicalTop(row_object.LogicalTop() + pagination_strut);
2110 layouter.SetChildNeedsLayout(&row_object); 2133 layouter.SetChildNeedsLayout(&row_object);
2111 row_object.LayoutIfNeeded(); 2134 row_object.LayoutIfNeeded();
2112 2135
2113 // It's very likely that re-laying out (and nuking pagination struts inside 2136 // It's very likely that re-laying out (and nuking pagination struts inside
2114 // cells) gave us a new height. 2137 // cells) gave us a new height.
2115 row_object.SetLogicalHeight(LayoutUnit(LogicalHeightForRow(row_object))); 2138 row_object.SetLogicalHeight(LayoutUnit(LogicalHeightForRow(row_object)));
2116 } 2139 }
2117 2140
2118 bool LayoutTableSection::IsRepeatingHeaderGroup() const { 2141 bool LayoutTableSection::IsRepeatingFooterGroup(
mstensho (USE GERRIT) 2017/05/08 13:55:59 This belongs AFTER IsRepeatingHeaderGroup().
rhogan 2017/05/09 19:57:16 Done.
2119 if (GetPaginationBreakability() == LayoutBox::kAllowAnyBreaks) 2142 LayoutTableSection* section_for_page) const {
2143 if (GetPaginationBreakability() == kAllowAnyBreaks)
2120 return false; 2144 return false;
2121 // TODO(rhogan): Should we paint a header repeatedly if it's self-painting? 2145
2146 // TODO(rhogan): Sections can be self-painting.
mstensho (USE GERRIT) 2017/05/08 13:55:59 Can you tell me how they can be self-painting?
rhogan 2017/05/09 19:57:16 Yes, if you put an opacity: 0.5 on them for exampl
mstensho (USE GERRIT) 2017/05/10 10:59:03 Ah, thanks!
2122 if (HasSelfPaintingLayer()) 2147 if (HasSelfPaintingLayer())
2123 return false; 2148 return false;
2124 LayoutUnit page_height = Table()->PageLogicalHeightForOffset(LayoutUnit()); 2149
2150 LayoutUnit page_height = section_for_page
2151 ? section_for_page->PageLogicalHeightForOffset(
mstensho (USE GERRIT) 2017/05/08 13:55:59 This is to avoid assertion failures and the bugs t
rhogan 2017/05/09 19:57:16 Correct.
2152 section_for_page->LogicalTop())
2153 : PageLogicalHeightForOffset(LogicalTop());
2125 if (!page_height) 2154 if (!page_height)
2126 return false; 2155 return false;
2127 2156
2128 if (LogicalHeight() > page_height) 2157 if (LogicalHeight() > 0 && page_height / LogicalHeight() < 4)
mstensho (USE GERRIT) 2017/05/08 13:55:59 The code looks fine, but I'd like a comment that m
rhogan 2017/05/09 19:57:16 Done.
2129 return false; 2158 return false;
2130 2159
2131 // If the first row of the section after the header group doesn't fit on the 2160 return true;
2132 // page, then don't repeat the header on each page. 2161 }
2133 // See https://drafts.csswg.org/css-tables-3/#repeated-headers 2162
2163 bool LayoutTableSection::IsRepeatingHeaderGroup() const {
2164 if (GetPaginationBreakability() == kAllowAnyBreaks)
2165 return false;
2166
2167 // TODO(rhogan): Sections can be self-painting.
2168 if (HasSelfPaintingLayer())
2169 return false;
2170
2171 LayoutUnit page_height = PageLogicalHeightForOffset(LogicalTop());
2172 if (!page_height)
2173 return false;
2174
2175 if (LogicalHeight() > 0 && page_height / LogicalHeight() < 4)
mstensho (USE GERRIT) 2017/05/08 13:55:59 The code looks fine, but I'd like a comment that m
rhogan 2017/05/09 19:57:16 Done.
2176 return false;
2177
2178 // TODO(crbug.com/710387): If the first row of the section after the header
2179 // group doesn't fit on the page, then we don't repeat the header on each page
2180 // and by extension only support repeating headers on the innermost table in a
2181 // set of nested tables.
2134 LayoutTableSection* section_below = Table()->SectionBelow(this); 2182 LayoutTableSection* section_below = Table()->SectionBelow(this);
2135 if (!section_below) 2183 if (!section_below)
2136 return true; 2184 return true;
2137 if (LayoutTableRow* first_row = section_below->FirstRow()) { 2185 if (LayoutTableRow* first_row = section_below->FirstRow()) {
2138 if (first_row->PaginationStrut() || 2186 if (first_row->PaginationStrut() ||
2139 first_row->LogicalHeight() > page_height) 2187 first_row->LogicalHeight() > page_height)
2140 return false; 2188 return false;
2141 } 2189 }
2142 2190
2143 return true; 2191 return true;
2144 } 2192 }
2145 2193
2146 bool LayoutTableSection::MapToVisualRectInAncestorSpaceInternal( 2194 bool LayoutTableSection::MapToVisualRectInAncestorSpaceInternal(
2147 const LayoutBoxModelObject* ancestor, 2195 const LayoutBoxModelObject* ancestor,
2148 TransformState& transform_state, 2196 TransformState& transform_state,
2149 VisualRectFlags flags) const { 2197 VisualRectFlags flags) const {
2150 if (ancestor == this) 2198 if (ancestor == this)
2151 return true; 2199 return true;
2152 // Repeating table headers are painted once per fragmentation page/column. 2200
2153 // This does not go through the regular fragmentation machinery, so we need 2201 // Repeating table headers and footers are painted once per fragmentation
2154 // special code to expand the invalidation rect to contain all positions of 2202 // page/column. This does not go through the regular fragmentation machinery,
2155 // the header in all columns. 2203 // so we need special code to expand the invalidation rect to contain all
2156 // Note that this is in flow thread coordinates, not visual coordinates. The 2204 // positions of the header in all columns. Note that this is in flow thread
2157 // enclosing LayoutFlowThread will convert to visual coordinates. 2205 // coordinates, not visual coordinates. The enclosing LayoutFlowThread will
2158 if (Table()->Header() == this && IsRepeatingHeaderGroup()) { 2206 // convert to visual coordinates.
2207 if ((Table()->Header() == this && IsRepeatingHeaderGroup()) ||
2208 (Table()->Footer() == this && IsRepeatingFooterGroup())) {
2159 transform_state.Flatten(); 2209 transform_state.Flatten();
2160 FloatRect rect = transform_state.LastPlanarQuad().BoundingBox(); 2210 FloatRect rect = transform_state.LastPlanarQuad().BoundingBox();
2161 rect.SetHeight(Table()->LogicalHeight()); 2211 rect.SetHeight(Table()->LogicalHeight());
2162 transform_state.SetQuad(FloatQuad(rect)); 2212 transform_state.SetQuad(FloatQuad(rect));
2213 return Table()->MapToVisualRectInAncestorSpaceInternal(
mstensho (USE GERRIT) 2017/05/08 13:55:59 How can we just skip one ancestor in the chain lik
rhogan 2017/05/09 19:57:16 I've updated the comment to explain this. For both
2214 ancestor, transform_state, flags);
2163 } 2215 }
2164 return LayoutTableBoxComponent::MapToVisualRectInAncestorSpaceInternal( 2216 return LayoutTableBoxComponent::MapToVisualRectInAncestorSpaceInternal(
2165 ancestor, transform_state, flags); 2217 ancestor, transform_state, flags);
2166 } 2218 }
2167 2219
2168 bool LayoutTableSection::PaintedOutputOfObjectHasNoEffectRegardlessOfSize() 2220 bool LayoutTableSection::PaintedOutputOfObjectHasNoEffectRegardlessOfSize()
2169 const { 2221 const {
2170 // LayoutTableSection paints background from columns. 2222 // LayoutTableSection paints background from columns.
2171 if (Table()->HasColElements()) 2223 if (Table()->HasColElements())
2172 return false; 2224 return false;
2173 return LayoutTableBoxComponent:: 2225 return LayoutTableBoxComponent::
2174 PaintedOutputOfObjectHasNoEffectRegardlessOfSize(); 2226 PaintedOutputOfObjectHasNoEffectRegardlessOfSize();
2175 } 2227 }
2176 2228
2177 } // namespace blink 2229 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698