Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / layout / tables / nsTableCellFrame.cpp
blob5c2d660ec77910587aba6c9847c1bbdd9e94d363
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 if (IS_TABLE_CELL(childFrame->GetType())) {
91 return (nsTableCellFrame*)childFrame;
93 childFrame = childFrame->GetNextSibling();
95 return nsnull;
98 NS_IMETHODIMP
99 nsTableCellFrame::Init(nsIContent* aContent,
100 nsIFrame* aParent,
101 nsIFrame* aPrevInFlow)
103 // Let the base class do its initialization
104 nsresult rv = nsHTMLContainerFrame::Init(aContent, aParent, aPrevInFlow);
106 if (aPrevInFlow) {
107 // Set the column index
108 nsTableCellFrame* cellFrame = (nsTableCellFrame*)aPrevInFlow;
109 PRInt32 colIndex;
110 cellFrame->GetColIndex(colIndex);
111 SetColIndex(colIndex);
114 return rv;
117 // nsIPercentHeightObserver methods
119 void
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
125 // outer table.
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;
149 rs != cellRS;
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
160 PRBool
161 nsTableCellFrame::NeedsToObserve(const nsHTMLReflowState& aReflowState)
163 const nsHTMLReflowState *rs = aReflowState.parentReflowState;
164 if (!rs)
165 return PR_FALSE;
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.
170 return PR_TRUE;
172 rs = rs->parentReflowState;
173 if (!rs) {
174 return PR_FALSE;
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) {
181 return PR_TRUE;
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);
192 nsresult
193 nsTableCellFrame::GetRowIndex(PRInt32 &aRowIndex) const
195 nsresult result;
196 nsTableRowFrame* row = static_cast<nsTableRowFrame*>(GetParent());
197 if (row) {
198 aRowIndex = row->GetRowIndex();
199 result = NS_OK;
201 else {
202 aRowIndex = 0;
203 result = NS_ERROR_NOT_INITIALIZED;
205 return result;
208 nsresult
209 nsTableCellFrame::GetColIndex(PRInt32 &aColIndex) const
211 if (GetPrevInFlow()) {
212 return ((nsTableCellFrame*)GetFirstInFlow())->GetColIndex(aColIndex);
214 else {
215 aColIndex = mColIndex;
216 return NS_OK;
220 NS_IMETHODIMP
221 nsTableCellFrame::AttributeChanged(PRInt32 aNameSpaceID,
222 nsIAtom* aAttribute,
223 PRInt32 aModType)
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);
234 if (tableFrame) {
235 tableFrame->AttributeChangedFor(this, mContent, aAttribute);
237 return NS_OK;
240 /* virtual */ void
241 nsTableCellFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
243 if (!aOldStyleContext) //avoid this on init
244 return;
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);
259 NS_IMETHODIMP
260 nsTableCellFrame::AppendFrames(nsIAtom* aListName,
261 nsIFrame* aFrameList)
263 NS_PRECONDITION(PR_FALSE, "unsupported operation");
264 return NS_ERROR_NOT_IMPLEMENTED;
267 NS_IMETHODIMP
268 nsTableCellFrame::InsertFrames(nsIAtom* aListName,
269 nsIFrame* aPrevFrame,
270 nsIFrame* aFrameList)
272 NS_PRECONDITION(PR_FALSE, "unsupported operation");
273 return NS_ERROR_NOT_IMPLEMENTED;
276 NS_IMETHODIMP
277 nsTableCellFrame::RemoveFrame(nsIAtom* aListName,
278 nsIFrame* aOldFrame)
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)
300 nscolor res;
301 res = NS_RGB(NS_GET_R(colorA) ^ 0xff,
302 NS_GET_G(colorA) ^ 0xff,
303 NS_GET_B(colorA) ^ 0xff);
304 return res;
306 return colorA;
309 void
310 nsTableCellFrame::DecorateForSelection(nsIRenderingContext& aRenderingContext,
311 nsPoint aPt)
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()) {
323 nscolor bordercolor;
324 if (displaySelection == nsISelectionController::SELECTION_DISABLED) {
325 bordercolor = NS_RGB(176,176,176);// disabled color
327 else {
328 presContext->LookAndFeel()->
329 GetColor(nsILookAndFeel::eColor_TextSelectBackground,
330 bordercolor);
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);
347 //middle
348 aRenderingContext.DrawRect(onePixel, onePixel, mRect.width-onePixel,
349 mRect.height-onePixel);
350 //shading
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);
360 void
361 nsTableCellFrame::PaintBackground(nsIRenderingContext& aRenderingContext,
362 const nsRect& aDirtyRect,
363 nsPoint aPt)
365 nsRect rect(aPt, GetSize());
366 nsCSSRendering::PaintBackground(PresContext(), aRenderingContext, this,
367 aDirtyRect, rect, PR_TRUE);
370 // Called by nsTablePainter
371 void
372 nsTableCellFrame::PaintCellBackground(nsIRenderingContext& aRenderingContext,
373 const nsRect& aDirtyRect, nsPoint aPt)
375 if (!GetStyleVisibility()->IsVisible())
376 return;
378 PaintBackground(aRenderingContext, aDirtyRect, aPt);
381 class nsDisplayTableCellBackground : public nsDisplayTableItem {
382 public:
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);
390 #endif
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));
408 nsRect
409 nsDisplayTableCellBackground::GetBounds(nsDisplayListBuilder* aBuilder)
411 // revert from nsDisplayTableItem's implementation ... cell backgrounds
412 // don't overflow the cell
413 return nsDisplayItem::GetBounds(aBuilder);
416 static void
417 PaintTableCellSelection(nsIFrame* aFrame, nsIRenderingContext* aCtx,
418 const nsRect& aRect, nsPoint aPt)
420 static_cast<nsTableCellFrame*>(aFrame)->DecorateForSelection(*aCtx, aPt);
423 NS_IMETHODIMP
424 nsTableCellFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
425 const nsRect& aDirtyRect,
426 const nsDisplayListSet& aLists)
428 if (!IsVisibleInSelection(aBuilder))
429 return NS_OK;
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();
443 if (!isRoot) {
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
477 PRBool isSelected =
478 (GetStateBits() & NS_FRAME_SELECTED_CONTENT) == NS_FRAME_SELECTED_CONTENT;
479 if (isSelected) {
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
497 // event handling).
498 return BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists);
501 //null range means the whole thing
502 NS_IMETHODIMP
503 nsTableCellFrame::SetSelected(nsPresContext* aPresContext,
504 nsIDOMRange* aRange,
505 PRBool aSelected,
506 nsSpread aSpread,
507 SelectionType aType)
509 //traverse through children unselect tables
510 #if 0
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);
519 #endif
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();
531 return NS_OK;
534 PRIntn
535 nsTableCellFrame::GetSkipSides() const
537 PRIntn skip = 0;
538 if (nsnull != GetPrevInFlow()) {
539 skip |= 1 << NS_SIDE_TOP;
541 if (nsnull != GetNextInFlow()) {
542 skip |= 1 << NS_SIDE_BOTTOM;
544 return skip;
547 PRBool nsTableCellFrame::ParentDisablesSelection() const //override default behavior
549 PRBool returnval;
550 if (NS_FAILED(GetSelected(&returnval)))
551 return PR_FALSE;
552 if (returnval)
553 return PR_TRUE;
554 return nsFrame::ParentDisablesSelection();
557 /* virtual */ void
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
597 nscoord kidYTop = 0;
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();
604 break;
606 case NS_STYLE_VERTICAL_ALIGN_TOP:
607 // Align the top of the child frame with the top of the content area,
608 kidYTop = topInset;
609 break;
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;
614 break;
616 default:
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();
644 if (HasView()) {
645 nsContainerFrame::SyncFrameViewAfterReflow(PresContext(), this,
646 GetView(),
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.
656 PRBool
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)
666 return PR_FALSE;
669 return PR_TRUE;
672 PRBool
673 nsTableCellFrame::CellHasVisibleContent(nscoord height,
674 nsTableFrame* tableFrame,
675 nsIFrame* kidFrame)
677 // see http://www.w3.org/TR/CSS21/tables.html#empty-cells
678 if (height > 0)
679 return PR_TRUE;
680 if (tableFrame->IsBorderCollapse())
681 return PR_TRUE;
682 nsIFrame* innerFrame = kidFrame->GetFirstChild(nsnull);
683 while(innerFrame) {
684 nsIAtom* frameType = innerFrame->GetType();
685 if (nsGkAtoms::textFrame == frameType) {
686 nsTextFrame* textFrame = static_cast<nsTextFrame*>(innerFrame);
687 if (textFrame->HasNoncollapsedCharacters())
688 return PR_TRUE;
690 else if (nsGkAtoms::placeholderFrame != frameType) {
691 return PR_TRUE;
693 else {
694 nsIFrame *floatFrame = nsLayoutUtils::GetFloatFromPlaceholder(innerFrame);
695 if (floatFrame)
696 return PR_TRUE;
698 innerFrame = innerFrame->GetNextSibling();
700 return PR_FALSE;
703 nscoord
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;
710 nscoord result;
711 if (nsLayoutUtils::GetFirstLineBaseline(inner, &result))
712 return result + borderPadding;
713 return inner->GetContentRect().YMost() - inner->GetPosition().y +
714 borderPadding;
717 PRInt32 nsTableCellFrame::GetRowSpan()
719 PRInt32 rowSpan=1;
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();
731 return rowSpan;
734 PRInt32 nsTableCellFrame::GetColSpan()
736 PRInt32 colSpan=1;
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();
748 return colSpan;
751 /* virtual */ nscoord
752 nsTableCellFrame::GetMinWidth(nsIRenderingContext *aRenderingContext)
754 nscoord result = 0;
755 DISPLAY_MIN_WIDTH(this, result);
757 nsIFrame *inner = mFrames.FirstChild();
758 result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, inner,
759 nsLayoutUtils::MIN_WIDTH);
760 return result;
763 /* virtual */ nscoord
764 nsTableCellFrame::GetPrefWidth(nsIRenderingContext *aRenderingContext)
766 nscoord result = 0;
767 DISPLAY_PREF_WIDTH(this, result);
769 nsIFrame *inner = mFrames.FirstChild();
770 result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, inner,
771 nsLayoutUtils::PREF_WIDTH);
772 return result;
775 /* virtual */ nsIFrame::IntrinsicWidthOffsetData
776 nsTableCellFrame::IntrinsicWidthOffsets(nsIRenderingContext* aRenderingContext)
778 IntrinsicWidthOffsetData result =
779 nsHTMLContainerFrame::IntrinsicWidthOffsets(aRenderingContext);
781 result.hMargin = 0;
782 result.hPctMargin = 0;
784 nsMargin border;
785 GetBorderWidth(border);
786 result.hBorder = border.LeftRight();
788 return result;
791 #ifdef DEBUG
792 #define PROBABLY_TOO_LARGE 1000000
793 static
794 void DebugCheckChildSize(nsIFrame* aChild,
795 nsHTMLReflowMetrics& aMet,
796 nsSize& aAvailSize)
798 if ((aMet.width < 0) || (aMet.width > PROBABLY_TOO_LARGE)) {
799 printf("WARNING: cell content %p has large width %d \n", aChild, aMet.width);
802 #endif
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.
807 static nscoord
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();
815 nsTableRowFrame* row
816 = static_cast<nsTableRowFrame*>(firstCellInFlow->GetParent());
817 nsTableRowGroupFrame* firstRGInFlow
818 = static_cast<nsTableRowGroupFrame*>(row->GetParent());
820 PRInt32 rowIndex;
821 firstCellInFlow->GetRowIndex(rowIndex);
822 PRInt32 rowSpan = aTableFrame.GetEffectiveRowSpan(*firstCellInFlow);
823 nscoord cellSpacing = firstTableInFlow->GetCellSpacingX();
825 nscoord computedHeight = ((rowSpan - 1) * cellSpacing) - aVerticalBorderPadding;
826 PRInt32 rowX;
827 for (row = firstRGInFlow->GetFirstRow(), rowX = 0; row; row = row->GetNextRow(), rowX++) {
828 if (rowX > rowIndex + rowSpan - 1) {
829 break;
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);
861 if (!tableFrame)
862 ABORT1(NS_ERROR_NULL_POINTER);
864 nsMargin borderPadding = aReflowState.mComputedPadding;
865 nsMargin border;
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();
903 else {
904 SetHasPctOverHeight(PR_FALSE);
907 nsHTMLReflowState kidReflowState(aPresContext, aReflowState, firstKid,
908 availSize);
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();
949 #ifdef NS_DEBUG
950 DebugCheckChildSize(firstKid, kidSize, availSize);
951 #endif
953 // 0 dimensioned cells need to be treated specially in Standard/NavQuirks mode
954 // see testcase "emptyCells.html"
955 nsIFrame* prevInFlow = GetPrevInFlow();
956 PRBool isEmpty;
957 if (prevInFlow) {
958 isEmpty = static_cast<nsTableCellFrame*>(prevInFlow)->GetContentEmpty();
959 } else {
960 isEmpty = !CellHasVisibleContent(kidSize.height, tableFrame, firstKid);
962 SetContentEmpty(isEmpty);
964 // Place the child
965 FinishReflowChild(firstKid, aPresContext, &kidReflowState, kidSize,
966 kidOrigin.x, kidOrigin.y, 0);
968 nsTableFrame::InvalidateFrame(firstKid, origRect, origOverflowRect,
969 firstReflow);
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);
1013 return NS_OK;
1016 /* ----- global methods ----- */
1018 NS_IMPL_ADDREF_INHERITED(nsTableCellFrame, nsHTMLContainerFrame)
1019 NS_IMPL_RELEASE_INHERITED(nsTableCellFrame, nsHTMLContainerFrame)
1021 NS_IMETHODIMP
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);
1028 return NS_OK;
1030 if (aIID.Equals(NS_GET_IID(nsIPercentHeightObserver))) {
1031 *aInstancePtr = static_cast<nsIPercentHeightObserver*>(this);
1032 return NS_OK;
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");
1043 if (accService) {
1044 return accService->CreateHTMLTableCellAccessible(static_cast<nsIFrame*>(this), aAccessible);
1047 return NS_ERROR_FAILURE;
1049 #endif
1051 /* This is primarily for editor access via nsITableLayout */
1052 NS_IMETHODIMP
1053 nsTableCellFrame::GetCellIndexes(PRInt32 &aRowIndex, PRInt32 &aColIndex)
1055 nsresult res = GetRowIndex(aRowIndex);
1056 if (NS_FAILED(res))
1058 aColIndex = 0;
1059 return res;
1061 aColIndex = mColIndex;
1062 return NS_OK;
1065 nsIFrame*
1066 NS_NewTableCellFrame(nsIPresShell* aPresShell,
1067 nsStyleContext* aContext,
1068 PRBool aIsBorderCollapse)
1070 if (aIsBorderCollapse)
1071 return new (aPresShell) nsBCTableCellFrame(aContext);
1072 else
1073 return new (aPresShell) nsTableCellFrame(aContext);
1076 nsMargin*
1077 nsTableCellFrame::GetBorderWidth(nsMargin& aBorder) const
1079 aBorder = GetStyleBorder()->GetActualBorder();
1080 return &aBorder;
1083 nsIAtom*
1084 nsTableCellFrame::GetType() const
1086 return nsGkAtoms::tableCellFrame;
1089 /* virtual */ PRBool
1090 nsTableCellFrame::IsContainingBlock() const
1092 return PR_TRUE;
1095 #ifdef DEBUG
1096 NS_IMETHODIMP
1097 nsTableCellFrame::GetFrameName(nsAString& aResult) const
1099 return MakeFrameName(NS_LITERAL_STRING("TableCell"), aResult);
1101 #endif
1103 // nsBCTableCellFrame
1105 nsBCTableCellFrame::nsBCTableCellFrame(nsStyleContext* aContext)
1106 :nsTableCellFrame(aContext)
1108 mTopBorder = mRightBorder = mBottomBorder = mLeftBorder = 0;
1111 nsBCTableCellFrame::~nsBCTableCellFrame()
1115 nsIAtom*
1116 nsBCTableCellFrame::GetType() const
1118 return nsGkAtoms::bcTableCellFrame;
1121 /* virtual */ nsMargin
1122 nsBCTableCellFrame::GetUsedBorder() const
1124 nsMargin result;
1125 GetBorderWidth(result);
1126 return result;
1129 #ifdef DEBUG
1130 NS_IMETHODIMP
1131 nsBCTableCellFrame::GetFrameName(nsAString& aResult) const
1133 return MakeFrameName(NS_LITERAL_STRING("BCTableCell"), aResult);
1135 #endif
1137 nsMargin*
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);
1145 return &aBorder;
1148 BCPixelSize
1149 nsBCTableCellFrame::GetBorderWidth(PRUint8 aSide) const
1151 switch(aSide) {
1152 case NS_SIDE_TOP:
1153 return BC_BORDER_BOTTOM_HALF(mTopBorder);
1154 case NS_SIDE_RIGHT:
1155 return BC_BORDER_LEFT_HALF(mRightBorder);
1156 case NS_SIDE_BOTTOM:
1157 return BC_BORDER_TOP_HALF(mBottomBorder);
1158 default:
1159 return BC_BORDER_RIGHT_HALF(mLeftBorder);
1163 void
1164 nsBCTableCellFrame::SetBorderWidth(PRUint8 aSide,
1165 BCPixelSize aValue)
1167 switch(aSide) {
1168 case NS_SIDE_TOP:
1169 mTopBorder = aValue;
1170 break;
1171 case NS_SIDE_RIGHT:
1172 mRightBorder = aValue;
1173 break;
1174 case NS_SIDE_BOTTOM:
1175 mBottomBorder = aValue;
1176 break;
1177 default:
1178 mLeftBorder = aValue;
1182 /* virtual */ void
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;
1198 void
1199 nsBCTableCellFrame::PaintBackground(nsIRenderingContext& aRenderingContext,
1200 const nsRect& aDirtyRect,
1201 nsPoint aPt)
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,
1218 aDirtyRect, rect,
1219 *GetStyleBackground(), myBorder,
1220 PR_TRUE, nsnull);