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
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.
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"
55 #include "nsIDOMHTMLTableCellElement.h"
57 #include "nsIAccessibilityService.h"
59 #include "nsIServiceManager.h"
60 #include "nsIDOMNode.h"
61 #include "nsINameSpaceManager.h"
62 #include "nsDisplayList.h"
63 #include "nsLayoutUtils.h"
64 #include "nsTextFrame.h"
67 #include "nsFrameSelection.h"
68 #include "nsILookAndFeel.h"
71 nsTableCellFrame::nsTableCellFrame(nsStyleContext
* aContext
) :
72 nsHTMLContainerFrame(aContext
)
77 SetContentEmpty(PR_FALSE
);
78 SetHasPctOverHeight(PR_FALSE
);
81 nsTableCellFrame::~nsTableCellFrame()
86 nsTableCellFrame::GetNextCell() const
88 nsIFrame
* childFrame
= GetNextSibling();
90 if (IS_TABLE_CELL(childFrame
->GetType())) {
91 return (nsTableCellFrame
*)childFrame
;
93 childFrame
= childFrame
->GetNextSibling();
99 nsTableCellFrame::Init(nsIContent
* aContent
,
101 nsIFrame
* aPrevInFlow
)
103 // Let the base class do its initialization
104 nsresult rv
= nsHTMLContainerFrame::Init(aContent
, aParent
, aPrevInFlow
);
107 // Set the column index
108 nsTableCellFrame
* cellFrame
= (nsTableCellFrame
*)aPrevInFlow
;
110 cellFrame
->GetColIndex(colIndex
);
111 SetColIndex(colIndex
);
117 // nsIPercentHeightObserver methods
120 nsTableCellFrame::NotifyPercentHeight(const nsHTMLReflowState
& aReflowState
)
122 // nsHTMLReflowState ensures the mCBReflowState of blocks inside a
123 // cell is the cell frame, not the inner-cell block, and that the
124 // containing block of an inner table is the containing block of its
126 // XXXldb Given the now-stricter |NeedsToObserve|, many if not all of
127 // these tests are probably unnecessary.
129 // Maybe the cell reflow state; we sure if we're inside the |if|.
130 const nsHTMLReflowState
*cellRS
= aReflowState
.mCBReflowState
;
132 if (cellRS
&& cellRS
->frame
== this &&
133 (cellRS
->ComputedHeight() == NS_UNCONSTRAINEDSIZE
||
134 cellRS
->ComputedHeight() == 0)) { // XXXldb Why 0?
135 // This is a percentage height on a frame whose percentage heights
136 // are based on the height of the cell, since its containing block
137 // is the inner cell frame.
139 // We'll only honor the percent height if sibling-cells/ancestors
140 // have specified/pct height. (Also, siblings only count for this if
141 // both this cell and the sibling cell span exactly 1 row.)
143 if (nsTableFrame::AncestorsHaveStyleHeight(*cellRS
) ||
144 (nsTableFrame::GetTableFrame(this)->GetEffectiveRowSpan(*this) == 1 &&
145 (cellRS
->parentReflowState
->frame
->GetStateBits() &
146 NS_ROW_HAS_CELL_WITH_STYLE_HEIGHT
))) {
148 for (const nsHTMLReflowState
*rs
= aReflowState
.parentReflowState
;
150 rs
= rs
->parentReflowState
) {
151 rs
->frame
->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT
);
154 nsTableFrame::RequestSpecialHeightReflow(*cellRS
);
159 // The cell needs to observe its block and things inside its block but nothing below that
161 nsTableCellFrame::NeedsToObserve(const nsHTMLReflowState
& aReflowState
)
163 const nsHTMLReflowState
*rs
= aReflowState
.parentReflowState
;
166 if (rs
->frame
== this) {
167 // We always observe the child block. It will never send any
168 // notifications, but we need this so that the observer gets
169 // propagated to its kids.
172 rs
= rs
->parentReflowState
;
177 // We always need to let the percent height observer be propagated
178 // from an outer table frame to an inner table frame.
179 nsIAtom
*fType
= aReflowState
.frame
->GetType();
180 if (fType
== nsGkAtoms::tableFrame
) {
184 // We need the observer to be propagated to all children of the cell
185 // (i.e., children of the child block) in quirks mode, but only to
186 // tables in standards mode.
187 return rs
->frame
== this &&
188 (PresContext()->CompatibilityMode() == eCompatibility_NavQuirks
||
189 fType
== nsGkAtoms::tableOuterFrame
);
193 nsTableCellFrame::GetRowIndex(PRInt32
&aRowIndex
) const
196 nsTableRowFrame
* row
= static_cast<nsTableRowFrame
*>(GetParent());
198 aRowIndex
= row
->GetRowIndex();
203 result
= NS_ERROR_NOT_INITIALIZED
;
209 nsTableCellFrame::GetColIndex(PRInt32
&aColIndex
) const
211 if (GetPrevInFlow()) {
212 return ((nsTableCellFrame
*)GetFirstInFlow())->GetColIndex(aColIndex
);
215 aColIndex
= mColIndex
;
221 nsTableCellFrame::AttributeChanged(PRInt32 aNameSpaceID
,
225 // We need to recalculate in this case because of the nowrap quirk in
226 // BasicTableLayoutStrategy
227 if (aNameSpaceID
== kNameSpaceID_None
&& aAttribute
== nsGkAtoms::nowrap
&&
228 PresContext()->CompatibilityMode() == eCompatibility_NavQuirks
) {
229 PresContext()->PresShell()->
230 FrameNeedsReflow(this, nsIPresShell::eTreeChange
, NS_FRAME_IS_DIRTY
);
232 // let the table frame decide what to do
233 nsTableFrame
* tableFrame
= nsTableFrame::GetTableFrame(this);
235 tableFrame
->AttributeChangedFor(this, mContent
, aAttribute
);
241 nsTableCellFrame::DidSetStyleContext(nsStyleContext
* aOldStyleContext
)
243 if (!aOldStyleContext
) //avoid this on init
246 nsTableFrame
* tableFrame
= nsTableFrame::GetTableFrame(this);
248 if (tableFrame
->IsBorderCollapse() &&
249 tableFrame
->BCRecalcNeeded(aOldStyleContext
, GetStyleContext())) {
250 PRInt32 colIndex
, rowIndex
;
251 GetColIndex(colIndex
);
252 GetRowIndex(rowIndex
);
253 nsRect
damageArea(colIndex
, rowIndex
, GetColSpan(), GetRowSpan());
254 tableFrame
->SetBCDamageArea(damageArea
);
260 nsTableCellFrame::AppendFrames(nsIAtom
* aListName
,
261 nsIFrame
* aFrameList
)
263 NS_PRECONDITION(PR_FALSE
, "unsupported operation");
264 return NS_ERROR_NOT_IMPLEMENTED
;
268 nsTableCellFrame::InsertFrames(nsIAtom
* aListName
,
269 nsIFrame
* aPrevFrame
,
270 nsIFrame
* aFrameList
)
272 NS_PRECONDITION(PR_FALSE
, "unsupported operation");
273 return NS_ERROR_NOT_IMPLEMENTED
;
277 nsTableCellFrame::RemoveFrame(nsIAtom
* aListName
,
280 NS_PRECONDITION(PR_FALSE
, "unsupported operation");
281 return NS_ERROR_NOT_IMPLEMENTED
;
284 void nsTableCellFrame::SetColIndex(PRInt32 aColIndex
)
286 mColIndex
= aColIndex
;
289 /* virtual */ nsMargin
290 nsTableCellFrame::GetUsedMargin() const
292 return nsMargin(0,0,0,0);
295 //ASSURE DIFFERENT COLORS for selection
296 inline nscolor
EnsureDifferentColors(nscolor colorA
, nscolor colorB
)
298 if (colorA
== colorB
)
301 res
= NS_RGB(NS_GET_R(colorA
) ^ 0xff,
302 NS_GET_G(colorA
) ^ 0xff,
303 NS_GET_B(colorA
) ^ 0xff);
310 nsTableCellFrame::DecorateForSelection(nsIRenderingContext
& aRenderingContext
,
313 NS_ASSERTION(GetStateBits() & NS_FRAME_SELECTED_CONTENT
,
314 "Should only be called for selected cells");
315 PRInt16 displaySelection
;
316 nsPresContext
* presContext
= PresContext();
317 displaySelection
= DisplaySelection(presContext
);
318 if (displaySelection
) {
319 nsCOMPtr
<nsFrameSelection
> frameSelection
=
320 presContext
->PresShell()->FrameSelection();
322 if (frameSelection
->GetTableCellSelection()) {
324 if (displaySelection
== nsISelectionController::SELECTION_DISABLED
) {
325 bordercolor
= NS_RGB(176,176,176);// disabled color
328 presContext
->LookAndFeel()->
329 GetColor(nsILookAndFeel::eColor_TextSelectBackground
,
332 nscoord threePx
= nsPresContext::CSSPixelsToAppUnits(3);
333 if ((mRect
.width
> threePx
) && (mRect
.height
> threePx
))
335 //compare bordercolor to ((nsStyleColor *)myColor)->mBackgroundColor)
336 bordercolor
= EnsureDifferentColors(bordercolor
,
337 GetStyleBackground()->mBackgroundColor
);
338 nsIRenderingContext::AutoPushTranslation
339 translate(&aRenderingContext
, aPt
.x
, aPt
.y
);
340 nscoord onePixel
= nsPresContext::CSSPixelsToAppUnits(1);
342 aRenderingContext
.SetColor(bordercolor
);
343 aRenderingContext
.DrawLine(onePixel
, 0, mRect
.width
, 0);
344 aRenderingContext
.DrawLine(0, onePixel
, 0, mRect
.height
);
345 aRenderingContext
.DrawLine(onePixel
, mRect
.height
, mRect
.width
, mRect
.height
);
346 aRenderingContext
.DrawLine(mRect
.width
, onePixel
, mRect
.width
, mRect
.height
);
348 aRenderingContext
.DrawRect(onePixel
, onePixel
, mRect
.width
-onePixel
,
349 mRect
.height
-onePixel
);
351 aRenderingContext
.DrawLine(2*onePixel
, mRect
.height
-2*onePixel
,
352 mRect
.width
-onePixel
, mRect
.height
- (2*onePixel
));
353 aRenderingContext
.DrawLine(mRect
.width
- (2*onePixel
), 2*onePixel
,
354 mRect
.width
- (2*onePixel
), mRect
.height
-onePixel
);
361 nsTableCellFrame::PaintBackground(nsIRenderingContext
& aRenderingContext
,
362 const nsRect
& aDirtyRect
,
365 nsRect
rect(aPt
, GetSize());
366 nsCSSRendering::PaintBackground(PresContext(), aRenderingContext
, this,
367 aDirtyRect
, rect
, PR_TRUE
);
370 // Called by nsTablePainter
372 nsTableCellFrame::PaintCellBackground(nsIRenderingContext
& aRenderingContext
,
373 const nsRect
& aDirtyRect
, nsPoint aPt
)
375 if (!GetStyleVisibility()->IsVisible())
378 PaintBackground(aRenderingContext
, aDirtyRect
, aPt
);
381 class nsDisplayTableCellBackground
: public nsDisplayTableItem
{
383 nsDisplayTableCellBackground(nsTableCellFrame
* aFrame
) : nsDisplayTableItem(aFrame
) {
384 MOZ_COUNT_CTOR(nsDisplayTableCellBackground
);
386 #ifdef NS_BUILD_REFCNT_LOGGING
387 virtual ~nsDisplayTableCellBackground() {
388 MOZ_COUNT_DTOR(nsDisplayTableCellBackground
);
392 virtual nsIFrame
* HitTest(nsDisplayListBuilder
* aBuilder
, nsPoint aPt
,
393 HitTestState
* aState
) { return mFrame
; }
394 virtual void Paint(nsDisplayListBuilder
* aBuilder
, nsIRenderingContext
* aCtx
,
395 const nsRect
& aDirtyRect
);
396 virtual nsRect
GetBounds(nsDisplayListBuilder
* aBuilder
);
398 NS_DISPLAY_DECL_NAME("TableCellBackground")
401 void nsDisplayTableCellBackground::Paint(nsDisplayListBuilder
* aBuilder
,
402 nsIRenderingContext
* aCtx
, const nsRect
& aDirtyRect
)
404 static_cast<nsTableCellFrame
*>(mFrame
)->
405 PaintBackground(*aCtx
, aDirtyRect
, aBuilder
->ToReferenceFrame(mFrame
));
409 nsDisplayTableCellBackground::GetBounds(nsDisplayListBuilder
* aBuilder
)
411 // revert from nsDisplayTableItem's implementation ... cell backgrounds
412 // don't overflow the cell
413 return nsDisplayItem::GetBounds(aBuilder
);
417 PaintTableCellSelection(nsIFrame
* aFrame
, nsIRenderingContext
* aCtx
,
418 const nsRect
& aRect
, nsPoint aPt
)
420 static_cast<nsTableCellFrame
*>(aFrame
)->DecorateForSelection(*aCtx
, aPt
);
424 nsTableCellFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
425 const nsRect
& aDirtyRect
,
426 const nsDisplayListSet
& aLists
)
428 if (!IsVisibleInSelection(aBuilder
))
431 DO_GLOBAL_REFLOW_COUNT_DSP("nsTableCellFrame");
432 nsTableFrame
* tableFrame
= nsTableFrame::GetTableFrame(this);
434 PRInt32 emptyCellStyle
= GetContentEmpty() && !tableFrame
->IsBorderCollapse() ?
435 GetStyleTableBorder()->mEmptyCells
436 : NS_STYLE_TABLE_EMPTY_CELLS_SHOW
;
437 // take account of 'empty-cells'
438 if (GetStyleVisibility()->IsVisible() &&
439 (NS_STYLE_TABLE_EMPTY_CELLS_HIDE
!= emptyCellStyle
)) {
442 PRBool isRoot
= aBuilder
->IsAtRootOfPseudoStackingContext();
444 nsDisplayTableItem
* currentItem
= aBuilder
->GetCurrentTableItem();
445 NS_ASSERTION(currentItem
, "No current table item???");
446 currentItem
->UpdateForFrameBackground(this);
449 if (GetStyleBorder()->mBoxShadow
) {
450 nsDisplayItem
* item
= new (aBuilder
) nsDisplayBoxShadow(this);
451 nsresult rv
= aLists
.BorderBackground()->AppendNewToTop(item
);
452 NS_ENSURE_SUCCESS(rv
, rv
);
455 // display background if we need to.
456 if (aBuilder
->IsForEventDelivery() ||
457 (((!tableFrame
->IsBorderCollapse() || isRoot
) &&
458 (!GetStyleBackground()->IsTransparent() || GetStyleDisplay()->mAppearance
)))) {
459 // The cell background was not painted by the nsTablePainter,
460 // so we need to do it. We have special background processing here
461 // so we need to duplicate some code from nsFrame::DisplayBorderBackgroundOutline
462 nsDisplayTableItem
* item
= new (aBuilder
) nsDisplayTableCellBackground(this);
463 nsresult rv
= aLists
.BorderBackground()->AppendNewToTop(item
);
464 NS_ENSURE_SUCCESS(rv
, rv
);
465 item
->UpdateForFrameBackground(this);
468 // display borders if we need to
469 if (!tableFrame
->IsBorderCollapse() && HasBorder() &&
470 emptyCellStyle
== NS_STYLE_TABLE_EMPTY_CELLS_SHOW
) {
471 nsresult rv
= aLists
.BorderBackground()->AppendNewToTop(new (aBuilder
)
472 nsDisplayBorder(this));
473 NS_ENSURE_SUCCESS(rv
, rv
);
476 // and display the selection border if we need to
478 (GetStateBits() & NS_FRAME_SELECTED_CONTENT
) == NS_FRAME_SELECTED_CONTENT
;
480 nsresult rv
= aLists
.BorderBackground()->AppendNewToTop(new (aBuilder
)
481 nsDisplayGeneric(this, ::PaintTableCellSelection
, "TableCellSelection"));
482 NS_ENSURE_SUCCESS(rv
, rv
);
486 // the 'empty-cells' property has no effect on 'outline'
487 nsresult rv
= DisplayOutline(aBuilder
, aLists
);
488 NS_ENSURE_SUCCESS(rv
, rv
);
490 nsIFrame
* kid
= mFrames
.FirstChild();
491 NS_ASSERTION(kid
&& !kid
->GetNextSibling(), "Table cells should have just one child");
492 // The child's background will go in our BorderBackground() list.
493 // This isn't a problem since it won't have a real background except for
494 // event handling. We do not call BuildDisplayListForNonBlockChildren
495 // because that/ would put the child's background in the Content() list
496 // which isn't right (e.g., would end up on top of our child floats for
498 return BuildDisplayListForChild(aBuilder
, kid
, aDirtyRect
, aLists
);
501 //null range means the whole thing
503 nsTableCellFrame::SetSelected(nsPresContext
* aPresContext
,
509 //traverse through children unselect tables
511 if ((aSpread
== eSpreadDown
)){
512 nsIFrame
* kid
= GetFirstChild(nsnull
);
513 while (nsnull
!= kid
) {
514 kid
->SetSelected(nsnull
, aSelected
, eSpreadDown
);
515 kid
= kid
->GetNextSibling();
518 //return nsFrame::SetSelected(aRange,aSelected,eSpreadNone);
520 // Must call base class to set mSelected state and trigger repaint of frame
521 // Note that in current version, aRange and aSpread are ignored,
522 // only this frame is considered
523 nsFrame::SetSelected(aPresContext
, aRange
, aSelected
, aSpread
, aType
);
525 nsCOMPtr
<nsFrameSelection
> frameSelection
=
526 aPresContext
->PresShell()->FrameSelection();
527 if (frameSelection
->GetTableCellSelection()) {
528 // Selection can affect content, border and outline
529 InvalidateOverflowRect();
535 nsTableCellFrame::GetSkipSides() const
538 if (nsnull
!= GetPrevInFlow()) {
539 skip
|= 1 << NS_SIDE_TOP
;
541 if (nsnull
!= GetNextInFlow()) {
542 skip
|= 1 << NS_SIDE_BOTTOM
;
547 PRBool
nsTableCellFrame::ParentDisablesSelection() const //override default behavior
550 if (NS_FAILED(GetSelected(&returnval
)))
554 return nsFrame::ParentDisablesSelection();
558 nsTableCellFrame::GetSelfOverflow(nsRect
& aOverflowArea
)
560 aOverflowArea
= nsRect(nsPoint(0,0), GetSize());
563 // Align the cell's child frame within the cell
565 void nsTableCellFrame::VerticallyAlignChild(nscoord aMaxAscent
)
567 const nsStyleTextReset
* textStyle
= GetStyleTextReset();
568 /* It's the 'border-collapse' on the table that matters */
569 nsMargin borderPadding
= GetUsedBorderAndPadding();
571 nscoord topInset
= borderPadding
.top
;
572 nscoord bottomInset
= borderPadding
.bottom
;
574 // As per bug 10207, we map 'sub', 'super', 'text-top', 'text-bottom',
575 // length and percentage values to 'baseline'
576 // XXX It seems that we don't get to see length and percentage values here
577 // because the Style System has already fixed the error and mapped them
578 // to whatever is inherited from the parent, i.e, 'middle' in most cases.
579 PRUint8 verticalAlignFlags
= NS_STYLE_VERTICAL_ALIGN_BASELINE
;
580 if (textStyle
->mVerticalAlign
.GetUnit() == eStyleUnit_Enumerated
) {
581 verticalAlignFlags
= textStyle
->mVerticalAlign
.GetIntValue();
582 if (verticalAlignFlags
!= NS_STYLE_VERTICAL_ALIGN_TOP
&&
583 verticalAlignFlags
!= NS_STYLE_VERTICAL_ALIGN_MIDDLE
&&
584 verticalAlignFlags
!= NS_STYLE_VERTICAL_ALIGN_BOTTOM
)
586 verticalAlignFlags
= NS_STYLE_VERTICAL_ALIGN_BASELINE
;
590 nscoord height
= mRect
.height
;
591 nsIFrame
* firstKid
= mFrames
.FirstChild();
592 NS_ASSERTION(firstKid
, "Frame construction error, a table cell always has an inner cell frame");
593 nsRect kidRect
= firstKid
->GetRect();
594 nscoord childHeight
= kidRect
.height
;
596 // Vertically align the child
598 switch (verticalAlignFlags
)
600 case NS_STYLE_VERTICAL_ALIGN_BASELINE
:
601 // Align the baselines of the child frame with the baselines of
602 // other children in the same row which have 'vertical-align: baseline'
603 kidYTop
= topInset
+ aMaxAscent
- GetCellBaseline();
606 case NS_STYLE_VERTICAL_ALIGN_TOP
:
607 // Align the top of the child frame with the top of the content area,
611 case NS_STYLE_VERTICAL_ALIGN_BOTTOM
:
612 // Align the bottom of the child frame with the bottom of the content area,
613 kidYTop
= height
- childHeight
- bottomInset
;
617 case NS_STYLE_VERTICAL_ALIGN_MIDDLE
:
618 // Align the middle of the child frame with the middle of the content area,
619 kidYTop
= (height
- childHeight
- bottomInset
+ topInset
) / 2;
621 // if the content is larger than the cell height align from top
622 kidYTop
= PR_MAX(0, kidYTop
);
624 if (kidYTop
!= kidRect
.y
) {
625 // Invalidate at the old position first
626 firstKid
->InvalidateOverflowRect();
629 firstKid
->SetPosition(nsPoint(kidRect
.x
, kidYTop
));
630 nsHTMLReflowMetrics desiredSize
;
631 desiredSize
.width
= mRect
.width
;
632 desiredSize
.height
= mRect
.height
;
633 GetSelfOverflow(desiredSize
.mOverflowArea
);
634 ConsiderChildOverflow(desiredSize
.mOverflowArea
, firstKid
);
635 FinishAndStoreOverflow(&desiredSize
);
636 if (kidYTop
!= kidRect
.y
) {
637 // Make sure any child views are correctly positioned. We know the inner table
638 // cell won't have a view
639 nsContainerFrame::PositionChildViews(firstKid
);
641 // Invalidate new overflow rect
642 firstKid
->InvalidateOverflowRect();
645 nsContainerFrame::SyncFrameViewAfterReflow(PresContext(), this,
647 &desiredSize
.mOverflowArea
, 0);
651 // As per bug 10207, we map 'sub', 'super', 'text-top', 'text-bottom',
652 // length and percentage values to 'baseline'
653 // XXX It seems that we don't get to see length and percentage values here
654 // because the Style System has already fixed the error and mapped them
655 // to whatever is inherited from the parent, i.e, 'middle' in most cases.
657 nsTableCellFrame::HasVerticalAlignBaseline()
659 const nsStyleTextReset
* textStyle
= GetStyleTextReset();
660 if (textStyle
->mVerticalAlign
.GetUnit() == eStyleUnit_Enumerated
) {
661 PRUint8 verticalAlignFlags
= textStyle
->mVerticalAlign
.GetIntValue();
662 if (verticalAlignFlags
== NS_STYLE_VERTICAL_ALIGN_TOP
||
663 verticalAlignFlags
== NS_STYLE_VERTICAL_ALIGN_MIDDLE
||
664 verticalAlignFlags
== NS_STYLE_VERTICAL_ALIGN_BOTTOM
)
673 nsTableCellFrame::CellHasVisibleContent(nscoord height
,
674 nsTableFrame
* tableFrame
,
677 // see http://www.w3.org/TR/CSS21/tables.html#empty-cells
680 if (tableFrame
->IsBorderCollapse())
682 nsIFrame
* innerFrame
= kidFrame
->GetFirstChild(nsnull
);
684 nsIAtom
* frameType
= innerFrame
->GetType();
685 if (nsGkAtoms::textFrame
== frameType
) {
686 nsTextFrame
* textFrame
= static_cast<nsTextFrame
*>(innerFrame
);
687 if (textFrame
->HasNoncollapsedCharacters())
690 else if (nsGkAtoms::placeholderFrame
!= frameType
) {
694 nsIFrame
*floatFrame
= nsLayoutUtils::GetFloatFromPlaceholder(innerFrame
);
698 innerFrame
= innerFrame
->GetNextSibling();
704 nsTableCellFrame::GetCellBaseline() const
706 // Ignore the position of the inner frame relative to the cell frame
707 // since we want the position as though the inner were top-aligned.
708 nsIFrame
*inner
= mFrames
.FirstChild();
709 nscoord borderPadding
= GetUsedBorderAndPadding().top
;
711 if (nsLayoutUtils::GetFirstLineBaseline(inner
, &result
))
712 return result
+ borderPadding
;
713 return inner
->GetContentRect().YMost() - inner
->GetPosition().y
+
717 PRInt32
nsTableCellFrame::GetRowSpan()
720 nsGenericHTMLElement
*hc
= nsGenericHTMLElement::FromContent(mContent
);
722 // Don't look at the content's rowspan if we're a pseudo cell
723 if (hc
&& !GetStyleContext()->GetPseudoType()) {
724 const nsAttrValue
* attr
= hc
->GetParsedAttr(nsGkAtoms::rowspan
);
725 // Note that we don't need to check the tag name, because only table cells
726 // and table headers parse the "rowspan" attribute into an integer.
727 if (attr
&& attr
->Type() == nsAttrValue::eInteger
) {
728 rowSpan
= attr
->GetIntegerValue();
734 PRInt32
nsTableCellFrame::GetColSpan()
737 nsGenericHTMLElement
*hc
= nsGenericHTMLElement::FromContent(mContent
);
739 // Don't look at the content's colspan if we're a pseudo cell
740 if (hc
&& !GetStyleContext()->GetPseudoType()) {
741 const nsAttrValue
* attr
= hc
->GetParsedAttr(nsGkAtoms::colspan
);
742 // Note that we don't need to check the tag name, because only table cells
743 // and table headers parse the "colspan" attribute into an integer.
744 if (attr
&& attr
->Type() == nsAttrValue::eInteger
) {
745 colSpan
= attr
->GetIntegerValue();
751 /* virtual */ nscoord
752 nsTableCellFrame::GetMinWidth(nsIRenderingContext
*aRenderingContext
)
755 DISPLAY_MIN_WIDTH(this, result
);
757 nsIFrame
*inner
= mFrames
.FirstChild();
758 result
= nsLayoutUtils::IntrinsicForContainer(aRenderingContext
, inner
,
759 nsLayoutUtils::MIN_WIDTH
);
763 /* virtual */ nscoord
764 nsTableCellFrame::GetPrefWidth(nsIRenderingContext
*aRenderingContext
)
767 DISPLAY_PREF_WIDTH(this, result
);
769 nsIFrame
*inner
= mFrames
.FirstChild();
770 result
= nsLayoutUtils::IntrinsicForContainer(aRenderingContext
, inner
,
771 nsLayoutUtils::PREF_WIDTH
);
775 /* virtual */ nsIFrame::IntrinsicWidthOffsetData
776 nsTableCellFrame::IntrinsicWidthOffsets(nsIRenderingContext
* aRenderingContext
)
778 IntrinsicWidthOffsetData result
=
779 nsHTMLContainerFrame::IntrinsicWidthOffsets(aRenderingContext
);
782 result
.hPctMargin
= 0;
785 GetBorderWidth(border
);
786 result
.hBorder
= border
.LeftRight();
792 #define PROBABLY_TOO_LARGE 1000000
794 void DebugCheckChildSize(nsIFrame
* aChild
,
795 nsHTMLReflowMetrics
& aMet
,
798 if ((aMet
.width
< 0) || (aMet
.width
> PROBABLY_TOO_LARGE
)) {
799 printf("WARNING: cell content %p has large width %d \n", aChild
, aMet
.width
);
804 // the computed height for the cell, which descendants use for percent height calculations
805 // it is the height (minus border, padding) of the cell's first in flow during its final
806 // reflow without an unconstrained height.
808 CalcUnpaginagedHeight(nsPresContext
* aPresContext
,
809 nsTableCellFrame
& aCellFrame
,
810 nsTableFrame
& aTableFrame
,
811 nscoord aVerticalBorderPadding
)
813 const nsTableCellFrame
* firstCellInFlow
= (nsTableCellFrame
*)aCellFrame
.GetFirstInFlow();
814 nsTableFrame
* firstTableInFlow
= (nsTableFrame
*)aTableFrame
.GetFirstInFlow();
816 = static_cast<nsTableRowFrame
*>(firstCellInFlow
->GetParent());
817 nsTableRowGroupFrame
* firstRGInFlow
818 = static_cast<nsTableRowGroupFrame
*>(row
->GetParent());
821 firstCellInFlow
->GetRowIndex(rowIndex
);
822 PRInt32 rowSpan
= aTableFrame
.GetEffectiveRowSpan(*firstCellInFlow
);
823 nscoord cellSpacing
= firstTableInFlow
->GetCellSpacingX();
825 nscoord computedHeight
= ((rowSpan
- 1) * cellSpacing
) - aVerticalBorderPadding
;
827 for (row
= firstRGInFlow
->GetFirstRow(), rowX
= 0; row
; row
= row
->GetNextRow(), rowX
++) {
828 if (rowX
> rowIndex
+ rowSpan
- 1) {
831 else if (rowX
>= rowIndex
) {
832 computedHeight
+= row
->GetUnpaginatedHeight(aPresContext
);
835 return computedHeight
;
838 NS_METHOD
nsTableCellFrame::Reflow(nsPresContext
* aPresContext
,
839 nsHTMLReflowMetrics
& aDesiredSize
,
840 const nsHTMLReflowState
& aReflowState
,
841 nsReflowStatus
& aStatus
)
843 DO_GLOBAL_REFLOW_COUNT("nsTableCellFrame");
844 DISPLAY_REFLOW(aPresContext
, this, aReflowState
, aDesiredSize
, aStatus
);
846 if (aReflowState
.mFlags
.mSpecialHeightReflow
) {
847 GetFirstInFlow()->AddStateBits(NS_TABLE_CELL_HAD_SPECIAL_REFLOW
);
850 // work around pixel rounding errors, round down to ensure we don't exceed the avail height in
851 nscoord availHeight
= aReflowState
.availableHeight
;
853 // see if a special height reflow needs to occur due to having a pct height
854 nsTableFrame::CheckRequestSpecialHeightReflow(aReflowState
);
856 aStatus
= NS_FRAME_COMPLETE
;
857 nsSize
availSize(aReflowState
.availableWidth
, availHeight
);
859 /* It's the 'border-collapse' on the table that matters */
860 nsTableFrame
* tableFrame
= nsTableFrame::GetTableFrame(this);
862 ABORT1(NS_ERROR_NULL_POINTER
);
864 nsMargin borderPadding
= aReflowState
.mComputedPadding
;
866 GetBorderWidth(border
);
867 borderPadding
+= border
;
869 nscoord topInset
= borderPadding
.top
;
870 nscoord rightInset
= borderPadding
.right
;
871 nscoord bottomInset
= borderPadding
.bottom
;
872 nscoord leftInset
= borderPadding
.left
;
874 // reduce available space by insets, if we're in a constrained situation
875 availSize
.width
-= leftInset
+rightInset
;
876 if (NS_UNCONSTRAINEDSIZE
!=availSize
.height
)
877 availSize
.height
-= topInset
+bottomInset
;
879 // Try to reflow the child into the available space. It might not
880 // fit or might need continuing.
881 if (availSize
.height
< 0)
882 availSize
.height
= 1;
884 nsHTMLReflowMetrics
kidSize(aDesiredSize
.mFlags
);
885 kidSize
.width
= kidSize
.height
= 0;
886 SetPriorAvailWidth(aReflowState
.availableWidth
);
887 nsIFrame
* firstKid
= mFrames
.FirstChild();
888 NS_ASSERTION(firstKid
, "Frame construction error, a table cell always has an inner cell frame");
890 nscoord computedPaginatedHeight
= 0;
892 if (aReflowState
.mFlags
.mSpecialHeightReflow
) {
893 const_cast<nsHTMLReflowState
&>(aReflowState
).SetComputedHeight(mRect
.height
- topInset
- bottomInset
);
894 DISPLAY_REFLOW_CHANGE();
896 else if (aPresContext
->IsPaginated()) {
897 computedPaginatedHeight
= CalcUnpaginagedHeight(aPresContext
, (nsTableCellFrame
&)*this, *tableFrame
, topInset
+ bottomInset
);
898 if (computedPaginatedHeight
> 0) {
899 const_cast<nsHTMLReflowState
&>(aReflowState
).SetComputedHeight(computedPaginatedHeight
);
900 DISPLAY_REFLOW_CHANGE();
904 SetHasPctOverHeight(PR_FALSE
);
907 nsHTMLReflowState
kidReflowState(aPresContext
, aReflowState
, firstKid
,
910 // Don't be a percent height observer if we're in the middle of
911 // special-height reflow, in case we get an accidental NotifyPercentHeight()
912 // call (which we shouldn't honor during special-height reflow)
913 if (!aReflowState
.mFlags
.mSpecialHeightReflow
) {
914 // mPercentHeightObserver is for children of cells in quirks mode,
915 // but only those than are tables in standards mode. NeedsToObserve
916 // will determine how far this is propagated to descendants.
917 kidReflowState
.mPercentHeightObserver
= this;
919 // Don't propagate special height reflow state to our kids
920 kidReflowState
.mFlags
.mSpecialHeightReflow
= PR_FALSE
;
922 if (aReflowState
.mFlags
.mSpecialHeightReflow
||
923 (GetFirstInFlow()->GetStateBits() & NS_TABLE_CELL_HAD_SPECIAL_REFLOW
)) {
924 // We need to force the kid to have mVResize set if we've had a
925 // special reflow in the past, since the non-special reflow needs to
926 // resize back to what it was without the special height reflow.
927 kidReflowState
.mFlags
.mVResize
= PR_TRUE
;
930 nsPoint
kidOrigin(leftInset
, topInset
);
931 nsRect origRect
= firstKid
->GetRect();
932 nsRect origOverflowRect
= firstKid
->GetOverflowRect();
933 PRBool firstReflow
= (firstKid
->GetStateBits() & NS_FRAME_FIRST_REFLOW
) != 0;
935 ReflowChild(firstKid
, aPresContext
, kidSize
, kidReflowState
,
936 kidOrigin
.x
, kidOrigin
.y
, NS_FRAME_INVALIDATE_ON_MOVE
, aStatus
);
937 if (NS_FRAME_OVERFLOW_IS_INCOMPLETE(aStatus
)) {
938 // Don't pass OVERFLOW_INCOMPLETE through tables until they can actually handle it
939 //XXX should paginate overflow as overflow, but not in this patch (bug 379349)
940 NS_FRAME_SET_INCOMPLETE(aStatus
);
941 printf("Set table cell incomplete %p\n", this);
944 // XXXbz is this invalidate actually needed, really?
945 if (GetStateBits() & NS_FRAME_IS_DIRTY
) {
946 InvalidateOverflowRect();
950 DebugCheckChildSize(firstKid
, kidSize
, availSize
);
953 // 0 dimensioned cells need to be treated specially in Standard/NavQuirks mode
954 // see testcase "emptyCells.html"
955 nsIFrame
* prevInFlow
= GetPrevInFlow();
958 isEmpty
= static_cast<nsTableCellFrame
*>(prevInFlow
)->GetContentEmpty();
960 isEmpty
= !CellHasVisibleContent(kidSize
.height
, tableFrame
, firstKid
);
962 SetContentEmpty(isEmpty
);
965 FinishReflowChild(firstKid
, aPresContext
, &kidReflowState
, kidSize
,
966 kidOrigin
.x
, kidOrigin
.y
, 0);
968 nsTableFrame::InvalidateFrame(firstKid
, origRect
, origOverflowRect
,
971 // first, compute the height which can be set w/o being restricted by aMaxSize.height
972 nscoord cellHeight
= kidSize
.height
;
974 if (NS_UNCONSTRAINEDSIZE
!= cellHeight
) {
975 cellHeight
+= topInset
+ bottomInset
;
978 // next determine the cell's width
979 nscoord cellWidth
= kidSize
.width
; // at this point, we've factored in the cell's style attributes
981 // factor in border and padding
982 if (NS_UNCONSTRAINEDSIZE
!= cellWidth
) {
983 cellWidth
+= leftInset
+ rightInset
;
986 // set the cell's desired size and max element size
987 aDesiredSize
.width
= cellWidth
;
988 aDesiredSize
.height
= cellHeight
;
990 // the overflow area will be computed when the child will be vertically aligned
992 if (aReflowState
.mFlags
.mSpecialHeightReflow
) {
993 if (aDesiredSize
.height
> mRect
.height
) {
994 // set a bit indicating that the pct height contents exceeded
995 // the height that they could honor in the pass 2 reflow
996 SetHasPctOverHeight(PR_TRUE
);
998 if (NS_UNCONSTRAINEDSIZE
== aReflowState
.availableHeight
) {
999 aDesiredSize
.height
= mRect
.height
;
1003 // If our parent is in initial reflow, it'll handle invalidating our
1004 // entire overflow rect.
1005 if (!(GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW
)) {
1006 CheckInvalidateSizeChange(aDesiredSize
);
1009 // remember the desired size for this reflow
1010 SetDesiredSize(aDesiredSize
);
1012 NS_FRAME_SET_TRUNCATION(aStatus
, aReflowState
, aDesiredSize
);
1016 /* ----- global methods ----- */
1018 NS_IMPL_ADDREF_INHERITED(nsTableCellFrame
, nsHTMLContainerFrame
)
1019 NS_IMPL_RELEASE_INHERITED(nsTableCellFrame
, nsHTMLContainerFrame
)
1022 nsTableCellFrame::QueryInterface(const nsIID
& aIID
, void** aInstancePtr
)
1024 NS_PRECONDITION(aInstancePtr
, "null out param");
1026 if (aIID
.Equals(NS_GET_IID(nsITableCellLayout
))) {
1027 *aInstancePtr
= static_cast<nsITableCellLayout
*>(this);
1030 if (aIID
.Equals(NS_GET_IID(nsIPercentHeightObserver
))) {
1031 *aInstancePtr
= static_cast<nsIPercentHeightObserver
*>(this);
1035 return nsHTMLContainerFrame::QueryInterface(aIID
, aInstancePtr
);
1038 #ifdef ACCESSIBILITY
1039 NS_IMETHODIMP
nsTableCellFrame::GetAccessible(nsIAccessible
** aAccessible
)
1041 nsCOMPtr
<nsIAccessibilityService
> accService
= do_GetService("@mozilla.org/accessibilityService;1");
1044 return accService
->CreateHTMLTableCellAccessible(static_cast<nsIFrame
*>(this), aAccessible
);
1047 return NS_ERROR_FAILURE
;
1051 /* This is primarily for editor access via nsITableLayout */
1053 nsTableCellFrame::GetCellIndexes(PRInt32
&aRowIndex
, PRInt32
&aColIndex
)
1055 nsresult res
= GetRowIndex(aRowIndex
);
1061 aColIndex
= mColIndex
;
1066 NS_NewTableCellFrame(nsIPresShell
* aPresShell
,
1067 nsStyleContext
* aContext
,
1068 PRBool aIsBorderCollapse
)
1070 if (aIsBorderCollapse
)
1071 return new (aPresShell
) nsBCTableCellFrame(aContext
);
1073 return new (aPresShell
) nsTableCellFrame(aContext
);
1077 nsTableCellFrame::GetBorderWidth(nsMargin
& aBorder
) const
1079 aBorder
= GetStyleBorder()->GetActualBorder();
1084 nsTableCellFrame::GetType() const
1086 return nsGkAtoms::tableCellFrame
;
1089 /* virtual */ PRBool
1090 nsTableCellFrame::IsContainingBlock() const
1097 nsTableCellFrame::GetFrameName(nsAString
& aResult
) const
1099 return MakeFrameName(NS_LITERAL_STRING("TableCell"), aResult
);
1103 // nsBCTableCellFrame
1105 nsBCTableCellFrame::nsBCTableCellFrame(nsStyleContext
* aContext
)
1106 :nsTableCellFrame(aContext
)
1108 mTopBorder
= mRightBorder
= mBottomBorder
= mLeftBorder
= 0;
1111 nsBCTableCellFrame::~nsBCTableCellFrame()
1116 nsBCTableCellFrame::GetType() const
1118 return nsGkAtoms::bcTableCellFrame
;
1121 /* virtual */ nsMargin
1122 nsBCTableCellFrame::GetUsedBorder() const
1125 GetBorderWidth(result
);
1131 nsBCTableCellFrame::GetFrameName(nsAString
& aResult
) const
1133 return MakeFrameName(NS_LITERAL_STRING("BCTableCell"), aResult
);
1138 nsBCTableCellFrame::GetBorderWidth(nsMargin
& aBorder
) const
1140 PRInt32 aPixelsToTwips
= nsPresContext::AppUnitsPerCSSPixel();
1141 aBorder
.top
= BC_BORDER_BOTTOM_HALF_COORD(aPixelsToTwips
, mTopBorder
);
1142 aBorder
.right
= BC_BORDER_LEFT_HALF_COORD(aPixelsToTwips
, mRightBorder
);
1143 aBorder
.bottom
= BC_BORDER_TOP_HALF_COORD(aPixelsToTwips
, mBottomBorder
);
1144 aBorder
.left
= BC_BORDER_RIGHT_HALF_COORD(aPixelsToTwips
, mLeftBorder
);
1149 nsBCTableCellFrame::GetBorderWidth(PRUint8 aSide
) const
1153 return BC_BORDER_BOTTOM_HALF(mTopBorder
);
1155 return BC_BORDER_LEFT_HALF(mRightBorder
);
1156 case NS_SIDE_BOTTOM
:
1157 return BC_BORDER_TOP_HALF(mBottomBorder
);
1159 return BC_BORDER_RIGHT_HALF(mLeftBorder
);
1164 nsBCTableCellFrame::SetBorderWidth(PRUint8 aSide
,
1169 mTopBorder
= aValue
;
1172 mRightBorder
= aValue
;
1174 case NS_SIDE_BOTTOM
:
1175 mBottomBorder
= aValue
;
1178 mLeftBorder
= aValue
;
1183 nsBCTableCellFrame::GetSelfOverflow(nsRect
& aOverflowArea
)
1185 nsMargin halfBorder
;
1186 PRInt32 p2t
= nsPresContext::AppUnitsPerCSSPixel();
1187 halfBorder
.top
= BC_BORDER_TOP_HALF_COORD(p2t
, mTopBorder
);
1188 halfBorder
.right
= BC_BORDER_RIGHT_HALF_COORD(p2t
, mRightBorder
);
1189 halfBorder
.bottom
= BC_BORDER_BOTTOM_HALF_COORD(p2t
, mBottomBorder
);
1190 halfBorder
.left
= BC_BORDER_LEFT_HALF_COORD(p2t
, mLeftBorder
);
1192 nsRect
overflow(nsPoint(0,0), GetSize());
1193 overflow
.Inflate(halfBorder
);
1194 aOverflowArea
= overflow
;
1199 nsBCTableCellFrame::PaintBackground(nsIRenderingContext
& aRenderingContext
,
1200 const nsRect
& aDirtyRect
,
1203 // make border-width reflect the half of the border-collapse
1204 // assigned border that's inside the cell
1205 nsMargin borderWidth
;
1206 GetBorderWidth(borderWidth
);
1208 nsStyleBorder
myBorder(*GetStyleBorder());
1210 NS_FOR_CSS_SIDES(side
) {
1211 myBorder
.SetBorderWidth(side
, borderWidth
.side(side
));
1214 nsRect
rect(aPt
, GetSize());
1215 // bypassing nsCSSRendering::PaintBackground is safe because this kind
1216 // of frame cannot be used for the root element
1217 nsCSSRendering::PaintBackgroundWithSC(PresContext(), aRenderingContext
, this,
1219 *GetStyleBackground(), myBorder
,