1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "third_party/blink/renderer/core/paint/collapsed_border_painter.h"
6 
7 #include "third_party/blink/renderer/core/paint/block_painter.h"
8 #include "third_party/blink/renderer/core/paint/object_painter.h"
9 #include "third_party/blink/renderer/core/paint/paint_info.h"
10 #include "third_party/blink/renderer/core/paint/scoped_paint_state.h"
11 #include "third_party/blink/renderer/core/paint/table_cell_painter.h"
12 
13 namespace blink {
14 
SetupBorders()15 void CollapsedBorderPainter::SetupBorders() {
16   const auto* values = cell_.GetCollapsedBorderValues();
17   DCHECK(values);
18   if (values->StartBorder().IsVisible()) {
19     start_.value = &values->StartBorder();
20     start_.inner_width = cell_.CollapsedInnerBorderStart();
21     start_.outer_width = cell_.CollapsedOuterBorderStart();
22   } else {
23     start_.value = nullptr;
24   }
25 
26   if (values->EndBorder().IsVisible()) {
27     end_.value = &values->EndBorder();
28     end_.inner_width = cell_.CollapsedInnerBorderEnd();
29     end_.outer_width = cell_.CollapsedOuterBorderEnd();
30   } else {
31     end_.value = nullptr;
32   }
33 
34   if (values->BeforeBorder().IsVisible()) {
35     before_.value = &values->BeforeBorder();
36     before_.inner_width = cell_.CollapsedInnerBorderBefore();
37     before_.outer_width = cell_.CollapsedOuterBorderBefore();
38   } else {
39     before_.value = nullptr;
40   }
41 
42   if (values->AfterBorder().IsVisible()) {
43     after_.value = &values->AfterBorder();
44     after_.inner_width = cell_.CollapsedInnerBorderAfter();
45     after_.outer_width = cell_.CollapsedOuterBorderAfter();
46   } else {
47     after_.value = nullptr;
48   }
49 
50   // At first, let all borders paint the joints. This is to keep the current
51   // behavior for web tests e.g. css2.1/t170602-bdr-conflict-w-01-d.html.
52   // TODO(crbug.com/672216): Determine the best way to deal with this.
53   if (start_.value && before_.value) {
54     start_.begin_outset = before_.outer_width;
55     before_.begin_outset = start_.outer_width;
56   }
57   if (end_.value && before_.value) {
58     end_.begin_outset = before_.outer_width;
59     before_.end_outset = end_.outer_width;
60   }
61   if (start_.value && after_.value) {
62     start_.end_outset = after_.outer_width;
63     after_.begin_outset = start_.outer_width;
64   }
65   if (after_.value && end_.value) {
66     end_.end_outset = after_.outer_width;
67     after_.end_outset = end_.outer_width;
68   }
69 
70   // TODO(crbug.com/727173): We have a lot of bugs about mixed row direction.
71   // Just bail out not to optimize duplicated borders.
72   if (!cell_.Row()->HasSameDirectionAs(&table_) ||
73       !cell_.Section()->HasSameDirectionAs(&table_))
74     return;
75 
76   // Skip painting the start border if it will be painted by the preceding cell
77   // as its end border.
78   if (start_.value) {
79     const auto* cell_preceding = table_.CellPreceding(cell_);
80     if (cell_.StartsAtSameRow(cell_preceding) &&
81         cell_preceding->ResolvedRowSpan() >= cell_.ResolvedRowSpan() &&
82         // |cell_preceding| didn't paint the border if it is invisible.
83         cell_preceding->StyleRef().Visibility() == EVisibility::kVisible) {
84       start_.value = nullptr;
85       // Otherwise we'll still paint the shared border twice which may cause
86       // incorrect border conflict resolution for row/col spanning cells.
87       // TODO(crbug.com/2902 etc.): Paint collapsed borders by grid cells.
88     }
89   }
90 
91   // Skip painting the before border if it will be painted by the above cell
92   // as its after border. If we break page before the row with non-zero strut
93   // (which means a gap between this row and the row above), or if we are
94   // painting the top row of a footer that repeats on each page we need to paint
95   // the before border separately.
96   // TODO(crbug.com/751177) : This will double-paint the top border of the
97   // footer on the last page.
98   if (before_.value && !cell_.Row()->PaginationStrut()) {
99     const auto* cell_above = table_.CellAbove(cell_);
100     if (cell_.StartsAtSameColumn(cell_above) &&
101         cell_above->ColSpan() >= cell_.ColSpan() &&
102         // |cell_above| didn't paint the border if it is invisible.
103         cell_above->StyleRef().Visibility() == EVisibility::kVisible &&
104         cell_above->Row()->HasSameDirectionAs(&table_)) {
105       bool cell_is_top_of_repeating_footer =
106           cell_.Section()->IsRepeatingFooterGroup() &&
107           (!cell_above || cell_above->Section() != cell_.Section());
108       if (!cell_is_top_of_repeating_footer)
109         before_.value = nullptr;
110       // Otherwise we'll still paint the shared border twice which may cause
111       // incorrect border conflict resolution for row/col spanning cells.
112       // TODO(crbug.com/2902 etc.): Paint collapsed borders by grid cells.
113     }
114   }
115 }
116 
GetCollapsedBorderValues(const LayoutTableCell * cell)117 static const CollapsedBorderValues* GetCollapsedBorderValues(
118     const LayoutTableCell* cell) {
119   return cell ? cell->GetCollapsedBorderValues() : nullptr;
120 }
121 
AdjustJoints()122 void CollapsedBorderPainter::AdjustJoints() {
123   // TODO(crbug.com/727173): We have a lot of bugs about mixed row direction.
124   // Just bail out not to adjust the joints.
125   if (!cell_.Row()->HasSameDirectionAs(&table_) ||
126       !cell_.Section()->HasSameDirectionAs(&table_))
127     return;
128 
129   // If we break page before the row with non-zero strut, we need to paint the
130   // before border as if there is no cell above.
131   const auto* cell_above =
132       cell_.Row()->PaginationStrut() ? nullptr : table_.CellAbove(cell_);
133   if (cell_above && (!cell_above->Row()->HasSameDirectionAs(&table_) ||
134                      !cell_above->Section()->HasSameDirectionAs(&table_)))
135     cell_above = nullptr;
136 
137   const auto* cell_below = table_.CellBelow(cell_);
138   if (cell_below && (!cell_below->Row()->HasSameDirectionAs(&table_) ||
139                      !cell_below->Section()->HasSameDirectionAs(&table_)))
140     cell_above = nullptr;
141 
142   const auto* cell_preceding = table_.CellPreceding(cell_);
143   const auto* cell_following = table_.CellFollowing(cell_);
144 
145   const auto* borders_preceding = GetCollapsedBorderValues(cell_preceding);
146   const auto* borders_following = GetCollapsedBorderValues(cell_following);
147   const auto* borders_above = GetCollapsedBorderValues(cell_above);
148   const auto* borders_below = GetCollapsedBorderValues(cell_below);
149 
150   // These variables indicate whether |cell_| forms joints at the corners
151   // with the borders of the adjacent cells. For example,
152   // |before_start_adjoins_preceding| indicates that |cell_| shares the
153   // before-start (logical top-left) corner with |cell_preceding_|.
154   bool before_start_adjoins_preceding =
155       borders_preceding && cell_.StartsAtSameRow(cell_preceding);
156   bool before_start_adjoins_above =
157       borders_above && cell_.StartsAtSameColumn(cell_above);
158   bool before_end_adjoins_above =
159       borders_above && cell_.EndsAtSameColumn(cell_above);
160   bool before_end_adjoins_following =
161       borders_following && cell_.StartsAtSameRow(cell_following);
162   bool after_end_adjoins_following =
163       borders_following && cell_.EndsAtSameRow(cell_following);
164   bool after_end_adjoins_below =
165       borders_below && cell_.EndsAtSameColumn(cell_below);
166   bool after_start_adjoins_below =
167       borders_below && cell_.StartsAtSameColumn(cell_below);
168   bool after_start_adjoins_preceding =
169       borders_preceding && cell_.EndsAtSameRow(cell_preceding);
170 
171   if (start_.value) {
172     if (start_.value->CoversJoint(
173             before_.value,
174             before_start_adjoins_preceding ? &borders_preceding->BeforeBorder()
175                                            : nullptr,
176             before_start_adjoins_above ? &borders_above->StartBorder()
177                                        : nullptr)) {
178       if (before_start_adjoins_preceding) {
179         start_.begin_outset = std::max<int>(
180             start_.begin_outset, cell_preceding->CollapsedOuterBorderBefore());
181       }
182     } else {
183       start_.begin_outset =
184           -std::max<int>(cell_.CollapsedInnerBorderBefore(),
185                          before_start_adjoins_preceding
186                              ? cell_preceding->CollapsedInnerBorderBefore()
187                              : 0);
188     }
189     if (start_.value->CoversJoint(
190             after_.value,
191             after_start_adjoins_preceding ? &borders_preceding->AfterBorder()
192                                           : nullptr,
193             after_start_adjoins_below ? &borders_below->StartBorder()
194                                       : nullptr)) {
195       if (after_start_adjoins_preceding) {
196         start_.end_outset = std::max<int>(
197             start_.end_outset, cell_preceding->CollapsedOuterBorderAfter());
198       }
199     } else {
200       start_.end_outset =
201           -std::max<int>(cell_.CollapsedInnerBorderAfter(),
202                          after_start_adjoins_preceding
203                              ? cell_preceding->CollapsedInnerBorderAfter()
204                              : 0);
205     }
206   }
207 
208   if (end_.value) {
209     if (end_.value->CoversJoint(
210             before_.value,
211             before_end_adjoins_following ? &borders_following->BeforeBorder()
212                                          : nullptr,
213             before_end_adjoins_above ? &borders_above->EndBorder() : nullptr)) {
214       if (before_end_adjoins_following) {
215         end_.begin_outset = std::max<int>(
216             end_.begin_outset, cell_following->CollapsedOuterBorderBefore());
217       }
218     } else {
219       end_.begin_outset =
220           -std::max<int>(cell_.CollapsedInnerBorderBefore(),
221                          before_end_adjoins_following
222                              ? cell_following->CollapsedInnerBorderBefore()
223                              : 0);
224     }
225     if (end_.value->CoversJoint(
226             after_.value,
227             after_end_adjoins_following ? &borders_following->AfterBorder()
228                                         : nullptr,
229             after_end_adjoins_below ? &borders_below->EndBorder() : nullptr)) {
230       if (after_end_adjoins_following) {
231         end_.end_outset = std::max<int>(
232             end_.end_outset, cell_following->CollapsedOuterBorderAfter());
233       }
234     } else {
235       end_.end_outset =
236           -std::max<int>(cell_.CollapsedInnerBorderAfter(),
237                          after_end_adjoins_following
238                              ? cell_following->CollapsedInnerBorderAfter()
239                              : 0);
240     }
241   }
242 
243   if (before_.value) {
244     if (before_.value->CoversJoint(
245             start_.value,
246             before_start_adjoins_preceding ? &borders_preceding->BeforeBorder()
247                                            : nullptr,
248             before_start_adjoins_above ? &borders_above->StartBorder()
249                                        : nullptr)) {
250       if (before_start_adjoins_above) {
251         before_.begin_outset = std::max<int>(
252             before_.begin_outset, cell_above->CollapsedOuterBorderStart());
253       }
254     } else {
255       before_.begin_outset = -std::max<int>(
256           cell_.CollapsedInnerBorderStart(),
257           before_start_adjoins_above ? cell_above->CollapsedInnerBorderStart()
258                                      : 0);
259     }
260     if (before_.value->CoversJoint(
261             end_.value,
262             before_end_adjoins_following ? &borders_following->BeforeBorder()
263                                          : nullptr,
264             before_end_adjoins_above ? &borders_above->EndBorder() : nullptr)) {
265       if (before_end_adjoins_above) {
266         before_.end_outset = std::max<int>(
267             before_.end_outset, cell_above->CollapsedOuterBorderEnd());
268       }
269     } else {
270       before_.end_outset = -std::max<int>(
271           cell_.CollapsedInnerBorderEnd(),
272           before_end_adjoins_above ? cell_above->CollapsedInnerBorderEnd() : 0);
273     }
274   }
275 
276   if (after_.value) {
277     if (after_.value->CoversJoint(
278             start_.value,
279             after_start_adjoins_preceding ? &borders_preceding->AfterBorder()
280                                           : nullptr,
281             after_start_adjoins_below ? &borders_below->StartBorder()
282                                       : nullptr)) {
283       if (after_start_adjoins_below) {
284         after_.begin_outset = std::max<int>(
285             after_.begin_outset, cell_below->CollapsedOuterBorderStart());
286       }
287     } else {
288       after_.begin_outset = -std::max<int>(
289           cell_.CollapsedInnerBorderStart(),
290           after_start_adjoins_below ? cell_below->CollapsedInnerBorderStart()
291                                     : 0);
292     }
293     if (after_.value->CoversJoint(
294             end_.value,
295             after_end_adjoins_following ? &borders_following->AfterBorder()
296                                         : nullptr,
297             after_end_adjoins_below ? &borders_below->EndBorder() : nullptr)) {
298       if (after_end_adjoins_below) {
299         after_.end_outset = std::max<int>(
300             after_.end_outset, cell_below->CollapsedOuterBorderEnd());
301       }
302     } else {
303       after_.end_outset = -std::max<int>(
304           cell_.CollapsedInnerBorderEnd(),
305           after_end_adjoins_below ? cell_below->CollapsedInnerBorderEnd() : 0);
306     }
307   }
308 }
309 
AdjustForWritingModeAndDirection()310 void CollapsedBorderPainter::AdjustForWritingModeAndDirection() {
311   const auto& style = cell_.TableStyle();
312   if (!style.IsLeftToRightDirection()) {
313     std::swap(start_, end_);
314     std::swap(before_.begin_outset, before_.end_outset);
315     std::swap(after_.begin_outset, after_.end_outset);
316   }
317   if (!style.IsHorizontalWritingMode()) {
318     std::swap(after_, end_);
319     std::swap(before_, start_);
320     if (style.IsFlippedBlocksWritingMode()) {
321       std::swap(start_, end_);
322       std::swap(before_.begin_outset, before_.end_outset);
323       std::swap(after_.begin_outset, after_.end_outset);
324     }
325   }
326 }
327 
CollapsedBorderStyle(EBorderStyle style)328 static EBorderStyle CollapsedBorderStyle(EBorderStyle style) {
329   if (style == EBorderStyle::kOutset)
330     return EBorderStyle::kGroove;
331   if (style == EBorderStyle::kInset)
332     return EBorderStyle::kRidge;
333   return style;
334 }
335 
PaintCollapsedBorders(const PaintInfo & paint_info)336 void CollapsedBorderPainter::PaintCollapsedBorders(
337     const PaintInfo& paint_info) {
338   if (cell_.StyleRef().Visibility() != EVisibility::kVisible)
339     return;
340 
341   if (!cell_.GetCollapsedBorderValues())
342     return;
343 
344   GraphicsContext& context = paint_info.context;
345 
346   SetupBorders();
347   if (table_.NeedsAdjustCollapsedBorderJoints())
348     AdjustJoints();
349   AdjustForWritingModeAndDirection();
350   // Now left=start_, right=end_, before_=top, after_=bottom.
351 
352   // Collapsed borders are half inside and half outside of |rect|.
353   ScopedPaintState paint_state(cell_, paint_info);
354   IntRect rect = PixelSnappedIntRect(
355       TableCellPainter(cell_).PaintRectNotIncludingVisualOverflow(
356           paint_state.PaintOffset()));
357   // |paint_rect| covers the whole collapsed borders.
358   IntRect paint_rect = rect;
359   paint_rect.Expand(IntRectOutsets(before_.outer_width, end_.outer_width,
360                                    after_.outer_width, start_.outer_width));
361 
362   // We never paint diagonals at the joins.  We simply let the border with the
363   // highest precedence paint on top of borders with lower precedence.
364   if (before_.value) {
365     ObjectPainter::DrawLineForBoxSide(
366         context, rect.X() - before_.begin_outset,
367         rect.Y() - before_.outer_width, rect.MaxX() + before_.end_outset,
368         rect.Y() + before_.inner_width, BoxSide::kTop,
369         before_.value->GetColor(), CollapsedBorderStyle(before_.value->Style()),
370         0, 0, true);
371   }
372   if (after_.value) {
373     ObjectPainter::DrawLineForBoxSide(
374         context, rect.X() - after_.begin_outset,
375         rect.MaxY() - after_.inner_width, rect.MaxX() + after_.end_outset,
376         rect.MaxY() + after_.outer_width, BoxSide::kBottom,
377         after_.value->GetColor(), CollapsedBorderStyle(after_.value->Style()),
378         0, 0, true);
379   }
380   if (start_.value) {
381     ObjectPainter::DrawLineForBoxSide(
382         context, rect.X() - start_.outer_width, rect.Y() - start_.begin_outset,
383         rect.X() + start_.inner_width, rect.MaxY() + start_.end_outset,
384         BoxSide::kLeft, start_.value->GetColor(),
385         CollapsedBorderStyle(start_.value->Style()), 0, 0, true);
386   }
387   if (end_.value) {
388     ObjectPainter::DrawLineForBoxSide(
389         context, rect.MaxX() - end_.inner_width, rect.Y() - end_.begin_outset,
390         rect.MaxX() + end_.outer_width, rect.MaxY() + end_.end_outset,
391         BoxSide::kRight, end_.value->GetColor(),
392         CollapsedBorderStyle(end_.value->Style()), 0, 0, true);
393   }
394 }
395 
396 }  // namespace blink
397