Bug 563259: Fix shark/dtrace enabled combo. (r=me)
[mozilla-central.git] / layout / tables / nsTableCellFrame.cpp
blobea9b03cc0e4c2d45a72d0c7920890ffd301da20b
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Pierre Phaneuf <pp@ludusdesign.com>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either of the GNU General Public License Version 2 or later (the "GPL"),
27 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
38 #include "nsTableFrame.h"
39 #include "nsTableColFrame.h"
40 #include "nsTableCellFrame.h"
41 #include "nsTableFrame.h"
42 #include "nsTableRowGroupFrame.h"
43 #include "nsTablePainter.h"
44 #include "nsStyleContext.h"
45 #include "nsStyleConsts.h"
46 #include "nsPresContext.h"
47 #include "nsIRenderingContext.h"
48 #include "nsCSSRendering.h"
49 #include "nsIContent.h"
50 #include "nsGenericHTMLElement.h"
51 #include "nsHTMLParts.h"
52 #include "nsGkAtoms.h"
53 #include "nsIPresShell.h"
54 #include "nsCOMPtr.h"
55 #include "nsIDOMHTMLTableCellElement.h"
56 #ifdef ACCESSIBILITY
57 #include "nsIAccessibilityService.h"
58 #endif
59 #include "nsIServiceManager.h"
60 #include "nsIDOMNode.h"
61 #include "nsINameSpaceManager.h"
62 #include "nsDisplayList.h"
63 #include "nsLayoutUtils.h"
64 #include "nsTextFrame.h"
66 //TABLECELL SELECTION
67 #include "nsFrameSelection.h"
68 #include "nsILookAndFeel.h"
71 nsTableCellFrame::nsTableCellFrame(nsStyleContext* aContext) :
72 nsHTMLContainerFrame(aContext)
74 mColIndex = 0;
75 mPriorAvailWidth = 0;
77 SetContentEmpty(PR_FALSE);
78 SetHasPctOverHeight(PR_FALSE);
81 nsTableCellFrame::~nsTableCellFrame()
85 NS_IMPL_FRAMEARENA_HELPERS(nsTableCellFrame)
87 nsTableCellFrame*
88 nsTableCellFrame::GetNextCell() const
90 nsIFrame* childFrame = GetNextSibling();
91 while (childFrame) {
92 nsTableCellFrame *cellFrame = do_QueryFrame(childFrame);
93 if (cellFrame) {
94 return cellFrame;
96 childFrame = childFrame->GetNextSibling();
98 return nsnull;
101 NS_IMETHODIMP
102 nsTableCellFrame::Init(nsIContent* aContent,
103 nsIFrame* aParent,
104 nsIFrame* aPrevInFlow)
106 // Let the base class do its initialization
107 nsresult rv = nsHTMLContainerFrame::Init(aContent, aParent, aPrevInFlow);
109 if (aPrevInFlow) {
110 // Set the column index
111 nsTableCellFrame* cellFrame = (nsTableCellFrame*)aPrevInFlow;
112 PRInt32 colIndex;
113 cellFrame->GetColIndex(colIndex);
114 SetColIndex(colIndex);
117 return rv;
120 // nsIPercentHeightObserver methods
122 void
123 nsTableCellFrame::NotifyPercentHeight(const nsHTMLReflowState& aReflowState)
125 // nsHTMLReflowState ensures the mCBReflowState of blocks inside a
126 // cell is the cell frame, not the inner-cell block, and that the
127 // containing block of an inner table is the containing block of its
128 // outer table.
129 // XXXldb Given the now-stricter |NeedsToObserve|, many if not all of
130 // these tests are probably unnecessary.
132 // Maybe the cell reflow state; we sure if we're inside the |if|.
133 const nsHTMLReflowState *cellRS = aReflowState.mCBReflowState;
135 if (cellRS && cellRS->frame == this &&
136 (cellRS->ComputedHeight() == NS_UNCONSTRAINEDSIZE ||
137 cellRS->ComputedHeight() == 0)) { // XXXldb Why 0?
138 // This is a percentage height on a frame whose percentage heights
139 // are based on the height of the cell, since its containing block
140 // is the inner cell frame.
142 // We'll only honor the percent height if sibling-cells/ancestors
143 // have specified/pct height. (Also, siblings only count for this if
144 // both this cell and the sibling cell span exactly 1 row.)
146 if (nsTableFrame::AncestorsHaveStyleHeight(*cellRS) ||
147 (nsTableFrame::GetTableFrame(this)->GetEffectiveRowSpan(*this) == 1 &&
148 (cellRS->parentReflowState->frame->GetStateBits() &
149 NS_ROW_HAS_CELL_WITH_STYLE_HEIGHT))) {
151 for (const nsHTMLReflowState *rs = aReflowState.parentReflowState;
152 rs != cellRS;
153 rs = rs->parentReflowState) {
154 rs->frame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT);
157 nsTableFrame::RequestSpecialHeightReflow(*cellRS);
162 // The cell needs to observe its block and things inside its block but nothing below that
163 PRBool
164 nsTableCellFrame::NeedsToObserve(const nsHTMLReflowState& aReflowState)
166 const nsHTMLReflowState *rs = aReflowState.parentReflowState;
167 if (!rs)
168 return PR_FALSE;
169 if (rs->frame == this) {
170 // We always observe the child block. It will never send any
171 // notifications, but we need this so that the observer gets
172 // propagated to its kids.
173 return PR_TRUE;
175 rs = rs->parentReflowState;
176 if (!rs) {
177 return PR_FALSE;
180 // We always need to let the percent height observer be propagated
181 // from an outer table frame to an inner table frame.
182 nsIAtom *fType = aReflowState.frame->GetType();
183 if (fType == nsGkAtoms::tableFrame) {
184 return PR_TRUE;
187 // We need the observer to be propagated to all children of the cell
188 // (i.e., children of the child block) in quirks mode, but only to
189 // tables in standards mode.
190 return rs->frame == this &&
191 (PresContext()->CompatibilityMode() == eCompatibility_NavQuirks ||
192 fType == nsGkAtoms::tableOuterFrame);
195 nsresult
196 nsTableCellFrame::GetRowIndex(PRInt32 &aRowIndex) const
198 nsresult result;
199 nsTableRowFrame* row = static_cast<nsTableRowFrame*>(GetParent());
200 if (row) {
201 aRowIndex = row->GetRowIndex();
202 result = NS_OK;
204 else {
205 aRowIndex = 0;
206 result = NS_ERROR_NOT_INITIALIZED;
208 return result;
211 nsresult
212 nsTableCellFrame::GetColIndex(PRInt32 &aColIndex) const
214 if (GetPrevInFlow()) {
215 return ((nsTableCellFrame*)GetFirstInFlow())->GetColIndex(aColIndex);
217 else {
218 aColIndex = mColIndex;
219 return NS_OK;
223 NS_IMETHODIMP
224 nsTableCellFrame::AttributeChanged(PRInt32 aNameSpaceID,
225 nsIAtom* aAttribute,
226 PRInt32 aModType)
228 // We need to recalculate in this case because of the nowrap quirk in
229 // BasicTableLayoutStrategy
230 if (aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::nowrap &&
231 PresContext()->CompatibilityMode() == eCompatibility_NavQuirks) {
232 PresContext()->PresShell()->
233 FrameNeedsReflow(this, nsIPresShell::eTreeChange, NS_FRAME_IS_DIRTY);
235 // let the table frame decide what to do
236 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
237 if (tableFrame) {
238 tableFrame->AttributeChangedFor(this, mContent, aAttribute);
240 return NS_OK;
243 /* virtual */ void
244 nsTableCellFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
246 if (!aOldStyleContext) //avoid this on init
247 return;
249 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
251 if (tableFrame->IsBorderCollapse() &&
252 tableFrame->BCRecalcNeeded(aOldStyleContext, GetStyleContext())) {
253 PRInt32 colIndex, rowIndex;
254 GetColIndex(colIndex);
255 GetRowIndex(rowIndex);
256 nsRect damageArea(colIndex, rowIndex, GetColSpan(), GetRowSpan());
257 tableFrame->SetBCDamageArea(damageArea);
262 NS_IMETHODIMP
263 nsTableCellFrame::AppendFrames(nsIAtom* aListName,
264 nsFrameList& aFrameList)
266 NS_PRECONDITION(PR_FALSE, "unsupported operation");
267 return NS_ERROR_NOT_IMPLEMENTED;
270 NS_IMETHODIMP
271 nsTableCellFrame::InsertFrames(nsIAtom* aListName,
272 nsIFrame* aPrevFrame,
273 nsFrameList& aFrameList)
275 NS_PRECONDITION(PR_FALSE, "unsupported operation");
276 return NS_ERROR_NOT_IMPLEMENTED;
279 NS_IMETHODIMP
280 nsTableCellFrame::RemoveFrame(nsIAtom* aListName,
281 nsIFrame* aOldFrame)
283 NS_PRECONDITION(PR_FALSE, "unsupported operation");
284 return NS_ERROR_NOT_IMPLEMENTED;
287 void nsTableCellFrame::SetColIndex(PRInt32 aColIndex)
289 mColIndex = aColIndex;
292 /* virtual */ nsMargin
293 nsTableCellFrame::GetUsedMargin() const
295 return nsMargin(0,0,0,0);
298 //ASSURE DIFFERENT COLORS for selection
299 inline nscolor EnsureDifferentColors(nscolor colorA, nscolor colorB)
301 if (colorA == colorB)
303 nscolor res;
304 res = NS_RGB(NS_GET_R(colorA) ^ 0xff,
305 NS_GET_G(colorA) ^ 0xff,
306 NS_GET_B(colorA) ^ 0xff);
307 return res;
309 return colorA;
312 void
313 nsTableCellFrame::DecorateForSelection(nsIRenderingContext& aRenderingContext,
314 nsPoint aPt)
316 NS_ASSERTION(GetStateBits() & NS_FRAME_SELECTED_CONTENT,
317 "Should only be called for selected cells");
318 PRInt16 displaySelection;
319 nsPresContext* presContext = PresContext();
320 displaySelection = DisplaySelection(presContext);
321 if (displaySelection) {
322 nsCOMPtr<nsFrameSelection> frameSelection =
323 presContext->PresShell()->FrameSelection();
325 if (frameSelection->GetTableCellSelection()) {
326 nscolor bordercolor;
327 if (displaySelection == nsISelectionController::SELECTION_DISABLED) {
328 bordercolor = NS_RGB(176,176,176);// disabled color
330 else {
331 presContext->LookAndFeel()->
332 GetColor(nsILookAndFeel::eColor_TextSelectBackground,
333 bordercolor);
335 nscoord threePx = nsPresContext::CSSPixelsToAppUnits(3);
336 if ((mRect.width > threePx) && (mRect.height > threePx))
338 //compare bordercolor to ((nsStyleColor *)myColor)->mBackgroundColor)
339 bordercolor = EnsureDifferentColors(bordercolor,
340 GetStyleBackground()->mBackgroundColor);
341 nsIRenderingContext::AutoPushTranslation
342 translate(&aRenderingContext, aPt.x, aPt.y);
343 nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
345 aRenderingContext.SetColor(bordercolor);
346 aRenderingContext.DrawLine(onePixel, 0, mRect.width, 0);
347 aRenderingContext.DrawLine(0, onePixel, 0, mRect.height);
348 aRenderingContext.DrawLine(onePixel, mRect.height, mRect.width, mRect.height);
349 aRenderingContext.DrawLine(mRect.width, onePixel, mRect.width, mRect.height);
350 //middle
351 aRenderingContext.DrawRect(onePixel, onePixel, mRect.width-onePixel,
352 mRect.height-onePixel);
353 //shading
354 aRenderingContext.DrawLine(2*onePixel, mRect.height-2*onePixel,
355 mRect.width-onePixel, mRect.height- (2*onePixel));
356 aRenderingContext.DrawLine(mRect.width - (2*onePixel), 2*onePixel,
357 mRect.width - (2*onePixel), mRect.height-onePixel);
363 void
364 nsTableCellFrame::PaintBackground(nsIRenderingContext& aRenderingContext,
365 const nsRect& aDirtyRect,
366 nsPoint aPt,
367 PRUint32 aFlags)
369 nsRect rect(aPt, GetSize());
370 nsCSSRendering::PaintBackground(PresContext(), aRenderingContext, this,
371 aDirtyRect, rect, aFlags);
374 // Called by nsTablePainter
375 void
376 nsTableCellFrame::PaintCellBackground(nsIRenderingContext& aRenderingContext,
377 const nsRect& aDirtyRect, nsPoint aPt,
378 PRUint32 aFlags)
380 if (!GetStyleVisibility()->IsVisible())
381 return;
383 PaintBackground(aRenderingContext, aDirtyRect, aPt, aFlags);
386 class nsDisplayTableCellBackground : public nsDisplayTableItem {
387 public:
388 nsDisplayTableCellBackground(nsTableCellFrame* aFrame) : nsDisplayTableItem(aFrame) {
389 MOZ_COUNT_CTOR(nsDisplayTableCellBackground);
391 #ifdef NS_BUILD_REFCNT_LOGGING
392 virtual ~nsDisplayTableCellBackground() {
393 MOZ_COUNT_DTOR(nsDisplayTableCellBackground);
395 #endif
397 virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
398 HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) {
399 aOutFrames->AppendElement(mFrame);
401 virtual void Paint(nsDisplayListBuilder* aBuilder,
402 nsIRenderingContext* aCtx);
403 virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder);
405 NS_DISPLAY_DECL_NAME("TableCellBackground")
408 void nsDisplayTableCellBackground::Paint(nsDisplayListBuilder* aBuilder,
409 nsIRenderingContext* aCtx)
411 static_cast<nsTableCellFrame*>(mFrame)->
412 PaintBackground(*aCtx, mVisibleRect, aBuilder->ToReferenceFrame(mFrame),
413 aBuilder->GetBackgroundPaintFlags());
416 nsRect
417 nsDisplayTableCellBackground::GetBounds(nsDisplayListBuilder* aBuilder)
419 // revert from nsDisplayTableItem's implementation ... cell backgrounds
420 // don't overflow the cell
421 return nsDisplayItem::GetBounds(aBuilder);
424 static void
425 PaintTableCellSelection(nsIFrame* aFrame, nsIRenderingContext* aCtx,
426 const nsRect& aRect, nsPoint aPt)
428 static_cast<nsTableCellFrame*>(aFrame)->DecorateForSelection(*aCtx, aPt);
431 NS_IMETHODIMP
432 nsTableCellFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
433 const nsRect& aDirtyRect,
434 const nsDisplayListSet& aLists)
436 if (!IsVisibleInSelection(aBuilder))
437 return NS_OK;
439 DO_GLOBAL_REFLOW_COUNT_DSP("nsTableCellFrame");
440 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
442 PRInt32 emptyCellStyle = GetContentEmpty() && !tableFrame->IsBorderCollapse() ?
443 GetStyleTableBorder()->mEmptyCells
444 : NS_STYLE_TABLE_EMPTY_CELLS_SHOW;
445 // take account of 'empty-cells'
446 if (GetStyleVisibility()->IsVisible() &&
447 (NS_STYLE_TABLE_EMPTY_CELLS_HIDE != emptyCellStyle)) {
450 PRBool isRoot = aBuilder->IsAtRootOfPseudoStackingContext();
451 if (!isRoot) {
452 nsDisplayTableItem* currentItem = aBuilder->GetCurrentTableItem();
453 if (currentItem) {
454 currentItem->UpdateForFrameBackground(this);
458 // display outset box-shadows if we need to.
459 PRBool hasBoxShadow = !!(GetStyleBorder()->mBoxShadow);
460 if (hasBoxShadow) {
461 nsDisplayItem* item = new (aBuilder) nsDisplayBoxShadowOuter(this);
462 nsresult rv = aLists.BorderBackground()->AppendNewToTop(item);
463 NS_ENSURE_SUCCESS(rv, rv);
466 // display background if we need to.
467 if (aBuilder->IsForEventDelivery() ||
468 (((!tableFrame->IsBorderCollapse() || isRoot) &&
469 (!GetStyleBackground()->IsTransparent() || GetStyleDisplay()->mAppearance)))) {
470 // The cell background was not painted by the nsTablePainter,
471 // so we need to do it. We have special background processing here
472 // so we need to duplicate some code from nsFrame::DisplayBorderBackgroundOutline
473 nsDisplayTableItem* item = new (aBuilder) nsDisplayTableCellBackground(this);
474 nsresult rv = aLists.BorderBackground()->AppendNewToTop(item);
475 NS_ENSURE_SUCCESS(rv, rv);
476 item->UpdateForFrameBackground(this);
479 // display inset box-shadows if we need to.
480 if (hasBoxShadow) {
481 nsDisplayItem* item = new (aBuilder) nsDisplayBoxShadowInner(this);
482 nsresult rv = aLists.BorderBackground()->AppendNewToTop(item);
483 NS_ENSURE_SUCCESS(rv, rv);
486 // display borders if we need to
487 if (!tableFrame->IsBorderCollapse() && HasBorder() &&
488 emptyCellStyle == NS_STYLE_TABLE_EMPTY_CELLS_SHOW) {
489 nsresult rv = aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
490 nsDisplayBorder(this));
491 NS_ENSURE_SUCCESS(rv, rv);
494 // and display the selection border if we need to
495 PRBool isSelected =
496 (GetStateBits() & NS_FRAME_SELECTED_CONTENT) == NS_FRAME_SELECTED_CONTENT;
497 if (isSelected) {
498 nsresult rv = aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
499 nsDisplayGeneric(this, ::PaintTableCellSelection, "TableCellSelection"));
500 NS_ENSURE_SUCCESS(rv, rv);
504 // the 'empty-cells' property has no effect on 'outline'
505 nsresult rv = DisplayOutline(aBuilder, aLists);
506 NS_ENSURE_SUCCESS(rv, rv);
508 // Push a null 'current table item' so that descendant tables can't
509 // accidentally mess with our table
510 nsAutoPushCurrentTableItem pushTableItem;
511 pushTableItem.Push(aBuilder, nsnull);
513 nsIFrame* kid = mFrames.FirstChild();
514 NS_ASSERTION(kid && !kid->GetNextSibling(), "Table cells should have just one child");
515 // The child's background will go in our BorderBackground() list.
516 // This isn't a problem since it won't have a real background except for
517 // event handling. We do not call BuildDisplayListForNonBlockChildren
518 // because that/ would put the child's background in the Content() list
519 // which isn't right (e.g., would end up on top of our child floats for
520 // event handling).
521 return BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists);
524 PRIntn
525 nsTableCellFrame::GetSkipSides() const
527 PRIntn skip = 0;
528 if (nsnull != GetPrevInFlow()) {
529 skip |= 1 << NS_SIDE_TOP;
531 if (nsnull != GetNextInFlow()) {
532 skip |= 1 << NS_SIDE_BOTTOM;
534 return skip;
537 /* virtual */ void
538 nsTableCellFrame::GetSelfOverflow(nsRect& aOverflowArea)
540 aOverflowArea = nsRect(nsPoint(0,0), GetSize());
543 // Align the cell's child frame within the cell
545 void nsTableCellFrame::VerticallyAlignChild(nscoord aMaxAscent)
547 const nsStyleTextReset* textStyle = GetStyleTextReset();
548 /* It's the 'border-collapse' on the table that matters */
549 nsMargin borderPadding = GetUsedBorderAndPadding();
551 nscoord topInset = borderPadding.top;
552 nscoord bottomInset = borderPadding.bottom;
554 // As per bug 10207, we map 'sub', 'super', 'text-top', 'text-bottom',
555 // length and percentage values to 'baseline'
556 // XXX It seems that we don't get to see length and percentage values here
557 // because the Style System has already fixed the error and mapped them
558 // to whatever is inherited from the parent, i.e, 'middle' in most cases.
559 PRUint8 verticalAlignFlags = NS_STYLE_VERTICAL_ALIGN_BASELINE;
560 if (textStyle->mVerticalAlign.GetUnit() == eStyleUnit_Enumerated) {
561 verticalAlignFlags = textStyle->mVerticalAlign.GetIntValue();
562 if (verticalAlignFlags != NS_STYLE_VERTICAL_ALIGN_TOP &&
563 verticalAlignFlags != NS_STYLE_VERTICAL_ALIGN_MIDDLE &&
564 verticalAlignFlags != NS_STYLE_VERTICAL_ALIGN_BOTTOM)
566 verticalAlignFlags = NS_STYLE_VERTICAL_ALIGN_BASELINE;
570 nscoord height = mRect.height;
571 nsIFrame* firstKid = mFrames.FirstChild();
572 NS_ASSERTION(firstKid, "Frame construction error, a table cell always has an inner cell frame");
573 nsRect kidRect = firstKid->GetRect();
574 nscoord childHeight = kidRect.height;
576 // Vertically align the child
577 nscoord kidYTop = 0;
578 switch (verticalAlignFlags)
580 case NS_STYLE_VERTICAL_ALIGN_BASELINE:
581 // Align the baselines of the child frame with the baselines of
582 // other children in the same row which have 'vertical-align: baseline'
583 kidYTop = topInset + aMaxAscent - GetCellBaseline();
584 break;
586 case NS_STYLE_VERTICAL_ALIGN_TOP:
587 // Align the top of the child frame with the top of the content area,
588 kidYTop = topInset;
589 break;
591 case NS_STYLE_VERTICAL_ALIGN_BOTTOM:
592 // Align the bottom of the child frame with the bottom of the content area,
593 kidYTop = height - childHeight - bottomInset;
594 break;
596 default:
597 case NS_STYLE_VERTICAL_ALIGN_MIDDLE:
598 // Align the middle of the child frame with the middle of the content area,
599 kidYTop = (height - childHeight - bottomInset + topInset) / 2;
601 // if the content is larger than the cell height align from top
602 kidYTop = NS_MAX(0, kidYTop);
604 if (kidYTop != kidRect.y) {
605 // Invalidate at the old position first
606 firstKid->InvalidateOverflowRect();
609 firstKid->SetPosition(nsPoint(kidRect.x, kidYTop));
610 nsHTMLReflowMetrics desiredSize;
611 desiredSize.width = mRect.width;
612 desiredSize.height = mRect.height;
613 GetSelfOverflow(desiredSize.mOverflowArea);
614 ConsiderChildOverflow(desiredSize.mOverflowArea, firstKid);
615 FinishAndStoreOverflow(&desiredSize);
616 if (kidYTop != kidRect.y) {
617 // Make sure any child views are correctly positioned. We know the inner table
618 // cell won't have a view
619 nsContainerFrame::PositionChildViews(firstKid);
621 // Invalidate new overflow rect
622 firstKid->InvalidateOverflowRect();
624 if (HasView()) {
625 nsContainerFrame::SyncFrameViewAfterReflow(PresContext(), this,
626 GetView(),
627 &desiredSize.mOverflowArea, 0);
631 // As per bug 10207, we map 'sub', 'super', 'text-top', 'text-bottom',
632 // length and percentage values to 'baseline'
633 // XXX It seems that we don't get to see length and percentage values here
634 // because the Style System has already fixed the error and mapped them
635 // to whatever is inherited from the parent, i.e, 'middle' in most cases.
636 PRBool
637 nsTableCellFrame::HasVerticalAlignBaseline()
639 const nsStyleTextReset* textStyle = GetStyleTextReset();
640 if (textStyle->mVerticalAlign.GetUnit() == eStyleUnit_Enumerated) {
641 PRUint8 verticalAlignFlags = textStyle->mVerticalAlign.GetIntValue();
642 if (verticalAlignFlags == NS_STYLE_VERTICAL_ALIGN_TOP ||
643 verticalAlignFlags == NS_STYLE_VERTICAL_ALIGN_MIDDLE ||
644 verticalAlignFlags == NS_STYLE_VERTICAL_ALIGN_BOTTOM)
646 return PR_FALSE;
649 return PR_TRUE;
652 PRBool
653 nsTableCellFrame::CellHasVisibleContent(nscoord height,
654 nsTableFrame* tableFrame,
655 nsIFrame* kidFrame)
657 // see http://www.w3.org/TR/CSS21/tables.html#empty-cells
658 if (height > 0)
659 return PR_TRUE;
660 if (tableFrame->IsBorderCollapse())
661 return PR_TRUE;
662 nsIFrame* innerFrame = kidFrame->GetFirstChild(nsnull);
663 while(innerFrame) {
664 nsIAtom* frameType = innerFrame->GetType();
665 if (nsGkAtoms::textFrame == frameType) {
666 nsTextFrame* textFrame = static_cast<nsTextFrame*>(innerFrame);
667 if (textFrame->HasNoncollapsedCharacters())
668 return PR_TRUE;
670 else if (nsGkAtoms::placeholderFrame != frameType) {
671 return PR_TRUE;
673 else {
674 nsIFrame *floatFrame = nsLayoutUtils::GetFloatFromPlaceholder(innerFrame);
675 if (floatFrame)
676 return PR_TRUE;
678 innerFrame = innerFrame->GetNextSibling();
680 return PR_FALSE;
683 nscoord
684 nsTableCellFrame::GetCellBaseline() const
686 // Ignore the position of the inner frame relative to the cell frame
687 // since we want the position as though the inner were top-aligned.
688 nsIFrame *inner = mFrames.FirstChild();
689 nscoord borderPadding = GetUsedBorderAndPadding().top;
690 nscoord result;
691 if (nsLayoutUtils::GetFirstLineBaseline(inner, &result))
692 return result + borderPadding;
693 return inner->GetContentRect().YMost() - inner->GetPosition().y +
694 borderPadding;
697 PRInt32 nsTableCellFrame::GetRowSpan()
699 PRInt32 rowSpan=1;
700 nsGenericHTMLElement *hc = nsGenericHTMLElement::FromContent(mContent);
702 // Don't look at the content's rowspan if we're a pseudo cell
703 if (hc && !GetStyleContext()->GetPseudo()) {
704 const nsAttrValue* attr = hc->GetParsedAttr(nsGkAtoms::rowspan);
705 // Note that we don't need to check the tag name, because only table cells
706 // and table headers parse the "rowspan" attribute into an integer.
707 if (attr && attr->Type() == nsAttrValue::eInteger) {
708 rowSpan = attr->GetIntegerValue();
711 return rowSpan;
714 PRInt32 nsTableCellFrame::GetColSpan()
716 PRInt32 colSpan=1;
717 nsGenericHTMLElement *hc = nsGenericHTMLElement::FromContent(mContent);
719 // Don't look at the content's colspan if we're a pseudo cell
720 if (hc && !GetStyleContext()->GetPseudo()) {
721 const nsAttrValue* attr = hc->GetParsedAttr(nsGkAtoms::colspan);
722 // Note that we don't need to check the tag name, because only table cells
723 // and table headers parse the "colspan" attribute into an integer.
724 if (attr && attr->Type() == nsAttrValue::eInteger) {
725 colSpan = attr->GetIntegerValue();
728 return colSpan;
731 /* virtual */ nscoord
732 nsTableCellFrame::GetMinWidth(nsIRenderingContext *aRenderingContext)
734 nscoord result = 0;
735 DISPLAY_MIN_WIDTH(this, result);
737 nsIFrame *inner = mFrames.FirstChild();
738 result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, inner,
739 nsLayoutUtils::MIN_WIDTH);
740 return result;
743 /* virtual */ nscoord
744 nsTableCellFrame::GetPrefWidth(nsIRenderingContext *aRenderingContext)
746 nscoord result = 0;
747 DISPLAY_PREF_WIDTH(this, result);
749 nsIFrame *inner = mFrames.FirstChild();
750 result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, inner,
751 nsLayoutUtils::PREF_WIDTH);
752 return result;
755 /* virtual */ nsIFrame::IntrinsicWidthOffsetData
756 nsTableCellFrame::IntrinsicWidthOffsets(nsIRenderingContext* aRenderingContext)
758 IntrinsicWidthOffsetData result =
759 nsHTMLContainerFrame::IntrinsicWidthOffsets(aRenderingContext);
761 result.hMargin = 0;
762 result.hPctMargin = 0;
764 nsMargin border;
765 GetBorderWidth(border);
766 result.hBorder = border.LeftRight();
768 return result;
771 #ifdef DEBUG
772 #define PROBABLY_TOO_LARGE 1000000
773 static
774 void DebugCheckChildSize(nsIFrame* aChild,
775 nsHTMLReflowMetrics& aMet,
776 nsSize& aAvailSize)
778 if ((aMet.width < 0) || (aMet.width > PROBABLY_TOO_LARGE)) {
779 printf("WARNING: cell content %p has large width %d \n",
780 static_cast<void*>(aChild), aMet.width);
783 #endif
785 // the computed height for the cell, which descendants use for percent height calculations
786 // it is the height (minus border, padding) of the cell's first in flow during its final
787 // reflow without an unconstrained height.
788 static nscoord
789 CalcUnpaginagedHeight(nsPresContext* aPresContext,
790 nsTableCellFrame& aCellFrame,
791 nsTableFrame& aTableFrame,
792 nscoord aVerticalBorderPadding)
794 const nsTableCellFrame* firstCellInFlow = (nsTableCellFrame*)aCellFrame.GetFirstInFlow();
795 nsTableFrame* firstTableInFlow = (nsTableFrame*)aTableFrame.GetFirstInFlow();
796 nsTableRowFrame* row
797 = static_cast<nsTableRowFrame*>(firstCellInFlow->GetParent());
798 nsTableRowGroupFrame* firstRGInFlow
799 = static_cast<nsTableRowGroupFrame*>(row->GetParent());
801 PRInt32 rowIndex;
802 firstCellInFlow->GetRowIndex(rowIndex);
803 PRInt32 rowSpan = aTableFrame.GetEffectiveRowSpan(*firstCellInFlow);
804 nscoord cellSpacing = firstTableInFlow->GetCellSpacingX();
806 nscoord computedHeight = ((rowSpan - 1) * cellSpacing) - aVerticalBorderPadding;
807 PRInt32 rowX;
808 for (row = firstRGInFlow->GetFirstRow(), rowX = 0; row; row = row->GetNextRow(), rowX++) {
809 if (rowX > rowIndex + rowSpan - 1) {
810 break;
812 else if (rowX >= rowIndex) {
813 computedHeight += row->GetUnpaginatedHeight(aPresContext);
816 return computedHeight;
819 NS_METHOD nsTableCellFrame::Reflow(nsPresContext* aPresContext,
820 nsHTMLReflowMetrics& aDesiredSize,
821 const nsHTMLReflowState& aReflowState,
822 nsReflowStatus& aStatus)
824 DO_GLOBAL_REFLOW_COUNT("nsTableCellFrame");
825 DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
827 if (aReflowState.mFlags.mSpecialHeightReflow) {
828 GetFirstInFlow()->AddStateBits(NS_TABLE_CELL_HAD_SPECIAL_REFLOW);
831 // see if a special height reflow needs to occur due to having a pct height
832 nsTableFrame::CheckRequestSpecialHeightReflow(aReflowState);
834 aStatus = NS_FRAME_COMPLETE;
835 nsSize availSize(aReflowState.availableWidth, aReflowState.availableHeight);
837 /* It's the 'border-collapse' on the table that matters */
838 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
839 if (!tableFrame)
840 ABORT1(NS_ERROR_NULL_POINTER);
842 nsMargin borderPadding = aReflowState.mComputedPadding;
843 nsMargin border;
844 GetBorderWidth(border);
845 borderPadding += border;
847 nscoord topInset = borderPadding.top;
848 nscoord rightInset = borderPadding.right;
849 nscoord bottomInset = borderPadding.bottom;
850 nscoord leftInset = borderPadding.left;
852 // reduce available space by insets, if we're in a constrained situation
853 availSize.width -= leftInset + rightInset;
854 if (NS_UNCONSTRAINEDSIZE != availSize.height)
855 availSize.height -= topInset + bottomInset;
857 // Try to reflow the child into the available space. It might not
858 // fit or might need continuing.
859 if (availSize.height < 0)
860 availSize.height = 1;
862 nsHTMLReflowMetrics kidSize(aDesiredSize.mFlags);
863 kidSize.width = kidSize.height = 0;
864 SetPriorAvailWidth(aReflowState.availableWidth);
865 nsIFrame* firstKid = mFrames.FirstChild();
866 NS_ASSERTION(firstKid, "Frame construction error, a table cell always has an inner cell frame");
868 if (aReflowState.mFlags.mSpecialHeightReflow) {
869 const_cast<nsHTMLReflowState&>(aReflowState).SetComputedHeight(mRect.height - topInset - bottomInset);
870 DISPLAY_REFLOW_CHANGE();
872 else if (aPresContext->IsPaginated()) {
873 nscoord computedUnpaginatedHeight =
874 CalcUnpaginagedHeight(aPresContext, (nsTableCellFrame&)*this,
875 *tableFrame, topInset + bottomInset);
876 if (computedUnpaginatedHeight > 0) {
877 const_cast<nsHTMLReflowState&>(aReflowState).SetComputedHeight(computedUnpaginatedHeight);
878 DISPLAY_REFLOW_CHANGE();
881 else {
882 SetHasPctOverHeight(PR_FALSE);
885 nsHTMLReflowState kidReflowState(aPresContext, aReflowState, firstKid,
886 availSize);
888 // Don't be a percent height observer if we're in the middle of
889 // special-height reflow, in case we get an accidental NotifyPercentHeight()
890 // call (which we shouldn't honor during special-height reflow)
891 if (!aReflowState.mFlags.mSpecialHeightReflow) {
892 // mPercentHeightObserver is for children of cells in quirks mode,
893 // but only those than are tables in standards mode. NeedsToObserve
894 // will determine how far this is propagated to descendants.
895 kidReflowState.mPercentHeightObserver = this;
897 // Don't propagate special height reflow state to our kids
898 kidReflowState.mFlags.mSpecialHeightReflow = PR_FALSE;
900 if (aReflowState.mFlags.mSpecialHeightReflow ||
901 (GetFirstInFlow()->GetStateBits() & NS_TABLE_CELL_HAD_SPECIAL_REFLOW)) {
902 // We need to force the kid to have mVResize set if we've had a
903 // special reflow in the past, since the non-special reflow needs to
904 // resize back to what it was without the special height reflow.
905 kidReflowState.mFlags.mVResize = PR_TRUE;
908 nsPoint kidOrigin(leftInset, topInset);
909 nsRect origRect = firstKid->GetRect();
910 nsRect origOverflowRect = firstKid->GetOverflowRect();
911 PRBool firstReflow = (firstKid->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0;
913 ReflowChild(firstKid, aPresContext, kidSize, kidReflowState,
914 kidOrigin.x, kidOrigin.y, NS_FRAME_INVALIDATE_ON_MOVE, aStatus);
915 if (NS_FRAME_OVERFLOW_IS_INCOMPLETE(aStatus)) {
916 // Don't pass OVERFLOW_INCOMPLETE through tables until they can actually handle it
917 //XXX should paginate overflow as overflow, but not in this patch (bug 379349)
918 NS_FRAME_SET_INCOMPLETE(aStatus);
919 printf("Set table cell incomplete %p\n", static_cast<void*>(this));
922 // XXXbz is this invalidate actually needed, really?
923 if (GetStateBits() & NS_FRAME_IS_DIRTY) {
924 InvalidateOverflowRect();
927 #ifdef NS_DEBUG
928 DebugCheckChildSize(firstKid, kidSize, availSize);
929 #endif
931 // 0 dimensioned cells need to be treated specially in Standard/NavQuirks mode
932 // see testcase "emptyCells.html"
933 nsIFrame* prevInFlow = GetPrevInFlow();
934 PRBool isEmpty;
935 if (prevInFlow) {
936 isEmpty = static_cast<nsTableCellFrame*>(prevInFlow)->GetContentEmpty();
937 } else {
938 isEmpty = !CellHasVisibleContent(kidSize.height, tableFrame, firstKid);
940 SetContentEmpty(isEmpty);
942 // Place the child
943 FinishReflowChild(firstKid, aPresContext, &kidReflowState, kidSize,
944 kidOrigin.x, kidOrigin.y, 0);
946 nsTableFrame::InvalidateFrame(firstKid, origRect, origOverflowRect,
947 firstReflow);
949 // first, compute the height which can be set w/o being restricted by aMaxSize.height
950 nscoord cellHeight = kidSize.height;
952 if (NS_UNCONSTRAINEDSIZE != cellHeight) {
953 cellHeight += topInset + bottomInset;
956 // next determine the cell's width
957 nscoord cellWidth = kidSize.width; // at this point, we've factored in the cell's style attributes
959 // factor in border and padding
960 if (NS_UNCONSTRAINEDSIZE != cellWidth) {
961 cellWidth += leftInset + rightInset;
964 // set the cell's desired size and max element size
965 aDesiredSize.width = cellWidth;
966 aDesiredSize.height = cellHeight;
968 // the overflow area will be computed when the child will be vertically aligned
970 if (aReflowState.mFlags.mSpecialHeightReflow) {
971 if (aDesiredSize.height > mRect.height) {
972 // set a bit indicating that the pct height contents exceeded
973 // the height that they could honor in the pass 2 reflow
974 SetHasPctOverHeight(PR_TRUE);
976 if (NS_UNCONSTRAINEDSIZE == aReflowState.availableHeight) {
977 aDesiredSize.height = mRect.height;
981 // If our parent is in initial reflow, it'll handle invalidating our
982 // entire overflow rect.
983 if (!(GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
984 CheckInvalidateSizeChange(aDesiredSize);
987 // remember the desired size for this reflow
988 SetDesiredSize(aDesiredSize);
990 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
991 return NS_OK;
994 /* ----- global methods ----- */
996 NS_QUERYFRAME_HEAD(nsTableCellFrame)
997 NS_QUERYFRAME_ENTRY(nsTableCellFrame)
998 NS_QUERYFRAME_ENTRY(nsITableCellLayout)
999 NS_QUERYFRAME_ENTRY(nsIPercentHeightObserver)
1000 NS_QUERYFRAME_TAIL_INHERITING(nsHTMLContainerFrame)
1002 #ifdef ACCESSIBILITY
1003 NS_IMETHODIMP nsTableCellFrame::GetAccessible(nsIAccessible** aAccessible)
1005 nsCOMPtr<nsIAccessibilityService> accService = do_GetService("@mozilla.org/accessibilityService;1");
1007 if (accService) {
1008 return accService->CreateHTMLTableCellAccessible(static_cast<nsIFrame*>(this), aAccessible);
1011 return NS_ERROR_FAILURE;
1013 #endif
1015 /* This is primarily for editor access via nsITableLayout */
1016 NS_IMETHODIMP
1017 nsTableCellFrame::GetCellIndexes(PRInt32 &aRowIndex, PRInt32 &aColIndex)
1019 nsresult res = GetRowIndex(aRowIndex);
1020 if (NS_FAILED(res))
1022 aColIndex = 0;
1023 return res;
1025 aColIndex = mColIndex;
1026 return NS_OK;
1029 nsIFrame*
1030 NS_NewTableCellFrame(nsIPresShell* aPresShell,
1031 nsStyleContext* aContext,
1032 PRBool aIsBorderCollapse)
1034 if (aIsBorderCollapse)
1035 return new (aPresShell) nsBCTableCellFrame(aContext);
1036 else
1037 return new (aPresShell) nsTableCellFrame(aContext);
1040 NS_IMPL_FRAMEARENA_HELPERS(nsBCTableCellFrame)
1042 nsMargin*
1043 nsTableCellFrame::GetBorderWidth(nsMargin& aBorder) const
1045 aBorder = GetStyleBorder()->GetActualBorder();
1046 return &aBorder;
1049 nsIAtom*
1050 nsTableCellFrame::GetType() const
1052 return nsGkAtoms::tableCellFrame;
1055 /* virtual */ PRBool
1056 nsTableCellFrame::IsContainingBlock() const
1058 return PR_TRUE;
1061 #ifdef DEBUG
1062 NS_IMETHODIMP
1063 nsTableCellFrame::GetFrameName(nsAString& aResult) const
1065 return MakeFrameName(NS_LITERAL_STRING("TableCell"), aResult);
1067 #endif
1069 // nsBCTableCellFrame
1071 nsBCTableCellFrame::nsBCTableCellFrame(nsStyleContext* aContext)
1072 :nsTableCellFrame(aContext)
1074 mTopBorder = mRightBorder = mBottomBorder = mLeftBorder = 0;
1077 nsBCTableCellFrame::~nsBCTableCellFrame()
1081 nsIAtom*
1082 nsBCTableCellFrame::GetType() const
1084 return nsGkAtoms::bcTableCellFrame;
1087 /* virtual */ nsMargin
1088 nsBCTableCellFrame::GetUsedBorder() const
1090 nsMargin result;
1091 GetBorderWidth(result);
1092 return result;
1095 #ifdef DEBUG
1096 NS_IMETHODIMP
1097 nsBCTableCellFrame::GetFrameName(nsAString& aResult) const
1099 return MakeFrameName(NS_LITERAL_STRING("BCTableCell"), aResult);
1101 #endif
1103 nsMargin*
1104 nsBCTableCellFrame::GetBorderWidth(nsMargin& aBorder) const
1106 PRInt32 aPixelsToTwips = nsPresContext::AppUnitsPerCSSPixel();
1107 aBorder.top = BC_BORDER_BOTTOM_HALF_COORD(aPixelsToTwips, mTopBorder);
1108 aBorder.right = BC_BORDER_LEFT_HALF_COORD(aPixelsToTwips, mRightBorder);
1109 aBorder.bottom = BC_BORDER_TOP_HALF_COORD(aPixelsToTwips, mBottomBorder);
1110 aBorder.left = BC_BORDER_RIGHT_HALF_COORD(aPixelsToTwips, mLeftBorder);
1111 return &aBorder;
1114 BCPixelSize
1115 nsBCTableCellFrame::GetBorderWidth(mozilla::css::Side aSide) const
1117 switch(aSide) {
1118 case NS_SIDE_TOP:
1119 return BC_BORDER_BOTTOM_HALF(mTopBorder);
1120 case NS_SIDE_RIGHT:
1121 return BC_BORDER_LEFT_HALF(mRightBorder);
1122 case NS_SIDE_BOTTOM:
1123 return BC_BORDER_TOP_HALF(mBottomBorder);
1124 default:
1125 return BC_BORDER_RIGHT_HALF(mLeftBorder);
1129 void
1130 nsBCTableCellFrame::SetBorderWidth(mozilla::css::Side aSide,
1131 BCPixelSize aValue)
1133 switch(aSide) {
1134 case NS_SIDE_TOP:
1135 mTopBorder = aValue;
1136 break;
1137 case NS_SIDE_RIGHT:
1138 mRightBorder = aValue;
1139 break;
1140 case NS_SIDE_BOTTOM:
1141 mBottomBorder = aValue;
1142 break;
1143 default:
1144 mLeftBorder = aValue;
1148 /* virtual */ void
1149 nsBCTableCellFrame::GetSelfOverflow(nsRect& aOverflowArea)
1151 nsMargin halfBorder;
1152 PRInt32 p2t = nsPresContext::AppUnitsPerCSSPixel();
1153 halfBorder.top = BC_BORDER_TOP_HALF_COORD(p2t, mTopBorder);
1154 halfBorder.right = BC_BORDER_RIGHT_HALF_COORD(p2t, mRightBorder);
1155 halfBorder.bottom = BC_BORDER_BOTTOM_HALF_COORD(p2t, mBottomBorder);
1156 halfBorder.left = BC_BORDER_LEFT_HALF_COORD(p2t, mLeftBorder);
1158 nsRect overflow(nsPoint(0,0), GetSize());
1159 overflow.Inflate(halfBorder);
1160 aOverflowArea = overflow;
1164 void
1165 nsBCTableCellFrame::PaintBackground(nsIRenderingContext& aRenderingContext,
1166 const nsRect& aDirtyRect,
1167 nsPoint aPt,
1168 PRUint32 aFlags)
1170 // make border-width reflect the half of the border-collapse
1171 // assigned border that's inside the cell
1172 nsMargin borderWidth;
1173 GetBorderWidth(borderWidth);
1175 nsStyleBorder myBorder(*GetStyleBorder());
1177 NS_FOR_CSS_SIDES(side) {
1178 myBorder.SetBorderWidth(side, borderWidth.side(side));
1181 nsRect rect(aPt, GetSize());
1182 // bypassing nsCSSRendering::PaintBackground is safe because this kind
1183 // of frame cannot be used for the root element
1184 nsCSSRendering::PaintBackgroundWithSC(PresContext(), aRenderingContext, this,
1185 aDirtyRect, rect,
1186 GetStyleContext(), myBorder,
1187 aFlags, nsnull);