|
1 /* |
|
2 * Copyright (C) 1997 Martin Jones (mjones@kde.org) |
|
3 * (C) 1997 Torben Weis (weis@kde.org) |
|
4 * (C) 1998 Waldo Bastian (bastian@kde.org) |
|
5 * (C) 1999 Lars Knoll (knoll@kde.org) |
|
6 * (C) 1999 Antti Koivisto (koivisto@kde.org) |
|
7 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. |
|
8 * |
|
9 * This library is free software; you can redistribute it and/or |
|
10 * modify it under the terms of the GNU Library General Public |
|
11 * License as published by the Free Software Foundation; either |
|
12 * version 2 of the License, or (at your option) any later version. |
|
13 * |
|
14 * This library is distributed in the hope that it will be useful, |
|
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
17 * Library General Public License for more details. |
|
18 * |
|
19 * You should have received a copy of the GNU Library General Public License |
|
20 * along with this library; see the file COPYING.LIB. If not, write to |
|
21 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
|
22 * Boston, MA 02110-1301, USA. |
|
23 */ |
|
24 |
|
25 #include "config.h" |
|
26 #include "RenderTableCell.h" |
|
27 |
|
28 #include "FloatQuad.h" |
|
29 #include "GraphicsContext.h" |
|
30 #include "HTMLNames.h" |
|
31 #include "HTMLTableCellElement.h" |
|
32 #include "RenderTableCol.h" |
|
33 #include "RenderView.h" |
|
34 #include "TransformState.h" |
|
35 |
|
36 using namespace std; |
|
37 |
|
38 namespace WebCore { |
|
39 |
|
40 using namespace HTMLNames; |
|
41 |
|
42 RenderTableCell::RenderTableCell(Node* node) |
|
43 : RenderBlock(node) |
|
44 , m_row(-1) |
|
45 , m_column(-1) |
|
46 , m_rowSpan(1) |
|
47 , m_columnSpan(1) |
|
48 , m_intrinsicPaddingTop(0) |
|
49 , m_intrinsicPaddingBottom(0) |
|
50 , m_percentageHeight(0) |
|
51 { |
|
52 updateFromElement(); |
|
53 } |
|
54 |
|
55 void RenderTableCell::destroy() |
|
56 { |
|
57 RenderTableSection* recalcSection = parent() ? section() : 0; |
|
58 |
|
59 RenderBlock::destroy(); |
|
60 |
|
61 if (recalcSection) |
|
62 recalcSection->setNeedsCellRecalc(); |
|
63 } |
|
64 |
|
65 void RenderTableCell::updateFromElement() |
|
66 { |
|
67 Node* n = node(); |
|
68 if (n && (n->hasTagName(tdTag) || n->hasTagName(thTag))) { |
|
69 HTMLTableCellElement* tc = static_cast<HTMLTableCellElement*>(n); |
|
70 int oldRSpan = m_rowSpan; |
|
71 int oldCSpan = m_columnSpan; |
|
72 |
|
73 m_columnSpan = tc->colSpan(); |
|
74 m_rowSpan = tc->rowSpan(); |
|
75 if ((oldRSpan != m_rowSpan || oldCSpan != m_columnSpan) && style() && parent()) { |
|
76 setNeedsLayoutAndPrefWidthsRecalc(); |
|
77 if (section()) |
|
78 section()->setNeedsCellRecalc(); |
|
79 } |
|
80 } |
|
81 } |
|
82 |
|
83 Length RenderTableCell::styleOrColWidth() const |
|
84 { |
|
85 Length w = style()->width(); |
|
86 if (!w.isAuto()) |
|
87 return w; |
|
88 |
|
89 RenderTableCol* tableCol = table()->colElement(col()); |
|
90 |
|
91 if (tableCol) { |
|
92 int colSpanCount = colSpan(); |
|
93 |
|
94 Length colWidthSum = Length(0, Fixed); |
|
95 for (int i = 1; i <= colSpanCount; i++) { |
|
96 Length colWidth = tableCol->style()->width(); |
|
97 |
|
98 // Percentage value should be returned only for colSpan == 1. |
|
99 // Otherwise we return original width for the cell. |
|
100 if (!colWidth.isFixed()) { |
|
101 if (colSpanCount > 1) |
|
102 return w; |
|
103 return colWidth; |
|
104 } |
|
105 |
|
106 colWidthSum = Length(colWidthSum.value() + colWidth.value(), Fixed); |
|
107 |
|
108 tableCol = table()->nextColElement(tableCol); |
|
109 // If no next <col> tag found for the span we just return what we have for now. |
|
110 if (!tableCol) |
|
111 break; |
|
112 } |
|
113 |
|
114 // Column widths specified on <col> apply to the border box of the cell. |
|
115 // Percentages don't need to be handled since they're always treated this way (even when specified on the cells). |
|
116 // See Bugzilla bug 8126 for details. |
|
117 if (colWidthSum.isFixed() && colWidthSum.value() > 0) |
|
118 colWidthSum = Length(max(0, colWidthSum.value() - borderAndPaddingWidth()), Fixed); |
|
119 return colWidthSum; |
|
120 } |
|
121 |
|
122 return w; |
|
123 } |
|
124 |
|
125 void RenderTableCell::calcPrefWidths() |
|
126 { |
|
127 // The child cells rely on the grids up in the sections to do their calcPrefWidths work. Normally the sections are set up early, as table |
|
128 // cells are added, but relayout can cause the cells to be freed, leaving stale pointers in the sections' |
|
129 // grids. We must refresh those grids before the child cells try to use them. |
|
130 table()->recalcSectionsIfNeeded(); |
|
131 |
|
132 RenderBlock::calcPrefWidths(); |
|
133 if (node() && style()->autoWrap()) { |
|
134 // See if nowrap was set. |
|
135 Length w = styleOrColWidth(); |
|
136 String nowrap = static_cast<Element*>(node())->getAttribute(nowrapAttr); |
|
137 if (!nowrap.isNull() && w.isFixed()) |
|
138 // Nowrap is set, but we didn't actually use it because of the |
|
139 // fixed width set on the cell. Even so, it is a WinIE/Moz trait |
|
140 // to make the minwidth of the cell into the fixed width. They do this |
|
141 // even in strict mode, so do not make this a quirk. Affected the top |
|
142 // of hiptop.com. |
|
143 m_minPrefWidth = max(w.value(), m_minPrefWidth); |
|
144 } |
|
145 } |
|
146 |
|
147 void RenderTableCell::calcWidth() |
|
148 { |
|
149 } |
|
150 |
|
151 void RenderTableCell::updateWidth(int w) |
|
152 { |
|
153 if (w != width()) { |
|
154 setWidth(w); |
|
155 setCellWidthChanged(true); |
|
156 } |
|
157 } |
|
158 |
|
159 void RenderTableCell::layout() |
|
160 { |
|
161 layoutBlock(cellWidthChanged()); |
|
162 setCellWidthChanged(false); |
|
163 } |
|
164 |
|
165 int RenderTableCell::paddingTop(bool includeIntrinsicPadding) const |
|
166 { |
|
167 return RenderBlock::paddingTop() + (includeIntrinsicPadding ? intrinsicPaddingTop() : 0); |
|
168 } |
|
169 |
|
170 int RenderTableCell::paddingBottom(bool includeIntrinsicPadding) const |
|
171 { |
|
172 return RenderBlock::paddingBottom() + (includeIntrinsicPadding ? intrinsicPaddingBottom() : 0); |
|
173 } |
|
174 |
|
175 void RenderTableCell::setOverrideSize(int size) |
|
176 { |
|
177 clearIntrinsicPadding(); |
|
178 RenderBlock::setOverrideSize(size); |
|
179 } |
|
180 |
|
181 IntSize RenderTableCell::offsetFromContainer(RenderObject* o, const IntPoint& point) const |
|
182 { |
|
183 ASSERT(o == container()); |
|
184 |
|
185 IntSize offset = RenderBlock::offsetFromContainer(o, point); |
|
186 if (parent()) |
|
187 offset.expand(-parentBox()->x(), -parentBox()->y()); |
|
188 |
|
189 return offset; |
|
190 } |
|
191 |
|
192 IntRect RenderTableCell::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer) |
|
193 { |
|
194 // If the table grid is dirty, we cannot get reliable information about adjoining cells, |
|
195 // so we ignore outside borders. This should not be a problem because it means that |
|
196 // the table is going to recalculate the grid, relayout and repaint its current rect, which |
|
197 // includes any outside borders of this cell. |
|
198 if (!table()->collapseBorders() || table()->needsSectionRecalc()) |
|
199 return RenderBlock::clippedOverflowRectForRepaint(repaintContainer); |
|
200 |
|
201 bool rtl = table()->style()->direction() == RTL; |
|
202 int outlineSize = style()->outlineSize(); |
|
203 int left = max(borderHalfLeft(true), outlineSize); |
|
204 int right = max(borderHalfRight(true), outlineSize); |
|
205 int top = max(borderHalfTop(true), outlineSize); |
|
206 int bottom = max(borderHalfBottom(true), outlineSize); |
|
207 if ((left && !rtl) || (right && rtl)) { |
|
208 if (RenderTableCell* before = table()->cellBefore(this)) { |
|
209 top = max(top, before->borderHalfTop(true)); |
|
210 bottom = max(bottom, before->borderHalfBottom(true)); |
|
211 } |
|
212 } |
|
213 if ((left && rtl) || (right && !rtl)) { |
|
214 if (RenderTableCell* after = table()->cellAfter(this)) { |
|
215 top = max(top, after->borderHalfTop(true)); |
|
216 bottom = max(bottom, after->borderHalfBottom(true)); |
|
217 } |
|
218 } |
|
219 if (top) { |
|
220 if (RenderTableCell* above = table()->cellAbove(this)) { |
|
221 left = max(left, above->borderHalfLeft(true)); |
|
222 right = max(right, above->borderHalfRight(true)); |
|
223 } |
|
224 } |
|
225 if (bottom) { |
|
226 if (RenderTableCell* below = table()->cellBelow(this)) { |
|
227 left = max(left, below->borderHalfLeft(true)); |
|
228 right = max(right, below->borderHalfRight(true)); |
|
229 } |
|
230 } |
|
231 left = max(left, -leftVisibleOverflow()); |
|
232 top = max(top, -topVisibleOverflow()); |
|
233 IntRect r(-left, - top, left + max(width() + right, rightVisibleOverflow()), top + max(height() + bottom, bottomVisibleOverflow())); |
|
234 |
|
235 if (RenderView* v = view()) { |
|
236 // FIXME: layoutDelta needs to be applied in parts before/after transforms and |
|
237 // repaint containers. https://bugs.webkit.org/show_bug.cgi?id=23308 |
|
238 r.move(v->layoutDelta()); |
|
239 } |
|
240 computeRectForRepaint(repaintContainer, r); |
|
241 return r; |
|
242 } |
|
243 |
|
244 void RenderTableCell::computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect& r, bool fixed) |
|
245 { |
|
246 if (repaintContainer == this) |
|
247 return; |
|
248 r.setY(r.y()); |
|
249 RenderView* v = view(); |
|
250 if ((!v || !v->layoutStateEnabled() || repaintContainer) && parent()) |
|
251 r.move(-parentBox()->x(), -parentBox()->y()); // Rows are in the same coordinate space, so don't add their offset in. |
|
252 RenderBlock::computeRectForRepaint(repaintContainer, r, fixed); |
|
253 } |
|
254 |
|
255 int RenderTableCell::baselinePosition(bool firstLine, bool isRootLineBox) const |
|
256 { |
|
257 if (isRootLineBox) |
|
258 return RenderBox::baselinePosition(firstLine, isRootLineBox); |
|
259 |
|
260 // <http://www.w3.org/TR/2007/CR-CSS21-20070719/tables.html#height-layout>: The baseline of a cell is the baseline of |
|
261 // the first in-flow line box in the cell, or the first in-flow table-row in the cell, whichever comes first. If there |
|
262 // is no such line box or table-row, the baseline is the bottom of content edge of the cell box. |
|
263 int firstLineBaseline = firstLineBoxBaseline(); |
|
264 if (firstLineBaseline != -1) |
|
265 return firstLineBaseline; |
|
266 return paddingTop() + borderTop() + contentHeight(); |
|
267 } |
|
268 |
|
269 void RenderTableCell::styleWillChange(StyleDifference diff, const RenderStyle* newStyle) |
|
270 { |
|
271 if (parent() && section() && style() && style()->height() != newStyle->height()) |
|
272 section()->setNeedsCellRecalc(); |
|
273 |
|
274 ASSERT(newStyle->display() == TABLE_CELL); |
|
275 |
|
276 RenderBlock::styleWillChange(diff, newStyle); |
|
277 } |
|
278 |
|
279 void RenderTableCell::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) |
|
280 { |
|
281 RenderBlock::styleDidChange(diff, oldStyle); |
|
282 setHasBoxDecorations(true); |
|
283 } |
|
284 |
|
285 // The following rules apply for resolving conflicts and figuring out which border |
|
286 // to use. |
|
287 // (1) Borders with the 'border-style' of 'hidden' take precedence over all other conflicting |
|
288 // borders. Any border with this value suppresses all borders at this location. |
|
289 // (2) Borders with a style of 'none' have the lowest priority. Only if the border properties of all |
|
290 // the elements meeting at this edge are 'none' will the border be omitted (but note that 'none' is |
|
291 // the default value for the border style.) |
|
292 // (3) If none of the styles are 'hidden' and at least one of them is not 'none', then narrow borders |
|
293 // are discarded in favor of wider ones. If several have the same 'border-width' then styles are preferred |
|
294 // in this order: 'double', 'solid', 'dashed', 'dotted', 'ridge', 'outset', 'groove', and the lowest: 'inset'. |
|
295 // (4) If border styles differ only in color, then a style set on a cell wins over one on a row, |
|
296 // which wins over a row group, column, column group and, lastly, table. It is undefined which color |
|
297 // is used when two elements of the same type disagree. |
|
298 static CollapsedBorderValue compareBorders(const CollapsedBorderValue& border1, const CollapsedBorderValue& border2) |
|
299 { |
|
300 // Sanity check the values passed in. If either is null, return the other. |
|
301 if (!border2.exists()) |
|
302 return border1; |
|
303 if (!border1.exists()) |
|
304 return border2; |
|
305 |
|
306 // Rule #1 above. |
|
307 if (border1.style() == BHIDDEN || border2.style() == BHIDDEN) |
|
308 return CollapsedBorderValue(); // No border should exist at this location. |
|
309 |
|
310 // Rule #2 above. A style of 'none' has lowest priority and always loses to any other border. |
|
311 if (border2.style() == BNONE) |
|
312 return border1; |
|
313 if (border1.style() == BNONE) |
|
314 return border2; |
|
315 |
|
316 // The first part of rule #3 above. Wider borders win. |
|
317 if (border1.width() != border2.width()) |
|
318 return border1.width() > border2.width() ? border1 : border2; |
|
319 |
|
320 // The borders have equal width. Sort by border style. |
|
321 if (border1.style() != border2.style()) |
|
322 return border1.style() > border2.style() ? border1 : border2; |
|
323 |
|
324 // The border have the same width and style. Rely on precedence (cell over row over row group, etc.) |
|
325 return border1.precedence() >= border2.precedence() ? border1 : border2; |
|
326 } |
|
327 |
|
328 CollapsedBorderValue RenderTableCell::collapsedLeftBorder(bool rtl) const |
|
329 { |
|
330 RenderTable* tableElt = table(); |
|
331 bool leftmostColumn; |
|
332 if (!rtl) |
|
333 leftmostColumn = col() == 0; |
|
334 else { |
|
335 int effCol = tableElt->colToEffCol(col() + colSpan() - 1); |
|
336 leftmostColumn = effCol == tableElt->numEffCols() - 1; |
|
337 } |
|
338 |
|
339 // For border left, we need to check, in order of precedence: |
|
340 // (1) Our left border. |
|
341 int left = CSSPropertyBorderLeftColor; |
|
342 int right = CSSPropertyBorderRightColor; |
|
343 CollapsedBorderValue result(&style()->borderLeft(), style()->visitedDependentColor(left), BCELL); |
|
344 |
|
345 // (2) The right border of the cell to the left. |
|
346 RenderTableCell* prevCell = rtl ? tableElt->cellAfter(this) : tableElt->cellBefore(this); |
|
347 if (prevCell) { |
|
348 CollapsedBorderValue prevCellBorder = CollapsedBorderValue(&prevCell->style()->borderRight(), prevCell->style()->visitedDependentColor(right), BCELL); |
|
349 result = rtl ? compareBorders(result, prevCellBorder) : compareBorders(prevCellBorder, result); |
|
350 if (!result.exists()) |
|
351 return result; |
|
352 } else if (leftmostColumn) { |
|
353 // (3) Our row's left border. |
|
354 result = compareBorders(result, CollapsedBorderValue(&parent()->style()->borderLeft(), parent()->style()->visitedDependentColor(left), BROW)); |
|
355 if (!result.exists()) |
|
356 return result; |
|
357 |
|
358 // (4) Our row group's left border. |
|
359 result = compareBorders(result, CollapsedBorderValue(§ion()->style()->borderLeft(), section()->style()->visitedDependentColor(left), BROWGROUP)); |
|
360 if (!result.exists()) |
|
361 return result; |
|
362 } |
|
363 |
|
364 // (5) Our column and column group's left borders. |
|
365 bool startColEdge; |
|
366 bool endColEdge; |
|
367 RenderTableCol* colElt = tableElt->colElement(col() + (rtl ? colSpan() - 1 : 0), &startColEdge, &endColEdge); |
|
368 if (colElt && (!rtl ? startColEdge : endColEdge)) { |
|
369 result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderLeft(), colElt->style()->visitedDependentColor(left), BCOL)); |
|
370 if (!result.exists()) |
|
371 return result; |
|
372 if (colElt->parent()->isTableCol() && (!rtl ? !colElt->previousSibling() : !colElt->nextSibling())) { |
|
373 result = compareBorders(result, CollapsedBorderValue(&colElt->parent()->style()->borderLeft(), colElt->parent()->style()->visitedDependentColor(left), BCOLGROUP)); |
|
374 if (!result.exists()) |
|
375 return result; |
|
376 } |
|
377 } |
|
378 |
|
379 // (6) The right border of the column to the left. |
|
380 if (!leftmostColumn) { |
|
381 colElt = tableElt->colElement(col() + (rtl ? colSpan() : -1), &startColEdge, &endColEdge); |
|
382 if (colElt && (!rtl ? endColEdge : startColEdge)) { |
|
383 CollapsedBorderValue rightBorder = CollapsedBorderValue(&colElt->style()->borderRight(), colElt->style()->visitedDependentColor(right), BCOL); |
|
384 result = rtl ? compareBorders(result, rightBorder) : compareBorders(rightBorder, result); |
|
385 if (!result.exists()) |
|
386 return result; |
|
387 } |
|
388 } else { |
|
389 // (7) The table's left border. |
|
390 result = compareBorders(result, CollapsedBorderValue(&tableElt->style()->borderLeft(), tableElt->style()->visitedDependentColor(left), BTABLE)); |
|
391 if (!result.exists()) |
|
392 return result; |
|
393 } |
|
394 |
|
395 return result; |
|
396 } |
|
397 |
|
398 CollapsedBorderValue RenderTableCell::collapsedRightBorder(bool rtl) const |
|
399 { |
|
400 RenderTable* tableElt = table(); |
|
401 bool rightmostColumn; |
|
402 if (rtl) |
|
403 rightmostColumn = col() == 0; |
|
404 else { |
|
405 int effCol = tableElt->colToEffCol(col() + colSpan() - 1); |
|
406 rightmostColumn = effCol == tableElt->numEffCols() - 1; |
|
407 } |
|
408 |
|
409 // For border right, we need to check, in order of precedence: |
|
410 // (1) Our right border. |
|
411 int left = CSSPropertyBorderLeftColor; |
|
412 int right = CSSPropertyBorderRightColor; |
|
413 CollapsedBorderValue result = CollapsedBorderValue(&style()->borderRight(), style()->visitedDependentColor(right), BCELL); |
|
414 |
|
415 // (2) The left border of the cell to the right. |
|
416 if (!rightmostColumn) { |
|
417 RenderTableCell* nextCell = rtl ? tableElt->cellBefore(this) : tableElt->cellAfter(this); |
|
418 if (nextCell && nextCell->style()) { |
|
419 CollapsedBorderValue leftBorder = CollapsedBorderValue(&nextCell->style()->borderLeft(), nextCell->style()->visitedDependentColor(left), BCELL); |
|
420 result = rtl ? compareBorders(leftBorder, result) : compareBorders(result, leftBorder); |
|
421 if (!result.exists()) |
|
422 return result; |
|
423 } |
|
424 } else { |
|
425 // (3) Our row's right border. |
|
426 result = compareBorders(result, CollapsedBorderValue(&parent()->style()->borderRight(), parent()->style()->visitedDependentColor(right), BROW)); |
|
427 if (!result.exists()) |
|
428 return result; |
|
429 |
|
430 // (4) Our row group's right border. |
|
431 result = compareBorders(result, CollapsedBorderValue(§ion()->style()->borderRight(), section()->style()->visitedDependentColor(right), BROWGROUP)); |
|
432 if (!result.exists()) |
|
433 return result; |
|
434 } |
|
435 |
|
436 // (5) Our column and column group's right borders. |
|
437 bool startColEdge; |
|
438 bool endColEdge; |
|
439 RenderTableCol* colElt = tableElt->colElement(col() + (rtl ? 0 : colSpan() - 1), &startColEdge, &endColEdge); |
|
440 if (colElt && (!rtl ? endColEdge : startColEdge)) { |
|
441 result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderRight(), colElt->style()->visitedDependentColor(right), BCOL)); |
|
442 if (!result.exists()) |
|
443 return result; |
|
444 if (colElt->parent()->isTableCol() && (!rtl ? !colElt->nextSibling() : !colElt->previousSibling())) { |
|
445 result = compareBorders(result, CollapsedBorderValue(&colElt->parent()->style()->borderRight(), colElt->parent()->style()->visitedDependentColor(right), BCOLGROUP)); |
|
446 if (!result.exists()) |
|
447 return result; |
|
448 } |
|
449 } |
|
450 |
|
451 // (6) The left border of the column to the right. |
|
452 if (!rightmostColumn) { |
|
453 colElt = tableElt->colElement(col() + (rtl ? -1 : colSpan()), &startColEdge, &endColEdge); |
|
454 if (colElt && (!rtl ? startColEdge : endColEdge)) { |
|
455 CollapsedBorderValue leftBorder = CollapsedBorderValue(&colElt->style()->borderLeft(), colElt->style()->visitedDependentColor(left), BCOL); |
|
456 result = rtl ? compareBorders(leftBorder, result) : compareBorders(result, leftBorder); |
|
457 if (!result.exists()) |
|
458 return result; |
|
459 } |
|
460 } else { |
|
461 // (7) The table's right border. |
|
462 result = compareBorders(result, CollapsedBorderValue(&tableElt->style()->borderRight(), tableElt->style()->visitedDependentColor(right), BTABLE)); |
|
463 if (!result.exists()) |
|
464 return result; |
|
465 } |
|
466 |
|
467 return result; |
|
468 } |
|
469 |
|
470 CollapsedBorderValue RenderTableCell::collapsedTopBorder() const |
|
471 { |
|
472 // For border top, we need to check, in order of precedence: |
|
473 // (1) Our top border. |
|
474 int top = CSSPropertyBorderTopColor; |
|
475 int bottom = CSSPropertyBorderBottomColor; |
|
476 CollapsedBorderValue result = CollapsedBorderValue(&style()->borderTop(), style()->visitedDependentColor(top), BCELL); |
|
477 |
|
478 RenderTableCell* prevCell = table()->cellAbove(this); |
|
479 if (prevCell) { |
|
480 // (2) A previous cell's bottom border. |
|
481 result = compareBorders(CollapsedBorderValue(&prevCell->style()->borderBottom(), prevCell->style()->visitedDependentColor(bottom), BCELL), result); |
|
482 if (!result.exists()) |
|
483 return result; |
|
484 } |
|
485 |
|
486 // (3) Our row's top border. |
|
487 result = compareBorders(result, CollapsedBorderValue(&parent()->style()->borderTop(), parent()->style()->visitedDependentColor(top), BROW)); |
|
488 if (!result.exists()) |
|
489 return result; |
|
490 |
|
491 // (4) The previous row's bottom border. |
|
492 if (prevCell) { |
|
493 RenderObject* prevRow = 0; |
|
494 if (prevCell->section() == section()) |
|
495 prevRow = parent()->previousSibling(); |
|
496 else |
|
497 prevRow = prevCell->section()->lastChild(); |
|
498 |
|
499 if (prevRow) { |
|
500 result = compareBorders(CollapsedBorderValue(&prevRow->style()->borderBottom(), prevRow->style()->visitedDependentColor(bottom), BROW), result); |
|
501 if (!result.exists()) |
|
502 return result; |
|
503 } |
|
504 } |
|
505 |
|
506 // Now check row groups. |
|
507 RenderTableSection* currSection = section(); |
|
508 if (!row()) { |
|
509 // (5) Our row group's top border. |
|
510 result = compareBorders(result, CollapsedBorderValue(&currSection->style()->borderTop(), currSection->style()->visitedDependentColor(top), BROWGROUP)); |
|
511 if (!result.exists()) |
|
512 return result; |
|
513 |
|
514 // (6) Previous row group's bottom border. |
|
515 currSection = table()->sectionAbove(currSection); |
|
516 if (currSection) { |
|
517 result = compareBorders(CollapsedBorderValue(&currSection->style()->borderBottom(), currSection->style()->visitedDependentColor(bottom), BROWGROUP), result); |
|
518 if (!result.exists()) |
|
519 return result; |
|
520 } |
|
521 } |
|
522 |
|
523 if (!currSection) { |
|
524 // (8) Our column and column group's top borders. |
|
525 RenderTableCol* colElt = table()->colElement(col()); |
|
526 if (colElt) { |
|
527 result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderTop(), colElt->style()->visitedDependentColor(top), BCOL)); |
|
528 if (!result.exists()) |
|
529 return result; |
|
530 if (colElt->parent()->isTableCol()) { |
|
531 result = compareBorders(result, CollapsedBorderValue(&colElt->parent()->style()->borderTop(), colElt->parent()->style()->visitedDependentColor(top), BCOLGROUP)); |
|
532 if (!result.exists()) |
|
533 return result; |
|
534 } |
|
535 } |
|
536 |
|
537 // (9) The table's top border. |
|
538 RenderTable* enclosingTable = table(); |
|
539 result = compareBorders(result, CollapsedBorderValue(&enclosingTable->style()->borderTop(), enclosingTable->style()->visitedDependentColor(top), BTABLE)); |
|
540 if (!result.exists()) |
|
541 return result; |
|
542 } |
|
543 |
|
544 return result; |
|
545 } |
|
546 |
|
547 CollapsedBorderValue RenderTableCell::collapsedBottomBorder() const |
|
548 { |
|
549 // For border top, we need to check, in order of precedence: |
|
550 // (1) Our bottom border. |
|
551 int top = CSSPropertyBorderTopColor; |
|
552 int bottom = CSSPropertyBorderBottomColor; |
|
553 CollapsedBorderValue result = CollapsedBorderValue(&style()->borderBottom(), style()->visitedDependentColor(bottom), BCELL); |
|
554 |
|
555 RenderTableCell* nextCell = table()->cellBelow(this); |
|
556 if (nextCell) { |
|
557 // (2) A following cell's top border. |
|
558 result = compareBorders(result, CollapsedBorderValue(&nextCell->style()->borderTop(), nextCell->style()->visitedDependentColor(top), BCELL)); |
|
559 if (!result.exists()) |
|
560 return result; |
|
561 } |
|
562 |
|
563 // (3) Our row's bottom border. (FIXME: Deal with rowspan!) |
|
564 result = compareBorders(result, CollapsedBorderValue(&parent()->style()->borderBottom(), parent()->style()->visitedDependentColor(bottom), BROW)); |
|
565 if (!result.exists()) |
|
566 return result; |
|
567 |
|
568 // (4) The next row's top border. |
|
569 if (nextCell) { |
|
570 result = compareBorders(result, CollapsedBorderValue(&nextCell->parent()->style()->borderTop(), nextCell->parent()->style()->visitedDependentColor(top), BROW)); |
|
571 if (!result.exists()) |
|
572 return result; |
|
573 } |
|
574 |
|
575 // Now check row groups. |
|
576 RenderTableSection* currSection = section(); |
|
577 if (row() + rowSpan() >= currSection->numRows()) { |
|
578 // (5) Our row group's bottom border. |
|
579 result = compareBorders(result, CollapsedBorderValue(&currSection->style()->borderBottom(), currSection->style()->visitedDependentColor(bottom), BROWGROUP)); |
|
580 if (!result.exists()) |
|
581 return result; |
|
582 |
|
583 // (6) Following row group's top border. |
|
584 currSection = table()->sectionBelow(currSection); |
|
585 if (currSection) { |
|
586 result = compareBorders(result, CollapsedBorderValue(&currSection->style()->borderTop(), currSection->style()->visitedDependentColor(top), BROWGROUP)); |
|
587 if (!result.exists()) |
|
588 return result; |
|
589 } |
|
590 } |
|
591 |
|
592 if (!currSection) { |
|
593 // (8) Our column and column group's bottom borders. |
|
594 RenderTableCol* colElt = table()->colElement(col()); |
|
595 if (colElt) { |
|
596 result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderBottom(), colElt->style()->visitedDependentColor(bottom), BCOL)); |
|
597 if (!result.exists()) return result; |
|
598 if (colElt->parent()->isTableCol()) { |
|
599 result = compareBorders(result, CollapsedBorderValue(&colElt->parent()->style()->borderBottom(), colElt->parent()->style()->visitedDependentColor(bottom), BCOLGROUP)); |
|
600 if (!result.exists()) |
|
601 return result; |
|
602 } |
|
603 } |
|
604 |
|
605 // (9) The table's bottom border. |
|
606 RenderTable* enclosingTable = table(); |
|
607 result = compareBorders(result, CollapsedBorderValue(&enclosingTable->style()->borderBottom(), enclosingTable->style()->visitedDependentColor(bottom), BTABLE)); |
|
608 if (!result.exists()) |
|
609 return result; |
|
610 } |
|
611 |
|
612 return result; |
|
613 } |
|
614 |
|
615 int RenderTableCell::borderLeft() const |
|
616 { |
|
617 return table()->collapseBorders() ? borderHalfLeft(false) : RenderBlock::borderLeft(); |
|
618 } |
|
619 |
|
620 int RenderTableCell::borderRight() const |
|
621 { |
|
622 return table()->collapseBorders() ? borderHalfRight(false) : RenderBlock::borderRight(); |
|
623 } |
|
624 |
|
625 int RenderTableCell::borderTop() const |
|
626 { |
|
627 return table()->collapseBorders() ? borderHalfTop(false) : RenderBlock::borderTop(); |
|
628 } |
|
629 |
|
630 int RenderTableCell::borderBottom() const |
|
631 { |
|
632 return table()->collapseBorders() ? borderHalfBottom(false) : RenderBlock::borderBottom(); |
|
633 } |
|
634 |
|
635 int RenderTableCell::borderHalfLeft(bool outer) const |
|
636 { |
|
637 CollapsedBorderValue border = collapsedLeftBorder(table()->style()->direction() == RTL); |
|
638 if (border.exists()) |
|
639 return (border.width() + (outer ? 0 : 1)) / 2; // Give the extra pixel to top and left. |
|
640 return 0; |
|
641 } |
|
642 |
|
643 int RenderTableCell::borderHalfRight(bool outer) const |
|
644 { |
|
645 CollapsedBorderValue border = collapsedRightBorder(table()->style()->direction() == RTL); |
|
646 if (border.exists()) |
|
647 return (border.width() + (outer ? 1 : 0)) / 2; |
|
648 return 0; |
|
649 } |
|
650 |
|
651 int RenderTableCell::borderHalfTop(bool outer) const |
|
652 { |
|
653 CollapsedBorderValue border = collapsedTopBorder(); |
|
654 if (border.exists()) |
|
655 return (border.width() + (outer ? 0 : 1)) / 2; // Give the extra pixel to top and left. |
|
656 return 0; |
|
657 } |
|
658 |
|
659 int RenderTableCell::borderHalfBottom(bool outer) const |
|
660 { |
|
661 CollapsedBorderValue border = collapsedBottomBorder(); |
|
662 if (border.exists()) |
|
663 return (border.width() + (outer ? 1 : 0)) / 2; |
|
664 return 0; |
|
665 } |
|
666 |
|
667 void RenderTableCell::paint(PaintInfo& paintInfo, int tx, int ty) |
|
668 { |
|
669 if (paintInfo.phase == PaintPhaseCollapsedTableBorders && style()->visibility() == VISIBLE) { |
|
670 if (!paintInfo.shouldPaintWithinRoot(this)) |
|
671 return; |
|
672 |
|
673 tx += x(); |
|
674 ty += y(); |
|
675 int os = 2 * maximalOutlineSize(paintInfo.phase); |
|
676 if (ty - table()->outerBorderTop() < paintInfo.rect.bottom() + os && |
|
677 ty + height() + table()->outerBorderBottom() > paintInfo.rect.y() - os) |
|
678 paintCollapsedBorder(paintInfo.context, tx, ty, width(), height()); |
|
679 return; |
|
680 } |
|
681 |
|
682 RenderBlock::paint(paintInfo, tx, ty); |
|
683 } |
|
684 |
|
685 static EBorderStyle collapsedBorderStyle(EBorderStyle style) |
|
686 { |
|
687 if (style == OUTSET) |
|
688 return GROOVE; |
|
689 if (style == INSET) |
|
690 return RIDGE; |
|
691 return style; |
|
692 } |
|
693 |
|
694 struct CollapsedBorder { |
|
695 CollapsedBorderValue borderValue; |
|
696 BoxSide side; |
|
697 bool shouldPaint; |
|
698 int x1; |
|
699 int y1; |
|
700 int x2; |
|
701 int y2; |
|
702 EBorderStyle style; |
|
703 }; |
|
704 |
|
705 class CollapsedBorders { |
|
706 public: |
|
707 CollapsedBorders() |
|
708 : m_count(0) |
|
709 { |
|
710 } |
|
711 |
|
712 void addBorder(const CollapsedBorderValue& borderValue, BoxSide borderSide, bool shouldPaint, |
|
713 int x1, int y1, int x2, int y2, EBorderStyle borderStyle) |
|
714 { |
|
715 if (borderValue.exists() && shouldPaint) { |
|
716 m_borders[m_count].borderValue = borderValue; |
|
717 m_borders[m_count].side = borderSide; |
|
718 m_borders[m_count].shouldPaint = shouldPaint; |
|
719 m_borders[m_count].x1 = x1; |
|
720 m_borders[m_count].x2 = x2; |
|
721 m_borders[m_count].y1 = y1; |
|
722 m_borders[m_count].y2 = y2; |
|
723 m_borders[m_count].style = borderStyle; |
|
724 m_count++; |
|
725 } |
|
726 } |
|
727 |
|
728 CollapsedBorder* nextBorder() |
|
729 { |
|
730 for (int i = 0; i < m_count; i++) { |
|
731 if (m_borders[i].borderValue.exists() && m_borders[i].shouldPaint) { |
|
732 m_borders[i].shouldPaint = false; |
|
733 return &m_borders[i]; |
|
734 } |
|
735 } |
|
736 |
|
737 return 0; |
|
738 } |
|
739 |
|
740 CollapsedBorder m_borders[4]; |
|
741 int m_count; |
|
742 }; |
|
743 |
|
744 static void addBorderStyle(RenderTableCell::CollapsedBorderStyles& borderStyles, CollapsedBorderValue borderValue) |
|
745 { |
|
746 if (!borderValue.exists()) |
|
747 return; |
|
748 size_t count = borderStyles.size(); |
|
749 for (size_t i = 0; i < count; ++i) |
|
750 if (borderStyles[i] == borderValue) |
|
751 return; |
|
752 borderStyles.append(borderValue); |
|
753 } |
|
754 |
|
755 void RenderTableCell::collectBorderStyles(CollapsedBorderStyles& borderStyles) const |
|
756 { |
|
757 bool rtl = table()->style()->direction() == RTL; |
|
758 addBorderStyle(borderStyles, collapsedLeftBorder(rtl)); |
|
759 addBorderStyle(borderStyles, collapsedRightBorder(rtl)); |
|
760 addBorderStyle(borderStyles, collapsedTopBorder()); |
|
761 addBorderStyle(borderStyles, collapsedBottomBorder()); |
|
762 } |
|
763 |
|
764 static int compareBorderStylesForQSort(const void* pa, const void* pb) |
|
765 { |
|
766 const CollapsedBorderValue* a = static_cast<const CollapsedBorderValue*>(pa); |
|
767 const CollapsedBorderValue* b = static_cast<const CollapsedBorderValue*>(pb); |
|
768 if (*a == *b) |
|
769 return 0; |
|
770 CollapsedBorderValue borderWithHigherPrecedence = compareBorders(*a, *b); |
|
771 if (*a == borderWithHigherPrecedence) |
|
772 return 1; |
|
773 return -1; |
|
774 } |
|
775 |
|
776 void RenderTableCell::sortBorderStyles(CollapsedBorderStyles& borderStyles) |
|
777 { |
|
778 qsort(borderStyles.data(), borderStyles.size(), sizeof(CollapsedBorderValue), |
|
779 compareBorderStylesForQSort); |
|
780 } |
|
781 |
|
782 void RenderTableCell::paintCollapsedBorder(GraphicsContext* graphicsContext, int tx, int ty, int w, int h) |
|
783 { |
|
784 if (!table()->currentBorderStyle()) |
|
785 return; |
|
786 |
|
787 bool rtl = table()->style()->direction() == RTL; |
|
788 CollapsedBorderValue leftVal = collapsedLeftBorder(rtl); |
|
789 CollapsedBorderValue rightVal = collapsedRightBorder(rtl); |
|
790 CollapsedBorderValue topVal = collapsedTopBorder(); |
|
791 CollapsedBorderValue bottomVal = collapsedBottomBorder(); |
|
792 |
|
793 // Adjust our x/y/width/height so that we paint the collapsed borders at the correct location. |
|
794 int topWidth = topVal.width(); |
|
795 int bottomWidth = bottomVal.width(); |
|
796 int leftWidth = leftVal.width(); |
|
797 int rightWidth = rightVal.width(); |
|
798 |
|
799 tx -= leftWidth / 2; |
|
800 ty -= topWidth / 2; |
|
801 w += leftWidth / 2 + (rightWidth + 1) / 2; |
|
802 h += topWidth / 2 + (bottomWidth + 1) / 2; |
|
803 |
|
804 EBorderStyle topStyle = collapsedBorderStyle(topVal.style()); |
|
805 EBorderStyle bottomStyle = collapsedBorderStyle(bottomVal.style()); |
|
806 EBorderStyle leftStyle = collapsedBorderStyle(leftVal.style()); |
|
807 EBorderStyle rightStyle = collapsedBorderStyle(rightVal.style()); |
|
808 |
|
809 bool renderTop = topStyle > BHIDDEN && !topVal.isTransparent(); |
|
810 bool renderBottom = bottomStyle > BHIDDEN && !bottomVal.isTransparent(); |
|
811 bool renderLeft = leftStyle > BHIDDEN && !leftVal.isTransparent(); |
|
812 bool renderRight = rightStyle > BHIDDEN && !rightVal.isTransparent(); |
|
813 |
|
814 // We never paint diagonals at the joins. We simply let the border with the highest |
|
815 // precedence paint on top of borders with lower precedence. |
|
816 CollapsedBorders borders; |
|
817 borders.addBorder(topVal, BSTop, renderTop, tx, ty, tx + w, ty + topWidth, topStyle); |
|
818 borders.addBorder(bottomVal, BSBottom, renderBottom, tx, ty + h - bottomWidth, tx + w, ty + h, bottomStyle); |
|
819 borders.addBorder(leftVal, BSLeft, renderLeft, tx, ty, tx + leftWidth, ty + h, leftStyle); |
|
820 borders.addBorder(rightVal, BSRight, renderRight, tx + w - rightWidth, ty, tx + w, ty + h, rightStyle); |
|
821 |
|
822 for (CollapsedBorder* border = borders.nextBorder(); border; border = borders.nextBorder()) { |
|
823 if (border->borderValue == *table()->currentBorderStyle()) |
|
824 drawLineForBoxSide(graphicsContext, border->x1, border->y1, border->x2, border->y2, border->side, |
|
825 border->borderValue.color(), border->style, 0, 0); |
|
826 } |
|
827 } |
|
828 |
|
829 void RenderTableCell::paintBackgroundsBehindCell(PaintInfo& paintInfo, int tx, int ty, RenderObject* backgroundObject) |
|
830 { |
|
831 if (!paintInfo.shouldPaintWithinRoot(this)) |
|
832 return; |
|
833 |
|
834 if (!backgroundObject) |
|
835 return; |
|
836 |
|
837 if (style()->visibility() != VISIBLE) |
|
838 return; |
|
839 |
|
840 RenderTable* tableElt = table(); |
|
841 if (!tableElt->collapseBorders() && style()->emptyCells() == HIDE && !firstChild()) |
|
842 return; |
|
843 |
|
844 if (backgroundObject != this) { |
|
845 tx += x(); |
|
846 ty += y(); |
|
847 } |
|
848 |
|
849 int w = width(); |
|
850 int h = height(); |
|
851 |
|
852 Color c = backgroundObject->style()->visitedDependentColor(CSSPropertyBackgroundColor); |
|
853 const FillLayer* bgLayer = backgroundObject->style()->backgroundLayers(); |
|
854 |
|
855 if (bgLayer->hasImage() || c.isValid()) { |
|
856 // We have to clip here because the background would paint |
|
857 // on top of the borders otherwise. This only matters for cells and rows. |
|
858 bool shouldClip = backgroundObject->hasLayer() && (backgroundObject == this || backgroundObject == parent()) && tableElt->collapseBorders(); |
|
859 if (shouldClip) { |
|
860 IntRect clipRect(tx + borderLeft(), ty + borderTop(), |
|
861 w - borderLeft() - borderRight(), h - borderTop() - borderBottom()); |
|
862 paintInfo.context->save(); |
|
863 paintInfo.context->clip(clipRect); |
|
864 } |
|
865 paintFillLayers(paintInfo, c, bgLayer, tx, ty, w, h, CompositeSourceOver, backgroundObject); |
|
866 if (shouldClip) |
|
867 paintInfo.context->restore(); |
|
868 } |
|
869 } |
|
870 |
|
871 void RenderTableCell::paintBoxDecorations(PaintInfo& paintInfo, int tx, int ty) |
|
872 { |
|
873 if (!paintInfo.shouldPaintWithinRoot(this)) |
|
874 return; |
|
875 |
|
876 RenderTable* tableElt = table(); |
|
877 if (!tableElt->collapseBorders() && style()->emptyCells() == HIDE && !firstChild()) |
|
878 return; |
|
879 |
|
880 int w = width(); |
|
881 int h = height(); |
|
882 |
|
883 if (style()->boxShadow()) |
|
884 paintBoxShadow(paintInfo.context, tx, ty, w, h, style(), Normal); |
|
885 |
|
886 // Paint our cell background. |
|
887 paintBackgroundsBehindCell(paintInfo, tx, ty, this); |
|
888 if (style()->boxShadow()) |
|
889 paintBoxShadow(paintInfo.context, tx, ty, w, h, style(), Inset); |
|
890 |
|
891 if (!style()->hasBorder() || tableElt->collapseBorders()) |
|
892 return; |
|
893 |
|
894 paintBorder(paintInfo.context, tx, ty, w, h, style()); |
|
895 } |
|
896 |
|
897 void RenderTableCell::paintMask(PaintInfo& paintInfo, int tx, int ty) |
|
898 { |
|
899 if (style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask) |
|
900 return; |
|
901 |
|
902 RenderTable* tableElt = table(); |
|
903 if (!tableElt->collapseBorders() && style()->emptyCells() == HIDE && !firstChild()) |
|
904 return; |
|
905 |
|
906 int w = width(); |
|
907 int h = height(); |
|
908 |
|
909 paintMaskImages(paintInfo, tx, ty, w, h); |
|
910 } |
|
911 |
|
912 } // namespace WebCore |