Backed out changeset 8366e5cc9f57 (bug 125282) because of four windows unit test...
[mozilla-central.git] / layout / tables / nsTableCellFrame.cpp
blob9c6a076d7f9e625f3a45ffde2c05b8bdde2e67b3
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 nsTableCellFrame*
86 nsTableCellFrame::GetNextCell() const
88 nsIFrame* childFrame = GetNextSibling();
89 while (childFrame) {
90 nsTableCellFrame *cellFrame = do_QueryFrame(childFrame);
91 if (cellFrame) {
92 return cellFrame;
94 childFrame = childFrame->GetNextSibling();
96 return nsnull;
99 NS_IMETHODIMP
100 nsTableCellFrame::Init(nsIContent* aContent,
101 nsIFrame* aParent,
102 nsIFrame* aPrevInFlow)
104 // Let the base class do its initialization
105 nsresult rv = nsHTMLContainerFrame::Init(aContent, aParent, aPrevInFlow);
107 if (aPrevInFlow) {
108 // Set the column index
109 nsTableCellFrame* cellFrame = (nsTableCellFrame*)aPrevInFlow;
110 PRInt32 colIndex;
111 cellFrame->GetColIndex(colIndex);
112 SetColIndex(colIndex);
115 return rv;
118 // nsIPercentHeightObserver methods
120 void
121 nsTableCellFrame::NotifyPercentHeight(const nsHTMLReflowState& aReflowState)
123 // nsHTMLReflowState ensures the mCBReflowState of blocks inside a
124 // cell is the cell frame, not the inner-cell block, and that the
125 // containing block of an inner table is the containing block of its
126 // outer table.
127 // XXXldb Given the now-stricter |NeedsToObserve|, many if not all of
128 // these tests are probably unnecessary.
130 // Maybe the cell reflow state; we sure if we're inside the |if|.
131 const nsHTMLReflowState *cellRS = aReflowState.mCBReflowState;
133 if (cellRS && cellRS->frame == this &&
134 (cellRS->ComputedHeight() == NS_UNCONSTRAINEDSIZE ||
135 cellRS->ComputedHeight() == 0)) { // XXXldb Why 0?
136 // This is a percentage height on a frame whose percentage heights
137 // are based on the height of the cell, since its containing block
138 // is the inner cell frame.
140 // We'll only honor the percent height if sibling-cells/ancestors
141 // have specified/pct height. (Also, siblings only count for this if
142 // both this cell and the sibling cell span exactly 1 row.)
144 if (nsTableFrame::AncestorsHaveStyleHeight(*cellRS) ||
145 (nsTableFrame::GetTableFrame(this)->GetEffectiveRowSpan(*this) == 1 &&
146 (cellRS->parentReflowState->frame->GetStateBits() &
147 NS_ROW_HAS_CELL_WITH_STYLE_HEIGHT))) {
149 for (const nsHTMLReflowState *rs = aReflowState.parentReflowState;
150 rs != cellRS;
151 rs = rs->parentReflowState) {
152 rs->frame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT);
155 nsTableFrame::RequestSpecialHeightReflow(*cellRS);
160 // The cell needs to observe its block and things inside its block but nothing below that
161 PRBool
162 nsTableCellFrame::NeedsToObserve(const nsHTMLReflowState& aReflowState)
164 const nsHTMLReflowState *rs = aReflowState.parentReflowState;
165 if (!rs)
166 return PR_FALSE;
167 if (rs->frame == this) {
168 // We always observe the child block. It will never send any
169 // notifications, but we need this so that the observer gets
170 // propagated to its kids.
171 return PR_TRUE;
173 rs = rs->parentReflowState;
174 if (!rs) {
175 return PR_FALSE;
178 // We always need to let the percent height observer be propagated
179 // from an outer table frame to an inner table frame.
180 nsIAtom *fType = aReflowState.frame->GetType();
181 if (fType == nsGkAtoms::tableFrame) {
182 return PR_TRUE;
185 // We need the observer to be propagated to all children of the cell
186 // (i.e., children of the child block) in quirks mode, but only to
187 // tables in standards mode.
188 return rs->frame == this &&
189 (PresContext()->CompatibilityMode() == eCompatibility_NavQuirks ||
190 fType == nsGkAtoms::tableOuterFrame);
193 nsresult
194 nsTableCellFrame::GetRowIndex(PRInt32 &aRowIndex) const
196 nsresult result;
197 nsTableRowFrame* row = static_cast<nsTableRowFrame*>(GetParent());
198 if (row) {
199 aRowIndex = row->GetRowIndex();
200 result = NS_OK;
202 else {
203 aRowIndex = 0;
204 result = NS_ERROR_NOT_INITIALIZED;
206 return result;
209 nsresult
210 nsTableCellFrame::GetColIndex(PRInt32 &aColIndex) const
212 if (GetPrevInFlow()) {
213 return ((nsTableCellFrame*)GetFirstInFlow())->GetColIndex(aColIndex);
215 else {
216 aColIndex = mColIndex;
217 return NS_OK;
221 NS_IMETHODIMP
222 nsTableCellFrame::AttributeChanged(PRInt32 aNameSpaceID,
223 nsIAtom* aAttribute,
224 PRInt32 aModType)
226 // We need to recalculate in this case because of the nowrap quirk in
227 // BasicTableLayoutStrategy
228 if (aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::nowrap &&
229 PresContext()->CompatibilityMode() == eCompatibility_NavQuirks) {
230 PresContext()->PresShell()->
231 FrameNeedsReflow(this, nsIPresShell::eTreeChange, NS_FRAME_IS_DIRTY);
233 // let the table frame decide what to do
234 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
235 if (tableFrame) {
236 tableFrame->AttributeChangedFor(this, mContent, aAttribute);
238 return NS_OK;
241 /* virtual */ void
242 nsTableCellFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
244 if (!aOldStyleContext) //avoid this on init
245 return;
247 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
249 if (tableFrame->IsBorderCollapse() &&
250 tableFrame->BCRecalcNeeded(aOldStyleContext, GetStyleContext())) {
251 PRInt32 colIndex, rowIndex;
252 GetColIndex(colIndex);
253 GetRowIndex(rowIndex);
254 nsRect damageArea(colIndex, rowIndex, GetColSpan(), GetRowSpan());
255 tableFrame->SetBCDamageArea(damageArea);
260 NS_IMETHODIMP
261 nsTableCellFrame::AppendFrames(nsIAtom* aListName,
262 nsFrameList& aFrameList)
264 NS_PRECONDITION(PR_FALSE, "unsupported operation");
265 return NS_ERROR_NOT_IMPLEMENTED;
268 NS_IMETHODIMP
269 nsTableCellFrame::InsertFrames(nsIAtom* aListName,
270 nsIFrame* aPrevFrame,
271 nsFrameList& aFrameList)
273 NS_PRECONDITION(PR_FALSE, "unsupported operation");
274 return NS_ERROR_NOT_IMPLEMENTED;
277 NS_IMETHODIMP
278 nsTableCellFrame::RemoveFrame(nsIAtom* aListName,
279 nsIFrame* aOldFrame)
281 NS_PRECONDITION(PR_FALSE, "unsupported operation");
282 return NS_ERROR_NOT_IMPLEMENTED;
285 void nsTableCellFrame::SetColIndex(PRInt32 aColIndex)
287 mColIndex = aColIndex;
290 /* virtual */ nsMargin
291 nsTableCellFrame::GetUsedMargin() const
293 return nsMargin(0,0,0,0);
296 //ASSURE DIFFERENT COLORS for selection
297 inline nscolor EnsureDifferentColors(nscolor colorA, nscolor colorB)
299 if (colorA == colorB)
301 nscolor res;
302 res = NS_RGB(NS_GET_R(colorA) ^ 0xff,
303 NS_GET_G(colorA) ^ 0xff,
304 NS_GET_B(colorA) ^ 0xff);
305 return res;
307 return colorA;
310 void
311 nsTableCellFrame::DecorateForSelection(nsIRenderingContext& aRenderingContext,
312 nsPoint aPt)
314 NS_ASSERTION(GetStateBits() & NS_FRAME_SELECTED_CONTENT,
315 "Should only be called for selected cells");
316 PRInt16 displaySelection;
317 nsPresContext* presContext = PresContext();
318 displaySelection = DisplaySelection(presContext);
319 if (displaySelection) {
320 nsCOMPtr<nsFrameSelection> frameSelection =
321 presContext->PresShell()->FrameSelection();
323 if (frameSelection->GetTableCellSelection()) {
324 nscolor bordercolor;
325 if (displaySelection == nsISelectionController::SELECTION_DISABLED) {
326 bordercolor = NS_RGB(176,176,176);// disabled color
328 else {
329 presContext->LookAndFeel()->
330 GetColor(nsILookAndFeel::eColor_TextSelectBackground,
331 bordercolor);
333 nscoord threePx = nsPresContext::CSSPixelsToAppUnits(3);
334 if ((mRect.width > threePx) && (mRect.height > threePx))
336 //compare bordercolor to ((nsStyleColor *)myColor)->mBackgroundColor)
337 bordercolor = EnsureDifferentColors(bordercolor,
338 GetStyleBackground()->mBackgroundColor);
339 nsIRenderingContext::AutoPushTranslation
340 translate(&aRenderingContext, aPt.x, aPt.y);
341 nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
343 aRenderingContext.SetColor(bordercolor);
344 aRenderingContext.DrawLine(onePixel, 0, mRect.width, 0);
345 aRenderingContext.DrawLine(0, onePixel, 0, mRect.height);
346 aRenderingContext.DrawLine(onePixel, mRect.height, mRect.width, mRect.height);
347 aRenderingContext.DrawLine(mRect.width, onePixel, mRect.width, mRect.height);
348 //middle
349 aRenderingContext.DrawRect(onePixel, onePixel, mRect.width-onePixel,
350 mRect.height-onePixel);
351 //shading
352 aRenderingContext.DrawLine(2*onePixel, mRect.height-2*onePixel,
353 mRect.width-onePixel, mRect.height- (2*onePixel));
354 aRenderingContext.DrawLine(mRect.width - (2*onePixel), 2*onePixel,
355 mRect.width - (2*onePixel), mRect.height-onePixel);
361 void
362 nsTableCellFrame::PaintBackground(nsIRenderingContext& aRenderingContext,
363 const nsRect& aDirtyRect,
364 nsPoint aPt)
366 nsRect rect(aPt, GetSize());
367 nsCSSRendering::PaintBackground(PresContext(), aRenderingContext, this,
368 aDirtyRect, rect, 0);
371 // Called by nsTablePainter
372 void
373 nsTableCellFrame::PaintCellBackground(nsIRenderingContext& aRenderingContext,
374 const nsRect& aDirtyRect, nsPoint aPt)
376 if (!GetStyleVisibility()->IsVisible())
377 return;
379 PaintBackground(aRenderingContext, aDirtyRect, aPt);
382 class nsDisplayTableCellBackground : public nsDisplayTableItem {
383 public:
384 nsDisplayTableCellBackground(nsTableCellFrame* aFrame) : nsDisplayTableItem(aFrame) {
385 MOZ_COUNT_CTOR(nsDisplayTableCellBackground);
387 #ifdef NS_BUILD_REFCNT_LOGGING
388 virtual ~nsDisplayTableCellBackground() {
389 MOZ_COUNT_DTOR(nsDisplayTableCellBackground);
391 #endif
393 virtual nsIFrame* HitTest(nsDisplayListBuilder* aBuilder, nsPoint aPt,
394 HitTestState* aState) { return mFrame; }
395 virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx,
396 const nsRect& aDirtyRect);
397 virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder);
399 NS_DISPLAY_DECL_NAME("TableCellBackground")
402 void nsDisplayTableCellBackground::Paint(nsDisplayListBuilder* aBuilder,
403 nsIRenderingContext* aCtx, const nsRect& aDirtyRect)
405 static_cast<nsTableCellFrame*>(mFrame)->
406 PaintBackground(*aCtx, aDirtyRect, aBuilder->ToReferenceFrame(mFrame));
409 nsRect
410 nsDisplayTableCellBackground::GetBounds(nsDisplayListBuilder* aBuilder)
412 // revert from nsDisplayTableItem's implementation ... cell backgrounds
413 // don't overflow the cell
414 return nsDisplayItem::GetBounds(aBuilder);
417 static void
418 PaintTableCellSelection(nsIFrame* aFrame, nsIRenderingContext* aCtx,
419 const nsRect& aRect, nsPoint aPt)
421 static_cast<nsTableCellFrame*>(aFrame)->DecorateForSelection(*aCtx, aPt);
424 NS_IMETHODIMP
425 nsTableCellFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
426 const nsRect& aDirtyRect,
427 const nsDisplayListSet& aLists)
429 if (!IsVisibleInSelection(aBuilder))
430 return NS_OK;
432 DO_GLOBAL_REFLOW_COUNT_DSP("nsTableCellFrame");
433 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
435 PRInt32 emptyCellStyle = GetContentEmpty() && !tableFrame->IsBorderCollapse() ?
436 GetStyleTableBorder()->mEmptyCells
437 : NS_STYLE_TABLE_EMPTY_CELLS_SHOW;
438 // take account of 'empty-cells'
439 if (GetStyleVisibility()->IsVisible() &&
440 (NS_STYLE_TABLE_EMPTY_CELLS_HIDE != emptyCellStyle)) {
443 PRBool isRoot = aBuilder->IsAtRootOfPseudoStackingContext();
444 if (!isRoot) {
445 nsDisplayTableItem* currentItem = aBuilder->GetCurrentTableItem();
446 NS_ASSERTION(currentItem, "No current table item???");
447 currentItem->UpdateForFrameBackground(this);
450 // display outset box-shadows if we need to.
451 PRBool hasBoxShadow = !!(GetStyleBorder()->mBoxShadow);
452 if (hasBoxShadow) {
453 nsDisplayItem* item = new (aBuilder) nsDisplayBoxShadowOuter(this);
454 nsresult rv = aLists.BorderBackground()->AppendNewToTop(item);
455 NS_ENSURE_SUCCESS(rv, rv);
458 // display background if we need to.
459 if (aBuilder->IsForEventDelivery() ||
460 (((!tableFrame->IsBorderCollapse() || isRoot) &&
461 (!GetStyleBackground()->IsTransparent() || GetStyleDisplay()->mAppearance)))) {
462 // The cell background was not painted by the nsTablePainter,
463 // so we need to do it. We have special background processing here
464 // so we need to duplicate some code from nsFrame::DisplayBorderBackgroundOutline
465 nsDisplayTableItem* item = new (aBuilder) nsDisplayTableCellBackground(this);
466 nsresult rv = aLists.BorderBackground()->AppendNewToTop(item);
467 NS_ENSURE_SUCCESS(rv, rv);
468 item->UpdateForFrameBackground(this);
471 // display inset box-shadows if we need to.
472 if (hasBoxShadow) {
473 nsDisplayItem* item = new (aBuilder) nsDisplayBoxShadowInner(this);
474 nsresult rv = aLists.BorderBackground()->AppendNewToTop(item);
475 NS_ENSURE_SUCCESS(rv, rv);
478 // display borders if we need to
479 if (!tableFrame->IsBorderCollapse() && HasBorder() &&
480 emptyCellStyle == NS_STYLE_TABLE_EMPTY_CELLS_SHOW) {
481 nsresult rv = aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
482 nsDisplayBorder(this));
483 NS_ENSURE_SUCCESS(rv, rv);
486 // and display the selection border if we need to
487 PRBool isSelected =
488 (GetStateBits() & NS_FRAME_SELECTED_CONTENT) == NS_FRAME_SELECTED_CONTENT;
489 if (isSelected) {
490 nsresult rv = aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
491 nsDisplayGeneric(this, ::PaintTableCellSelection, "TableCellSelection"));
492 NS_ENSURE_SUCCESS(rv, rv);
496 // the 'empty-cells' property has no effect on 'outline'
497 nsresult rv = DisplayOutline(aBuilder, aLists);
498 NS_ENSURE_SUCCESS(rv, rv);
500 nsIFrame* kid = mFrames.FirstChild();
501 NS_ASSERTION(kid && !kid->GetNextSibling(), "Table cells should have just one child");
502 // The child's background will go in our BorderBackground() list.
503 // This isn't a problem since it won't have a real background except for
504 // event handling. We do not call BuildDisplayListForNonBlockChildren
505 // because that/ would put the child's background in the Content() list
506 // which isn't right (e.g., would end up on top of our child floats for
507 // event handling).
508 return BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists);
511 PRIntn
512 nsTableCellFrame::GetSkipSides() const
514 PRIntn skip = 0;
515 if (nsnull != GetPrevInFlow()) {
516 skip |= 1 << NS_SIDE_TOP;
518 if (nsnull != GetNextInFlow()) {
519 skip |= 1 << NS_SIDE_BOTTOM;
521 return skip;
524 /* virtual */ void
525 nsTableCellFrame::GetSelfOverflow(nsRect& aOverflowArea)
527 aOverflowArea = nsRect(nsPoint(0,0), GetSize());
530 // Align the cell's child frame within the cell
532 void nsTableCellFrame::VerticallyAlignChild(nscoord aMaxAscent)
534 const nsStyleTextReset* textStyle = GetStyleTextReset();
535 /* It's the 'border-collapse' on the table that matters */
536 nsMargin borderPadding = GetUsedBorderAndPadding();
538 nscoord topInset = borderPadding.top;
539 nscoord bottomInset = borderPadding.bottom;
541 // As per bug 10207, we map 'sub', 'super', 'text-top', 'text-bottom',
542 // length and percentage values to 'baseline'
543 // XXX It seems that we don't get to see length and percentage values here
544 // because the Style System has already fixed the error and mapped them
545 // to whatever is inherited from the parent, i.e, 'middle' in most cases.
546 PRUint8 verticalAlignFlags = NS_STYLE_VERTICAL_ALIGN_BASELINE;
547 if (textStyle->mVerticalAlign.GetUnit() == eStyleUnit_Enumerated) {
548 verticalAlignFlags = textStyle->mVerticalAlign.GetIntValue();
549 if (verticalAlignFlags != NS_STYLE_VERTICAL_ALIGN_TOP &&
550 verticalAlignFlags != NS_STYLE_VERTICAL_ALIGN_MIDDLE &&
551 verticalAlignFlags != NS_STYLE_VERTICAL_ALIGN_BOTTOM)
553 verticalAlignFlags = NS_STYLE_VERTICAL_ALIGN_BASELINE;
557 nscoord height = mRect.height;
558 nsIFrame* firstKid = mFrames.FirstChild();
559 NS_ASSERTION(firstKid, "Frame construction error, a table cell always has an inner cell frame");
560 nsRect kidRect = firstKid->GetRect();
561 nscoord childHeight = kidRect.height;
563 // Vertically align the child
564 nscoord kidYTop = 0;
565 switch (verticalAlignFlags)
567 case NS_STYLE_VERTICAL_ALIGN_BASELINE:
568 // Align the baselines of the child frame with the baselines of
569 // other children in the same row which have 'vertical-align: baseline'
570 kidYTop = topInset + aMaxAscent - GetCellBaseline();
571 break;
573 case NS_STYLE_VERTICAL_ALIGN_TOP:
574 // Align the top of the child frame with the top of the content area,
575 kidYTop = topInset;
576 break;
578 case NS_STYLE_VERTICAL_ALIGN_BOTTOM:
579 // Align the bottom of the child frame with the bottom of the content area,
580 kidYTop = height - childHeight - bottomInset;
581 break;
583 default:
584 case NS_STYLE_VERTICAL_ALIGN_MIDDLE:
585 // Align the middle of the child frame with the middle of the content area,
586 kidYTop = (height - childHeight - bottomInset + topInset) / 2;
588 // if the content is larger than the cell height align from top
589 kidYTop = PR_MAX(0, kidYTop);
591 if (kidYTop != kidRect.y) {
592 // Invalidate at the old position first
593 firstKid->InvalidateOverflowRect();
596 firstKid->SetPosition(nsPoint(kidRect.x, kidYTop));
597 nsHTMLReflowMetrics desiredSize;
598 desiredSize.width = mRect.width;
599 desiredSize.height = mRect.height;
600 GetSelfOverflow(desiredSize.mOverflowArea);
601 ConsiderChildOverflow(desiredSize.mOverflowArea, firstKid);
602 FinishAndStoreOverflow(&desiredSize);
603 if (kidYTop != kidRect.y) {
604 // Make sure any child views are correctly positioned. We know the inner table
605 // cell won't have a view
606 nsContainerFrame::PositionChildViews(firstKid);
608 // Invalidate new overflow rect
609 firstKid->InvalidateOverflowRect();
611 if (HasView()) {
612 nsContainerFrame::SyncFrameViewAfterReflow(PresContext(), this,
613 GetView(),
614 &desiredSize.mOverflowArea, 0);
618 // As per bug 10207, we map 'sub', 'super', 'text-top', 'text-bottom',
619 // length and percentage values to 'baseline'
620 // XXX It seems that we don't get to see length and percentage values here
621 // because the Style System has already fixed the error and mapped them
622 // to whatever is inherited from the parent, i.e, 'middle' in most cases.
623 PRBool
624 nsTableCellFrame::HasVerticalAlignBaseline()
626 const nsStyleTextReset* textStyle = GetStyleTextReset();
627 if (textStyle->mVerticalAlign.GetUnit() == eStyleUnit_Enumerated) {
628 PRUint8 verticalAlignFlags = textStyle->mVerticalAlign.GetIntValue();
629 if (verticalAlignFlags == NS_STYLE_VERTICAL_ALIGN_TOP ||
630 verticalAlignFlags == NS_STYLE_VERTICAL_ALIGN_MIDDLE ||
631 verticalAlignFlags == NS_STYLE_VERTICAL_ALIGN_BOTTOM)
633 return PR_FALSE;
636 return PR_TRUE;
639 PRBool
640 nsTableCellFrame::CellHasVisibleContent(nscoord height,
641 nsTableFrame* tableFrame,
642 nsIFrame* kidFrame)
644 // see http://www.w3.org/TR/CSS21/tables.html#empty-cells
645 if (height > 0)
646 return PR_TRUE;
647 if (tableFrame->IsBorderCollapse())
648 return PR_TRUE;
649 nsIFrame* innerFrame = kidFrame->GetFirstChild(nsnull);
650 while(innerFrame) {
651 nsIAtom* frameType = innerFrame->GetType();
652 if (nsGkAtoms::textFrame == frameType) {
653 nsTextFrame* textFrame = static_cast<nsTextFrame*>(innerFrame);
654 if (textFrame->HasNoncollapsedCharacters())
655 return PR_TRUE;
657 else if (nsGkAtoms::placeholderFrame != frameType) {
658 return PR_TRUE;
660 else {
661 nsIFrame *floatFrame = nsLayoutUtils::GetFloatFromPlaceholder(innerFrame);
662 if (floatFrame)
663 return PR_TRUE;
665 innerFrame = innerFrame->GetNextSibling();
667 return PR_FALSE;
670 nscoord
671 nsTableCellFrame::GetCellBaseline() const
673 // Ignore the position of the inner frame relative to the cell frame
674 // since we want the position as though the inner were top-aligned.
675 nsIFrame *inner = mFrames.FirstChild();
676 nscoord borderPadding = GetUsedBorderAndPadding().top;
677 nscoord result;
678 if (nsLayoutUtils::GetFirstLineBaseline(inner, &result))
679 return result + borderPadding;
680 return inner->GetContentRect().YMost() - inner->GetPosition().y +
681 borderPadding;
684 PRInt32 nsTableCellFrame::GetRowSpan()
686 PRInt32 rowSpan=1;
687 nsGenericHTMLElement *hc = nsGenericHTMLElement::FromContent(mContent);
689 // Don't look at the content's rowspan if we're a pseudo cell
690 if (hc && !GetStyleContext()->GetPseudoType()) {
691 const nsAttrValue* attr = hc->GetParsedAttr(nsGkAtoms::rowspan);
692 // Note that we don't need to check the tag name, because only table cells
693 // and table headers parse the "rowspan" attribute into an integer.
694 if (attr && attr->Type() == nsAttrValue::eInteger) {
695 rowSpan = attr->GetIntegerValue();
698 return rowSpan;
701 PRInt32 nsTableCellFrame::GetColSpan()
703 PRInt32 colSpan=1;
704 nsGenericHTMLElement *hc = nsGenericHTMLElement::FromContent(mContent);
706 // Don't look at the content's colspan if we're a pseudo cell
707 if (hc && !GetStyleContext()->GetPseudoType()) {
708 const nsAttrValue* attr = hc->GetParsedAttr(nsGkAtoms::colspan);
709 // Note that we don't need to check the tag name, because only table cells
710 // and table headers parse the "colspan" attribute into an integer.
711 if (attr && attr->Type() == nsAttrValue::eInteger) {
712 colSpan = attr->GetIntegerValue();
715 return colSpan;
718 /* virtual */ nscoord
719 nsTableCellFrame::GetMinWidth(nsIRenderingContext *aRenderingContext)
721 nscoord result = 0;
722 DISPLAY_MIN_WIDTH(this, result);
724 nsIFrame *inner = mFrames.FirstChild();
725 result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, inner,
726 nsLayoutUtils::MIN_WIDTH);
727 return result;
730 /* virtual */ nscoord
731 nsTableCellFrame::GetPrefWidth(nsIRenderingContext *aRenderingContext)
733 nscoord result = 0;
734 DISPLAY_PREF_WIDTH(this, result);
736 nsIFrame *inner = mFrames.FirstChild();
737 result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, inner,
738 nsLayoutUtils::PREF_WIDTH);
739 return result;
742 /* virtual */ nsIFrame::IntrinsicWidthOffsetData
743 nsTableCellFrame::IntrinsicWidthOffsets(nsIRenderingContext* aRenderingContext)
745 IntrinsicWidthOffsetData result =
746 nsHTMLContainerFrame::IntrinsicWidthOffsets(aRenderingContext);
748 result.hMargin = 0;
749 result.hPctMargin = 0;
751 nsMargin border;
752 GetBorderWidth(border);
753 result.hBorder = border.LeftRight();
755 return result;
758 #ifdef DEBUG
759 #define PROBABLY_TOO_LARGE 1000000
760 static
761 void DebugCheckChildSize(nsIFrame* aChild,
762 nsHTMLReflowMetrics& aMet,
763 nsSize& aAvailSize)
765 if ((aMet.width < 0) || (aMet.width > PROBABLY_TOO_LARGE)) {
766 printf("WARNING: cell content %p has large width %d \n",
767 static_cast<void*>(aChild), aMet.width);
770 #endif
772 // the computed height for the cell, which descendants use for percent height calculations
773 // it is the height (minus border, padding) of the cell's first in flow during its final
774 // reflow without an unconstrained height.
775 static nscoord
776 CalcUnpaginagedHeight(nsPresContext* aPresContext,
777 nsTableCellFrame& aCellFrame,
778 nsTableFrame& aTableFrame,
779 nscoord aVerticalBorderPadding)
781 const nsTableCellFrame* firstCellInFlow = (nsTableCellFrame*)aCellFrame.GetFirstInFlow();
782 nsTableFrame* firstTableInFlow = (nsTableFrame*)aTableFrame.GetFirstInFlow();
783 nsTableRowFrame* row
784 = static_cast<nsTableRowFrame*>(firstCellInFlow->GetParent());
785 nsTableRowGroupFrame* firstRGInFlow
786 = static_cast<nsTableRowGroupFrame*>(row->GetParent());
788 PRInt32 rowIndex;
789 firstCellInFlow->GetRowIndex(rowIndex);
790 PRInt32 rowSpan = aTableFrame.GetEffectiveRowSpan(*firstCellInFlow);
791 nscoord cellSpacing = firstTableInFlow->GetCellSpacingX();
793 nscoord computedHeight = ((rowSpan - 1) * cellSpacing) - aVerticalBorderPadding;
794 PRInt32 rowX;
795 for (row = firstRGInFlow->GetFirstRow(), rowX = 0; row; row = row->GetNextRow(), rowX++) {
796 if (rowX > rowIndex + rowSpan - 1) {
797 break;
799 else if (rowX >= rowIndex) {
800 computedHeight += row->GetUnpaginatedHeight(aPresContext);
803 return computedHeight;
806 NS_METHOD nsTableCellFrame::Reflow(nsPresContext* aPresContext,
807 nsHTMLReflowMetrics& aDesiredSize,
808 const nsHTMLReflowState& aReflowState,
809 nsReflowStatus& aStatus)
811 DO_GLOBAL_REFLOW_COUNT("nsTableCellFrame");
812 DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
814 if (aReflowState.mFlags.mSpecialHeightReflow) {
815 GetFirstInFlow()->AddStateBits(NS_TABLE_CELL_HAD_SPECIAL_REFLOW);
818 // see if a special height reflow needs to occur due to having a pct height
819 nsTableFrame::CheckRequestSpecialHeightReflow(aReflowState);
821 aStatus = NS_FRAME_COMPLETE;
822 nsSize availSize(aReflowState.availableWidth, aReflowState.availableHeight);
824 /* It's the 'border-collapse' on the table that matters */
825 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
826 if (!tableFrame)
827 ABORT1(NS_ERROR_NULL_POINTER);
829 nsMargin borderPadding = aReflowState.mComputedPadding;
830 nsMargin border;
831 GetBorderWidth(border);
832 borderPadding += border;
834 nscoord topInset = borderPadding.top;
835 nscoord rightInset = borderPadding.right;
836 nscoord bottomInset = borderPadding.bottom;
837 nscoord leftInset = borderPadding.left;
839 // reduce available space by insets, if we're in a constrained situation
840 availSize.width -= leftInset + rightInset;
841 if (NS_UNCONSTRAINEDSIZE != availSize.height)
842 availSize.height -= topInset + bottomInset;
844 // Try to reflow the child into the available space. It might not
845 // fit or might need continuing.
846 if (availSize.height < 0)
847 availSize.height = 1;
849 nsHTMLReflowMetrics kidSize(aDesiredSize.mFlags);
850 kidSize.width = kidSize.height = 0;
851 SetPriorAvailWidth(aReflowState.availableWidth);
852 nsIFrame* firstKid = mFrames.FirstChild();
853 NS_ASSERTION(firstKid, "Frame construction error, a table cell always has an inner cell frame");
855 if (aReflowState.mFlags.mSpecialHeightReflow) {
856 const_cast<nsHTMLReflowState&>(aReflowState).SetComputedHeight(mRect.height - topInset - bottomInset);
857 DISPLAY_REFLOW_CHANGE();
859 else if (aPresContext->IsPaginated()) {
860 nscoord computedUnpaginatedHeight =
861 CalcUnpaginagedHeight(aPresContext, (nsTableCellFrame&)*this,
862 *tableFrame, topInset + bottomInset);
863 if (computedUnpaginatedHeight > 0) {
864 const_cast<nsHTMLReflowState&>(aReflowState).SetComputedHeight(computedUnpaginatedHeight);
865 DISPLAY_REFLOW_CHANGE();
868 else {
869 SetHasPctOverHeight(PR_FALSE);
872 nsHTMLReflowState kidReflowState(aPresContext, aReflowState, firstKid,
873 availSize);
875 // Don't be a percent height observer if we're in the middle of
876 // special-height reflow, in case we get an accidental NotifyPercentHeight()
877 // call (which we shouldn't honor during special-height reflow)
878 if (!aReflowState.mFlags.mSpecialHeightReflow) {
879 // mPercentHeightObserver is for children of cells in quirks mode,
880 // but only those than are tables in standards mode. NeedsToObserve
881 // will determine how far this is propagated to descendants.
882 kidReflowState.mPercentHeightObserver = this;
884 // Don't propagate special height reflow state to our kids
885 kidReflowState.mFlags.mSpecialHeightReflow = PR_FALSE;
887 if (aReflowState.mFlags.mSpecialHeightReflow ||
888 (GetFirstInFlow()->GetStateBits() & NS_TABLE_CELL_HAD_SPECIAL_REFLOW)) {
889 // We need to force the kid to have mVResize set if we've had a
890 // special reflow in the past, since the non-special reflow needs to
891 // resize back to what it was without the special height reflow.
892 kidReflowState.mFlags.mVResize = PR_TRUE;
895 nsPoint kidOrigin(leftInset, topInset);
896 nsRect origRect = firstKid->GetRect();
897 nsRect origOverflowRect = firstKid->GetOverflowRect();
898 PRBool firstReflow = (firstKid->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0;
900 ReflowChild(firstKid, aPresContext, kidSize, kidReflowState,
901 kidOrigin.x, kidOrigin.y, NS_FRAME_INVALIDATE_ON_MOVE, aStatus);
902 if (NS_FRAME_OVERFLOW_IS_INCOMPLETE(aStatus)) {
903 // Don't pass OVERFLOW_INCOMPLETE through tables until they can actually handle it
904 //XXX should paginate overflow as overflow, but not in this patch (bug 379349)
905 NS_FRAME_SET_INCOMPLETE(aStatus);
906 printf("Set table cell incomplete %p\n", static_cast<void*>(this));
909 // XXXbz is this invalidate actually needed, really?
910 if (GetStateBits() & NS_FRAME_IS_DIRTY) {
911 InvalidateOverflowRect();
914 #ifdef NS_DEBUG
915 DebugCheckChildSize(firstKid, kidSize, availSize);
916 #endif
918 // 0 dimensioned cells need to be treated specially in Standard/NavQuirks mode
919 // see testcase "emptyCells.html"
920 nsIFrame* prevInFlow = GetPrevInFlow();
921 PRBool isEmpty;
922 if (prevInFlow) {
923 isEmpty = static_cast<nsTableCellFrame*>(prevInFlow)->GetContentEmpty();
924 } else {
925 isEmpty = !CellHasVisibleContent(kidSize.height, tableFrame, firstKid);
927 SetContentEmpty(isEmpty);
929 // Place the child
930 FinishReflowChild(firstKid, aPresContext, &kidReflowState, kidSize,
931 kidOrigin.x, kidOrigin.y, 0);
933 nsTableFrame::InvalidateFrame(firstKid, origRect, origOverflowRect,
934 firstReflow);
936 // first, compute the height which can be set w/o being restricted by aMaxSize.height
937 nscoord cellHeight = kidSize.height;
939 if (NS_UNCONSTRAINEDSIZE != cellHeight) {
940 cellHeight += topInset + bottomInset;
943 // next determine the cell's width
944 nscoord cellWidth = kidSize.width; // at this point, we've factored in the cell's style attributes
946 // factor in border and padding
947 if (NS_UNCONSTRAINEDSIZE != cellWidth) {
948 cellWidth += leftInset + rightInset;
951 // set the cell's desired size and max element size
952 aDesiredSize.width = cellWidth;
953 aDesiredSize.height = cellHeight;
955 // the overflow area will be computed when the child will be vertically aligned
957 if (aReflowState.mFlags.mSpecialHeightReflow) {
958 if (aDesiredSize.height > mRect.height) {
959 // set a bit indicating that the pct height contents exceeded
960 // the height that they could honor in the pass 2 reflow
961 SetHasPctOverHeight(PR_TRUE);
963 if (NS_UNCONSTRAINEDSIZE == aReflowState.availableHeight) {
964 aDesiredSize.height = mRect.height;
968 // If our parent is in initial reflow, it'll handle invalidating our
969 // entire overflow rect.
970 if (!(GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
971 CheckInvalidateSizeChange(aDesiredSize);
974 // remember the desired size for this reflow
975 SetDesiredSize(aDesiredSize);
977 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
978 return NS_OK;
981 /* ----- global methods ----- */
983 NS_QUERYFRAME_HEAD(nsTableCellFrame)
984 NS_QUERYFRAME_ENTRY(nsTableCellFrame)
985 NS_QUERYFRAME_ENTRY(nsITableCellLayout)
986 NS_QUERYFRAME_ENTRY(nsIPercentHeightObserver)
987 NS_QUERYFRAME_TAIL_INHERITING(nsHTMLContainerFrame)
989 #ifdef ACCESSIBILITY
990 NS_IMETHODIMP nsTableCellFrame::GetAccessible(nsIAccessible** aAccessible)
992 nsCOMPtr<nsIAccessibilityService> accService = do_GetService("@mozilla.org/accessibilityService;1");
994 if (accService) {
995 return accService->CreateHTMLTableCellAccessible(static_cast<nsIFrame*>(this), aAccessible);
998 return NS_ERROR_FAILURE;
1000 #endif
1002 /* This is primarily for editor access via nsITableLayout */
1003 NS_IMETHODIMP
1004 nsTableCellFrame::GetCellIndexes(PRInt32 &aRowIndex, PRInt32 &aColIndex)
1006 nsresult res = GetRowIndex(aRowIndex);
1007 if (NS_FAILED(res))
1009 aColIndex = 0;
1010 return res;
1012 aColIndex = mColIndex;
1013 return NS_OK;
1016 nsIFrame*
1017 NS_NewTableCellFrame(nsIPresShell* aPresShell,
1018 nsStyleContext* aContext,
1019 PRBool aIsBorderCollapse)
1021 if (aIsBorderCollapse)
1022 return new (aPresShell) nsBCTableCellFrame(aContext);
1023 else
1024 return new (aPresShell) nsTableCellFrame(aContext);
1027 nsMargin*
1028 nsTableCellFrame::GetBorderWidth(nsMargin& aBorder) const
1030 aBorder = GetStyleBorder()->GetActualBorder();
1031 return &aBorder;
1034 nsIAtom*
1035 nsTableCellFrame::GetType() const
1037 return nsGkAtoms::tableCellFrame;
1040 /* virtual */ PRBool
1041 nsTableCellFrame::IsContainingBlock() const
1043 return PR_TRUE;
1046 #ifdef DEBUG
1047 NS_IMETHODIMP
1048 nsTableCellFrame::GetFrameName(nsAString& aResult) const
1050 return MakeFrameName(NS_LITERAL_STRING("TableCell"), aResult);
1052 #endif
1054 // nsBCTableCellFrame
1056 nsBCTableCellFrame::nsBCTableCellFrame(nsStyleContext* aContext)
1057 :nsTableCellFrame(aContext)
1059 mTopBorder = mRightBorder = mBottomBorder = mLeftBorder = 0;
1062 nsBCTableCellFrame::~nsBCTableCellFrame()
1066 nsIAtom*
1067 nsBCTableCellFrame::GetType() const
1069 return nsGkAtoms::bcTableCellFrame;
1072 /* virtual */ nsMargin
1073 nsBCTableCellFrame::GetUsedBorder() const
1075 nsMargin result;
1076 GetBorderWidth(result);
1077 return result;
1080 #ifdef DEBUG
1081 NS_IMETHODIMP
1082 nsBCTableCellFrame::GetFrameName(nsAString& aResult) const
1084 return MakeFrameName(NS_LITERAL_STRING("BCTableCell"), aResult);
1086 #endif
1088 nsMargin*
1089 nsBCTableCellFrame::GetBorderWidth(nsMargin& aBorder) const
1091 PRInt32 aPixelsToTwips = nsPresContext::AppUnitsPerCSSPixel();
1092 aBorder.top = BC_BORDER_BOTTOM_HALF_COORD(aPixelsToTwips, mTopBorder);
1093 aBorder.right = BC_BORDER_LEFT_HALF_COORD(aPixelsToTwips, mRightBorder);
1094 aBorder.bottom = BC_BORDER_TOP_HALF_COORD(aPixelsToTwips, mBottomBorder);
1095 aBorder.left = BC_BORDER_RIGHT_HALF_COORD(aPixelsToTwips, mLeftBorder);
1096 return &aBorder;
1099 BCPixelSize
1100 nsBCTableCellFrame::GetBorderWidth(PRUint8 aSide) const
1102 switch(aSide) {
1103 case NS_SIDE_TOP:
1104 return BC_BORDER_BOTTOM_HALF(mTopBorder);
1105 case NS_SIDE_RIGHT:
1106 return BC_BORDER_LEFT_HALF(mRightBorder);
1107 case NS_SIDE_BOTTOM:
1108 return BC_BORDER_TOP_HALF(mBottomBorder);
1109 default:
1110 return BC_BORDER_RIGHT_HALF(mLeftBorder);
1114 void
1115 nsBCTableCellFrame::SetBorderWidth(PRUint8 aSide,
1116 BCPixelSize aValue)
1118 switch(aSide) {
1119 case NS_SIDE_TOP:
1120 mTopBorder = aValue;
1121 break;
1122 case NS_SIDE_RIGHT:
1123 mRightBorder = aValue;
1124 break;
1125 case NS_SIDE_BOTTOM:
1126 mBottomBorder = aValue;
1127 break;
1128 default:
1129 mLeftBorder = aValue;
1133 /* virtual */ void
1134 nsBCTableCellFrame::GetSelfOverflow(nsRect& aOverflowArea)
1136 nsMargin halfBorder;
1137 PRInt32 p2t = nsPresContext::AppUnitsPerCSSPixel();
1138 halfBorder.top = BC_BORDER_TOP_HALF_COORD(p2t, mTopBorder);
1139 halfBorder.right = BC_BORDER_RIGHT_HALF_COORD(p2t, mRightBorder);
1140 halfBorder.bottom = BC_BORDER_BOTTOM_HALF_COORD(p2t, mBottomBorder);
1141 halfBorder.left = BC_BORDER_LEFT_HALF_COORD(p2t, mLeftBorder);
1143 nsRect overflow(nsPoint(0,0), GetSize());
1144 overflow.Inflate(halfBorder);
1145 aOverflowArea = overflow;
1149 void
1150 nsBCTableCellFrame::PaintBackground(nsIRenderingContext& aRenderingContext,
1151 const nsRect& aDirtyRect,
1152 nsPoint aPt)
1154 // make border-width reflect the half of the border-collapse
1155 // assigned border that's inside the cell
1156 nsMargin borderWidth;
1157 GetBorderWidth(borderWidth);
1159 nsStyleBorder myBorder(*GetStyleBorder());
1161 NS_FOR_CSS_SIDES(side) {
1162 myBorder.SetBorderWidth(side, borderWidth.side(side));
1165 nsRect rect(aPt, GetSize());
1166 // bypassing nsCSSRendering::PaintBackground is safe because this kind
1167 // of frame cannot be used for the root element
1168 nsCSSRendering::PaintBackgroundWithSC(PresContext(), aRenderingContext, this,
1169 aDirtyRect, rect,
1170 *GetStyleBackground(), myBorder,
1171 0, nsnull);