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