| /* |
| * Copyright (C) 1997 Martin Jones (mjones@kde.org) |
| * (C) 1997 Torben Weis (weis@kde.org) |
| * (C) 1998 Waldo Bastian (bastian@kde.org) |
| * (C) 1999 Lars Knoll (knoll@kde.org) |
| * (C) 1999 Antti Koivisto (koivisto@kde.org) |
| * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. |
| * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com) |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public License |
| * along with this library; see the file COPYING.LIB. If not, write to |
| * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| */ |
| |
| #include "config.h" |
| #include "RenderTable.h" |
| |
| #include "AutoTableLayout.h" |
| #include "CollapsedBorderValue.h" |
| #include "DeleteButtonController.h" |
| #include "Document.h" |
| #include "FixedTableLayout.h" |
| #include "FrameView.h" |
| #include "HitTestResult.h" |
| #include "HTMLNames.h" |
| #include "RenderLayer.h" |
| #include "RenderTableCell.h" |
| #include "RenderTableCol.h" |
| #include "RenderTableSection.h" |
| #ifdef ANDROID_LAYOUT |
| #include "Settings.h" |
| #endif |
| #include "RenderView.h" |
| |
| using namespace std; |
| |
| namespace WebCore { |
| |
| using namespace HTMLNames; |
| |
| RenderTable::RenderTable(Node* node) |
| : RenderBlock(node) |
| , m_caption(0) |
| , m_head(0) |
| , m_foot(0) |
| , m_firstBody(0) |
| , m_currentBorder(0) |
| , m_hasColElements(false) |
| , m_needsSectionRecalc(0) |
| , m_hSpacing(0) |
| , m_vSpacing(0) |
| , m_borderStart(0) |
| , m_borderEnd(0) |
| { |
| setChildrenInline(false); |
| m_columnPos.fill(0, 2); |
| m_columns.fill(ColumnStruct(), 1); |
| |
| #ifdef ANDROID_LAYOUT |
| m_singleColumn = false; |
| #endif |
| } |
| |
| RenderTable::~RenderTable() |
| { |
| } |
| |
| void RenderTable::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) |
| { |
| RenderBlock::styleDidChange(diff, oldStyle); |
| propagateStyleToAnonymousChildren(); |
| |
| ETableLayout oldTableLayout = oldStyle ? oldStyle->tableLayout() : TAUTO; |
| |
| // In the collapsed border model, there is no cell spacing. |
| m_hSpacing = collapseBorders() ? 0 : style()->horizontalBorderSpacing(); |
| m_vSpacing = collapseBorders() ? 0 : style()->verticalBorderSpacing(); |
| m_columnPos[0] = m_hSpacing; |
| |
| if (!m_tableLayout || style()->tableLayout() != oldTableLayout) { |
| // According to the CSS2 spec, you only use fixed table layout if an |
| // explicit width is specified on the table. Auto width implies auto table layout. |
| if (style()->tableLayout() == TFIXED && !style()->logicalWidth().isAuto()) |
| m_tableLayout.set(new FixedTableLayout(this)); |
| else |
| m_tableLayout.set(new AutoTableLayout(this)); |
| } |
| } |
| |
| static inline void resetSectionPointerIfNotBefore(RenderTableSection*& ptr, RenderObject* before) |
| { |
| if (!before || !ptr) |
| return; |
| RenderObject* o = before->previousSibling(); |
| while (o && o != ptr) |
| o = o->previousSibling(); |
| if (!o) |
| ptr = 0; |
| } |
| |
| void RenderTable::addChild(RenderObject* child, RenderObject* beforeChild) |
| { |
| // Make sure we don't append things after :after-generated content if we have it. |
| if (!beforeChild && isAfterContent(lastChild())) |
| beforeChild = lastChild(); |
| |
| bool wrapInAnonymousSection = !child->isPositioned(); |
| |
| if (child->isRenderBlock() && child->style()->display() == TABLE_CAPTION) { |
| // First caption wins. |
| if (beforeChild && m_caption) { |
| RenderObject* o = beforeChild->previousSibling(); |
| while (o && o != m_caption) |
| o = o->previousSibling(); |
| if (!o) { |
| m_caption = 0; |
| setNeedsSectionRecalc(); |
| } |
| } |
| if (!m_caption) |
| m_caption = toRenderBlock(child); |
| else |
| setNeedsSectionRecalc(); |
| wrapInAnonymousSection = false; |
| } else if (child->isTableCol()) { |
| m_hasColElements = true; |
| wrapInAnonymousSection = false; |
| } else if (child->isTableSection()) { |
| switch (child->style()->display()) { |
| case TABLE_HEADER_GROUP: |
| resetSectionPointerIfNotBefore(m_head, beforeChild); |
| if (!m_head) { |
| m_head = toRenderTableSection(child); |
| } else { |
| resetSectionPointerIfNotBefore(m_firstBody, beforeChild); |
| if (!m_firstBody) |
| m_firstBody = toRenderTableSection(child); |
| } |
| wrapInAnonymousSection = false; |
| break; |
| case TABLE_FOOTER_GROUP: |
| resetSectionPointerIfNotBefore(m_foot, beforeChild); |
| if (!m_foot) { |
| m_foot = toRenderTableSection(child); |
| wrapInAnonymousSection = false; |
| break; |
| } |
| // Fall through. |
| case TABLE_ROW_GROUP: |
| resetSectionPointerIfNotBefore(m_firstBody, beforeChild); |
| if (!m_firstBody) |
| m_firstBody = toRenderTableSection(child); |
| wrapInAnonymousSection = false; |
| break; |
| default: |
| ASSERT_NOT_REACHED(); |
| } |
| } else if (child->isTableCell() || child->isTableRow()) |
| wrapInAnonymousSection = true; |
| else |
| wrapInAnonymousSection = true; |
| |
| if (!wrapInAnonymousSection) { |
| // If the next renderer is actually wrapped in an anonymous table section, we need to go up and find that. |
| while (beforeChild && beforeChild->parent() != this) |
| beforeChild = beforeChild->parent(); |
| |
| RenderBox::addChild(child, beforeChild); |
| return; |
| } |
| |
| if (!beforeChild && lastChild() && lastChild()->isTableSection() && lastChild()->isAnonymous()) { |
| lastChild()->addChild(child); |
| return; |
| } |
| |
| RenderObject* lastBox = beforeChild; |
| while (lastBox && lastBox->parent()->isAnonymous() && !lastBox->isTableSection() && lastBox->style()->display() != TABLE_CAPTION && lastBox->style()->display() != TABLE_COLUMN_GROUP) |
| lastBox = lastBox->parent(); |
| if (lastBox && lastBox->isAnonymous() && !isAfterContent(lastBox)) { |
| if (beforeChild == lastBox) |
| beforeChild = lastBox->firstChild(); |
| lastBox->addChild(child, beforeChild); |
| return; |
| } |
| |
| if (beforeChild && !beforeChild->isTableSection() && beforeChild->style()->display() != TABLE_CAPTION && beforeChild->style()->display() != TABLE_COLUMN_GROUP) |
| beforeChild = 0; |
| RenderTableSection* section = new (renderArena()) RenderTableSection(document() /* anonymous */); |
| RefPtr<RenderStyle> newStyle = RenderStyle::create(); |
| newStyle->inheritFrom(style()); |
| newStyle->setDisplay(TABLE_ROW_GROUP); |
| section->setStyle(newStyle.release()); |
| addChild(section, beforeChild); |
| section->addChild(child); |
| } |
| |
| void RenderTable::removeChild(RenderObject* oldChild) |
| { |
| RenderBox::removeChild(oldChild); |
| |
| if (m_caption && oldChild == m_caption && node()) |
| node()->setNeedsStyleRecalc(); |
| setNeedsSectionRecalc(); |
| } |
| |
| void RenderTable::computeLogicalWidth() |
| { |
| #ifdef ANDROID_LAYOUT |
| if (view()->frameView()) |
| setVisibleWidth(view()->frameView()->textWrapWidth()); |
| #endif |
| |
| if (isPositioned()) |
| computePositionedLogicalWidth(); |
| |
| RenderBlock* cb = containingBlock(); |
| |
| int availableLogicalWidth = containingBlockLogicalWidthForContent(); |
| bool hasPerpendicularContainingBlock = cb->style()->isHorizontalWritingMode() != style()->isHorizontalWritingMode(); |
| int containerWidthInInlineDirection = hasPerpendicularContainingBlock ? perpendicularContainingBlockLogicalHeight() : availableLogicalWidth; |
| |
| LengthType logicalWidthType = style()->logicalWidth().type(); |
| if (logicalWidthType > Relative && style()->logicalWidth().isPositive()) { |
| // Percent or fixed table |
| setLogicalWidth(style()->logicalWidth().calcMinValue(containerWidthInInlineDirection)); |
| setLogicalWidth(max(minPreferredLogicalWidth(), logicalWidth())); |
| } else { |
| // Subtract out any fixed margins from our available width for auto width tables. |
| int marginTotal = 0; |
| if (!style()->marginStart().isAuto()) |
| marginTotal += style()->marginStart().calcValue(availableLogicalWidth); |
| if (!style()->marginEnd().isAuto()) |
| marginTotal += style()->marginEnd().calcValue(availableLogicalWidth); |
| |
| // Subtract out our margins to get the available content width. |
| int availableContentLogicalWidth = max(0, containerWidthInInlineDirection - marginTotal); |
| |
| // Ensure we aren't bigger than our max width or smaller than our min width. |
| setLogicalWidth(min(availableContentLogicalWidth, maxPreferredLogicalWidth())); |
| } |
| |
| setLogicalWidth(max(logicalWidth(), minPreferredLogicalWidth())); |
| |
| // Finally, with our true width determined, compute our margins for real. |
| setMarginStart(0); |
| setMarginEnd(0); |
| #ifdef ANDROID_LAYOUT |
| // in SSR mode, we ignore left/right margin for table |
| if (document()->settings()->layoutAlgorithm() == Settings::kLayoutSSR) |
| return; |
| #endif |
| if (!hasPerpendicularContainingBlock) |
| computeInlineDirectionMargins(cb, availableLogicalWidth, logicalWidth()); |
| else { |
| setMarginStart(style()->marginStart().calcMinValue(availableLogicalWidth)); |
| setMarginEnd(style()->marginEnd().calcMinValue(availableLogicalWidth)); |
| } |
| } |
| |
| void RenderTable::adjustLogicalHeightForCaption() |
| { |
| ASSERT(m_caption); |
| IntRect captionRect(m_caption->x(), m_caption->y(), m_caption->width(), m_caption->height()); |
| |
| m_caption->setLogicalLocation(m_caption->marginStart(), logicalHeight()); |
| if (!selfNeedsLayout() && m_caption->checkForRepaintDuringLayout()) |
| m_caption->repaintDuringLayoutIfMoved(captionRect); |
| |
| setLogicalHeight(logicalHeight() + m_caption->logicalHeight() + m_caption->marginBefore() + m_caption->marginAfter()); |
| } |
| |
| void RenderTable::layout() |
| { |
| ASSERT(needsLayout()); |
| |
| if (simplifiedLayout()) |
| return; |
| |
| recalcSectionsIfNeeded(); |
| |
| LayoutRepainter repainter(*this, checkForRepaintDuringLayout()); |
| LayoutStateMaintainer statePusher(view(), this, IntSize(x(), y()), style()->isFlippedBlocksWritingMode()); |
| |
| setLogicalHeight(0); |
| m_overflow.clear(); |
| |
| initMaxMarginValues(); |
| |
| #ifdef ANDROID_LAYOUT |
| bool relayoutChildren = false; |
| #endif |
| |
| int oldLogicalWidth = logicalWidth(); |
| computeLogicalWidth(); |
| |
| #ifdef ANDROID_LAYOUT |
| if (!checkAndSetRelayoutChildren(&relayoutChildren) |
| && document()->settings()->layoutAlgorithm() == Settings::kLayoutSSR) { |
| // if the width of a table is wider than its container width, or it has a nested table, |
| // we will render it with single column. |
| int cw = containingBlockLogicalWidthForContent(); |
| bool shouldRenderAsSingleColumn = (width() > cw); |
| if (!shouldRenderAsSingleColumn) { |
| RenderObject* child = firstChild(); |
| while (child) { |
| if (child->isTable()) { |
| shouldRenderAsSingleColumn = true; |
| break; |
| } |
| child = child->nextInPreOrder(); |
| } |
| } |
| |
| if (shouldRenderAsSingleColumn) { |
| m_singleColumn = true; |
| if (width() > cw) |
| setWidth(cw); |
| if (m_minPreferredLogicalWidth > cw) |
| m_minPreferredLogicalWidth = cw; |
| if (m_maxPreferredLogicalWidth > cw) |
| m_maxPreferredLogicalWidth = cw; |
| } |
| } |
| #endif |
| if (m_caption && logicalWidth() != oldLogicalWidth) |
| m_caption->setNeedsLayout(true, false); |
| |
| // FIXME: The optimisation below doesn't work since the internal table |
| // layout could have changed. we need to add a flag to the table |
| // layout that tells us if something has changed in the min max |
| // calculations to do it correctly. |
| // if ( oldWidth != width() || columns.size() + 1 != columnPos.size() ) |
| m_tableLayout->layout(); |
| |
| setCellLogicalWidths(); |
| |
| int totalSectionLogicalHeight = 0; |
| int oldTableLogicalTop = m_caption ? m_caption->logicalHeight() + m_caption->marginBefore() + m_caption->marginAfter() : 0; |
| |
| bool collapsing = collapseBorders(); |
| |
| for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { |
| #ifdef ANDROID_LAYOUT |
| if (relayoutChildren) { |
| child->setNeedsLayout(true, false); |
| if (!child->isTableSection()) { |
| child->layoutIfNeeded(); |
| continue; |
| } |
| // fall through |
| } |
| #endif |
| if (child->isTableSection()) { |
| child->layoutIfNeeded(); |
| RenderTableSection* section = toRenderTableSection(child); |
| totalSectionLogicalHeight += section->calcRowLogicalHeight(); |
| if (collapsing) |
| section->recalcOuterBorder(); |
| ASSERT(!section->needsLayout()); |
| } else if (child->isTableCol()) { |
| child->layoutIfNeeded(); |
| ASSERT(!child->needsLayout()); |
| } |
| } |
| |
| // Only lay out one caption, since it's the only one we're going to end up painting. |
| if (m_caption) |
| m_caption->layoutIfNeeded(); |
| |
| // If any table section moved vertically, we will just repaint everything from that |
| // section down (it is quite unlikely that any of the following sections |
| // did not shift). |
| bool sectionMoved = false; |
| int movedSectionLogicalTop = 0; |
| |
| // FIXME: Collapse caption margin. |
| if (m_caption && m_caption->style()->captionSide() != CAPBOTTOM) { |
| adjustLogicalHeightForCaption(); |
| if (logicalHeight() != oldTableLogicalTop) { |
| sectionMoved = true; |
| movedSectionLogicalTop = min(logicalHeight(), oldTableLogicalTop); |
| } |
| } |
| |
| int borderAndPaddingBefore = borderBefore() + (collapsing ? 0 : paddingBefore()); |
| int borderAndPaddingAfter = borderAfter() + (collapsing ? 0 : paddingAfter()); |
| |
| setLogicalHeight(logicalHeight() + borderAndPaddingBefore); |
| |
| if (!isPositioned()) |
| computeLogicalHeight(); |
| |
| Length logicalHeightLength = style()->logicalHeight(); |
| int computedLogicalHeight = 0; |
| if (logicalHeightLength.isFixed()) { |
| // Tables size as though CSS height includes border/padding. |
| computedLogicalHeight = logicalHeightLength.value() - (borderAndPaddingBefore + borderAndPaddingAfter); |
| } else if (logicalHeightLength.isPercent()) |
| computedLogicalHeight = computePercentageLogicalHeight(logicalHeightLength); |
| computedLogicalHeight = max(0, computedLogicalHeight); |
| |
| for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { |
| if (child->isTableSection()) |
| // FIXME: Distribute extra height between all table body sections instead of giving it all to the first one. |
| toRenderTableSection(child)->layoutRows(child == m_firstBody ? max(0, computedLogicalHeight - totalSectionLogicalHeight) : 0); |
| } |
| |
| if (!m_firstBody && computedLogicalHeight > totalSectionLogicalHeight && !document()->inQuirksMode()) { |
| // Completely empty tables (with no sections or anything) should at least honor specified height |
| // in strict mode. |
| setLogicalHeight(logicalHeight() + computedLogicalHeight); |
| } |
| |
| int sectionLogicalLeft = style()->isLeftToRightDirection() ? borderStart() : borderEnd(); |
| if (!collapsing) |
| sectionLogicalLeft += style()->isLeftToRightDirection() ? paddingStart() : paddingEnd(); |
| |
| // position the table sections |
| RenderTableSection* section = m_head ? m_head : (m_firstBody ? m_firstBody : m_foot); |
| while (section) { |
| if (!sectionMoved && section->logicalTop() != logicalHeight()) { |
| sectionMoved = true; |
| movedSectionLogicalTop = min(logicalHeight(), section->logicalTop()) + (style()->isHorizontalWritingMode() ? section->minYVisualOverflow() : section->minXVisualOverflow()); |
| } |
| section->setLogicalLocation(sectionLogicalLeft, logicalHeight()); |
| |
| setLogicalHeight(logicalHeight() + section->logicalHeight()); |
| section = sectionBelow(section); |
| } |
| |
| setLogicalHeight(logicalHeight() + borderAndPaddingAfter); |
| |
| if (m_caption && m_caption->style()->captionSide() == CAPBOTTOM) |
| adjustLogicalHeightForCaption(); |
| |
| if (isPositioned()) |
| computeLogicalHeight(); |
| |
| // table can be containing block of positioned elements. |
| // FIXME: Only pass true if width or height changed. |
| layoutPositionedObjects(true); |
| |
| updateLayerTransform(); |
| |
| computeOverflow(clientLogicalBottom()); |
| |
| statePusher.pop(); |
| |
| if (view()->layoutState()->pageLogicalHeight()) |
| setPageLogicalOffset(view()->layoutState()->pageLogicalOffset(logicalTop())); |
| |
| bool didFullRepaint = repainter.repaintAfterLayout(); |
| // Repaint with our new bounds if they are different from our old bounds. |
| if (!didFullRepaint && sectionMoved) { |
| if (style()->isHorizontalWritingMode()) |
| repaintRectangle(IntRect(minXVisualOverflow(), movedSectionLogicalTop, maxXVisualOverflow() - minXVisualOverflow(), maxYVisualOverflow() - movedSectionLogicalTop)); |
| else |
| repaintRectangle(IntRect(movedSectionLogicalTop, minYVisualOverflow(), maxXVisualOverflow() - movedSectionLogicalTop, maxYVisualOverflow() - minYVisualOverflow())); |
| } |
| |
| setNeedsLayout(false); |
| } |
| |
| void RenderTable::addOverflowFromChildren() |
| { |
| // Add overflow from borders. |
| // Technically it's odd that we are incorporating the borders into layout overflow, which is only supposed to be about overflow from our |
| // descendant objects, but since tables don't support overflow:auto, this works out fine. |
| if (collapseBorders()) { |
| int rightBorderOverflow = width() + outerBorderRight() - borderRight(); |
| int leftBorderOverflow = borderLeft() - outerBorderLeft(); |
| int bottomBorderOverflow = height() + outerBorderBottom() - borderBottom(); |
| int topBorderOverflow = borderTop() - outerBorderTop(); |
| IntRect borderOverflowRect(leftBorderOverflow, topBorderOverflow, rightBorderOverflow - leftBorderOverflow, bottomBorderOverflow - topBorderOverflow); |
| if (borderOverflowRect != borderBoxRect()) { |
| addLayoutOverflow(borderOverflowRect); |
| addVisualOverflow(borderOverflowRect); |
| } |
| } |
| |
| // Add overflow from our caption. |
| if (m_caption) |
| addOverflowFromChild(m_caption); |
| |
| // Add overflow from our sections. |
| for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { |
| if (child->isTableSection()) { |
| RenderTableSection* section = toRenderTableSection(child); |
| addOverflowFromChild(section); |
| } |
| } |
| } |
| |
| void RenderTable::setCellLogicalWidths() |
| { |
| for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { |
| if (child->isTableSection()) |
| toRenderTableSection(child)->setCellLogicalWidths(); |
| } |
| } |
| |
| void RenderTable::paint(PaintInfo& paintInfo, int tx, int ty) |
| { |
| tx += x(); |
| ty += y(); |
| |
| PaintPhase paintPhase = paintInfo.phase; |
| |
| if (!isRoot()) { |
| IntRect overflowBox = visualOverflowRect(); |
| flipForWritingMode(overflowBox); |
| overflowBox.inflate(maximalOutlineSize(paintInfo.phase)); |
| overflowBox.move(tx, ty); |
| if (!overflowBox.intersects(paintInfo.rect)) |
| return; |
| } |
| |
| bool pushedClip = pushContentsClip(paintInfo, tx, ty); |
| paintObject(paintInfo, tx, ty); |
| if (pushedClip) |
| popContentsClip(paintInfo, paintPhase, tx, ty); |
| } |
| |
| void RenderTable::paintObject(PaintInfo& paintInfo, int tx, int ty) |
| { |
| PaintPhase paintPhase = paintInfo.phase; |
| if ((paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) && hasBoxDecorations() && style()->visibility() == VISIBLE) |
| paintBoxDecorations(paintInfo, tx, ty); |
| |
| if (paintPhase == PaintPhaseMask) { |
| paintMask(paintInfo, tx, ty); |
| return; |
| } |
| |
| // We're done. We don't bother painting any children. |
| if (paintPhase == PaintPhaseBlockBackground) |
| return; |
| |
| // We don't paint our own background, but we do let the kids paint their backgrounds. |
| if (paintPhase == PaintPhaseChildBlockBackgrounds) |
| paintPhase = PaintPhaseChildBlockBackground; |
| |
| PaintInfo info(paintInfo); |
| info.phase = paintPhase; |
| info.updatePaintingRootForChildren(this); |
| |
| for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { |
| if (child->isBox() && !toRenderBox(child)->hasSelfPaintingLayer() && (child->isTableSection() || child == m_caption)) { |
| IntPoint childPoint = flipForWritingMode(toRenderBox(child), IntPoint(tx, ty), ParentToChildFlippingAdjustment); |
| child->paint(info, childPoint.x(), childPoint.y()); |
| } |
| } |
| |
| if (collapseBorders() && paintPhase == PaintPhaseChildBlockBackground && style()->visibility() == VISIBLE) { |
| // Collect all the unique border styles that we want to paint in a sorted list. Once we |
| // have all the styles sorted, we then do individual passes, painting each style of border |
| // from lowest precedence to highest precedence. |
| info.phase = PaintPhaseCollapsedTableBorders; |
| RenderTableCell::CollapsedBorderStyles borderStyles; |
| RenderObject* stop = nextInPreOrderAfterChildren(); |
| for (RenderObject* o = firstChild(); o && o != stop; o = o->nextInPreOrder()) { |
| if (o->isTableCell()) |
| toRenderTableCell(o)->collectBorderStyles(borderStyles); |
| } |
| RenderTableCell::sortBorderStyles(borderStyles); |
| size_t count = borderStyles.size(); |
| for (size_t i = 0; i < count; ++i) { |
| m_currentBorder = &borderStyles[i]; |
| for (RenderObject* child = firstChild(); child; child = child->nextSibling()) |
| if (child->isTableSection()) { |
| IntPoint childPoint = flipForWritingMode(toRenderTableSection(child), IntPoint(tx, ty), ParentToChildFlippingAdjustment); |
| child->paint(info, childPoint.x(), childPoint.y()); |
| } |
| } |
| m_currentBorder = 0; |
| } |
| |
| // Paint outline. |
| if ((paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseSelfOutline) && hasOutline() && style()->visibility() == VISIBLE) |
| paintOutline(paintInfo.context, tx, ty, width(), height()); |
| } |
| |
| void RenderTable::subtractCaptionRect(IntRect& rect) const |
| { |
| if (!m_caption) |
| return; |
| |
| int captionLogicalHeight = m_caption->logicalHeight() + m_caption->marginBefore() + m_caption->marginAfter(); |
| bool captionIsBefore = (m_caption->style()->captionSide() != CAPBOTTOM) ^ style()->isFlippedBlocksWritingMode(); |
| if (style()->isHorizontalWritingMode()) { |
| rect.setHeight(rect.height() - captionLogicalHeight); |
| if (captionIsBefore) |
| rect.move(0, captionLogicalHeight); |
| } else { |
| rect.setWidth(rect.width() - captionLogicalHeight); |
| if (captionIsBefore) |
| rect.move(captionLogicalHeight, 0); |
| } |
| } |
| |
| void RenderTable::paintBoxDecorations(PaintInfo& paintInfo, int tx, int ty) |
| { |
| if (!paintInfo.shouldPaintWithinRoot(this)) |
| return; |
| |
| IntRect rect(tx, ty, width(), height()); |
| subtractCaptionRect(rect); |
| |
| paintBoxShadow(paintInfo.context, rect.x(), rect.y(), rect.width(), rect.height(), style(), Normal); |
| |
| if (isRoot()) |
| paintRootBoxFillLayers(paintInfo); |
| else if (!isBody() || document()->documentElement()->renderer()->hasBackground()) |
| // The <body> only paints its background if the root element has defined a background |
| // independent of the body. |
| paintFillLayers(paintInfo, style()->visitedDependentColor(CSSPropertyBackgroundColor), style()->backgroundLayers(), rect.x(), rect.y(), rect.width(), rect.height()); |
| |
| paintBoxShadow(paintInfo.context, rect.x(), rect.y(), rect.width(), rect.height(), style(), Inset); |
| |
| if (style()->hasBorder() && !collapseBorders()) |
| paintBorder(paintInfo.context, rect.x(), rect.y(), rect.width(), rect.height(), style()); |
| } |
| |
| void RenderTable::paintMask(PaintInfo& paintInfo, int tx, int ty) |
| { |
| if (style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask) |
| return; |
| |
| IntRect rect(tx, ty, width(), height()); |
| subtractCaptionRect(rect); |
| |
| paintMaskImages(paintInfo, rect.x(), rect.y(), rect.width(), rect.height()); |
| } |
| |
| void RenderTable::computePreferredLogicalWidths() |
| { |
| ASSERT(preferredLogicalWidthsDirty()); |
| |
| recalcSectionsIfNeeded(); |
| recalcBordersInRowDirection(); |
| |
| m_tableLayout->computePreferredLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth); |
| |
| if (m_caption) |
| m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, m_caption->minPreferredLogicalWidth()); |
| |
| setPreferredLogicalWidthsDirty(false); |
| } |
| |
| void RenderTable::splitColumn(int pos, int firstSpan) |
| { |
| // we need to add a new columnStruct |
| int oldSize = m_columns.size(); |
| m_columns.grow(oldSize + 1); |
| int oldSpan = m_columns[pos].span; |
| ASSERT(oldSpan > firstSpan); |
| m_columns[pos].span = firstSpan; |
| memmove(m_columns.data() + pos + 1, m_columns.data() + pos, (oldSize - pos) * sizeof(ColumnStruct)); |
| m_columns[pos + 1].span = oldSpan - firstSpan; |
| |
| // change width of all rows. |
| for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { |
| if (child->isTableSection()) |
| toRenderTableSection(child)->splitColumn(pos, firstSpan); |
| } |
| |
| m_columnPos.grow(numEffCols() + 1); |
| setNeedsLayoutAndPrefWidthsRecalc(); |
| } |
| |
| void RenderTable::appendColumn(int span) |
| { |
| // easy case. |
| int pos = m_columns.size(); |
| int newSize = pos + 1; |
| m_columns.grow(newSize); |
| m_columns[pos].span = span; |
| |
| // change width of all rows. |
| for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { |
| if (child->isTableSection()) |
| toRenderTableSection(child)->appendColumn(pos); |
| } |
| |
| m_columnPos.grow(numEffCols() + 1); |
| setNeedsLayoutAndPrefWidthsRecalc(); |
| } |
| |
| RenderTableCol* RenderTable::nextColElement(RenderTableCol* current) const |
| { |
| RenderObject* next = current->firstChild(); |
| if (!next) |
| next = current->nextSibling(); |
| if (!next && current->parent()->isTableCol()) |
| next = current->parent()->nextSibling(); |
| |
| while (next) { |
| if (next->isTableCol()) |
| return toRenderTableCol(next); |
| if (next != m_caption) |
| return 0; |
| next = next->nextSibling(); |
| } |
| |
| return 0; |
| } |
| |
| RenderTableCol* RenderTable::colElement(int col, bool* startEdge, bool* endEdge) const |
| { |
| if (!m_hasColElements) |
| return 0; |
| RenderObject* child = firstChild(); |
| int cCol = 0; |
| |
| while (child) { |
| if (child->isTableCol()) |
| break; |
| if (child != m_caption) |
| return 0; |
| child = child->nextSibling(); |
| } |
| if (!child) |
| return 0; |
| |
| RenderTableCol* colElem = toRenderTableCol(child); |
| while (colElem) { |
| int span = colElem->span(); |
| if (!colElem->firstChild()) { |
| int startCol = cCol; |
| int endCol = cCol + span - 1; |
| cCol += span; |
| if (cCol > col) { |
| if (startEdge) |
| *startEdge = startCol == col; |
| if (endEdge) |
| *endEdge = endCol == col; |
| return colElem; |
| } |
| } |
| colElem = nextColElement(colElem); |
| } |
| |
| return 0; |
| } |
| |
| void RenderTable::recalcCaption(RenderBlock* caption) const |
| { |
| if (!m_caption) { |
| m_caption = caption; |
| m_caption->setNeedsLayout(true); |
| } else { |
| // Make sure to null out the child's renderer. |
| if (Node* node = caption->node()) |
| node->setRenderer(0); |
| |
| // Destroy the child now. |
| caption->destroy(); |
| } |
| } |
| |
| void RenderTable::recalcSections() const |
| { |
| m_caption = 0; |
| m_head = 0; |
| m_foot = 0; |
| m_firstBody = 0; |
| m_hasColElements = false; |
| |
| // We need to get valid pointers to caption, head, foot and first body again |
| RenderObject* nextSibling; |
| for (RenderObject* child = firstChild(); child; child = nextSibling) { |
| nextSibling = child->nextSibling(); |
| switch (child->style()->display()) { |
| case TABLE_CAPTION: |
| if (child->isRenderBlock()) |
| recalcCaption(toRenderBlock(child)); |
| break; |
| case TABLE_COLUMN: |
| case TABLE_COLUMN_GROUP: |
| m_hasColElements = true; |
| break; |
| case TABLE_HEADER_GROUP: |
| if (child->isTableSection()) { |
| RenderTableSection* section = toRenderTableSection(child); |
| if (!m_head) |
| m_head = section; |
| else if (!m_firstBody) |
| m_firstBody = section; |
| section->recalcCellsIfNeeded(); |
| } |
| break; |
| case TABLE_FOOTER_GROUP: |
| if (child->isTableSection()) { |
| RenderTableSection* section = toRenderTableSection(child); |
| if (!m_foot) |
| m_foot = section; |
| else if (!m_firstBody) |
| m_firstBody = section; |
| section->recalcCellsIfNeeded(); |
| } |
| break; |
| case TABLE_ROW_GROUP: |
| if (child->isTableSection()) { |
| RenderTableSection* section = toRenderTableSection(child); |
| if (!m_firstBody) |
| m_firstBody = section; |
| section->recalcCellsIfNeeded(); |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| |
| // repair column count (addChild can grow it too much, because it always adds elements to the last row of a section) |
| int maxCols = 0; |
| for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { |
| if (child->isTableSection()) { |
| RenderTableSection* section = toRenderTableSection(child); |
| int sectionCols = section->numColumns(); |
| if (sectionCols > maxCols) |
| maxCols = sectionCols; |
| } |
| } |
| |
| m_columns.resize(maxCols); |
| m_columnPos.resize(maxCols + 1); |
| |
| ASSERT(selfNeedsLayout()); |
| |
| m_needsSectionRecalc = false; |
| } |
| |
| int RenderTable::calcBorderStart() const |
| { |
| if (collapseBorders()) { |
| // Determined by the first cell of the first row. See the CSS 2.1 spec, section 17.6.2. |
| if (!numEffCols()) |
| return 0; |
| |
| unsigned borderWidth = 0; |
| |
| const BorderValue& tb = style()->borderStart(); |
| if (tb.style() == BHIDDEN) |
| return 0; |
| if (tb.style() > BHIDDEN) |
| borderWidth = tb.width(); |
| |
| if (RenderTableCol* colGroup = colElement(0)) { |
| const BorderValue& gb = colGroup->style()->borderStart(); |
| if (gb.style() == BHIDDEN) |
| return 0; |
| if (gb.style() > BHIDDEN) |
| borderWidth = max(borderWidth, static_cast<unsigned>(gb.width())); |
| } |
| |
| RenderTableSection* firstNonEmptySection = m_head ? m_head : (m_firstBody ? m_firstBody : m_foot); |
| if (firstNonEmptySection && !firstNonEmptySection->numRows()) |
| firstNonEmptySection = sectionBelow(firstNonEmptySection, true); |
| |
| if (firstNonEmptySection) { |
| const BorderValue& sb = firstNonEmptySection->style()->borderStart(); |
| if (sb.style() == BHIDDEN) |
| return 0; |
| |
| if (sb.style() > BHIDDEN) |
| borderWidth = max(borderWidth, static_cast<unsigned>(sb.width())); |
| |
| const RenderTableSection::CellStruct& cs = firstNonEmptySection->cellAt(0, 0); |
| |
| if (cs.hasCells()) { |
| const BorderValue& cb = cs.primaryCell()->style()->borderStart(); // FIXME: Make this work with perpendicualr and flipped cells. |
| if (cb.style() == BHIDDEN) |
| return 0; |
| |
| const BorderValue& rb = cs.primaryCell()->parent()->style()->borderStart(); |
| if (rb.style() == BHIDDEN) |
| return 0; |
| |
| if (cb.style() > BHIDDEN) |
| borderWidth = max(borderWidth, static_cast<unsigned>(cb.width())); |
| if (rb.style() > BHIDDEN) |
| borderWidth = max(borderWidth, static_cast<unsigned>(rb.width())); |
| } |
| } |
| return (borderWidth + (style()->isLeftToRightDirection() ? 0 : 1)) / 2; |
| } |
| return RenderBlock::borderStart(); |
| } |
| |
| int RenderTable::calcBorderEnd() const |
| { |
| if (collapseBorders()) { |
| // Determined by the last cell of the first row. See the CSS 2.1 spec, section 17.6.2. |
| if (!numEffCols()) |
| return 0; |
| |
| unsigned borderWidth = 0; |
| |
| const BorderValue& tb = style()->borderEnd(); |
| if (tb.style() == BHIDDEN) |
| return 0; |
| if (tb.style() > BHIDDEN) |
| borderWidth = tb.width(); |
| |
| int endColumn = numEffCols() - 1; |
| if (RenderTableCol* colGroup = colElement(endColumn)) { |
| const BorderValue& gb = colGroup->style()->borderEnd(); |
| if (gb.style() == BHIDDEN) |
| return 0; |
| if (gb.style() > BHIDDEN) |
| borderWidth = max(borderWidth, static_cast<unsigned>(gb.width())); |
| } |
| |
| RenderTableSection* firstNonEmptySection = m_head ? m_head : (m_firstBody ? m_firstBody : m_foot); |
| if (firstNonEmptySection && !firstNonEmptySection->numRows()) |
| firstNonEmptySection = sectionBelow(firstNonEmptySection, true); |
| |
| if (firstNonEmptySection) { |
| const BorderValue& sb = firstNonEmptySection->style()->borderEnd(); |
| if (sb.style() == BHIDDEN) |
| return 0; |
| |
| if (sb.style() > BHIDDEN) |
| borderWidth = max(borderWidth, static_cast<unsigned>(sb.width())); |
| |
| const RenderTableSection::CellStruct& cs = firstNonEmptySection->cellAt(0, endColumn); |
| |
| if (cs.hasCells()) { |
| const BorderValue& cb = cs.primaryCell()->style()->borderEnd(); // FIXME: Make this work with perpendicular and flipped cells. |
| if (cb.style() == BHIDDEN) |
| return 0; |
| |
| const BorderValue& rb = cs.primaryCell()->parent()->style()->borderEnd(); |
| if (rb.style() == BHIDDEN) |
| return 0; |
| |
| if (cb.style() > BHIDDEN) |
| borderWidth = max(borderWidth, static_cast<unsigned>(cb.width())); |
| if (rb.style() > BHIDDEN) |
| borderWidth = max(borderWidth, static_cast<unsigned>(rb.width())); |
| } |
| } |
| return (borderWidth + (style()->isLeftToRightDirection() ? 1 : 0)) / 2; |
| } |
| return RenderBlock::borderEnd(); |
| } |
| |
| void RenderTable::recalcBordersInRowDirection() |
| { |
| m_borderStart = calcBorderStart(); |
| m_borderEnd = calcBorderEnd(); |
| } |
| |
| int RenderTable::borderBefore() const |
| { |
| if (collapseBorders()) |
| return outerBorderBefore(); |
| return RenderBlock::borderBefore(); |
| } |
| |
| int RenderTable::borderAfter() const |
| { |
| if (collapseBorders()) |
| return outerBorderAfter(); |
| return RenderBlock::borderAfter(); |
| } |
| |
| int RenderTable::outerBorderBefore() const |
| { |
| if (!collapseBorders()) |
| return 0; |
| int borderWidth = 0; |
| RenderTableSection* topSection; |
| if (m_head) |
| topSection = m_head; |
| else if (m_firstBody) |
| topSection = m_firstBody; |
| else if (m_foot) |
| topSection = m_foot; |
| else |
| topSection = 0; |
| if (topSection) { |
| borderWidth = topSection->outerBorderBefore(); |
| if (borderWidth == -1) |
| return 0; // Overridden by hidden |
| } |
| const BorderValue& tb = style()->borderBefore(); |
| if (tb.style() == BHIDDEN) |
| return 0; |
| if (tb.style() > BHIDDEN) |
| borderWidth = max(borderWidth, static_cast<int>(tb.width() / 2)); |
| return borderWidth; |
| } |
| |
| int RenderTable::outerBorderAfter() const |
| { |
| if (!collapseBorders()) |
| return 0; |
| int borderWidth = 0; |
| RenderTableSection* bottomSection; |
| if (m_foot) |
| bottomSection = m_foot; |
| else { |
| RenderObject* child; |
| for (child = lastChild(); child && !child->isTableSection(); child = child->previousSibling()) { } |
| bottomSection = child ? toRenderTableSection(child) : 0; |
| } |
| if (bottomSection) { |
| borderWidth = bottomSection->outerBorderAfter(); |
| if (borderWidth == -1) |
| return 0; // Overridden by hidden |
| } |
| const BorderValue& tb = style()->borderAfter(); |
| if (tb.style() == BHIDDEN) |
| return 0; |
| if (tb.style() > BHIDDEN) |
| borderWidth = max(borderWidth, static_cast<int>((tb.width() + 1) / 2)); |
| return borderWidth; |
| } |
| |
| int RenderTable::outerBorderStart() const |
| { |
| if (!collapseBorders()) |
| return 0; |
| |
| int borderWidth = 0; |
| |
| const BorderValue& tb = style()->borderStart(); |
| if (tb.style() == BHIDDEN) |
| return 0; |
| if (tb.style() > BHIDDEN) |
| borderWidth = (tb.width() + (style()->isLeftToRightDirection() ? 0 : 1)) / 2; |
| |
| bool allHidden = true; |
| for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { |
| if (!child->isTableSection()) |
| continue; |
| int sw = toRenderTableSection(child)->outerBorderStart(); |
| if (sw == -1) |
| continue; |
| else |
| allHidden = false; |
| borderWidth = max(borderWidth, sw); |
| } |
| if (allHidden) |
| return 0; |
| |
| return borderWidth; |
| } |
| |
| int RenderTable::outerBorderEnd() const |
| { |
| if (!collapseBorders()) |
| return 0; |
| |
| int borderWidth = 0; |
| |
| const BorderValue& tb = style()->borderEnd(); |
| if (tb.style() == BHIDDEN) |
| return 0; |
| if (tb.style() > BHIDDEN) |
| borderWidth = (tb.width() + (style()->isLeftToRightDirection() ? 1 : 0)) / 2; |
| |
| bool allHidden = true; |
| for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { |
| if (!child->isTableSection()) |
| continue; |
| int sw = toRenderTableSection(child)->outerBorderEnd(); |
| if (sw == -1) |
| continue; |
| else |
| allHidden = false; |
| borderWidth = max(borderWidth, sw); |
| } |
| if (allHidden) |
| return 0; |
| |
| return borderWidth; |
| } |
| |
| RenderTableSection* RenderTable::sectionAbove(const RenderTableSection* section, bool skipEmptySections) const |
| { |
| recalcSectionsIfNeeded(); |
| |
| if (section == m_head) |
| return 0; |
| |
| RenderObject* prevSection = section == m_foot ? lastChild() : section->previousSibling(); |
| while (prevSection) { |
| if (prevSection->isTableSection() && prevSection != m_head && prevSection != m_foot && (!skipEmptySections || toRenderTableSection(prevSection)->numRows())) |
| break; |
| prevSection = prevSection->previousSibling(); |
| } |
| if (!prevSection && m_head && (!skipEmptySections || m_head->numRows())) |
| prevSection = m_head; |
| return toRenderTableSection(prevSection); |
| } |
| |
| RenderTableSection* RenderTable::sectionBelow(const RenderTableSection* section, bool skipEmptySections) const |
| { |
| recalcSectionsIfNeeded(); |
| |
| if (section == m_foot) |
| return 0; |
| |
| RenderObject* nextSection = section == m_head ? firstChild() : section->nextSibling(); |
| while (nextSection) { |
| if (nextSection->isTableSection() && nextSection != m_head && nextSection != m_foot && (!skipEmptySections || toRenderTableSection(nextSection)->numRows())) |
| break; |
| nextSection = nextSection->nextSibling(); |
| } |
| if (!nextSection && m_foot && (!skipEmptySections || m_foot->numRows())) |
| nextSection = m_foot; |
| return toRenderTableSection(nextSection); |
| } |
| |
| RenderTableCell* RenderTable::cellAbove(const RenderTableCell* cell) const |
| { |
| recalcSectionsIfNeeded(); |
| |
| // Find the section and row to look in |
| int r = cell->row(); |
| RenderTableSection* section = 0; |
| int rAbove = 0; |
| if (r > 0) { |
| // cell is not in the first row, so use the above row in its own section |
| section = cell->section(); |
| rAbove = r - 1; |
| } else { |
| section = sectionAbove(cell->section(), true); |
| if (section) |
| rAbove = section->numRows() - 1; |
| } |
| |
| // Look up the cell in the section's grid, which requires effective col index |
| if (section) { |
| int effCol = colToEffCol(cell->col()); |
| RenderTableSection::CellStruct& aboveCell = section->cellAt(rAbove, effCol); |
| return aboveCell.primaryCell(); |
| } else |
| return 0; |
| } |
| |
| RenderTableCell* RenderTable::cellBelow(const RenderTableCell* cell) const |
| { |
| recalcSectionsIfNeeded(); |
| |
| // Find the section and row to look in |
| int r = cell->row() + cell->rowSpan() - 1; |
| RenderTableSection* section = 0; |
| int rBelow = 0; |
| if (r < cell->section()->numRows() - 1) { |
| // The cell is not in the last row, so use the next row in the section. |
| section = cell->section(); |
| rBelow = r + 1; |
| } else { |
| section = sectionBelow(cell->section(), true); |
| if (section) |
| rBelow = 0; |
| } |
| |
| // Look up the cell in the section's grid, which requires effective col index |
| if (section) { |
| int effCol = colToEffCol(cell->col()); |
| RenderTableSection::CellStruct& belowCell = section->cellAt(rBelow, effCol); |
| return belowCell.primaryCell(); |
| } else |
| return 0; |
| } |
| |
| RenderTableCell* RenderTable::cellBefore(const RenderTableCell* cell) const |
| { |
| recalcSectionsIfNeeded(); |
| |
| RenderTableSection* section = cell->section(); |
| int effCol = colToEffCol(cell->col()); |
| if (!effCol) |
| return 0; |
| |
| // If we hit a colspan back up to a real cell. |
| RenderTableSection::CellStruct& prevCell = section->cellAt(cell->row(), effCol - 1); |
| return prevCell.primaryCell(); |
| } |
| |
| RenderTableCell* RenderTable::cellAfter(const RenderTableCell* cell) const |
| { |
| recalcSectionsIfNeeded(); |
| |
| int effCol = colToEffCol(cell->col() + cell->colSpan()); |
| if (effCol >= numEffCols()) |
| return 0; |
| return cell->section()->primaryCellAt(cell->row(), effCol); |
| } |
| |
| RenderBlock* RenderTable::firstLineBlock() const |
| { |
| return 0; |
| } |
| |
| void RenderTable::updateFirstLetter() |
| { |
| } |
| |
| int RenderTable::firstLineBoxBaseline() const |
| { |
| if (isWritingModeRoot()) |
| return -1; |
| |
| recalcSectionsIfNeeded(); |
| |
| RenderTableSection* firstNonEmptySection = m_head ? m_head : (m_firstBody ? m_firstBody : m_foot); |
| if (firstNonEmptySection && !firstNonEmptySection->numRows()) |
| firstNonEmptySection = sectionBelow(firstNonEmptySection, true); |
| |
| if (!firstNonEmptySection) |
| return -1; |
| |
| return firstNonEmptySection->logicalTop() + firstNonEmptySection->firstLineBoxBaseline(); |
| } |
| |
| IntRect RenderTable::overflowClipRect(int tx, int ty, OverlayScrollbarSizeRelevancy relevancy) |
| { |
| IntRect rect = RenderBlock::overflowClipRect(tx, ty, relevancy); |
| |
| // If we have a caption, expand the clip to include the caption. |
| // FIXME: Technically this is wrong, but it's virtually impossible to fix this |
| // for real until captions have been re-written. |
| // FIXME: This code assumes (like all our other caption code) that only top/bottom are |
| // supported. When we actually support left/right and stop mapping them to top/bottom, |
| // we might have to hack this code first (depending on what order we do these bug fixes in). |
| if (m_caption) { |
| if (style()->isHorizontalWritingMode()) { |
| rect.setHeight(height()); |
| rect.setY(ty); |
| } else { |
| rect.setWidth(width()); |
| rect.setX(tx); |
| } |
| } |
| |
| return rect; |
| } |
| |
| bool RenderTable::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int xPos, int yPos, int tx, int ty, HitTestAction action) |
| { |
| tx += x(); |
| ty += y(); |
| |
| // Check kids first. |
| if (!hasOverflowClip() || overflowClipRect(tx, ty).intersects(result.rectForPoint(xPos, yPos))) { |
| for (RenderObject* child = lastChild(); child; child = child->previousSibling()) { |
| if (child->isBox() && !toRenderBox(child)->hasSelfPaintingLayer() && (child->isTableSection() || child == m_caption)) { |
| IntPoint childPoint = flipForWritingMode(toRenderBox(child), IntPoint(tx, ty), ParentToChildFlippingAdjustment); |
| if (child->nodeAtPoint(request, result, xPos, yPos, childPoint.x(), childPoint.y(), action)) { |
| updateHitTestResult(result, IntPoint(xPos - childPoint.x(), yPos - childPoint.y())); |
| return true; |
| } |
| } |
| } |
| } |
| |
| // Check our bounds next. |
| IntRect boundsRect = IntRect(tx, ty, width(), height()); |
| if (visibleToHitTesting() && (action == HitTestBlockBackground || action == HitTestChildBlockBackground) && boundsRect.intersects(result.rectForPoint(xPos, yPos))) { |
| updateHitTestResult(result, flipForWritingMode(IntPoint(xPos - tx, yPos - ty))); |
| if (!result.addNodeToRectBasedTestResult(node(), xPos, yPos, boundsRect)) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| } |