michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: #include "nsTableFrame.h" michael@0: #include "nsTableColFrame.h" michael@0: #include "nsTableCellFrame.h" michael@0: #include "nsTableRowFrame.h" michael@0: #include "nsTableRowGroupFrame.h" michael@0: #include "nsTablePainter.h" michael@0: #include "nsStyleContext.h" michael@0: #include "nsStyleConsts.h" michael@0: #include "nsPresContext.h" michael@0: #include "nsRenderingContext.h" michael@0: #include "nsCSSRendering.h" michael@0: #include "nsIContent.h" michael@0: #include "nsGenericHTMLElement.h" michael@0: #include "nsAttrValueInlines.h" michael@0: #include "nsHTMLParts.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsIPresShell.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsIServiceManager.h" michael@0: #include "nsIDOMNode.h" michael@0: #include "nsNameSpaceManager.h" michael@0: #include "nsDisplayList.h" michael@0: #include "nsLayoutUtils.h" michael@0: #include "nsTextFrame.h" michael@0: #include "FrameLayerBuilder.h" michael@0: #include michael@0: michael@0: //TABLECELL SELECTION michael@0: #include "nsFrameSelection.h" michael@0: #include "mozilla/LookAndFeel.h" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: michael@0: nsTableCellFrame::nsTableCellFrame(nsStyleContext* aContext) : michael@0: nsContainerFrame(aContext) michael@0: { michael@0: mColIndex = 0; michael@0: mPriorAvailWidth = 0; michael@0: michael@0: SetContentEmpty(false); michael@0: SetHasPctOverHeight(false); michael@0: } michael@0: michael@0: nsTableCellFrame::~nsTableCellFrame() michael@0: { michael@0: } michael@0: michael@0: NS_IMPL_FRAMEARENA_HELPERS(nsTableCellFrame) michael@0: michael@0: nsTableCellFrame* michael@0: nsTableCellFrame::GetNextCell() const michael@0: { michael@0: nsIFrame* childFrame = GetNextSibling(); michael@0: while (childFrame) { michael@0: nsTableCellFrame *cellFrame = do_QueryFrame(childFrame); michael@0: if (cellFrame) { michael@0: return cellFrame; michael@0: } michael@0: childFrame = childFrame->GetNextSibling(); michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: void michael@0: nsTableCellFrame::Init(nsIContent* aContent, michael@0: nsIFrame* aParent, michael@0: nsIFrame* aPrevInFlow) michael@0: { michael@0: // Let the base class do its initialization michael@0: nsContainerFrame::Init(aContent, aParent, aPrevInFlow); michael@0: michael@0: if (GetStateBits() & NS_FRAME_FONT_INFLATION_CONTAINER) { michael@0: AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT); michael@0: } michael@0: michael@0: if (aPrevInFlow) { michael@0: // Set the column index michael@0: nsTableCellFrame* cellFrame = (nsTableCellFrame*)aPrevInFlow; michael@0: int32_t colIndex; michael@0: cellFrame->GetColIndex(colIndex); michael@0: SetColIndex(colIndex); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsTableCellFrame::DestroyFrom(nsIFrame* aDestructRoot) michael@0: { michael@0: if (GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN) { michael@0: nsTableFrame::UnregisterPositionedTablePart(this, aDestructRoot); michael@0: } michael@0: michael@0: nsContainerFrame::DestroyFrom(aDestructRoot); michael@0: } michael@0: michael@0: // nsIPercentHeightObserver methods michael@0: michael@0: void michael@0: nsTableCellFrame::NotifyPercentHeight(const nsHTMLReflowState& aReflowState) michael@0: { michael@0: // nsHTMLReflowState ensures the mCBReflowState of blocks inside a michael@0: // cell is the cell frame, not the inner-cell block, and that the michael@0: // containing block of an inner table is the containing block of its michael@0: // outer table. michael@0: // XXXldb Given the now-stricter |NeedsToObserve|, many if not all of michael@0: // these tests are probably unnecessary. michael@0: michael@0: // Maybe the cell reflow state; we sure if we're inside the |if|. michael@0: const nsHTMLReflowState *cellRS = aReflowState.mCBReflowState; michael@0: michael@0: if (cellRS && cellRS->frame == this && michael@0: (cellRS->ComputedHeight() == NS_UNCONSTRAINEDSIZE || michael@0: cellRS->ComputedHeight() == 0)) { // XXXldb Why 0? michael@0: // This is a percentage height on a frame whose percentage heights michael@0: // are based on the height of the cell, since its containing block michael@0: // is the inner cell frame. michael@0: michael@0: // We'll only honor the percent height if sibling-cells/ancestors michael@0: // have specified/pct height. (Also, siblings only count for this if michael@0: // both this cell and the sibling cell span exactly 1 row.) michael@0: michael@0: if (nsTableFrame::AncestorsHaveStyleHeight(*cellRS) || michael@0: (nsTableFrame::GetTableFrame(this)->GetEffectiveRowSpan(*this) == 1 && michael@0: (cellRS->parentReflowState->frame->GetStateBits() & michael@0: NS_ROW_HAS_CELL_WITH_STYLE_HEIGHT))) { michael@0: michael@0: for (const nsHTMLReflowState *rs = aReflowState.parentReflowState; michael@0: rs != cellRS; michael@0: rs = rs->parentReflowState) { michael@0: rs->frame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT); michael@0: } michael@0: michael@0: nsTableFrame::RequestSpecialHeightReflow(*cellRS); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // The cell needs to observe its block and things inside its block but nothing below that michael@0: bool michael@0: nsTableCellFrame::NeedsToObserve(const nsHTMLReflowState& aReflowState) michael@0: { michael@0: const nsHTMLReflowState *rs = aReflowState.parentReflowState; michael@0: if (!rs) michael@0: return false; michael@0: if (rs->frame == this) { michael@0: // We always observe the child block. It will never send any michael@0: // notifications, but we need this so that the observer gets michael@0: // propagated to its kids. michael@0: return true; michael@0: } michael@0: rs = rs->parentReflowState; michael@0: if (!rs) { michael@0: return false; michael@0: } michael@0: michael@0: // We always need to let the percent height observer be propagated michael@0: // from an outer table frame to an inner table frame. michael@0: nsIAtom *fType = aReflowState.frame->GetType(); michael@0: if (fType == nsGkAtoms::tableFrame) { michael@0: return true; michael@0: } michael@0: michael@0: // We need the observer to be propagated to all children of the cell michael@0: // (i.e., children of the child block) in quirks mode, but only to michael@0: // tables in standards mode. michael@0: return rs->frame == this && michael@0: (PresContext()->CompatibilityMode() == eCompatibility_NavQuirks || michael@0: fType == nsGkAtoms::tableOuterFrame); michael@0: } michael@0: michael@0: nsresult michael@0: nsTableCellFrame::GetRowIndex(int32_t &aRowIndex) const michael@0: { michael@0: nsresult result; michael@0: nsTableRowFrame* row = static_cast(GetParent()); michael@0: if (row) { michael@0: aRowIndex = row->GetRowIndex(); michael@0: result = NS_OK; michael@0: } michael@0: else { michael@0: aRowIndex = 0; michael@0: result = NS_ERROR_NOT_INITIALIZED; michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: nsresult michael@0: nsTableCellFrame::GetColIndex(int32_t &aColIndex) const michael@0: { michael@0: if (GetPrevInFlow()) { michael@0: return static_cast(FirstInFlow())->GetColIndex(aColIndex); michael@0: } michael@0: else { michael@0: aColIndex = mColIndex; michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsTableCellFrame::AttributeChanged(int32_t aNameSpaceID, michael@0: nsIAtom* aAttribute, michael@0: int32_t aModType) michael@0: { michael@0: // We need to recalculate in this case because of the nowrap quirk in michael@0: // BasicTableLayoutStrategy michael@0: if (aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::nowrap && michael@0: PresContext()->CompatibilityMode() == eCompatibility_NavQuirks) { michael@0: PresContext()->PresShell()-> michael@0: FrameNeedsReflow(this, nsIPresShell::eTreeChange, NS_FRAME_IS_DIRTY); michael@0: } michael@0: // let the table frame decide what to do michael@0: nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this); michael@0: tableFrame->AttributeChangedFor(this, mContent, aAttribute); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* virtual */ void michael@0: nsTableCellFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) michael@0: { michael@0: nsContainerFrame::DidSetStyleContext(aOldStyleContext); michael@0: michael@0: if (!aOldStyleContext) //avoid this on init michael@0: return; michael@0: michael@0: nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this); michael@0: if (tableFrame->IsBorderCollapse() && michael@0: tableFrame->BCRecalcNeeded(aOldStyleContext, StyleContext())) { michael@0: int32_t colIndex, rowIndex; michael@0: GetColIndex(colIndex); michael@0: GetRowIndex(rowIndex); michael@0: // row span needs to be clamped as we do not create rows in the cellmap michael@0: // which do not have cells originating in them michael@0: nsIntRect damageArea(colIndex, rowIndex, GetColSpan(), michael@0: std::min(GetRowSpan(), tableFrame->GetRowCount() - rowIndex)); michael@0: tableFrame->AddBCDamageArea(damageArea); michael@0: } michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: nsTableCellFrame::AppendFrames(ChildListID aListID, michael@0: nsFrameList& aFrameList) michael@0: { michael@0: NS_PRECONDITION(false, "unsupported operation"); michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: nsresult michael@0: nsTableCellFrame::InsertFrames(ChildListID aListID, michael@0: nsIFrame* aPrevFrame, michael@0: nsFrameList& aFrameList) michael@0: { michael@0: NS_PRECONDITION(false, "unsupported operation"); michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: nsresult michael@0: nsTableCellFrame::RemoveFrame(ChildListID aListID, michael@0: nsIFrame* aOldFrame) michael@0: { michael@0: NS_PRECONDITION(false, "unsupported operation"); michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: void nsTableCellFrame::SetColIndex(int32_t aColIndex) michael@0: { michael@0: mColIndex = aColIndex; michael@0: } michael@0: michael@0: /* virtual */ nsMargin michael@0: nsTableCellFrame::GetUsedMargin() const michael@0: { michael@0: return nsMargin(0,0,0,0); michael@0: } michael@0: michael@0: //ASSURE DIFFERENT COLORS for selection michael@0: inline nscolor EnsureDifferentColors(nscolor colorA, nscolor colorB) michael@0: { michael@0: if (colorA == colorB) michael@0: { michael@0: nscolor res; michael@0: res = NS_RGB(NS_GET_R(colorA) ^ 0xff, michael@0: NS_GET_G(colorA) ^ 0xff, michael@0: NS_GET_B(colorA) ^ 0xff); michael@0: return res; michael@0: } michael@0: return colorA; michael@0: } michael@0: michael@0: void michael@0: nsTableCellFrame::DecorateForSelection(nsRenderingContext& aRenderingContext, michael@0: nsPoint aPt) michael@0: { michael@0: NS_ASSERTION(IsSelected(), "Should only be called for selected cells"); michael@0: int16_t displaySelection; michael@0: nsPresContext* presContext = PresContext(); michael@0: displaySelection = DisplaySelection(presContext); michael@0: if (displaySelection) { michael@0: nsRefPtr frameSelection = michael@0: presContext->PresShell()->FrameSelection(); michael@0: michael@0: if (frameSelection->GetTableCellSelection()) { michael@0: nscolor bordercolor; michael@0: if (displaySelection == nsISelectionController::SELECTION_DISABLED) { michael@0: bordercolor = NS_RGB(176,176,176);// disabled color michael@0: } michael@0: else { michael@0: bordercolor = michael@0: LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectBackground); michael@0: } michael@0: nscoord threePx = nsPresContext::CSSPixelsToAppUnits(3); michael@0: if ((mRect.width > threePx) && (mRect.height > threePx)) michael@0: { michael@0: //compare bordercolor to ((nsStyleColor *)myColor)->mBackgroundColor) michael@0: bordercolor = EnsureDifferentColors(bordercolor, michael@0: StyleBackground()->mBackgroundColor); michael@0: nsRenderingContext::AutoPushTranslation michael@0: translate(&aRenderingContext, aPt); michael@0: nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); michael@0: michael@0: aRenderingContext.SetColor(bordercolor); michael@0: aRenderingContext.DrawLine(onePixel, 0, mRect.width, 0); michael@0: aRenderingContext.DrawLine(0, onePixel, 0, mRect.height); michael@0: aRenderingContext.DrawLine(onePixel, mRect.height, mRect.width, mRect.height); michael@0: aRenderingContext.DrawLine(mRect.width, onePixel, mRect.width, mRect.height); michael@0: //middle michael@0: aRenderingContext.DrawRect(onePixel, onePixel, mRect.width-onePixel, michael@0: mRect.height-onePixel); michael@0: //shading michael@0: aRenderingContext.DrawLine(2*onePixel, mRect.height-2*onePixel, michael@0: mRect.width-onePixel, mRect.height- (2*onePixel)); michael@0: aRenderingContext.DrawLine(mRect.width - (2*onePixel), 2*onePixel, michael@0: mRect.width - (2*onePixel), mRect.height-onePixel); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsTableCellFrame::PaintBackground(nsRenderingContext& aRenderingContext, michael@0: const nsRect& aDirtyRect, michael@0: nsPoint aPt, michael@0: uint32_t aFlags) michael@0: { michael@0: nsRect rect(aPt, GetSize()); michael@0: nsCSSRendering::PaintBackground(PresContext(), aRenderingContext, this, michael@0: aDirtyRect, rect, aFlags); michael@0: } michael@0: michael@0: // Called by nsTablePainter michael@0: void michael@0: nsTableCellFrame::PaintCellBackground(nsRenderingContext& aRenderingContext, michael@0: const nsRect& aDirtyRect, nsPoint aPt, michael@0: uint32_t aFlags) michael@0: { michael@0: if (!StyleVisibility()->IsVisible()) michael@0: return; michael@0: michael@0: PaintBackground(aRenderingContext, aDirtyRect, aPt, aFlags); michael@0: } michael@0: michael@0: nsresult michael@0: nsTableCellFrame::ProcessBorders(nsTableFrame* aFrame, michael@0: nsDisplayListBuilder* aBuilder, michael@0: const nsDisplayListSet& aLists) michael@0: { michael@0: const nsStyleBorder* borderStyle = StyleBorder(); michael@0: if (aFrame->IsBorderCollapse() || !borderStyle->HasBorder()) michael@0: return NS_OK; michael@0: michael@0: if (!GetContentEmpty() || michael@0: StyleTableBorder()->mEmptyCells == NS_STYLE_TABLE_EMPTY_CELLS_SHOW) { michael@0: aLists.BorderBackground()->AppendNewToTop(new (aBuilder) michael@0: nsDisplayBorder(aBuilder, this)); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: class nsDisplayTableCellBackground : public nsDisplayTableItem { michael@0: public: michael@0: nsDisplayTableCellBackground(nsDisplayListBuilder* aBuilder, michael@0: nsTableCellFrame* aFrame) : michael@0: nsDisplayTableItem(aBuilder, aFrame) { michael@0: MOZ_COUNT_CTOR(nsDisplayTableCellBackground); michael@0: } michael@0: #ifdef NS_BUILD_REFCNT_LOGGING michael@0: virtual ~nsDisplayTableCellBackground() { michael@0: MOZ_COUNT_DTOR(nsDisplayTableCellBackground); michael@0: } michael@0: #endif michael@0: michael@0: virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, michael@0: HitTestState* aState, michael@0: nsTArray *aOutFrames) MOZ_OVERRIDE { michael@0: aOutFrames->AppendElement(mFrame); michael@0: } michael@0: virtual void Paint(nsDisplayListBuilder* aBuilder, michael@0: nsRenderingContext* aCtx) MOZ_OVERRIDE; michael@0: virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, michael@0: bool* aSnap) MOZ_OVERRIDE; michael@0: virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, michael@0: const nsDisplayItemGeometry* aGeometry, michael@0: nsRegion *aInvalidRegion) MOZ_OVERRIDE; michael@0: michael@0: NS_DISPLAY_DECL_NAME("TableCellBackground", TYPE_TABLE_CELL_BACKGROUND) michael@0: }; michael@0: michael@0: void nsDisplayTableCellBackground::Paint(nsDisplayListBuilder* aBuilder, michael@0: nsRenderingContext* aCtx) michael@0: { michael@0: static_cast(mFrame)-> michael@0: PaintBackground(*aCtx, mVisibleRect, ToReferenceFrame(), michael@0: aBuilder->GetBackgroundPaintFlags()); michael@0: } michael@0: michael@0: nsRect michael@0: nsDisplayTableCellBackground::GetBounds(nsDisplayListBuilder* aBuilder, michael@0: bool* aSnap) michael@0: { michael@0: // revert from nsDisplayTableItem's implementation ... cell backgrounds michael@0: // don't overflow the cell michael@0: return nsDisplayItem::GetBounds(aBuilder, aSnap); michael@0: } michael@0: michael@0: void michael@0: nsDisplayTableCellBackground::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, michael@0: const nsDisplayItemGeometry* aGeometry, michael@0: nsRegion *aInvalidRegion) michael@0: { michael@0: if (aBuilder->ShouldSyncDecodeImages()) { michael@0: if (!nsCSSRendering::AreAllBackgroundImagesDecodedForFrame(mFrame)) { michael@0: bool snap; michael@0: aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap)); michael@0: } michael@0: } michael@0: michael@0: nsDisplayTableItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion); michael@0: } michael@0: michael@0: void nsTableCellFrame::InvalidateFrame(uint32_t aDisplayItemKey) michael@0: { michael@0: nsIFrame::InvalidateFrame(aDisplayItemKey); michael@0: GetParent()->InvalidateFrameWithRect(GetVisualOverflowRect() + GetPosition(), aDisplayItemKey); michael@0: } michael@0: michael@0: void nsTableCellFrame::InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItemKey) michael@0: { michael@0: nsIFrame::InvalidateFrameWithRect(aRect, aDisplayItemKey); michael@0: // If we have filters applied that would affects our bounds, then michael@0: // we get an inactive layer created and this is computed michael@0: // within FrameLayerBuilder michael@0: GetParent()->InvalidateFrameWithRect(aRect + GetPosition(), aDisplayItemKey); michael@0: } michael@0: michael@0: static void michael@0: PaintTableCellSelection(nsIFrame* aFrame, nsRenderingContext* aCtx, michael@0: const nsRect& aRect, nsPoint aPt) michael@0: { michael@0: static_cast(aFrame)->DecorateForSelection(*aCtx, aPt); michael@0: } michael@0: michael@0: void michael@0: nsTableCellFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, michael@0: const nsRect& aDirtyRect, michael@0: const nsDisplayListSet& aLists) michael@0: { michael@0: DO_GLOBAL_REFLOW_COUNT_DSP("nsTableCellFrame"); michael@0: if (IsVisibleInSelection(aBuilder)) { michael@0: nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this); michael@0: int32_t emptyCellStyle = GetContentEmpty() && !tableFrame->IsBorderCollapse() ? michael@0: StyleTableBorder()->mEmptyCells michael@0: : NS_STYLE_TABLE_EMPTY_CELLS_SHOW; michael@0: // take account of 'empty-cells' michael@0: if (StyleVisibility()->IsVisible() && michael@0: (NS_STYLE_TABLE_EMPTY_CELLS_HIDE != emptyCellStyle)) { michael@0: michael@0: michael@0: bool isRoot = aBuilder->IsAtRootOfPseudoStackingContext(); michael@0: if (!isRoot) { michael@0: nsDisplayTableItem* currentItem = aBuilder->GetCurrentTableItem(); michael@0: if (currentItem) { michael@0: currentItem->UpdateForFrameBackground(this); michael@0: } michael@0: } michael@0: michael@0: // display outset box-shadows if we need to. michael@0: const nsStyleBorder* borderStyle = StyleBorder(); michael@0: bool hasBoxShadow = !!borderStyle->mBoxShadow; michael@0: if (hasBoxShadow) { michael@0: aLists.BorderBackground()->AppendNewToTop( michael@0: new (aBuilder) nsDisplayBoxShadowOuter(aBuilder, this)); michael@0: } michael@0: michael@0: // display background if we need to. michael@0: if (aBuilder->IsForEventDelivery() || michael@0: (((!tableFrame->IsBorderCollapse() || isRoot) && michael@0: (!StyleBackground()->IsTransparent() || StyleDisplay()->mAppearance)))) { michael@0: // The cell background was not painted by the nsTablePainter, michael@0: // so we need to do it. We have special background processing here michael@0: // so we need to duplicate some code from nsFrame::DisplayBorderBackgroundOutline michael@0: nsDisplayTableItem* item = michael@0: new (aBuilder) nsDisplayTableCellBackground(aBuilder, this); michael@0: aLists.BorderBackground()->AppendNewToTop(item); michael@0: item->UpdateForFrameBackground(this); michael@0: } michael@0: michael@0: // display inset box-shadows if we need to. michael@0: if (hasBoxShadow) { michael@0: aLists.BorderBackground()->AppendNewToTop( michael@0: new (aBuilder) nsDisplayBoxShadowInner(aBuilder, this)); michael@0: } michael@0: michael@0: // display borders if we need to michael@0: ProcessBorders(tableFrame, aBuilder, aLists); michael@0: michael@0: // and display the selection border if we need to michael@0: if (IsSelected()) { michael@0: aLists.BorderBackground()->AppendNewToTop(new (aBuilder) michael@0: nsDisplayGeneric(aBuilder, this, ::PaintTableCellSelection, michael@0: "TableCellSelection", michael@0: nsDisplayItem::TYPE_TABLE_CELL_SELECTION)); michael@0: } michael@0: } michael@0: michael@0: // the 'empty-cells' property has no effect on 'outline' michael@0: DisplayOutline(aBuilder, aLists); michael@0: } michael@0: michael@0: // Push a null 'current table item' so that descendant tables can't michael@0: // accidentally mess with our table michael@0: nsAutoPushCurrentTableItem pushTableItem; michael@0: pushTableItem.Push(aBuilder, nullptr); michael@0: michael@0: nsIFrame* kid = mFrames.FirstChild(); michael@0: NS_ASSERTION(kid && !kid->GetNextSibling(), "Table cells should have just one child"); michael@0: // The child's background will go in our BorderBackground() list. michael@0: // This isn't a problem since it won't have a real background except for michael@0: // event handling. We do not call BuildDisplayListForNonBlockChildren michael@0: // because that/ would put the child's background in the Content() list michael@0: // which isn't right (e.g., would end up on top of our child floats for michael@0: // event handling). michael@0: BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists); michael@0: } michael@0: michael@0: int michael@0: nsTableCellFrame::GetLogicalSkipSides(const nsHTMLReflowState* aReflowState) const michael@0: { michael@0: int skip = 0; michael@0: if (nullptr != GetPrevInFlow()) { michael@0: skip |= LOGICAL_SIDE_B_START; michael@0: } michael@0: if (nullptr != GetNextInFlow()) { michael@0: skip |= LOGICAL_SIDE_B_END; michael@0: } michael@0: return skip; michael@0: } michael@0: michael@0: /* virtual */ nsMargin michael@0: nsTableCellFrame::GetBorderOverflow() michael@0: { michael@0: return nsMargin(0, 0, 0, 0); michael@0: } michael@0: michael@0: // Align the cell's child frame within the cell michael@0: michael@0: void nsTableCellFrame::VerticallyAlignChild(nscoord aMaxAscent) michael@0: { michael@0: /* It's the 'border-collapse' on the table that matters */ michael@0: nsMargin borderPadding = GetUsedBorderAndPadding(); michael@0: michael@0: nscoord topInset = borderPadding.top; michael@0: nscoord bottomInset = borderPadding.bottom; michael@0: michael@0: uint8_t verticalAlignFlags = GetVerticalAlign(); michael@0: michael@0: nscoord height = mRect.height; michael@0: nsIFrame* firstKid = mFrames.FirstChild(); michael@0: NS_ASSERTION(firstKid, "Frame construction error, a table cell always has an inner cell frame"); michael@0: nsRect kidRect = firstKid->GetRect(); michael@0: nscoord childHeight = kidRect.height; michael@0: michael@0: // Vertically align the child michael@0: nscoord kidYTop = 0; michael@0: switch (verticalAlignFlags) michael@0: { michael@0: case NS_STYLE_VERTICAL_ALIGN_BASELINE: michael@0: // Align the baselines of the child frame with the baselines of michael@0: // other children in the same row which have 'vertical-align: baseline' michael@0: kidYTop = topInset + aMaxAscent - GetCellBaseline(); michael@0: break; michael@0: michael@0: case NS_STYLE_VERTICAL_ALIGN_TOP: michael@0: // Align the top of the child frame with the top of the content area, michael@0: kidYTop = topInset; michael@0: break; michael@0: michael@0: case NS_STYLE_VERTICAL_ALIGN_BOTTOM: michael@0: // Align the bottom of the child frame with the bottom of the content area, michael@0: kidYTop = height - childHeight - bottomInset; michael@0: break; michael@0: michael@0: default: michael@0: case NS_STYLE_VERTICAL_ALIGN_MIDDLE: michael@0: // Align the middle of the child frame with the middle of the content area, michael@0: kidYTop = (height - childHeight - bottomInset + topInset) / 2; michael@0: } michael@0: // if the content is larger than the cell height align from top michael@0: kidYTop = std::max(0, kidYTop); michael@0: michael@0: if (kidYTop != kidRect.y) { michael@0: // Invalidate at the old position first michael@0: firstKid->InvalidateFrameSubtree(); michael@0: } michael@0: michael@0: firstKid->SetPosition(nsPoint(kidRect.x, kidYTop)); michael@0: nsHTMLReflowMetrics desiredSize(GetWritingMode()); // ??? michael@0: desiredSize.Width() = mRect.width; michael@0: desiredSize.Height() = mRect.height; michael@0: michael@0: nsRect overflow(nsPoint(0,0), GetSize()); michael@0: overflow.Inflate(GetBorderOverflow()); michael@0: desiredSize.mOverflowAreas.SetAllTo(overflow); michael@0: ConsiderChildOverflow(desiredSize.mOverflowAreas, firstKid); michael@0: FinishAndStoreOverflow(&desiredSize); michael@0: if (kidYTop != kidRect.y) { michael@0: // Make sure any child views are correctly positioned. We know the inner table michael@0: // cell won't have a view michael@0: nsContainerFrame::PositionChildViews(firstKid); michael@0: michael@0: // Invalidate new overflow rect michael@0: firstKid->InvalidateFrameSubtree(); michael@0: } michael@0: if (HasView()) { michael@0: nsContainerFrame::SyncFrameViewAfterReflow(PresContext(), this, michael@0: GetView(), michael@0: desiredSize.VisualOverflow(), 0); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: nsTableCellFrame::UpdateOverflow() michael@0: { michael@0: nsRect bounds(nsPoint(0,0), GetSize()); michael@0: bounds.Inflate(GetBorderOverflow()); michael@0: nsOverflowAreas overflowAreas(bounds, bounds); michael@0: michael@0: nsLayoutUtils::UnionChildOverflow(this, overflowAreas); michael@0: michael@0: return FinishAndStoreOverflow(overflowAreas, GetSize()); michael@0: } michael@0: michael@0: // Per CSS 2.1, we map 'sub', 'super', 'text-top', 'text-bottom', michael@0: // length, percentage, and calc() values to 'baseline'. michael@0: uint8_t michael@0: nsTableCellFrame::GetVerticalAlign() const michael@0: { michael@0: const nsStyleCoord& verticalAlign = StyleTextReset()->mVerticalAlign; michael@0: if (verticalAlign.GetUnit() == eStyleUnit_Enumerated) { michael@0: uint8_t value = verticalAlign.GetIntValue(); michael@0: if (value == NS_STYLE_VERTICAL_ALIGN_TOP || michael@0: value == NS_STYLE_VERTICAL_ALIGN_MIDDLE || michael@0: value == NS_STYLE_VERTICAL_ALIGN_BOTTOM) { michael@0: return value; michael@0: } michael@0: } michael@0: return NS_STYLE_VERTICAL_ALIGN_BASELINE; michael@0: } michael@0: michael@0: bool michael@0: nsTableCellFrame::CellHasVisibleContent(nscoord height, michael@0: nsTableFrame* tableFrame, michael@0: nsIFrame* kidFrame) michael@0: { michael@0: // see http://www.w3.org/TR/CSS21/tables.html#empty-cells michael@0: if (height > 0) michael@0: return true; michael@0: if (tableFrame->IsBorderCollapse()) michael@0: return true; michael@0: nsIFrame* innerFrame = kidFrame->GetFirstPrincipalChild(); michael@0: while(innerFrame) { michael@0: nsIAtom* frameType = innerFrame->GetType(); michael@0: if (nsGkAtoms::textFrame == frameType) { michael@0: nsTextFrame* textFrame = static_cast(innerFrame); michael@0: if (textFrame->HasNoncollapsedCharacters()) michael@0: return true; michael@0: } michael@0: else if (nsGkAtoms::placeholderFrame != frameType) { michael@0: return true; michael@0: } michael@0: else { michael@0: nsIFrame *floatFrame = nsLayoutUtils::GetFloatFromPlaceholder(innerFrame); michael@0: if (floatFrame) michael@0: return true; michael@0: } michael@0: innerFrame = innerFrame->GetNextSibling(); michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: nscoord michael@0: nsTableCellFrame::GetCellBaseline() const michael@0: { michael@0: // Ignore the position of the inner frame relative to the cell frame michael@0: // since we want the position as though the inner were top-aligned. michael@0: nsIFrame *inner = mFrames.FirstChild(); michael@0: nscoord borderPadding = GetUsedBorderAndPadding().top; michael@0: nscoord result; michael@0: if (nsLayoutUtils::GetFirstLineBaseline(inner, &result)) michael@0: return result + borderPadding; michael@0: return inner->GetContentRect().YMost() - inner->GetPosition().y + michael@0: borderPadding; michael@0: } michael@0: michael@0: int32_t nsTableCellFrame::GetRowSpan() michael@0: { michael@0: int32_t rowSpan=1; michael@0: nsGenericHTMLElement *hc = nsGenericHTMLElement::FromContent(mContent); michael@0: michael@0: // Don't look at the content's rowspan if we're a pseudo cell michael@0: if (hc && !StyleContext()->GetPseudo()) { michael@0: const nsAttrValue* attr = hc->GetParsedAttr(nsGkAtoms::rowspan); michael@0: // Note that we don't need to check the tag name, because only table cells michael@0: // and table headers parse the "rowspan" attribute into an integer. michael@0: if (attr && attr->Type() == nsAttrValue::eInteger) { michael@0: rowSpan = attr->GetIntegerValue(); michael@0: } michael@0: } michael@0: return rowSpan; michael@0: } michael@0: michael@0: int32_t nsTableCellFrame::GetColSpan() michael@0: { michael@0: int32_t colSpan=1; michael@0: nsGenericHTMLElement *hc = nsGenericHTMLElement::FromContent(mContent); michael@0: michael@0: // Don't look at the content's colspan if we're a pseudo cell michael@0: if (hc && !StyleContext()->GetPseudo()) { michael@0: const nsAttrValue* attr = hc->GetParsedAttr(nsGkAtoms::colspan); michael@0: // Note that we don't need to check the tag name, because only table cells michael@0: // and table headers parse the "colspan" attribute into an integer. michael@0: if (attr && attr->Type() == nsAttrValue::eInteger) { michael@0: colSpan = attr->GetIntegerValue(); michael@0: } michael@0: } michael@0: return colSpan; michael@0: } michael@0: michael@0: /* virtual */ nscoord michael@0: nsTableCellFrame::GetMinWidth(nsRenderingContext *aRenderingContext) michael@0: { michael@0: nscoord result = 0; michael@0: DISPLAY_MIN_WIDTH(this, result); michael@0: michael@0: nsIFrame *inner = mFrames.FirstChild(); michael@0: result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, inner, michael@0: nsLayoutUtils::MIN_WIDTH); michael@0: return result; michael@0: } michael@0: michael@0: /* virtual */ nscoord michael@0: nsTableCellFrame::GetPrefWidth(nsRenderingContext *aRenderingContext) michael@0: { michael@0: nscoord result = 0; michael@0: DISPLAY_PREF_WIDTH(this, result); michael@0: michael@0: nsIFrame *inner = mFrames.FirstChild(); michael@0: result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, inner, michael@0: nsLayoutUtils::PREF_WIDTH); michael@0: return result; michael@0: } michael@0: michael@0: /* virtual */ nsIFrame::IntrinsicWidthOffsetData michael@0: nsTableCellFrame::IntrinsicWidthOffsets(nsRenderingContext* aRenderingContext) michael@0: { michael@0: IntrinsicWidthOffsetData result = michael@0: nsContainerFrame::IntrinsicWidthOffsets(aRenderingContext); michael@0: michael@0: result.hMargin = 0; michael@0: result.hPctMargin = 0; michael@0: michael@0: nsMargin border; michael@0: GetBorderWidth(border); michael@0: result.hBorder = border.LeftRight(); michael@0: michael@0: return result; michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: #define PROBABLY_TOO_LARGE 1000000 michael@0: static michael@0: void DebugCheckChildSize(nsIFrame* aChild, michael@0: nsHTMLReflowMetrics& aMet, michael@0: nsSize& aAvailSize) michael@0: { michael@0: if ((aMet.Width() < 0) || (aMet.Width() > PROBABLY_TOO_LARGE)) { michael@0: printf("WARNING: cell content %p has large width %d \n", michael@0: static_cast(aChild), int32_t(aMet.Width())); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: // the computed height for the cell, which descendants use for percent height calculations michael@0: // it is the height (minus border, padding) of the cell's first in flow during its final michael@0: // reflow without an unconstrained height. michael@0: static nscoord michael@0: CalcUnpaginagedHeight(nsPresContext* aPresContext, michael@0: nsTableCellFrame& aCellFrame, michael@0: nsTableFrame& aTableFrame, michael@0: nscoord aVerticalBorderPadding) michael@0: { michael@0: const nsTableCellFrame* firstCellInFlow = michael@0: static_cast(aCellFrame.FirstInFlow()); michael@0: nsTableFrame* firstTableInFlow = michael@0: static_cast(aTableFrame.FirstInFlow()); michael@0: nsTableRowFrame* row = michael@0: static_cast(firstCellInFlow->GetParent()); michael@0: nsTableRowGroupFrame* firstRGInFlow = michael@0: static_cast(row->GetParent()); michael@0: michael@0: int32_t rowIndex; michael@0: firstCellInFlow->GetRowIndex(rowIndex); michael@0: int32_t rowSpan = aTableFrame.GetEffectiveRowSpan(*firstCellInFlow); michael@0: nscoord cellSpacing = firstTableInFlow->GetCellSpacingX(); michael@0: michael@0: nscoord computedHeight = ((rowSpan - 1) * cellSpacing) - aVerticalBorderPadding; michael@0: int32_t rowX; michael@0: for (row = firstRGInFlow->GetFirstRow(), rowX = 0; row; row = row->GetNextRow(), rowX++) { michael@0: if (rowX > rowIndex + rowSpan - 1) { michael@0: break; michael@0: } michael@0: else if (rowX >= rowIndex) { michael@0: computedHeight += row->GetUnpaginatedHeight(aPresContext); michael@0: } michael@0: } michael@0: return computedHeight; michael@0: } michael@0: michael@0: nsresult nsTableCellFrame::Reflow(nsPresContext* aPresContext, michael@0: nsHTMLReflowMetrics& aDesiredSize, michael@0: const nsHTMLReflowState& aReflowState, michael@0: nsReflowStatus& aStatus) michael@0: { michael@0: DO_GLOBAL_REFLOW_COUNT("nsTableCellFrame"); michael@0: DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); michael@0: michael@0: if (aReflowState.mFlags.mSpecialHeightReflow) { michael@0: FirstInFlow()->AddStateBits(NS_TABLE_CELL_HAD_SPECIAL_REFLOW); michael@0: } michael@0: michael@0: // see if a special height reflow needs to occur due to having a pct height michael@0: nsTableFrame::CheckRequestSpecialHeightReflow(aReflowState); michael@0: michael@0: aStatus = NS_FRAME_COMPLETE; michael@0: nsSize availSize(aReflowState.AvailableWidth(), aReflowState.AvailableHeight()); michael@0: michael@0: nsMargin borderPadding = aReflowState.ComputedPhysicalPadding(); michael@0: nsMargin border; michael@0: GetBorderWidth(border); michael@0: borderPadding += border; michael@0: michael@0: nscoord topInset = borderPadding.top; michael@0: nscoord rightInset = borderPadding.right; michael@0: nscoord bottomInset = borderPadding.bottom; michael@0: nscoord leftInset = borderPadding.left; michael@0: michael@0: // reduce available space by insets, if we're in a constrained situation michael@0: availSize.width -= leftInset + rightInset; michael@0: if (NS_UNCONSTRAINEDSIZE != availSize.height) michael@0: availSize.height -= topInset + bottomInset; michael@0: michael@0: // Try to reflow the child into the available space. It might not michael@0: // fit or might need continuing. michael@0: if (availSize.height < 0) michael@0: availSize.height = 1; michael@0: michael@0: nsHTMLReflowMetrics kidSize(aReflowState.GetWritingMode(), aDesiredSize.mFlags); michael@0: kidSize.Width() = kidSize.Height() = 0; michael@0: SetPriorAvailWidth(aReflowState.AvailableWidth()); michael@0: nsIFrame* firstKid = mFrames.FirstChild(); michael@0: NS_ASSERTION(firstKid, "Frame construction error, a table cell always has an inner cell frame"); michael@0: nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this); michael@0: michael@0: if (aReflowState.mFlags.mSpecialHeightReflow) { michael@0: const_cast(aReflowState).SetComputedHeight(mRect.height - topInset - bottomInset); michael@0: DISPLAY_REFLOW_CHANGE(); michael@0: } michael@0: else if (aPresContext->IsPaginated()) { michael@0: nscoord computedUnpaginatedHeight = michael@0: CalcUnpaginagedHeight(aPresContext, (nsTableCellFrame&)*this, michael@0: *tableFrame, topInset + bottomInset); michael@0: if (computedUnpaginatedHeight > 0) { michael@0: const_cast(aReflowState).SetComputedHeight(computedUnpaginatedHeight); michael@0: DISPLAY_REFLOW_CHANGE(); michael@0: } michael@0: } michael@0: else { michael@0: SetHasPctOverHeight(false); michael@0: } michael@0: michael@0: nsHTMLReflowState kidReflowState(aPresContext, aReflowState, firstKid, michael@0: availSize); michael@0: michael@0: // Don't be a percent height observer if we're in the middle of michael@0: // special-height reflow, in case we get an accidental NotifyPercentHeight() michael@0: // call (which we shouldn't honor during special-height reflow) michael@0: if (!aReflowState.mFlags.mSpecialHeightReflow) { michael@0: // mPercentHeightObserver is for children of cells in quirks mode, michael@0: // but only those than are tables in standards mode. NeedsToObserve michael@0: // will determine how far this is propagated to descendants. michael@0: kidReflowState.mPercentHeightObserver = this; michael@0: } michael@0: // Don't propagate special height reflow state to our kids michael@0: kidReflowState.mFlags.mSpecialHeightReflow = false; michael@0: michael@0: if (aReflowState.mFlags.mSpecialHeightReflow || michael@0: (FirstInFlow()->GetStateBits() & NS_TABLE_CELL_HAD_SPECIAL_REFLOW)) { michael@0: // We need to force the kid to have mVResize set if we've had a michael@0: // special reflow in the past, since the non-special reflow needs to michael@0: // resize back to what it was without the special height reflow. michael@0: kidReflowState.mFlags.mVResize = true; michael@0: } michael@0: michael@0: nsPoint kidOrigin(leftInset, topInset); michael@0: nsRect origRect = firstKid->GetRect(); michael@0: nsRect origVisualOverflow = firstKid->GetVisualOverflowRect(); michael@0: bool firstReflow = (firstKid->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0; michael@0: michael@0: ReflowChild(firstKid, aPresContext, kidSize, kidReflowState, michael@0: kidOrigin.x, kidOrigin.y, 0, aStatus); michael@0: if (NS_FRAME_OVERFLOW_IS_INCOMPLETE(aStatus)) { michael@0: // Don't pass OVERFLOW_INCOMPLETE through tables until they can actually handle it michael@0: //XXX should paginate overflow as overflow, but not in this patch (bug 379349) michael@0: NS_FRAME_SET_INCOMPLETE(aStatus); michael@0: printf("Set table cell incomplete %p\n", static_cast(this)); michael@0: } michael@0: michael@0: // XXXbz is this invalidate actually needed, really? michael@0: if (GetStateBits() & NS_FRAME_IS_DIRTY) { michael@0: InvalidateFrameSubtree(); michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: DebugCheckChildSize(firstKid, kidSize, availSize); michael@0: #endif michael@0: michael@0: // 0 dimensioned cells need to be treated specially in Standard/NavQuirks mode michael@0: // see testcase "emptyCells.html" michael@0: nsIFrame* prevInFlow = GetPrevInFlow(); michael@0: bool isEmpty; michael@0: if (prevInFlow) { michael@0: isEmpty = static_cast(prevInFlow)->GetContentEmpty(); michael@0: } else { michael@0: isEmpty = !CellHasVisibleContent(kidSize.Height(), tableFrame, firstKid); michael@0: } michael@0: SetContentEmpty(isEmpty); michael@0: michael@0: // Place the child michael@0: FinishReflowChild(firstKid, aPresContext, kidSize, &kidReflowState, michael@0: kidOrigin.x, kidOrigin.y, 0); michael@0: michael@0: nsTableFrame::InvalidateTableFrame(firstKid, origRect, origVisualOverflow, michael@0: firstReflow); michael@0: michael@0: // first, compute the height which can be set w/o being restricted by aMaxSize.height michael@0: nscoord cellHeight = kidSize.Height(); michael@0: michael@0: if (NS_UNCONSTRAINEDSIZE != cellHeight) { michael@0: cellHeight += topInset + bottomInset; michael@0: } michael@0: michael@0: // next determine the cell's width michael@0: nscoord cellWidth = kidSize.Width(); // at this point, we've factored in the cell's style attributes michael@0: michael@0: // factor in border and padding michael@0: if (NS_UNCONSTRAINEDSIZE != cellWidth) { michael@0: cellWidth += leftInset + rightInset; michael@0: } michael@0: michael@0: // set the cell's desired size and max element size michael@0: aDesiredSize.Width() = cellWidth; michael@0: aDesiredSize.Height() = cellHeight; michael@0: michael@0: // the overflow area will be computed when the child will be vertically aligned michael@0: michael@0: if (aReflowState.mFlags.mSpecialHeightReflow) { michael@0: if (aDesiredSize.Height() > mRect.height) { michael@0: // set a bit indicating that the pct height contents exceeded michael@0: // the height that they could honor in the pass 2 reflow michael@0: SetHasPctOverHeight(true); michael@0: } michael@0: if (NS_UNCONSTRAINEDSIZE == aReflowState.AvailableHeight()) { michael@0: aDesiredSize.Height() = mRect.height; michael@0: } michael@0: } michael@0: michael@0: // If our parent is in initial reflow, it'll handle invalidating our michael@0: // entire overflow rect. michael@0: if (!(GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW) && michael@0: nsSize(aDesiredSize.Width(), aDesiredSize.Height()) != mRect.Size()) { michael@0: InvalidateFrame(); michael@0: } michael@0: michael@0: // remember the desired size for this reflow michael@0: SetDesiredSize(aDesiredSize); michael@0: michael@0: NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* ----- global methods ----- */ michael@0: michael@0: NS_QUERYFRAME_HEAD(nsTableCellFrame) michael@0: NS_QUERYFRAME_ENTRY(nsTableCellFrame) michael@0: NS_QUERYFRAME_ENTRY(nsITableCellLayout) michael@0: NS_QUERYFRAME_ENTRY(nsIPercentHeightObserver) michael@0: NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) michael@0: michael@0: #ifdef ACCESSIBILITY michael@0: a11y::AccType michael@0: nsTableCellFrame::AccessibleType() michael@0: { michael@0: return a11y::eHTMLTableCellType; michael@0: } michael@0: #endif michael@0: michael@0: /* This is primarily for editor access via nsITableLayout */ michael@0: NS_IMETHODIMP michael@0: nsTableCellFrame::GetCellIndexes(int32_t &aRowIndex, int32_t &aColIndex) michael@0: { michael@0: nsresult res = GetRowIndex(aRowIndex); michael@0: if (NS_FAILED(res)) michael@0: { michael@0: aColIndex = 0; michael@0: return res; michael@0: } michael@0: aColIndex = mColIndex; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsIFrame* michael@0: NS_NewTableCellFrame(nsIPresShell* aPresShell, michael@0: nsStyleContext* aContext, michael@0: bool aIsBorderCollapse) michael@0: { michael@0: if (aIsBorderCollapse) michael@0: return new (aPresShell) nsBCTableCellFrame(aContext); michael@0: else michael@0: return new (aPresShell) nsTableCellFrame(aContext); michael@0: } michael@0: michael@0: NS_IMPL_FRAMEARENA_HELPERS(nsBCTableCellFrame) michael@0: michael@0: nsMargin* michael@0: nsTableCellFrame::GetBorderWidth(nsMargin& aBorder) const michael@0: { michael@0: aBorder = StyleBorder()->GetComputedBorder(); michael@0: return &aBorder; michael@0: } michael@0: michael@0: nsIAtom* michael@0: nsTableCellFrame::GetType() const michael@0: { michael@0: return nsGkAtoms::tableCellFrame; michael@0: } michael@0: michael@0: #ifdef DEBUG_FRAME_DUMP michael@0: nsresult michael@0: nsTableCellFrame::GetFrameName(nsAString& aResult) const michael@0: { michael@0: return MakeFrameName(NS_LITERAL_STRING("TableCell"), aResult); michael@0: } michael@0: #endif michael@0: michael@0: // nsBCTableCellFrame michael@0: michael@0: nsBCTableCellFrame::nsBCTableCellFrame(nsStyleContext* aContext) michael@0: :nsTableCellFrame(aContext) michael@0: { michael@0: mTopBorder = mRightBorder = mBottomBorder = mLeftBorder = 0; michael@0: } michael@0: michael@0: nsBCTableCellFrame::~nsBCTableCellFrame() michael@0: { michael@0: } michael@0: michael@0: nsIAtom* michael@0: nsBCTableCellFrame::GetType() const michael@0: { michael@0: return nsGkAtoms::bcTableCellFrame; michael@0: } michael@0: michael@0: /* virtual */ nsMargin michael@0: nsBCTableCellFrame::GetUsedBorder() const michael@0: { michael@0: nsMargin result; michael@0: GetBorderWidth(result); michael@0: return result; michael@0: } michael@0: michael@0: /* virtual */ bool michael@0: nsBCTableCellFrame::GetBorderRadii(nscoord aRadii[8]) const michael@0: { michael@0: NS_FOR_CSS_HALF_CORNERS(corner) { michael@0: aRadii[corner] = 0; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: #ifdef DEBUG_FRAME_DUMP michael@0: nsresult michael@0: nsBCTableCellFrame::GetFrameName(nsAString& aResult) const michael@0: { michael@0: return MakeFrameName(NS_LITERAL_STRING("BCTableCell"), aResult); michael@0: } michael@0: #endif michael@0: michael@0: nsMargin* michael@0: nsBCTableCellFrame::GetBorderWidth(nsMargin& aBorder) const michael@0: { michael@0: int32_t aPixelsToTwips = nsPresContext::AppUnitsPerCSSPixel(); michael@0: aBorder.top = BC_BORDER_BOTTOM_HALF_COORD(aPixelsToTwips, mTopBorder); michael@0: aBorder.right = BC_BORDER_LEFT_HALF_COORD(aPixelsToTwips, mRightBorder); michael@0: aBorder.bottom = BC_BORDER_TOP_HALF_COORD(aPixelsToTwips, mBottomBorder); michael@0: aBorder.left = BC_BORDER_RIGHT_HALF_COORD(aPixelsToTwips, mLeftBorder); michael@0: return &aBorder; michael@0: } michael@0: michael@0: BCPixelSize michael@0: nsBCTableCellFrame::GetBorderWidth(mozilla::css::Side aSide) const michael@0: { michael@0: switch(aSide) { michael@0: case NS_SIDE_TOP: michael@0: return BC_BORDER_BOTTOM_HALF(mTopBorder); michael@0: case NS_SIDE_RIGHT: michael@0: return BC_BORDER_LEFT_HALF(mRightBorder); michael@0: case NS_SIDE_BOTTOM: michael@0: return BC_BORDER_TOP_HALF(mBottomBorder); michael@0: default: michael@0: return BC_BORDER_RIGHT_HALF(mLeftBorder); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsBCTableCellFrame::SetBorderWidth(mozilla::css::Side aSide, michael@0: BCPixelSize aValue) michael@0: { michael@0: switch(aSide) { michael@0: case NS_SIDE_TOP: michael@0: mTopBorder = aValue; michael@0: break; michael@0: case NS_SIDE_RIGHT: michael@0: mRightBorder = aValue; michael@0: break; michael@0: case NS_SIDE_BOTTOM: michael@0: mBottomBorder = aValue; michael@0: break; michael@0: default: michael@0: mLeftBorder = aValue; michael@0: } michael@0: } michael@0: michael@0: /* virtual */ nsMargin michael@0: nsBCTableCellFrame::GetBorderOverflow() michael@0: { michael@0: nsMargin halfBorder; michael@0: int32_t p2t = nsPresContext::AppUnitsPerCSSPixel(); michael@0: halfBorder.top = BC_BORDER_TOP_HALF_COORD(p2t, mTopBorder); michael@0: halfBorder.right = BC_BORDER_RIGHT_HALF_COORD(p2t, mRightBorder); michael@0: halfBorder.bottom = BC_BORDER_BOTTOM_HALF_COORD(p2t, mBottomBorder); michael@0: halfBorder.left = BC_BORDER_LEFT_HALF_COORD(p2t, mLeftBorder); michael@0: return halfBorder; michael@0: } michael@0: michael@0: michael@0: void michael@0: nsBCTableCellFrame::PaintBackground(nsRenderingContext& aRenderingContext, michael@0: const nsRect& aDirtyRect, michael@0: nsPoint aPt, michael@0: uint32_t aFlags) michael@0: { michael@0: // make border-width reflect the half of the border-collapse michael@0: // assigned border that's inside the cell michael@0: nsMargin borderWidth; michael@0: GetBorderWidth(borderWidth); michael@0: michael@0: nsStyleBorder myBorder(*StyleBorder()); michael@0: michael@0: NS_FOR_CSS_SIDES(side) { michael@0: myBorder.SetBorderWidth(side, borderWidth.Side(side)); michael@0: } michael@0: michael@0: nsRect rect(aPt, GetSize()); michael@0: // bypassing nsCSSRendering::PaintBackground is safe because this kind michael@0: // of frame cannot be used for the root element michael@0: nsCSSRendering::PaintBackgroundWithSC(PresContext(), aRenderingContext, this, michael@0: aDirtyRect, rect, michael@0: StyleContext(), myBorder, michael@0: aFlags, nullptr); michael@0: }