|
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 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 "GraphicsContext.h" |
|
29 #include "HTMLNames.h" |
|
30 #include "HTMLTableCellElement.h" |
|
31 #include "RenderTableCol.h" |
|
32 #include "RenderView.h" |
|
33 #include "TextStream.h" |
|
34 |
|
35 using namespace std; |
|
36 |
|
37 namespace WebCore { |
|
38 |
|
39 using namespace HTMLNames; |
|
40 |
|
41 RenderTableCell::RenderTableCell(Node* node) |
|
42 : RenderBlock(node) |
|
43 , m_row(-1) |
|
44 , m_column(-1) |
|
45 , m_rowSpan(1) |
|
46 , m_columnSpan(1) |
|
47 , m_topExtra(0) |
|
48 , m_bottomExtra(0) |
|
49 , m_widthChanged(false) |
|
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* node = element(); |
|
68 if (node && (node->hasTagName(tdTag) || node->hasTagName(thTag))) { |
|
69 HTMLTableCellElement* tc = static_cast<HTMLTableCellElement*>(node); |
|
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 (colSpan() > 1 || !w.isAuto()) |
|
87 return w; |
|
88 RenderTableCol* tableCol = table()->colElement(col()); |
|
89 if (tableCol) { |
|
90 w = tableCol->style()->width(); |
|
91 |
|
92 // Column widths specified on <col> apply to the border box of the cell. |
|
93 // Percentages don't need to be handled since they're always treated this way (even when specified on the cells). |
|
94 // See Bugzilla bug 8126 for details. |
|
95 if (w.isFixed() && w.value() > 0) |
|
96 w = Length(max(0, w.value() - borderLeft() - borderRight() - paddingLeft() - paddingRight()), Fixed); |
|
97 } |
|
98 return w; |
|
99 } |
|
100 |
|
101 void RenderTableCell::calcPrefWidths() |
|
102 { |
|
103 // 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 |
|
104 // cells are added, but relayout can cause the cells to be freed, leaving stale pointers in the sections' |
|
105 // grids. We must refresh those grids before the child cells try to use them. |
|
106 table()->recalcSectionsIfNeeded(); |
|
107 |
|
108 RenderBlock::calcPrefWidths(); |
|
109 if (element() && style()->autoWrap()) { |
|
110 // See if nowrap was set. |
|
111 Length w = styleOrColWidth(); |
|
112 String nowrap = static_cast<Element*>(element())->getAttribute(nowrapAttr); |
|
113 if (!nowrap.isNull() && w.isFixed()) |
|
114 // Nowrap is set, but we didn't actually use it because of the |
|
115 // fixed width set on the cell. Even so, it is a WinIE/Moz trait |
|
116 // to make the minwidth of the cell into the fixed width. They do this |
|
117 // even in strict mode, so do not make this a quirk. Affected the top |
|
118 // of hiptop.com. |
|
119 m_minPrefWidth = max(w.value(), m_minPrefWidth); |
|
120 } |
|
121 } |
|
122 |
|
123 void RenderTableCell::calcWidth() |
|
124 { |
|
125 } |
|
126 |
|
127 void RenderTableCell::setWidth(int width) |
|
128 { |
|
129 if (width != m_width) { |
|
130 m_width = width; |
|
131 m_widthChanged = true; |
|
132 } |
|
133 } |
|
134 |
|
135 void RenderTableCell::layout() |
|
136 { |
|
137 layoutBlock(m_widthChanged); |
|
138 m_widthChanged = false; |
|
139 } |
|
140 |
|
141 IntRect RenderTableCell::absoluteClippedOverflowRect() |
|
142 { |
|
143 // If the table grid is dirty, we cannot get reliable information about adjoining cells, |
|
144 // so we ignore outside borders. This should not be a problem because it means that |
|
145 // the table is going to recalculate the grid, relayout and repaint its current rect, which |
|
146 // includes any outside borders of this cell. |
|
147 if (table()->collapseBorders() && !table()->needsSectionRecalc()) { |
|
148 bool rtl = table()->style()->direction() == RTL; |
|
149 int outlineSize = style()->outlineSize(); |
|
150 int left = max(borderHalfLeft(true), outlineSize); |
|
151 int right = max(borderHalfRight(true), outlineSize); |
|
152 int top = max(borderHalfTop(true), outlineSize); |
|
153 int bottom = max(borderHalfBottom(true), outlineSize); |
|
154 if (left && !rtl || right && rtl) { |
|
155 if (RenderTableCell* before = table()->cellBefore(this)) { |
|
156 top = max(top, before->borderHalfTop(true)); |
|
157 bottom = max(bottom, before->borderHalfBottom(true)); |
|
158 } |
|
159 } |
|
160 if (left && rtl || right && !rtl) { |
|
161 if (RenderTableCell* after = table()->cellAfter(this)) { |
|
162 top = max(top, after->borderHalfTop(true)); |
|
163 bottom = max(bottom, after->borderHalfBottom(true)); |
|
164 } |
|
165 } |
|
166 if (top) { |
|
167 if (RenderTableCell* above = table()->cellAbove(this)) { |
|
168 left = max(left, above->borderHalfLeft(true)); |
|
169 right = max(right, above->borderHalfRight(true)); |
|
170 } |
|
171 } |
|
172 if (bottom) { |
|
173 if (RenderTableCell* below = table()->cellBelow(this)) { |
|
174 left = max(left, below->borderHalfLeft(true)); |
|
175 right = max(right, below->borderHalfRight(true)); |
|
176 } |
|
177 } |
|
178 left = max(left, -overflowLeft(false)); |
|
179 top = max(top, -overflowTop(false) - borderTopExtra()); |
|
180 IntRect r(-left, -borderTopExtra() - top, left + max(width() + right, overflowWidth(false)), borderTopExtra() + top + max(height() + bottom + borderBottomExtra(), overflowHeight(false))); |
|
181 |
|
182 if (RenderView* v = view()) |
|
183 r.move(v->layoutDelta()); |
|
184 |
|
185 computeAbsoluteRepaintRect(r); |
|
186 return r; |
|
187 } |
|
188 return RenderBlock::absoluteClippedOverflowRect(); |
|
189 } |
|
190 |
|
191 void RenderTableCell::computeAbsoluteRepaintRect(IntRect& r, bool fixed) |
|
192 { |
|
193 r.setY(r.y() + m_topExtra); |
|
194 RenderView* v = view(); |
|
195 if (!v || !v->layoutState()) |
|
196 r.move(-parent()->xPos(), -parent()->yPos()); // Rows are in the same coordinate space, so don't add their offset in. |
|
197 RenderBlock::computeAbsoluteRepaintRect(r, fixed); |
|
198 } |
|
199 |
|
200 bool RenderTableCell::absolutePosition(int& xPos, int& yPos, bool fixed) const |
|
201 { |
|
202 bool result = RenderBlock::absolutePosition(xPos, yPos, fixed); |
|
203 RenderView* v = view(); |
|
204 if (!v || !v->layoutState()) { |
|
205 xPos -= parent()->xPos(); // Rows are in the same coordinate space, so don't add their offset in. |
|
206 yPos -= parent()->yPos(); |
|
207 } |
|
208 return result; |
|
209 } |
|
210 |
|
211 short RenderTableCell::baselinePosition(bool /*firstLine*/, bool /*isRootLineBox*/) const |
|
212 { |
|
213 RenderObject* o = firstChild(); |
|
214 int offset = paddingTop() + borderTop(); |
|
215 |
|
216 if (!o) |
|
217 return offset + contentHeight(); |
|
218 while (o->firstChild() && !o->isReplaced()) { |
|
219 if (!o->isInline()) |
|
220 offset += o->paddingTop() + o->borderTop(); |
|
221 o = o->firstChild(); |
|
222 } |
|
223 |
|
224 if (!o->isInline()) |
|
225 return paddingTop() + borderTop() + contentHeight(); |
|
226 |
|
227 offset += o->baselinePosition(true); |
|
228 return offset; |
|
229 } |
|
230 |
|
231 void RenderTableCell::setStyle(RenderStyle* newStyle) |
|
232 { |
|
233 if (parent() && section() && style() && style()->height() != newStyle->height()) |
|
234 section()->setNeedsCellRecalc(); |
|
235 |
|
236 newStyle->setDisplay(TABLE_CELL); |
|
237 |
|
238 if (newStyle->whiteSpace() == KHTML_NOWRAP) { |
|
239 // Figure out if we are really nowrapping or if we should just |
|
240 // use normal instead. If the width of the cell is fixed, then |
|
241 // we don't actually use NOWRAP. |
|
242 if (newStyle->width().isFixed()) |
|
243 newStyle->setWhiteSpace(NORMAL); |
|
244 else |
|
245 newStyle->setWhiteSpace(NOWRAP); |
|
246 } |
|
247 |
|
248 RenderBlock::setStyle(newStyle); |
|
249 setHasBoxDecorations(true); |
|
250 } |
|
251 |
|
252 bool RenderTableCell::requiresLayer() |
|
253 { |
|
254 return isPositioned() || isTransparent() || hasOverflowClip(); |
|
255 } |
|
256 |
|
257 // The following rules apply for resolving conflicts and figuring out which border |
|
258 // to use. |
|
259 // (1) Borders with the 'border-style' of 'hidden' take precedence over all other conflicting |
|
260 // borders. Any border with this value suppresses all borders at this location. |
|
261 // (2) Borders with a style of 'none' have the lowest priority. Only if the border properties of all |
|
262 // the elements meeting at this edge are 'none' will the border be omitted (but note that 'none' is |
|
263 // the default value for the border style.) |
|
264 // (3) If none of the styles are 'hidden' and at least one of them is not 'none', then narrow borders |
|
265 // are discarded in favor of wider ones. If several have the same 'border-width' then styles are preferred |
|
266 // in this order: 'double', 'solid', 'dashed', 'dotted', 'ridge', 'outset', 'groove', and the lowest: 'inset'. |
|
267 // (4) If border styles differ only in color, then a style set on a cell wins over one on a row, |
|
268 // which wins over a row group, column, column group and, lastly, table. It is undefined which color |
|
269 // is used when two elements of the same type disagree. |
|
270 static CollapsedBorderValue compareBorders(const CollapsedBorderValue& border1, const CollapsedBorderValue& border2) |
|
271 { |
|
272 // Sanity check the values passed in. If either is null, return the other. |
|
273 if (!border2.exists()) |
|
274 return border1; |
|
275 if (!border1.exists()) |
|
276 return border2; |
|
277 |
|
278 // Rule #1 above. |
|
279 if (border1.style() == BHIDDEN || border2.style() == BHIDDEN) |
|
280 return CollapsedBorderValue(); // No border should exist at this location. |
|
281 |
|
282 // Rule #2 above. A style of 'none' has lowest priority and always loses to any other border. |
|
283 if (border2.style() == BNONE) |
|
284 return border1; |
|
285 if (border1.style() == BNONE) |
|
286 return border2; |
|
287 |
|
288 // The first part of rule #3 above. Wider borders win. |
|
289 if (border1.width() != border2.width()) |
|
290 return border1.width() > border2.width() ? border1 : border2; |
|
291 |
|
292 // The borders have equal width. Sort by border style. |
|
293 if (border1.style() != border2.style()) |
|
294 return border1.style() > border2.style() ? border1 : border2; |
|
295 |
|
296 // The border have the same width and style. Rely on precedence (cell over row over row group, etc.) |
|
297 return border1.precedence >= border2.precedence ? border1 : border2; |
|
298 } |
|
299 |
|
300 CollapsedBorderValue RenderTableCell::collapsedLeftBorder(bool rtl) const |
|
301 { |
|
302 RenderTable* tableElt = table(); |
|
303 bool leftmostColumn; |
|
304 if (!rtl) |
|
305 leftmostColumn = col() == 0; |
|
306 else { |
|
307 int effCol = tableElt->colToEffCol(col() + colSpan() - 1); |
|
308 leftmostColumn = effCol == tableElt->numEffCols() - 1; |
|
309 } |
|
310 |
|
311 // For border left, we need to check, in order of precedence: |
|
312 // (1) Our left border. |
|
313 CollapsedBorderValue result(&style()->borderLeft(), BCELL); |
|
314 |
|
315 // (2) The right border of the cell to the left. |
|
316 RenderTableCell* prevCell = rtl ? tableElt->cellAfter(this) : tableElt->cellBefore(this); |
|
317 if (prevCell) { |
|
318 result = compareBorders(result, CollapsedBorderValue(&prevCell->style()->borderRight(), BCELL)); |
|
319 if (!result.exists()) |
|
320 return result; |
|
321 } else if (leftmostColumn) { |
|
322 // (3) Our row's left border. |
|
323 result = compareBorders(result, CollapsedBorderValue(&parent()->style()->borderLeft(), BROW)); |
|
324 if (!result.exists()) |
|
325 return result; |
|
326 |
|
327 // (4) Our row group's left border. |
|
328 result = compareBorders(result, CollapsedBorderValue(§ion()->style()->borderLeft(), BROWGROUP)); |
|
329 if (!result.exists()) |
|
330 return result; |
|
331 } |
|
332 |
|
333 // (5) Our column and column group's left borders. |
|
334 bool startColEdge; |
|
335 bool endColEdge; |
|
336 RenderTableCol* colElt = tableElt->colElement(col() + (rtl ? colSpan() - 1 : 0), &startColEdge, &endColEdge); |
|
337 if (colElt && (!rtl ? startColEdge : endColEdge)) { |
|
338 result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderLeft(), BCOL)); |
|
339 if (!result.exists()) |
|
340 return result; |
|
341 if (colElt->parent()->isTableCol() && (!rtl ? !colElt->previousSibling() : !colElt->nextSibling())) { |
|
342 result = compareBorders(result, CollapsedBorderValue(&colElt->parent()->style()->borderLeft(), BCOLGROUP)); |
|
343 if (!result.exists()) |
|
344 return result; |
|
345 } |
|
346 } |
|
347 |
|
348 // (6) The right border of the column to the left. |
|
349 if (!leftmostColumn) { |
|
350 colElt = tableElt->colElement(col() + (rtl ? colSpan() : -1), &startColEdge, &endColEdge); |
|
351 if (colElt && (!rtl ? endColEdge : startColEdge)) { |
|
352 result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderRight(), BCOL)); |
|
353 if (!result.exists()) |
|
354 return result; |
|
355 } |
|
356 } else { |
|
357 // (7) The table's left border. |
|
358 result = compareBorders(result, CollapsedBorderValue(&tableElt->style()->borderLeft(), BTABLE)); |
|
359 if (!result.exists()) |
|
360 return result; |
|
361 } |
|
362 |
|
363 return result; |
|
364 } |
|
365 |
|
366 CollapsedBorderValue RenderTableCell::collapsedRightBorder(bool rtl) const |
|
367 { |
|
368 RenderTable* tableElt = table(); |
|
369 bool rightmostColumn; |
|
370 if (rtl) |
|
371 rightmostColumn = col() == 0; |
|
372 else { |
|
373 int effCol = tableElt->colToEffCol(col() + colSpan() - 1); |
|
374 rightmostColumn = effCol == tableElt->numEffCols() - 1; |
|
375 } |
|
376 |
|
377 // For border right, we need to check, in order of precedence: |
|
378 // (1) Our right border. |
|
379 CollapsedBorderValue result = CollapsedBorderValue(&style()->borderRight(), BCELL); |
|
380 |
|
381 // (2) The left border of the cell to the right. |
|
382 if (!rightmostColumn) { |
|
383 RenderTableCell* nextCell = rtl ? tableElt->cellBefore(this) : tableElt->cellAfter(this); |
|
384 if (nextCell && nextCell->style()) { |
|
385 result = compareBorders(result, CollapsedBorderValue(&nextCell->style()->borderLeft(), BCELL)); |
|
386 if (!result.exists()) |
|
387 return result; |
|
388 } |
|
389 } else { |
|
390 // (3) Our row's right border. |
|
391 result = compareBorders(result, CollapsedBorderValue(&parent()->style()->borderRight(), BROW)); |
|
392 if (!result.exists()) |
|
393 return result; |
|
394 |
|
395 // (4) Our row group's right border. |
|
396 result = compareBorders(result, CollapsedBorderValue(§ion()->style()->borderRight(), BROWGROUP)); |
|
397 if (!result.exists()) |
|
398 return result; |
|
399 } |
|
400 |
|
401 // (5) Our column and column group's right borders. |
|
402 bool startColEdge; |
|
403 bool endColEdge; |
|
404 RenderTableCol* colElt = tableElt->colElement(col() + (rtl ? 0 : colSpan() - 1), &startColEdge, &endColEdge); |
|
405 if (colElt && (!rtl ? endColEdge : startColEdge)) { |
|
406 result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderRight(), BCOL)); |
|
407 if (!result.exists()) |
|
408 return result; |
|
409 if (colElt->parent()->isTableCol() && (!rtl ? !colElt->nextSibling() : !colElt->previousSibling())) { |
|
410 result = compareBorders(result, CollapsedBorderValue(&colElt->parent()->style()->borderRight(), BCOLGROUP)); |
|
411 if (!result.exists()) |
|
412 return result; |
|
413 } |
|
414 } |
|
415 |
|
416 // (6) The left border of the column to the right. |
|
417 if (!rightmostColumn) { |
|
418 colElt = tableElt->colElement(col() + (rtl ? -1 : colSpan()), &startColEdge, &endColEdge); |
|
419 if (colElt && (!rtl ? startColEdge : endColEdge)) { |
|
420 result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderLeft(), BCOL)); |
|
421 if (!result.exists()) |
|
422 return result; |
|
423 } |
|
424 } else { |
|
425 // (7) The table's right border. |
|
426 result = compareBorders(result, CollapsedBorderValue(&tableElt->style()->borderRight(), BTABLE)); |
|
427 if (!result.exists()) |
|
428 return result; |
|
429 } |
|
430 |
|
431 return result; |
|
432 } |
|
433 |
|
434 CollapsedBorderValue RenderTableCell::collapsedTopBorder() const |
|
435 { |
|
436 // For border top, we need to check, in order of precedence: |
|
437 // (1) Our top border. |
|
438 CollapsedBorderValue result = CollapsedBorderValue(&style()->borderTop(), BCELL); |
|
439 |
|
440 RenderTableCell* prevCell = table()->cellAbove(this); |
|
441 if (prevCell) { |
|
442 // (2) A previous cell's bottom border. |
|
443 result = compareBorders(result, CollapsedBorderValue(&prevCell->style()->borderBottom(), BCELL)); |
|
444 if (!result.exists()) |
|
445 return result; |
|
446 } |
|
447 |
|
448 // (3) Our row's top border. |
|
449 result = compareBorders(result, CollapsedBorderValue(&parent()->style()->borderTop(), BROW)); |
|
450 if (!result.exists()) |
|
451 return result; |
|
452 |
|
453 // (4) The previous row's bottom border. |
|
454 if (prevCell) { |
|
455 RenderObject* prevRow = 0; |
|
456 if (prevCell->section() == section()) |
|
457 prevRow = parent()->previousSibling(); |
|
458 else |
|
459 prevRow = prevCell->section()->lastChild(); |
|
460 |
|
461 if (prevRow) { |
|
462 result = compareBorders(result, CollapsedBorderValue(&prevRow->style()->borderBottom(), BROW)); |
|
463 if (!result.exists()) |
|
464 return result; |
|
465 } |
|
466 } |
|
467 |
|
468 // Now check row groups. |
|
469 RenderTableSection* currSection = section(); |
|
470 if (!row()) { |
|
471 // (5) Our row group's top border. |
|
472 result = compareBorders(result, CollapsedBorderValue(&currSection->style()->borderTop(), BROWGROUP)); |
|
473 if (!result.exists()) |
|
474 return result; |
|
475 |
|
476 // (6) Previous row group's bottom border. |
|
477 currSection = table()->sectionAbove(currSection); |
|
478 if (currSection) { |
|
479 result = compareBorders(result, CollapsedBorderValue(&currSection->style()->borderBottom(), BROWGROUP)); |
|
480 if (!result.exists()) |
|
481 return result; |
|
482 } |
|
483 } |
|
484 |
|
485 if (!currSection) { |
|
486 // (8) Our column and column group's top borders. |
|
487 RenderTableCol* colElt = table()->colElement(col()); |
|
488 if (colElt) { |
|
489 result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderTop(), BCOL)); |
|
490 if (!result.exists()) |
|
491 return result; |
|
492 if (colElt->parent()->isTableCol()) { |
|
493 result = compareBorders(result, CollapsedBorderValue(&colElt->parent()->style()->borderTop(), BCOLGROUP)); |
|
494 if (!result.exists()) |
|
495 return result; |
|
496 } |
|
497 } |
|
498 |
|
499 // (9) The table's top border. |
|
500 result = compareBorders(result, CollapsedBorderValue(&table()->style()->borderTop(), BTABLE)); |
|
501 if (!result.exists()) |
|
502 return result; |
|
503 } |
|
504 |
|
505 return result; |
|
506 } |
|
507 |
|
508 CollapsedBorderValue RenderTableCell::collapsedBottomBorder() const |
|
509 { |
|
510 // For border top, we need to check, in order of precedence: |
|
511 // (1) Our bottom border. |
|
512 CollapsedBorderValue result = CollapsedBorderValue(&style()->borderBottom(), BCELL); |
|
513 |
|
514 RenderTableCell* nextCell = table()->cellBelow(this); |
|
515 if (nextCell) { |
|
516 // (2) A following cell's top border. |
|
517 result = compareBorders(result, CollapsedBorderValue(&nextCell->style()->borderTop(), BCELL)); |
|
518 if (!result.exists()) |
|
519 return result; |
|
520 } |
|
521 |
|
522 // (3) Our row's bottom border. (FIXME: Deal with rowspan!) |
|
523 result = compareBorders(result, CollapsedBorderValue(&parent()->style()->borderBottom(), BROW)); |
|
524 if (!result.exists()) |
|
525 return result; |
|
526 |
|
527 // (4) The next row's top border. |
|
528 if (nextCell) { |
|
529 result = compareBorders(result, CollapsedBorderValue(&nextCell->parent()->style()->borderTop(), BROW)); |
|
530 if (!result.exists()) |
|
531 return result; |
|
532 } |
|
533 |
|
534 // Now check row groups. |
|
535 RenderTableSection* currSection = section(); |
|
536 if (row() + rowSpan() >= static_cast<RenderTableSection*>(currSection)->numRows()) { |
|
537 // (5) Our row group's bottom border. |
|
538 result = compareBorders(result, CollapsedBorderValue(&currSection->style()->borderBottom(), BROWGROUP)); |
|
539 if (!result.exists()) |
|
540 return result; |
|
541 |
|
542 // (6) Following row group's top border. |
|
543 currSection = table()->sectionBelow(currSection); |
|
544 if (currSection) { |
|
545 result = compareBorders(result, CollapsedBorderValue(&currSection->style()->borderTop(), BROWGROUP)); |
|
546 if (!result.exists()) |
|
547 return result; |
|
548 } |
|
549 } |
|
550 |
|
551 if (!currSection) { |
|
552 // (8) Our column and column group's bottom borders. |
|
553 RenderTableCol* colElt = table()->colElement(col()); |
|
554 if (colElt) { |
|
555 result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderBottom(), BCOL)); |
|
556 if (!result.exists()) return result; |
|
557 if (colElt->parent()->isTableCol()) { |
|
558 result = compareBorders(result, CollapsedBorderValue(&colElt->parent()->style()->borderBottom(), BCOLGROUP)); |
|
559 if (!result.exists()) |
|
560 return result; |
|
561 } |
|
562 } |
|
563 |
|
564 // (9) The table's bottom border. |
|
565 result = compareBorders(result, CollapsedBorderValue(&table()->style()->borderBottom(), BTABLE)); |
|
566 if (!result.exists()) |
|
567 return result; |
|
568 } |
|
569 |
|
570 return result; |
|
571 } |
|
572 |
|
573 int RenderTableCell::borderLeft() const |
|
574 { |
|
575 return table()->collapseBorders() ? borderHalfLeft(false) : RenderBlock::borderLeft(); |
|
576 } |
|
577 |
|
578 int RenderTableCell::borderRight() const |
|
579 { |
|
580 return table()->collapseBorders() ? borderHalfRight(false) : RenderBlock::borderRight(); |
|
581 } |
|
582 |
|
583 int RenderTableCell::borderTop() const |
|
584 { |
|
585 return table()->collapseBorders() ? borderHalfTop(false) : RenderBlock::borderTop(); |
|
586 } |
|
587 |
|
588 int RenderTableCell::borderBottom() const |
|
589 { |
|
590 return table()->collapseBorders() ? borderHalfBottom(false) : RenderBlock::borderBottom(); |
|
591 } |
|
592 |
|
593 int RenderTableCell::borderHalfLeft(bool outer) const |
|
594 { |
|
595 CollapsedBorderValue border = collapsedLeftBorder(table()->style()->direction() == RTL); |
|
596 if (border.exists()) |
|
597 return (border.width() + (outer ? 0 : 1)) / 2; // Give the extra pixel to top and left. |
|
598 return 0; |
|
599 } |
|
600 |
|
601 int RenderTableCell::borderHalfRight(bool outer) const |
|
602 { |
|
603 CollapsedBorderValue border = collapsedRightBorder(table()->style()->direction() == RTL); |
|
604 if (border.exists()) |
|
605 return (border.width() + (outer ? 1 : 0)) / 2; |
|
606 return 0; |
|
607 } |
|
608 |
|
609 int RenderTableCell::borderHalfTop(bool outer) const |
|
610 { |
|
611 CollapsedBorderValue border = collapsedTopBorder(); |
|
612 if (border.exists()) |
|
613 return (border.width() + (outer ? 0 : 1)) / 2; // Give the extra pixel to top and left. |
|
614 return 0; |
|
615 } |
|
616 |
|
617 int RenderTableCell::borderHalfBottom(bool outer) const |
|
618 { |
|
619 CollapsedBorderValue border = collapsedBottomBorder(); |
|
620 if (border.exists()) |
|
621 return (border.width() + (outer ? 1 : 0)) / 2; |
|
622 return 0; |
|
623 } |
|
624 |
|
625 void RenderTableCell::paint(PaintInfo& paintInfo, int tx, int ty) |
|
626 { |
|
627 tx += m_x; |
|
628 ty += m_y; |
|
629 |
|
630 // check if we need to do anything at all... |
|
631 int os = 2 * maximalOutlineSize(paintInfo.phase); |
|
632 |
|
633 if (paintInfo.phase == PaintPhaseCollapsedTableBorders && style()->visibility() == VISIBLE) { |
|
634 if (ty - table()->outerBorderTop() >= paintInfo.rect.bottom() + os || |
|
635 ty + m_topExtra + m_height + m_bottomExtra + table()->outerBorderBottom() <= paintInfo.rect.y() - os) |
|
636 return; |
|
637 int w = width(); |
|
638 int h = height() + borderTopExtra() + borderBottomExtra(); |
|
639 paintCollapsedBorder(paintInfo.context, tx, ty, w, h); |
|
640 } else { |
|
641 if (ty + overflowTop(false) >= paintInfo.rect.bottom() + os || ty + m_topExtra + overflowHeight(false) + m_bottomExtra <= paintInfo.rect.y() - os) |
|
642 return; |
|
643 RenderBlock::paintObject(paintInfo, tx, ty + m_topExtra); |
|
644 } |
|
645 } |
|
646 |
|
647 static EBorderStyle collapsedBorderStyle(EBorderStyle style) |
|
648 { |
|
649 if (style == OUTSET) |
|
650 return GROOVE; |
|
651 if (style == INSET) |
|
652 return RIDGE; |
|
653 return style; |
|
654 } |
|
655 |
|
656 struct CollapsedBorder { |
|
657 CollapsedBorderValue borderValue; |
|
658 RenderObject::BorderSide side; |
|
659 bool shouldPaint; |
|
660 int x1; |
|
661 int y1; |
|
662 int x2; |
|
663 int y2; |
|
664 EBorderStyle style; |
|
665 }; |
|
666 |
|
667 class CollapsedBorders { |
|
668 public: |
|
669 CollapsedBorders() |
|
670 : m_count(0) |
|
671 { |
|
672 } |
|
673 |
|
674 void addBorder(const CollapsedBorderValue& borderValue, RenderObject::BorderSide borderSide, bool shouldPaint, |
|
675 int x1, int y1, int x2, int y2, EBorderStyle borderStyle) |
|
676 { |
|
677 if (borderValue.exists() && shouldPaint) { |
|
678 m_borders[m_count].borderValue = borderValue; |
|
679 m_borders[m_count].side = borderSide; |
|
680 m_borders[m_count].shouldPaint = shouldPaint; |
|
681 m_borders[m_count].x1 = x1; |
|
682 m_borders[m_count].x2 = x2; |
|
683 m_borders[m_count].y1 = y1; |
|
684 m_borders[m_count].y2 = y2; |
|
685 m_borders[m_count].style = borderStyle; |
|
686 m_count++; |
|
687 } |
|
688 } |
|
689 |
|
690 CollapsedBorder* nextBorder() |
|
691 { |
|
692 for (int i = 0; i < m_count; i++) { |
|
693 if (m_borders[i].borderValue.exists() && m_borders[i].shouldPaint) { |
|
694 m_borders[i].shouldPaint = false; |
|
695 return &m_borders[i]; |
|
696 } |
|
697 } |
|
698 |
|
699 return 0; |
|
700 } |
|
701 |
|
702 CollapsedBorder m_borders[4]; |
|
703 int m_count; |
|
704 }; |
|
705 |
|
706 static void addBorderStyle(RenderTableCell::CollapsedBorderStyles& borderStyles, CollapsedBorderValue borderValue) |
|
707 { |
|
708 if (!borderValue.exists()) |
|
709 return; |
|
710 size_t count = borderStyles.size(); |
|
711 for (size_t i = 0; i < count; ++i) |
|
712 if (borderStyles[i] == borderValue) |
|
713 return; |
|
714 borderStyles.append(borderValue); |
|
715 } |
|
716 |
|
717 void RenderTableCell::collectBorderStyles(CollapsedBorderStyles& borderStyles) const |
|
718 { |
|
719 bool rtl = table()->style()->direction() == RTL; |
|
720 addBorderStyle(borderStyles, collapsedLeftBorder(rtl)); |
|
721 addBorderStyle(borderStyles, collapsedRightBorder(rtl)); |
|
722 addBorderStyle(borderStyles, collapsedTopBorder()); |
|
723 addBorderStyle(borderStyles, collapsedBottomBorder()); |
|
724 } |
|
725 |
|
726 static int compareBorderStylesForQSort(const void* pa, const void* pb) |
|
727 { |
|
728 const CollapsedBorderValue* a = static_cast<const CollapsedBorderValue*>(pa); |
|
729 const CollapsedBorderValue* b = static_cast<const CollapsedBorderValue*>(pb); |
|
730 if (*a == *b) |
|
731 return 0; |
|
732 CollapsedBorderValue borderWithHigherPrecedence = compareBorders(*a, *b); |
|
733 if (*a == borderWithHigherPrecedence) |
|
734 return 1; |
|
735 return -1; |
|
736 } |
|
737 |
|
738 void RenderTableCell::sortBorderStyles(CollapsedBorderStyles& borderStyles) |
|
739 { |
|
740 qsort(borderStyles.data(), borderStyles.size(), sizeof(CollapsedBorderValue), |
|
741 compareBorderStylesForQSort); |
|
742 } |
|
743 |
|
744 void RenderTableCell::paintCollapsedBorder(GraphicsContext* graphicsContext, int tx, int ty, int w, int h) |
|
745 { |
|
746 if (!table()->currentBorderStyle()) |
|
747 return; |
|
748 |
|
749 bool rtl = table()->style()->direction() == RTL; |
|
750 CollapsedBorderValue leftVal = collapsedLeftBorder(rtl); |
|
751 CollapsedBorderValue rightVal = collapsedRightBorder(rtl); |
|
752 CollapsedBorderValue topVal = collapsedTopBorder(); |
|
753 CollapsedBorderValue bottomVal = collapsedBottomBorder(); |
|
754 |
|
755 // Adjust our x/y/width/height so that we paint the collapsed borders at the correct location. |
|
756 int topWidth = topVal.width(); |
|
757 int bottomWidth = bottomVal.width(); |
|
758 int leftWidth = leftVal.width(); |
|
759 int rightWidth = rightVal.width(); |
|
760 |
|
761 tx -= leftWidth / 2; |
|
762 ty -= topWidth / 2; |
|
763 w += leftWidth / 2 + (rightWidth + 1) / 2; |
|
764 h += topWidth / 2 + (bottomWidth + 1) / 2; |
|
765 |
|
766 EBorderStyle topStyle = collapsedBorderStyle(topVal.style()); |
|
767 EBorderStyle bottomStyle = collapsedBorderStyle(bottomVal.style()); |
|
768 EBorderStyle leftStyle = collapsedBorderStyle(leftVal.style()); |
|
769 EBorderStyle rightStyle = collapsedBorderStyle(rightVal.style()); |
|
770 |
|
771 bool renderTop = topStyle > BHIDDEN && !topVal.isTransparent(); |
|
772 bool renderBottom = bottomStyle > BHIDDEN && !bottomVal.isTransparent(); |
|
773 bool renderLeft = leftStyle > BHIDDEN && !leftVal.isTransparent(); |
|
774 bool renderRight = rightStyle > BHIDDEN && !rightVal.isTransparent(); |
|
775 |
|
776 // We never paint diagonals at the joins. We simply let the border with the highest |
|
777 // precedence paint on top of borders with lower precedence. |
|
778 CollapsedBorders borders; |
|
779 borders.addBorder(topVal, BSTop, renderTop, tx, ty, tx + w, ty + topWidth, topStyle); |
|
780 borders.addBorder(bottomVal, BSBottom, renderBottom, tx, ty + h - bottomWidth, tx + w, ty + h, bottomStyle); |
|
781 borders.addBorder(leftVal, BSLeft, renderLeft, tx, ty, tx + leftWidth, ty + h, leftStyle); |
|
782 borders.addBorder(rightVal, BSRight, renderRight, tx + w - rightWidth, ty, tx + w, ty + h, rightStyle); |
|
783 |
|
784 for (CollapsedBorder* border = borders.nextBorder(); border; border = borders.nextBorder()) { |
|
785 if (border->borderValue == *table()->currentBorderStyle()) |
|
786 drawBorder(graphicsContext, border->x1, border->y1, border->x2, border->y2, border->side, |
|
787 border->borderValue.color(), style()->color(), border->style, 0, 0); |
|
788 } |
|
789 } |
|
790 |
|
791 void RenderTableCell::paintBackgroundsBehindCell(PaintInfo& paintInfo, int tx, int ty, RenderObject* backgroundObject) |
|
792 { |
|
793 if (!backgroundObject) |
|
794 return; |
|
795 |
|
796 if (style()->visibility() != VISIBLE) |
|
797 return; |
|
798 |
|
799 RenderTable* tableElt = table(); |
|
800 if (!tableElt->collapseBorders() && style()->emptyCells() == HIDE && !firstChild()) |
|
801 return; |
|
802 |
|
803 if (backgroundObject != this) { |
|
804 tx += m_x; |
|
805 ty += m_y + m_topExtra; |
|
806 } |
|
807 |
|
808 int w = width(); |
|
809 int h = height() + borderTopExtra() + borderBottomExtra(); |
|
810 ty -= borderTopExtra(); |
|
811 |
|
812 int my = max(ty, paintInfo.rect.y()); |
|
813 int end = min(paintInfo.rect.bottom(), ty + h); |
|
814 int mh = end - my; |
|
815 |
|
816 Color c = backgroundObject->style()->backgroundColor(); |
|
817 const BackgroundLayer* bgLayer = backgroundObject->style()->backgroundLayers(); |
|
818 |
|
819 if (bgLayer->hasImage() || c.isValid()) { |
|
820 // We have to clip here because the background would paint |
|
821 // on top of the borders otherwise. This only matters for cells and rows. |
|
822 bool shouldClip = backgroundObject->hasLayer() && (backgroundObject == this || backgroundObject == parent()) && tableElt->collapseBorders(); |
|
823 if (shouldClip) { |
|
824 IntRect clipRect(tx + borderLeft(), ty + borderTop(), |
|
825 w - borderLeft() - borderRight(), h - borderTop() - borderBottom()); |
|
826 #if PLATFORM(SYMBIAN) |
|
827 // make sure we don't draw outside the current rendering area. |
|
828 clipRect.intersect(paintInfo.rect); |
|
829 #endif |
|
830 paintInfo.context->save(); |
|
831 paintInfo.context->clip(clipRect); |
|
832 } |
|
833 paintBackground(paintInfo.context, c, bgLayer, my, mh, tx, ty, w, h); |
|
834 if (shouldClip) |
|
835 paintInfo.context->restore(); |
|
836 } |
|
837 } |
|
838 |
|
839 void RenderTableCell::paintBoxDecorations(PaintInfo& paintInfo, int tx, int ty) |
|
840 { |
|
841 RenderTable* tableElt = table(); |
|
842 if (!tableElt->collapseBorders() && style()->emptyCells() == HIDE && !firstChild()) |
|
843 return; |
|
844 |
|
845 int w = width(); |
|
846 int h = height() + borderTopExtra() + borderBottomExtra(); |
|
847 |
|
848 if (style()->boxShadow()) |
|
849 paintBoxShadow(paintInfo.context, tx, ty - borderTopExtra(), w, h, style()); |
|
850 |
|
851 // Paint our cell background. |
|
852 paintBackgroundsBehindCell(paintInfo, tx, ty, this); |
|
853 |
|
854 if (!style()->hasBorder() || tableElt->collapseBorders()) |
|
855 return; |
|
856 |
|
857 ty -= borderTopExtra(); |
|
858 paintBorder(paintInfo.context, tx, ty, w, h, style()); |
|
859 } |
|
860 |
|
861 #ifndef NDEBUG |
|
862 void RenderTableCell::dump(TextStream* stream, DeprecatedString ind) const |
|
863 { |
|
864 *stream << " row=" << row(); |
|
865 *stream << " col=" << col(); |
|
866 *stream << " rSpan=" << rowSpan(); |
|
867 *stream << " cSpan=" << colSpan(); |
|
868 |
|
869 RenderBlock::dump(stream,ind); |
|
870 } |
|
871 #endif |
|
872 |
|
873 } // namespace WebCore |