Unreviewed, rolling out r100473.
[WebKit-https.git] / Source / WebCore / rendering / RenderTable.cpp
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, 2010 Apple Inc. All rights reserved.
8  * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Library General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Library General Public License for more details.
19  *
20  * You should have received a copy of the GNU Library General Public License
21  * along with this library; see the file COPYING.LIB.  If not, write to
22  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23  * Boston, MA 02110-1301, USA.
24  */
25
26 #include "config.h"
27 #include "RenderTable.h"
28
29 #include "AutoTableLayout.h"
30 #include "CollapsedBorderValue.h"
31 #include "DeleteButtonController.h"
32 #include "Document.h"
33 #include "FixedTableLayout.h"
34 #include "FrameView.h"
35 #include "HitTestResult.h"
36 #include "HTMLNames.h"
37 #include "LayoutRepainter.h"
38 #include "RenderLayer.h"
39 #include "RenderTableCell.h"
40 #include "RenderTableCol.h"
41 #include "RenderTableSection.h"
42 #include "RenderView.h"
43
44 using namespace std;
45
46 namespace WebCore {
47
48 using namespace HTMLNames;
49
50 RenderTable::RenderTable(Node* node)
51     : RenderBlock(node)
52     , m_head(0)
53     , m_foot(0)
54     , m_firstBody(0)
55     , m_currentBorder(0)
56     , m_collapsedBordersValid(false)
57     , m_hasColElements(false)
58     , m_needsSectionRecalc(false)
59     , m_hSpacing(0)
60     , m_vSpacing(0)
61     , m_borderStart(0)
62     , m_borderEnd(0)
63 {
64     setChildrenInline(false);
65     m_columnPos.fill(0, 1);
66     
67 }
68
69 RenderTable::~RenderTable()
70 {
71 }
72
73 void RenderTable::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
74 {
75     RenderBlock::styleDidChange(diff, oldStyle);
76     propagateStyleToAnonymousChildren();
77
78     ETableLayout oldTableLayout = oldStyle ? oldStyle->tableLayout() : TAUTO;
79
80     // In the collapsed border model, there is no cell spacing.
81     m_hSpacing = collapseBorders() ? 0 : style()->horizontalBorderSpacing();
82     m_vSpacing = collapseBorders() ? 0 : style()->verticalBorderSpacing();
83     m_columnPos[0] = m_hSpacing;
84
85     if (!m_tableLayout || style()->tableLayout() != oldTableLayout) {
86         // According to the CSS2 spec, you only use fixed table layout if an
87         // explicit width is specified on the table.  Auto width implies auto table layout.
88         if (style()->tableLayout() == TFIXED && !style()->logicalWidth().isAuto())
89             m_tableLayout = adoptPtr(new FixedTableLayout(this));
90         else
91             m_tableLayout = adoptPtr(new AutoTableLayout(this));
92     }
93
94     // If border was changed, invalidate collapsed borders cache.
95     if (!needsLayout() && oldStyle && oldStyle->border() != style()->border())
96         invalidateCollapsedBorders();
97 }
98
99 static inline void resetSectionPointerIfNotBefore(RenderTableSection*& ptr, RenderObject* before)
100 {
101     if (!before || !ptr)
102         return;
103     RenderObject* o = before->previousSibling();
104     while (o && o != ptr)
105         o = o->previousSibling();
106     if (!o)
107         ptr = 0;
108 }
109
110 void RenderTable::addChild(RenderObject* child, RenderObject* beforeChild)
111 {
112     // Make sure we don't append things after :after-generated content if we have it.
113     if (!beforeChild)
114         beforeChild = findAfterContentRenderer();
115
116     bool wrapInAnonymousSection = !child->isPositioned();
117
118     if (child->isRenderBlock() && child->style()->display() == TABLE_CAPTION) {
119         m_captions.append(toRenderBlock(child));
120         setNeedsSectionRecalc();
121         wrapInAnonymousSection = false;
122     } else if (child->isTableCol()) {
123         m_hasColElements = true;
124         wrapInAnonymousSection = false;
125     } else if (child->isTableSection()) {
126         switch (child->style()->display()) {
127             case TABLE_HEADER_GROUP:
128                 resetSectionPointerIfNotBefore(m_head, beforeChild);
129                 if (!m_head) {
130                     m_head = toRenderTableSection(child);
131                 } else {
132                     resetSectionPointerIfNotBefore(m_firstBody, beforeChild);
133                     if (!m_firstBody) 
134                         m_firstBody = toRenderTableSection(child);
135                 }
136                 wrapInAnonymousSection = false;
137                 break;
138             case TABLE_FOOTER_GROUP:
139                 resetSectionPointerIfNotBefore(m_foot, beforeChild);
140                 if (!m_foot) {
141                     m_foot = toRenderTableSection(child);
142                     wrapInAnonymousSection = false;
143                     break;
144                 }
145                 // Fall through.
146             case TABLE_ROW_GROUP:
147                 resetSectionPointerIfNotBefore(m_firstBody, beforeChild);
148                 if (!m_firstBody)
149                     m_firstBody = toRenderTableSection(child);
150                 wrapInAnonymousSection = false;
151                 break;
152             default:
153                 ASSERT_NOT_REACHED();
154         }
155     } else if (child->isTableCell() || child->isTableRow())
156         wrapInAnonymousSection = true;
157     else
158         wrapInAnonymousSection = true;
159
160     if (!wrapInAnonymousSection) {
161         // If the next renderer is actually wrapped in an anonymous table section, we need to go up and find that.
162         while (beforeChild && beforeChild->parent() != this)
163             beforeChild = beforeChild->parent();
164
165         RenderBox::addChild(child, beforeChild);
166         return;
167     }
168
169     if (!beforeChild && lastChild() && lastChild()->isTableSection() && lastChild()->isAnonymous() && !lastChild()->isBeforeContent()) {
170         lastChild()->addChild(child);
171         return;
172     }
173
174     if (beforeChild && !beforeChild->isAnonymous() && beforeChild->parent() == this) {
175         RenderObject* section = beforeChild->previousSibling();
176         if (section && section->isTableSection() && section->isAnonymous()) {
177             section->addChild(child);
178             return;
179         }
180     }
181
182     RenderObject* lastBox = beforeChild;
183     while (lastBox && lastBox->parent()->isAnonymous() && !lastBox->isTableSection() && lastBox->style()->display() != TABLE_CAPTION && lastBox->style()->display() != TABLE_COLUMN_GROUP)
184         lastBox = lastBox->parent();
185     if (lastBox && lastBox->isAnonymous() && !isAfterContent(lastBox)) {
186         if (beforeChild == lastBox)
187             beforeChild = lastBox->firstChild();
188         lastBox->addChild(child, beforeChild);
189         return;
190     }
191
192     if (beforeChild && !beforeChild->isTableSection() && beforeChild->style()->display() != TABLE_CAPTION && beforeChild->style()->display() != TABLE_COLUMN_GROUP)
193         beforeChild = 0;
194     RenderTableSection* section = new (renderArena()) RenderTableSection(document() /* anonymous */);
195     RefPtr<RenderStyle> newStyle = RenderStyle::create();
196     newStyle->inheritFrom(style());
197     newStyle->setDisplay(TABLE_ROW_GROUP);
198     section->setStyle(newStyle.release());
199     addChild(section, beforeChild);
200     section->addChild(child);
201 }
202
203 void RenderTable::removeChild(RenderObject* oldChild)
204 {
205     RenderBox::removeChild(oldChild);
206  
207     size_t index = m_captions.find(oldChild);
208     if (index != notFound) {
209         m_captions.remove(index);
210         if (node())
211             node()->setNeedsStyleRecalc();
212     }
213     setNeedsSectionRecalc();
214 }
215
216 void RenderTable::computeLogicalWidth()
217 {
218     if (isPositioned())
219         computePositionedLogicalWidth();
220
221     RenderBlock* cb = containingBlock();
222
223     LayoutUnit availableLogicalWidth = containingBlockLogicalWidthForContent();
224     bool hasPerpendicularContainingBlock = cb->style()->isHorizontalWritingMode() != style()->isHorizontalWritingMode();
225     LayoutUnit containerWidthInInlineDirection = hasPerpendicularContainingBlock ? perpendicularContainingBlockLogicalHeight() : availableLogicalWidth;
226
227     LengthType logicalWidthType = style()->logicalWidth().type();
228     if (logicalWidthType > Relative && style()->logicalWidth().isPositive()) {
229         // Percent or fixed table
230         // HTML tables size as though CSS width includes border/padding, CSS tables do not.
231         LayoutUnit borders = 0;
232         if (!node() || !node()->hasTagName(tableTag)) {
233             bool collapsing = collapseBorders();
234             LayoutUnit borderAndPaddingBefore = borderBefore() + (collapsing ? 0 : paddingBefore());
235             LayoutUnit borderAndPaddingAfter = borderAfter() + (collapsing ? 0 : paddingAfter());
236             borders = borderAndPaddingBefore + borderAndPaddingAfter;
237         }
238         setLogicalWidth(style()->logicalWidth().calcMinValue(containerWidthInInlineDirection) + borders);
239         setLogicalWidth(max(minPreferredLogicalWidth(), logicalWidth()));
240     } else {
241         // Subtract out any fixed margins from our available width for auto width tables.
242         LayoutUnit marginTotal = 0;
243         if (!style()->marginStart().isAuto())
244             marginTotal += style()->marginStart().calcValue(availableLogicalWidth);
245         if (!style()->marginEnd().isAuto())
246             marginTotal += style()->marginEnd().calcValue(availableLogicalWidth);
247             
248         // Subtract out our margins to get the available content width.
249         LayoutUnit availableContentLogicalWidth = max<LayoutUnit>(0, containerWidthInInlineDirection - marginTotal);
250         
251         // Ensure we aren't bigger than our max width or smaller than our min width.
252         setLogicalWidth(min(availableContentLogicalWidth, maxPreferredLogicalWidth()));
253     }
254
255     setLogicalWidth(max(logicalWidth(), minPreferredLogicalWidth()));
256
257     // Finally, with our true width determined, compute our margins for real.
258     setMarginStart(0);
259     setMarginEnd(0);
260     if (!hasPerpendicularContainingBlock)
261         computeInlineDirectionMargins(cb, availableLogicalWidth, logicalWidth());
262     else {
263         setMarginStart(style()->marginStart().calcMinValue(availableLogicalWidth));
264         setMarginEnd(style()->marginEnd().calcMinValue(availableLogicalWidth));
265     }
266 }
267
268 void RenderTable::adjustLogicalHeightForCaption(RenderBlock* caption)
269 {
270     IntRect captionRect(caption->x(), caption->y(), caption->width(), caption->height());
271
272     caption->setLogicalLocation(IntPoint(caption->marginStart(), caption->marginBefore() + logicalHeight()));
273     if (!selfNeedsLayout() && caption->checkForRepaintDuringLayout())
274         caption->repaintDuringLayoutIfMoved(captionRect);
275
276     setLogicalHeight(logicalHeight() + caption->logicalHeight() + caption->marginBefore() + caption->marginAfter());
277 }
278
279 void RenderTable::layout()
280 {
281     ASSERT(needsLayout());
282
283     if (simplifiedLayout())
284         return;
285
286     recalcSectionsIfNeeded();
287         
288     LayoutRepainter repainter(*this, checkForRepaintDuringLayout());
289     LayoutStateMaintainer statePusher(view(), this, locationOffset(), style()->isFlippedBlocksWritingMode());
290
291     setLogicalHeight(0);
292     m_overflow.clear();
293
294     initMaxMarginValues();
295     
296     LayoutUnit oldLogicalWidth = logicalWidth();
297     computeLogicalWidth();
298
299     if (logicalWidth() != oldLogicalWidth) {
300         for (unsigned i = 0; i < m_captions.size(); i++)
301             m_captions[i]->setNeedsLayout(true, false);
302     }
303     // FIXME: The optimisation below doesn't work since the internal table
304     // layout could have changed.  we need to add a flag to the table
305     // layout that tells us if something has changed in the min max
306     // calculations to do it correctly.
307 //     if ( oldWidth != width() || columns.size() + 1 != columnPos.size() )
308     m_tableLayout->layout();
309
310     setCellLogicalWidths();
311
312     LayoutUnit totalSectionLogicalHeight = 0;
313     LayoutUnit oldTableLogicalTop = 0;
314     for (unsigned i = 0; i < m_captions.size(); i++)
315         oldTableLogicalTop += m_captions[i]->logicalHeight() + m_captions[i]->marginBefore() + m_captions[i]->marginAfter();
316
317     bool collapsing = collapseBorders();
318
319     for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
320         if (child->isTableSection()) {
321             child->layoutIfNeeded();
322             RenderTableSection* section = toRenderTableSection(child);
323             totalSectionLogicalHeight += section->calcRowLogicalHeight();
324             if (collapsing)
325                 section->recalcOuterBorder();
326             ASSERT(!section->needsLayout());
327         } else if (child->isTableCol()) {
328             child->layoutIfNeeded();
329             ASSERT(!child->needsLayout());
330         }
331     }
332
333     // Only lay out one caption, since it's the only one we're going to end up painting.
334     for (unsigned i = 0; i < m_captions.size(); i++)
335         m_captions[i]->layoutIfNeeded();
336
337     // If any table section moved vertically, we will just repaint everything from that
338     // section down (it is quite unlikely that any of the following sections
339     // did not shift).
340     bool sectionMoved = false;
341     LayoutUnit movedSectionLogicalTop = 0;
342
343     // FIXME: Collapse caption margin.
344     if (!m_captions.isEmpty()) {
345         for (unsigned i = 0; i < m_captions.size(); i++) {
346             if (m_captions[i]->style()->captionSide() == CAPBOTTOM)
347                 continue;
348             adjustLogicalHeightForCaption(m_captions[i]);
349         }
350         if (logicalHeight() != oldTableLogicalTop) {
351             sectionMoved = true;
352             movedSectionLogicalTop = min(logicalHeight(), oldTableLogicalTop);
353         }
354     }
355
356     LayoutUnit borderAndPaddingBefore = borderBefore() + (collapsing ? 0 : paddingBefore());
357     LayoutUnit borderAndPaddingAfter = borderAfter() + (collapsing ? 0 : paddingAfter());
358
359     setLogicalHeight(logicalHeight() + borderAndPaddingBefore);
360
361     if (!isPositioned())
362         computeLogicalHeight();
363
364     Length logicalHeightLength = style()->logicalHeight();
365     LayoutUnit computedLogicalHeight = 0;
366     if (logicalHeightLength.isFixed()) {
367         // HTML tables size as though CSS height includes border/padding, CSS tables do not.
368         LayoutUnit borders = node() && node()->hasTagName(tableTag) ? (borderAndPaddingBefore + borderAndPaddingAfter) : 0;
369         computedLogicalHeight = logicalHeightLength.value() - borders;
370     } else if (logicalHeightLength.isPercent())
371         computedLogicalHeight = computePercentageLogicalHeight(logicalHeightLength);
372     computedLogicalHeight = max<LayoutUnit>(0, computedLogicalHeight);
373
374     for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
375         if (child->isTableSection())
376             // FIXME: Distribute extra height between all table body sections instead of giving it all to the first one.
377             toRenderTableSection(child)->layoutRows(child == m_firstBody ? max<LayoutUnit>(0, computedLogicalHeight - totalSectionLogicalHeight) : 0);
378     }
379
380     if (!m_firstBody && computedLogicalHeight > totalSectionLogicalHeight && !document()->inQuirksMode()) {
381         // Completely empty tables (with no sections or anything) should at least honor specified height
382         // in strict mode.
383         setLogicalHeight(logicalHeight() + computedLogicalHeight);
384     }
385
386     LayoutUnit sectionLogicalLeft = style()->isLeftToRightDirection() ? borderStart() : borderEnd();
387     if (!collapsing)
388         sectionLogicalLeft += style()->isLeftToRightDirection() ? paddingStart() : paddingEnd();
389
390     // position the table sections
391     RenderTableSection* section = topSection();
392     while (section) {
393         if (!sectionMoved && section->logicalTop() != logicalHeight()) {
394             sectionMoved = true;
395             movedSectionLogicalTop = min(logicalHeight(), section->logicalTop()) + (style()->isHorizontalWritingMode() ? section->minYVisualOverflow() : section->minXVisualOverflow());
396         }
397         section->setLogicalLocation(LayoutPoint(sectionLogicalLeft, logicalHeight()));
398
399         setLogicalHeight(logicalHeight() + section->logicalHeight());
400         section = sectionBelow(section);
401     }
402
403     setLogicalHeight(logicalHeight() + borderAndPaddingAfter);
404
405     for (unsigned i = 0; i < m_captions.size(); i++) {
406         if (m_captions[i]->style()->captionSide() != CAPBOTTOM)
407             continue;
408         adjustLogicalHeightForCaption(m_captions[i]);
409     }
410
411     if (isPositioned())
412         computeLogicalHeight();
413
414     // table can be containing block of positioned elements.
415     // FIXME: Only pass true if width or height changed.
416     layoutPositionedObjects(true);
417
418     updateLayerTransform();
419
420     // Layout was changed, so probably borders too.
421     invalidateCollapsedBorders();
422
423     computeOverflow(clientLogicalBottom());
424
425     statePusher.pop();
426
427     if (view()->layoutState()->pageLogicalHeight())
428         setPageLogicalOffset(view()->layoutState()->pageLogicalOffset(logicalTop()));
429
430     bool didFullRepaint = repainter.repaintAfterLayout();
431     // Repaint with our new bounds if they are different from our old bounds.
432     if (!didFullRepaint && sectionMoved) {
433         if (style()->isHorizontalWritingMode())
434             repaintRectangle(LayoutRect(minXVisualOverflow(), movedSectionLogicalTop, maxXVisualOverflow() - minXVisualOverflow(), maxYVisualOverflow() - movedSectionLogicalTop));
435         else
436             repaintRectangle(LayoutRect(movedSectionLogicalTop, minYVisualOverflow(), maxXVisualOverflow() - movedSectionLogicalTop, maxYVisualOverflow() - minYVisualOverflow()));
437     }
438
439     setNeedsLayout(false);
440 }
441
442 // Collect all the unique border values that we want to paint in a sorted list.
443 void RenderTable::recalcCollapsedBorders()
444 {
445     if (m_collapsedBordersValid)
446         return;
447     m_collapsedBordersValid = true;
448     m_collapsedBorders.clear();
449     RenderObject* stop = nextInPreOrderAfterChildren();
450     for (RenderObject* o = firstChild(); o && o != stop; o = o->nextInPreOrder()) {
451         if (o->isTableCell())
452             toRenderTableCell(o)->collectBorderValues(m_collapsedBorders);
453     }
454     RenderTableCell::sortBorderValues(m_collapsedBorders);
455 }
456
457
458 void RenderTable::addOverflowFromChildren()
459 {
460     // Add overflow from borders.
461     // Technically it's odd that we are incorporating the borders into layout overflow, which is only supposed to be about overflow from our
462     // descendant objects, but since tables don't support overflow:auto, this works out fine.
463     if (collapseBorders()) {
464         int rightBorderOverflow = width() + outerBorderRight() - borderRight();
465         int leftBorderOverflow = borderLeft() - outerBorderLeft();
466         int bottomBorderOverflow = height() + outerBorderBottom() - borderBottom();
467         int topBorderOverflow = borderTop() - outerBorderTop();
468         IntRect borderOverflowRect(leftBorderOverflow, topBorderOverflow, rightBorderOverflow - leftBorderOverflow, bottomBorderOverflow - topBorderOverflow);
469         if (borderOverflowRect != borderBoxRect()) {
470             addLayoutOverflow(borderOverflowRect);
471             addVisualOverflow(borderOverflowRect);
472         }
473     }
474
475     // Add overflow from our caption.
476     for (unsigned i = 0; i < m_captions.size(); i++) 
477         addOverflowFromChild(m_captions[i]);
478
479     // Add overflow from our sections.
480     for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
481         if (child->isTableSection()) {
482             RenderTableSection* section = toRenderTableSection(child);
483             addOverflowFromChild(section);
484         }
485     }
486 }
487
488 void RenderTable::setCellLogicalWidths()
489 {
490     for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
491         if (child->isTableSection())
492             toRenderTableSection(child)->setCellLogicalWidths();
493     }
494 }
495
496 void RenderTable::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
497 {
498     LayoutPoint adjustedPaintOffset = paintOffset + location();
499
500     PaintPhase paintPhase = paintInfo.phase;
501
502     if (!isRoot()) {
503         LayoutRect overflowBox = visualOverflowRect();
504         flipForWritingMode(overflowBox);
505         overflowBox.inflate(maximalOutlineSize(paintInfo.phase));
506         overflowBox.moveBy(adjustedPaintOffset);
507         if (!overflowBox.intersects(paintInfo.rect))
508             return;
509     }
510
511     bool pushedClip = pushContentsClip(paintInfo, adjustedPaintOffset);
512     paintObject(paintInfo, adjustedPaintOffset);
513     if (pushedClip)
514         popContentsClip(paintInfo, paintPhase, adjustedPaintOffset);
515 }
516
517 void RenderTable::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
518 {
519     PaintPhase paintPhase = paintInfo.phase;
520     if ((paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) && hasBoxDecorations() && style()->visibility() == VISIBLE)
521         paintBoxDecorations(paintInfo, paintOffset);
522
523     if (paintPhase == PaintPhaseMask) {
524         paintMask(paintInfo, paintOffset);
525         return;
526     }
527
528     // We're done.  We don't bother painting any children.
529     if (paintPhase == PaintPhaseBlockBackground)
530         return;
531     
532     // We don't paint our own background, but we do let the kids paint their backgrounds.
533     if (paintPhase == PaintPhaseChildBlockBackgrounds)
534         paintPhase = PaintPhaseChildBlockBackground;
535
536     PaintInfo info(paintInfo);
537     info.phase = paintPhase;
538     info.updatePaintingRootForChildren(this);
539
540     for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
541         if (child->isBox() && !toRenderBox(child)->hasSelfPaintingLayer() && (child->isTableSection() || (child->isRenderBlock() && child->style()->display() == TABLE_CAPTION))) {
542             LayoutPoint childPoint = flipForWritingModeForChild(toRenderBox(child), paintOffset);
543             child->paint(info, childPoint);
544         }
545     }
546     
547     if (collapseBorders() && paintPhase == PaintPhaseChildBlockBackground && style()->visibility() == VISIBLE) {
548         recalcCollapsedBorders();
549         // Using our cached sorted styles, we then do individual passes,
550         // painting each style of border from lowest precedence to highest precedence.
551         info.phase = PaintPhaseCollapsedTableBorders;
552         size_t count = m_collapsedBorders.size();
553         for (size_t i = 0; i < count; ++i) {
554             m_currentBorder = &m_collapsedBorders[i];
555             for (RenderObject* child = firstChild(); child; child = child->nextSibling())
556                 if (child->isTableSection()) {
557                     LayoutPoint childPoint = flipForWritingModeForChild(toRenderTableSection(child), paintOffset);
558                     child->paint(info, childPoint);
559                 }
560         }
561         m_currentBorder = 0;
562     }
563
564     // Paint outline.
565     if ((paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseSelfOutline) && hasOutline() && style()->visibility() == VISIBLE)
566         paintOutline(paintInfo.context, LayoutRect(paintOffset, size()));
567 }
568
569 void RenderTable::subtractCaptionRect(LayoutRect& rect) const
570 {
571     for (unsigned i = 0; i < m_captions.size(); i++) {
572         LayoutUnit captionLogicalHeight = m_captions[i]->logicalHeight() + m_captions[i]->marginBefore() + m_captions[i]->marginAfter();
573         bool captionIsBefore = (m_captions[i]->style()->captionSide() != CAPBOTTOM) ^ style()->isFlippedBlocksWritingMode();
574         if (style()->isHorizontalWritingMode()) {
575             rect.setHeight(rect.height() - captionLogicalHeight);
576             if (captionIsBefore)
577                 rect.move(0, captionLogicalHeight);
578         } else {
579             rect.setWidth(rect.width() - captionLogicalHeight);
580             if (captionIsBefore)
581                 rect.move(captionLogicalHeight, 0);
582         }
583     }
584 }
585
586 void RenderTable::paintBoxDecorations(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
587 {
588     if (!paintInfo.shouldPaintWithinRoot(this))
589         return;
590
591     LayoutRect rect(paintOffset, size());
592     subtractCaptionRect(rect);
593
594     paintBoxShadow(paintInfo, rect, style(), Normal);
595     paintBackground(paintInfo, rect);
596     paintBoxShadow(paintInfo, rect, style(), Inset);
597
598     if (style()->hasBorder() && !collapseBorders())
599         paintBorder(paintInfo, rect, style());
600 }
601
602 void RenderTable::paintMask(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
603 {
604     if (style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask)
605         return;
606
607     LayoutRect rect(paintOffset, size());
608     subtractCaptionRect(rect);
609
610     paintMaskImages(paintInfo, rect);
611 }
612
613 void RenderTable::computePreferredLogicalWidths()
614 {
615     ASSERT(preferredLogicalWidthsDirty());
616
617     recalcSectionsIfNeeded();
618     recalcBordersInRowDirection();
619
620     m_tableLayout->computePreferredLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth);
621
622     for (unsigned i = 0; i < m_captions.size(); i++)
623         m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, m_captions[i]->minPreferredLogicalWidth());
624
625     setPreferredLogicalWidthsDirty(false);
626 }
627
628 RenderTableSection* RenderTable::topNonEmptySection() const
629 {
630     RenderTableSection* section = topSection();
631     if (section && !section->numRows())
632         section = sectionBelow(section, SkipEmptySections);
633     return section;
634 }
635
636 void RenderTable::splitColumn(unsigned position, unsigned firstSpan)
637 {
638     // we need to add a new columnStruct
639     unsigned oldSize = m_columns.size();
640     m_columns.grow(oldSize + 1);
641     unsigned oldSpan = m_columns[position].span;
642     ASSERT(oldSpan > firstSpan);
643     m_columns[position].span = firstSpan;
644     memmove(m_columns.data() + position + 1, m_columns.data() + position, (oldSize - position) * sizeof(ColumnStruct));
645     m_columns[position + 1].span = oldSpan - firstSpan;
646
647     // Propagate the change in our columns representation to the sections that don't need
648     // cell recalc. If they do, they will be synced up directly with m_columns later.
649     for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
650         if (!child->isTableSection())
651             continue;
652
653         RenderTableSection* section = toRenderTableSection(child);
654         if (section->needsCellRecalc())
655             continue;
656
657         section->splitColumn(position, firstSpan);
658     }
659
660     m_columnPos.grow(numEffCols() + 1);
661     setNeedsLayoutAndPrefWidthsRecalc();
662 }
663
664 void RenderTable::appendColumn(unsigned span)
665 {
666     unsigned pos = m_columns.size();
667     unsigned newSize = pos + 1;
668     m_columns.grow(newSize);
669     m_columns[pos].span = span;
670
671     // Propagate the change in our columns representation to the sections that don't need
672     // cell recalc. If they do, they will be synced up directly with m_columns later.
673     for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
674         if (!child->isTableSection())
675             continue;
676
677         RenderTableSection* section = toRenderTableSection(child);
678         if (section->needsCellRecalc())
679             continue;
680
681         section->appendColumn(pos);
682     }
683
684     m_columnPos.grow(numEffCols() + 1);
685     setNeedsLayoutAndPrefWidthsRecalc();
686 }
687
688 RenderTableCol* RenderTable::nextColElement(RenderTableCol* current) const
689 {
690     RenderObject* next = current->firstChild();
691     if (!next)
692         next = current->nextSibling();
693     if (!next && current->parent()->isTableCol())
694         next = current->parent()->nextSibling();
695
696     while (next) {
697         if (next->isTableCol())
698             return toRenderTableCol(next);
699         if (!m_captions.contains(next))
700             return 0;
701         next = next->nextSibling();
702     }
703     
704     return 0;
705 }
706
707 RenderTableCol* RenderTable::colElement(unsigned col, bool* startEdge, bool* endEdge) const
708 {
709     if (!m_hasColElements)
710         return 0;
711     RenderObject* child = firstChild();
712     unsigned cCol = 0;
713
714     while (child) {
715         if (child->isTableCol())
716             break;
717         if (!m_captions.contains(child))
718             return 0;
719         child = child->nextSibling();
720     }
721     if (!child)
722         return 0;
723
724     RenderTableCol* colElem = toRenderTableCol(child);
725     while (colElem) {
726         unsigned span = colElem->span();
727         if (!colElem->firstChild()) {
728             unsigned startCol = cCol;
729             ASSERT(span >= 1);
730             unsigned endCol = cCol + span - 1;
731             cCol += span;
732             if (cCol > col) {
733                 if (startEdge)
734                     *startEdge = startCol == col;
735                 if (endEdge)
736                     *endEdge = endCol == col;
737                 return colElem;
738             }
739         }
740         colElem = nextColElement(colElem);
741     }
742
743     return 0;
744 }
745
746 void RenderTable::recalcSections() const
747 {
748     m_head = 0;
749     m_foot = 0;
750     m_firstBody = 0;
751     m_hasColElements = false;
752
753     // We need to get valid pointers to caption, head, foot and first body again
754     RenderObject* nextSibling;
755     for (RenderObject* child = firstChild(); child; child = nextSibling) {
756         nextSibling = child->nextSibling();
757         switch (child->style()->display()) {
758             case TABLE_COLUMN:
759             case TABLE_COLUMN_GROUP:
760                 m_hasColElements = true;
761                 break;
762             case TABLE_HEADER_GROUP:
763                 if (child->isTableSection()) {
764                     RenderTableSection* section = toRenderTableSection(child);
765                     if (!m_head)
766                         m_head = section;
767                     else if (!m_firstBody)
768                         m_firstBody = section;
769                     section->recalcCellsIfNeeded();
770                 }
771                 break;
772             case TABLE_FOOTER_GROUP:
773                 if (child->isTableSection()) {
774                     RenderTableSection* section = toRenderTableSection(child);
775                     if (!m_foot)
776                         m_foot = section;
777                     else if (!m_firstBody)
778                         m_firstBody = section;
779                     section->recalcCellsIfNeeded();
780                 }
781                 break;
782             case TABLE_ROW_GROUP:
783                 if (child->isTableSection()) {
784                     RenderTableSection* section = toRenderTableSection(child);
785                     if (!m_firstBody)
786                         m_firstBody = section;
787                     section->recalcCellsIfNeeded();
788                 }
789                 break;
790             default:
791                 break;
792         }
793     }
794
795     // repair column count (addChild can grow it too much, because it always adds elements to the last row of a section)
796     unsigned maxCols = 0;
797     for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
798         if (child->isTableSection()) {
799             RenderTableSection* section = toRenderTableSection(child);
800             unsigned sectionCols = section->numColumns();
801             if (sectionCols > maxCols)
802                 maxCols = sectionCols;
803         }
804     }
805     
806     m_columns.resize(maxCols);
807     m_columnPos.resize(maxCols + 1);
808
809     ASSERT(selfNeedsLayout());
810
811     m_needsSectionRecalc = false;
812 }
813
814 LayoutUnit RenderTable::calcBorderStart() const
815 {
816     if (collapseBorders()) {
817         // Determined by the first cell of the first row. See the CSS 2.1 spec, section 17.6.2.
818         if (!numEffCols())
819             return 0;
820
821         LayoutUnit borderWidth = 0;
822
823         const BorderValue& tb = style()->borderStart();
824         if (tb.style() == BHIDDEN)
825             return 0;
826         if (tb.style() > BHIDDEN)
827             borderWidth = tb.width();
828
829         if (RenderTableCol* colGroup = colElement(0)) {
830             const BorderValue& gb = colGroup->style()->borderStart();
831             if (gb.style() == BHIDDEN)
832                 return 0;
833             if (gb.style() > BHIDDEN)
834                 borderWidth = max<LayoutUnit>(borderWidth, gb.width());
835         }
836         
837         if (const RenderTableSection* topNonEmptySection = this->topNonEmptySection()) {
838             const BorderValue& sb = topNonEmptySection->style()->borderStart();
839             if (sb.style() == BHIDDEN)
840                 return 0;
841
842             if (sb.style() > BHIDDEN)
843                 borderWidth = max<LayoutUnit>(borderWidth, sb.width());
844
845             const RenderTableSection::CellStruct& cs = topNonEmptySection->cellAt(0, 0);
846             
847             if (cs.hasCells()) {
848                 const BorderValue& cb = cs.primaryCell()->style()->borderStart(); // FIXME: Make this work with perpendicualr and flipped cells.
849                 if (cb.style() == BHIDDEN)
850                     return 0;
851
852                 const BorderValue& rb = cs.primaryCell()->parent()->style()->borderStart();
853                 if (rb.style() == BHIDDEN)
854                     return 0;
855
856                 if (cb.style() > BHIDDEN)
857                     borderWidth = max<LayoutUnit>(borderWidth, cb.width());
858                 if (rb.style() > BHIDDEN)
859                     borderWidth = max<LayoutUnit>(borderWidth, rb.width());
860             }
861         }
862         return (borderWidth + (style()->isLeftToRightDirection() ? 0 : 1)) / 2;
863     }
864     return RenderBlock::borderStart();
865 }
866
867 LayoutUnit RenderTable::calcBorderEnd() const
868 {
869     if (collapseBorders()) {
870         // Determined by the last cell of the first row. See the CSS 2.1 spec, section 17.6.2.
871         if (!numEffCols())
872             return 0;
873
874         LayoutUnit borderWidth = 0;
875
876         const BorderValue& tb = style()->borderEnd();
877         if (tb.style() == BHIDDEN)
878             return 0;
879         if (tb.style() > BHIDDEN)
880             borderWidth = tb.width();
881
882         unsigned endColumn = numEffCols() - 1;
883         if (RenderTableCol* colGroup = colElement(endColumn)) {
884             const BorderValue& gb = colGroup->style()->borderEnd();
885             if (gb.style() == BHIDDEN)
886                 return 0;
887             if (gb.style() > BHIDDEN)
888                 borderWidth = max<LayoutUnit>(borderWidth, gb.width());
889         }
890
891         if (const RenderTableSection* topNonEmptySection = this->topNonEmptySection()) {
892             const BorderValue& sb = topNonEmptySection->style()->borderEnd();
893             if (sb.style() == BHIDDEN)
894                 return 0;
895
896             if (sb.style() > BHIDDEN)
897                 borderWidth = max<LayoutUnit>(borderWidth, sb.width());
898
899             const RenderTableSection::CellStruct& cs = topNonEmptySection->cellAt(0, endColumn);
900             
901             if (cs.hasCells()) {
902                 const BorderValue& cb = cs.primaryCell()->style()->borderEnd(); // FIXME: Make this work with perpendicular and flipped cells.
903                 if (cb.style() == BHIDDEN)
904                     return 0;
905
906                 const BorderValue& rb = cs.primaryCell()->parent()->style()->borderEnd();
907                 if (rb.style() == BHIDDEN)
908                     return 0;
909
910                 if (cb.style() > BHIDDEN)
911                     borderWidth = max<LayoutUnit>(borderWidth, cb.width());
912                 if (rb.style() > BHIDDEN)
913                     borderWidth = max<LayoutUnit>(borderWidth, rb.width());
914             }
915         }
916         return (borderWidth + (style()->isLeftToRightDirection() ? 1 : 0)) / 2;
917     }
918     return RenderBlock::borderEnd();
919 }
920
921 void RenderTable::recalcBordersInRowDirection()
922 {
923     m_borderStart = calcBorderStart();
924     m_borderEnd = calcBorderEnd();
925 }
926
927 LayoutUnit RenderTable::borderBefore() const
928 {
929     if (collapseBorders())
930         return outerBorderBefore();
931     return RenderBlock::borderBefore();
932 }
933
934 LayoutUnit RenderTable::borderAfter() const
935 {
936     if (collapseBorders())
937         return outerBorderAfter();
938     return RenderBlock::borderAfter();
939 }
940
941 LayoutUnit RenderTable::outerBorderBefore() const
942 {
943     if (!collapseBorders())
944         return 0;
945     LayoutUnit borderWidth = 0;
946     if (RenderTableSection* topSection = this->topSection()) {
947         borderWidth = topSection->outerBorderBefore();
948         if (borderWidth < 0)
949             return 0;   // Overridden by hidden
950     }
951     const BorderValue& tb = style()->borderBefore();
952     if (tb.style() == BHIDDEN)
953         return 0;
954     if (tb.style() > BHIDDEN)
955         borderWidth = max<LayoutUnit>(borderWidth, tb.width() / 2);
956     return borderWidth;
957 }
958
959 LayoutUnit RenderTable::outerBorderAfter() const
960 {
961     if (!collapseBorders())
962         return 0;
963     LayoutUnit borderWidth = 0;
964     RenderTableSection* bottomSection;
965     if (m_foot)
966         bottomSection = m_foot;
967     else {
968         RenderObject* child;
969         for (child = lastChild(); child && !child->isTableSection(); child = child->previousSibling()) { }
970         bottomSection = child ? toRenderTableSection(child) : 0;
971     }
972     if (bottomSection) {
973         borderWidth = bottomSection->outerBorderAfter();
974         if (borderWidth < 0)
975             return 0;   // Overridden by hidden
976     }
977     const BorderValue& tb = style()->borderAfter();
978     if (tb.style() == BHIDDEN)
979         return 0;
980     if (tb.style() > BHIDDEN)
981         borderWidth = max<LayoutUnit>(borderWidth, (tb.width() + 1) / 2);
982     return borderWidth;
983 }
984
985 LayoutUnit RenderTable::outerBorderStart() const
986 {
987     if (!collapseBorders())
988         return 0;
989
990     LayoutUnit borderWidth = 0;
991
992     const BorderValue& tb = style()->borderStart();
993     if (tb.style() == BHIDDEN)
994         return 0;
995     if (tb.style() > BHIDDEN)
996         borderWidth = (tb.width() + (style()->isLeftToRightDirection() ? 0 : 1)) / 2;
997
998     bool allHidden = true;
999     for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
1000         if (!child->isTableSection())
1001             continue;
1002         LayoutUnit sw = toRenderTableSection(child)->outerBorderStart();
1003         if (sw < 0)
1004             continue;
1005         allHidden = false;
1006         borderWidth = max(borderWidth, sw);
1007     }
1008     if (allHidden)
1009         return 0;
1010
1011     return borderWidth;
1012 }
1013
1014 LayoutUnit RenderTable::outerBorderEnd() const
1015 {
1016     if (!collapseBorders())
1017         return 0;
1018
1019     LayoutUnit borderWidth = 0;
1020
1021     const BorderValue& tb = style()->borderEnd();
1022     if (tb.style() == BHIDDEN)
1023         return 0;
1024     if (tb.style() > BHIDDEN)
1025         borderWidth = (tb.width() + (style()->isLeftToRightDirection() ? 1 : 0)) / 2;
1026
1027     bool allHidden = true;
1028     for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
1029         if (!child->isTableSection())
1030             continue;
1031         LayoutUnit sw = toRenderTableSection(child)->outerBorderEnd();
1032         if (sw < 0)
1033             continue;
1034         allHidden = false;
1035         borderWidth = max(borderWidth, sw);
1036     }
1037     if (allHidden)
1038         return 0;
1039
1040     return borderWidth;
1041 }
1042
1043 RenderTableSection* RenderTable::sectionAbove(const RenderTableSection* section, SkipEmptySectionsValue skipEmptySections) const
1044 {
1045     recalcSectionsIfNeeded();
1046
1047     if (section == m_head)
1048         return 0;
1049
1050     RenderObject* prevSection = section == m_foot ? lastChild() : section->previousSibling();
1051     while (prevSection) {
1052         if (prevSection->isTableSection() && prevSection != m_head && prevSection != m_foot && (skipEmptySections == DoNotSkipEmptySections || toRenderTableSection(prevSection)->numRows()))
1053             break;
1054         prevSection = prevSection->previousSibling();
1055     }
1056     if (!prevSection && m_head && (skipEmptySections == DoNotSkipEmptySections || m_head->numRows()))
1057         prevSection = m_head;
1058     return toRenderTableSection(prevSection);
1059 }
1060
1061 RenderTableSection* RenderTable::sectionBelow(const RenderTableSection* section, SkipEmptySectionsValue skipEmptySections) const
1062 {
1063     recalcSectionsIfNeeded();
1064
1065     if (section == m_foot)
1066         return 0;
1067
1068     RenderObject* nextSection = section == m_head ? firstChild() : section->nextSibling();
1069     while (nextSection) {
1070         if (nextSection->isTableSection() && nextSection != m_head && nextSection != m_foot && (skipEmptySections  == DoNotSkipEmptySections || toRenderTableSection(nextSection)->numRows()))
1071             break;
1072         nextSection = nextSection->nextSibling();
1073     }
1074     if (!nextSection && m_foot && (skipEmptySections == DoNotSkipEmptySections || m_foot->numRows()))
1075         nextSection = m_foot;
1076     return toRenderTableSection(nextSection);
1077 }
1078
1079 RenderTableCell* RenderTable::cellAbove(const RenderTableCell* cell) const
1080 {
1081     recalcSectionsIfNeeded();
1082
1083     // Find the section and row to look in
1084     unsigned r = cell->row();
1085     RenderTableSection* section = 0;
1086     unsigned rAbove = 0;
1087     if (r > 0) {
1088         // cell is not in the first row, so use the above row in its own section
1089         section = cell->section();
1090         rAbove = r - 1;
1091     } else {
1092         section = sectionAbove(cell->section(), SkipEmptySections);
1093         if (section) {
1094             ASSERT(section->numRows());
1095             rAbove = section->numRows() - 1;
1096         }
1097     }
1098
1099     // Look up the cell in the section's grid, which requires effective col index
1100     if (section) {
1101         unsigned effCol = colToEffCol(cell->col());
1102         RenderTableSection::CellStruct& aboveCell = section->cellAt(rAbove, effCol);
1103         return aboveCell.primaryCell();
1104     } else
1105         return 0;
1106 }
1107
1108 RenderTableCell* RenderTable::cellBelow(const RenderTableCell* cell) const
1109 {
1110     recalcSectionsIfNeeded();
1111
1112     // Find the section and row to look in
1113     unsigned r = cell->row() + cell->rowSpan() - 1;
1114     RenderTableSection* section = 0;
1115     unsigned rBelow = 0;
1116     if (r < cell->section()->numRows() - 1) {
1117         // The cell is not in the last row, so use the next row in the section.
1118         section = cell->section();
1119         rBelow = r + 1;
1120     } else {
1121         section = sectionBelow(cell->section(), SkipEmptySections);
1122         if (section)
1123             rBelow = 0;
1124     }
1125
1126     // Look up the cell in the section's grid, which requires effective col index
1127     if (section) {
1128         unsigned effCol = colToEffCol(cell->col());
1129         RenderTableSection::CellStruct& belowCell = section->cellAt(rBelow, effCol);
1130         return belowCell.primaryCell();
1131     } else
1132         return 0;
1133 }
1134
1135 RenderTableCell* RenderTable::cellBefore(const RenderTableCell* cell) const
1136 {
1137     recalcSectionsIfNeeded();
1138
1139     RenderTableSection* section = cell->section();
1140     unsigned effCol = colToEffCol(cell->col());
1141     if (!effCol)
1142         return 0;
1143     
1144     // If we hit a colspan back up to a real cell.
1145     RenderTableSection::CellStruct& prevCell = section->cellAt(cell->row(), effCol - 1);
1146     return prevCell.primaryCell();
1147 }
1148
1149 RenderTableCell* RenderTable::cellAfter(const RenderTableCell* cell) const
1150 {
1151     recalcSectionsIfNeeded();
1152
1153     unsigned effCol = colToEffCol(cell->col() + cell->colSpan());
1154     if (effCol >= numEffCols())
1155         return 0;
1156     return cell->section()->primaryCellAt(cell->row(), effCol);
1157 }
1158
1159 RenderBlock* RenderTable::firstLineBlock() const
1160 {
1161     return 0;
1162 }
1163
1164 void RenderTable::updateFirstLetter()
1165 {
1166 }
1167
1168 LayoutUnit RenderTable::firstLineBoxBaseline() const
1169 {
1170     if (isWritingModeRoot())
1171         return -1;
1172
1173     recalcSectionsIfNeeded();
1174
1175     const RenderTableSection* topNonEmptySection = this->topNonEmptySection();
1176     if (!topNonEmptySection)
1177         return -1;
1178
1179     return topNonEmptySection->logicalTop() + topNonEmptySection->firstLineBoxBaseline();
1180 }
1181
1182 LayoutRect RenderTable::overflowClipRect(const LayoutPoint& location, RenderRegion* region, OverlayScrollbarSizeRelevancy relevancy)
1183 {
1184     LayoutRect rect = RenderBlock::overflowClipRect(location, region, relevancy);
1185     
1186     // If we have a caption, expand the clip to include the caption.
1187     // FIXME: Technically this is wrong, but it's virtually impossible to fix this
1188     // for real until captions have been re-written.
1189     // FIXME: This code assumes (like all our other caption code) that only top/bottom are
1190     // supported.  When we actually support left/right and stop mapping them to top/bottom,
1191     // we might have to hack this code first (depending on what order we do these bug fixes in).
1192     if (!m_captions.isEmpty()) {
1193         if (style()->isHorizontalWritingMode()) {
1194             rect.setHeight(height());
1195             rect.setY(location.y());
1196         } else {
1197             rect.setWidth(width());
1198             rect.setX(location.x());
1199         }
1200     }
1201
1202     return rect;
1203 }
1204
1205 bool RenderTable::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const LayoutPoint& pointInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action)
1206 {
1207     LayoutPoint adjustedLocation = accumulatedOffset + location();
1208
1209     // Check kids first.
1210     if (!hasOverflowClip() || overflowClipRect(adjustedLocation, result.region()).intersects(result.rectForPoint(pointInContainer))) {
1211         for (RenderObject* child = lastChild(); child; child = child->previousSibling()) {
1212             if (child->isBox() && !toRenderBox(child)->hasSelfPaintingLayer() && (child->isTableSection() || (child->isRenderBlock() && child->style()->display() == TABLE_CAPTION))) {
1213                 LayoutPoint childPoint = flipForWritingModeForChild(toRenderBox(child), adjustedLocation);
1214                 if (child->nodeAtPoint(request, result, pointInContainer, childPoint, action)) {
1215                     updateHitTestResult(result, toLayoutPoint(pointInContainer - childPoint));
1216                     return true;
1217                 }
1218             }
1219         }
1220     }
1221
1222     // Check our bounds next.
1223     LayoutRect boundsRect(adjustedLocation, size());
1224     if (visibleToHitTesting() && (action == HitTestBlockBackground || action == HitTestChildBlockBackground) && boundsRect.intersects(result.rectForPoint(pointInContainer))) {
1225         updateHitTestResult(result, flipForWritingMode(pointInContainer - toLayoutSize(adjustedLocation)));
1226         if (!result.addNodeToRectBasedTestResult(node(), pointInContainer, boundsRect))
1227             return true;
1228     }
1229
1230     return false;
1231 }
1232
1233 }