Thu, 15 Jan 2015 21:03:48 +0100
Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "nsTableFrame.h"
6 #include "nsTableColFrame.h"
7 #include "nsTableCellFrame.h"
8 #include "nsTableRowFrame.h"
9 #include "nsTableRowGroupFrame.h"
10 #include "nsTablePainter.h"
11 #include "nsStyleContext.h"
12 #include "nsStyleConsts.h"
13 #include "nsPresContext.h"
14 #include "nsRenderingContext.h"
15 #include "nsCSSRendering.h"
16 #include "nsIContent.h"
17 #include "nsGenericHTMLElement.h"
18 #include "nsAttrValueInlines.h"
19 #include "nsHTMLParts.h"
20 #include "nsGkAtoms.h"
21 #include "nsIPresShell.h"
22 #include "nsCOMPtr.h"
23 #include "nsIServiceManager.h"
24 #include "nsIDOMNode.h"
25 #include "nsNameSpaceManager.h"
26 #include "nsDisplayList.h"
27 #include "nsLayoutUtils.h"
28 #include "nsTextFrame.h"
29 #include "FrameLayerBuilder.h"
30 #include <algorithm>
32 //TABLECELL SELECTION
33 #include "nsFrameSelection.h"
34 #include "mozilla/LookAndFeel.h"
36 using namespace mozilla;
39 nsTableCellFrame::nsTableCellFrame(nsStyleContext* aContext) :
40 nsContainerFrame(aContext)
41 {
42 mColIndex = 0;
43 mPriorAvailWidth = 0;
45 SetContentEmpty(false);
46 SetHasPctOverHeight(false);
47 }
49 nsTableCellFrame::~nsTableCellFrame()
50 {
51 }
53 NS_IMPL_FRAMEARENA_HELPERS(nsTableCellFrame)
55 nsTableCellFrame*
56 nsTableCellFrame::GetNextCell() const
57 {
58 nsIFrame* childFrame = GetNextSibling();
59 while (childFrame) {
60 nsTableCellFrame *cellFrame = do_QueryFrame(childFrame);
61 if (cellFrame) {
62 return cellFrame;
63 }
64 childFrame = childFrame->GetNextSibling();
65 }
66 return nullptr;
67 }
69 void
70 nsTableCellFrame::Init(nsIContent* aContent,
71 nsIFrame* aParent,
72 nsIFrame* aPrevInFlow)
73 {
74 // Let the base class do its initialization
75 nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
77 if (GetStateBits() & NS_FRAME_FONT_INFLATION_CONTAINER) {
78 AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT);
79 }
81 if (aPrevInFlow) {
82 // Set the column index
83 nsTableCellFrame* cellFrame = (nsTableCellFrame*)aPrevInFlow;
84 int32_t colIndex;
85 cellFrame->GetColIndex(colIndex);
86 SetColIndex(colIndex);
87 }
88 }
90 void
91 nsTableCellFrame::DestroyFrom(nsIFrame* aDestructRoot)
92 {
93 if (GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN) {
94 nsTableFrame::UnregisterPositionedTablePart(this, aDestructRoot);
95 }
97 nsContainerFrame::DestroyFrom(aDestructRoot);
98 }
100 // nsIPercentHeightObserver methods
102 void
103 nsTableCellFrame::NotifyPercentHeight(const nsHTMLReflowState& aReflowState)
104 {
105 // nsHTMLReflowState ensures the mCBReflowState of blocks inside a
106 // cell is the cell frame, not the inner-cell block, and that the
107 // containing block of an inner table is the containing block of its
108 // outer table.
109 // XXXldb Given the now-stricter |NeedsToObserve|, many if not all of
110 // these tests are probably unnecessary.
112 // Maybe the cell reflow state; we sure if we're inside the |if|.
113 const nsHTMLReflowState *cellRS = aReflowState.mCBReflowState;
115 if (cellRS && cellRS->frame == this &&
116 (cellRS->ComputedHeight() == NS_UNCONSTRAINEDSIZE ||
117 cellRS->ComputedHeight() == 0)) { // XXXldb Why 0?
118 // This is a percentage height on a frame whose percentage heights
119 // are based on the height of the cell, since its containing block
120 // is the inner cell frame.
122 // We'll only honor the percent height if sibling-cells/ancestors
123 // have specified/pct height. (Also, siblings only count for this if
124 // both this cell and the sibling cell span exactly 1 row.)
126 if (nsTableFrame::AncestorsHaveStyleHeight(*cellRS) ||
127 (nsTableFrame::GetTableFrame(this)->GetEffectiveRowSpan(*this) == 1 &&
128 (cellRS->parentReflowState->frame->GetStateBits() &
129 NS_ROW_HAS_CELL_WITH_STYLE_HEIGHT))) {
131 for (const nsHTMLReflowState *rs = aReflowState.parentReflowState;
132 rs != cellRS;
133 rs = rs->parentReflowState) {
134 rs->frame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT);
135 }
137 nsTableFrame::RequestSpecialHeightReflow(*cellRS);
138 }
139 }
140 }
142 // The cell needs to observe its block and things inside its block but nothing below that
143 bool
144 nsTableCellFrame::NeedsToObserve(const nsHTMLReflowState& aReflowState)
145 {
146 const nsHTMLReflowState *rs = aReflowState.parentReflowState;
147 if (!rs)
148 return false;
149 if (rs->frame == this) {
150 // We always observe the child block. It will never send any
151 // notifications, but we need this so that the observer gets
152 // propagated to its kids.
153 return true;
154 }
155 rs = rs->parentReflowState;
156 if (!rs) {
157 return false;
158 }
160 // We always need to let the percent height observer be propagated
161 // from an outer table frame to an inner table frame.
162 nsIAtom *fType = aReflowState.frame->GetType();
163 if (fType == nsGkAtoms::tableFrame) {
164 return true;
165 }
167 // We need the observer to be propagated to all children of the cell
168 // (i.e., children of the child block) in quirks mode, but only to
169 // tables in standards mode.
170 return rs->frame == this &&
171 (PresContext()->CompatibilityMode() == eCompatibility_NavQuirks ||
172 fType == nsGkAtoms::tableOuterFrame);
173 }
175 nsresult
176 nsTableCellFrame::GetRowIndex(int32_t &aRowIndex) const
177 {
178 nsresult result;
179 nsTableRowFrame* row = static_cast<nsTableRowFrame*>(GetParent());
180 if (row) {
181 aRowIndex = row->GetRowIndex();
182 result = NS_OK;
183 }
184 else {
185 aRowIndex = 0;
186 result = NS_ERROR_NOT_INITIALIZED;
187 }
188 return result;
189 }
191 nsresult
192 nsTableCellFrame::GetColIndex(int32_t &aColIndex) const
193 {
194 if (GetPrevInFlow()) {
195 return static_cast<nsTableCellFrame*>(FirstInFlow())->GetColIndex(aColIndex);
196 }
197 else {
198 aColIndex = mColIndex;
199 return NS_OK;
200 }
201 }
203 nsresult
204 nsTableCellFrame::AttributeChanged(int32_t aNameSpaceID,
205 nsIAtom* aAttribute,
206 int32_t aModType)
207 {
208 // We need to recalculate in this case because of the nowrap quirk in
209 // BasicTableLayoutStrategy
210 if (aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::nowrap &&
211 PresContext()->CompatibilityMode() == eCompatibility_NavQuirks) {
212 PresContext()->PresShell()->
213 FrameNeedsReflow(this, nsIPresShell::eTreeChange, NS_FRAME_IS_DIRTY);
214 }
215 // let the table frame decide what to do
216 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
217 tableFrame->AttributeChangedFor(this, mContent, aAttribute);
218 return NS_OK;
219 }
221 /* virtual */ void
222 nsTableCellFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
223 {
224 nsContainerFrame::DidSetStyleContext(aOldStyleContext);
226 if (!aOldStyleContext) //avoid this on init
227 return;
229 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
230 if (tableFrame->IsBorderCollapse() &&
231 tableFrame->BCRecalcNeeded(aOldStyleContext, StyleContext())) {
232 int32_t colIndex, rowIndex;
233 GetColIndex(colIndex);
234 GetRowIndex(rowIndex);
235 // row span needs to be clamped as we do not create rows in the cellmap
236 // which do not have cells originating in them
237 nsIntRect damageArea(colIndex, rowIndex, GetColSpan(),
238 std::min(GetRowSpan(), tableFrame->GetRowCount() - rowIndex));
239 tableFrame->AddBCDamageArea(damageArea);
240 }
241 }
244 nsresult
245 nsTableCellFrame::AppendFrames(ChildListID aListID,
246 nsFrameList& aFrameList)
247 {
248 NS_PRECONDITION(false, "unsupported operation");
249 return NS_ERROR_NOT_IMPLEMENTED;
250 }
252 nsresult
253 nsTableCellFrame::InsertFrames(ChildListID aListID,
254 nsIFrame* aPrevFrame,
255 nsFrameList& aFrameList)
256 {
257 NS_PRECONDITION(false, "unsupported operation");
258 return NS_ERROR_NOT_IMPLEMENTED;
259 }
261 nsresult
262 nsTableCellFrame::RemoveFrame(ChildListID aListID,
263 nsIFrame* aOldFrame)
264 {
265 NS_PRECONDITION(false, "unsupported operation");
266 return NS_ERROR_NOT_IMPLEMENTED;
267 }
269 void nsTableCellFrame::SetColIndex(int32_t aColIndex)
270 {
271 mColIndex = aColIndex;
272 }
274 /* virtual */ nsMargin
275 nsTableCellFrame::GetUsedMargin() const
276 {
277 return nsMargin(0,0,0,0);
278 }
280 //ASSURE DIFFERENT COLORS for selection
281 inline nscolor EnsureDifferentColors(nscolor colorA, nscolor colorB)
282 {
283 if (colorA == colorB)
284 {
285 nscolor res;
286 res = NS_RGB(NS_GET_R(colorA) ^ 0xff,
287 NS_GET_G(colorA) ^ 0xff,
288 NS_GET_B(colorA) ^ 0xff);
289 return res;
290 }
291 return colorA;
292 }
294 void
295 nsTableCellFrame::DecorateForSelection(nsRenderingContext& aRenderingContext,
296 nsPoint aPt)
297 {
298 NS_ASSERTION(IsSelected(), "Should only be called for selected cells");
299 int16_t displaySelection;
300 nsPresContext* presContext = PresContext();
301 displaySelection = DisplaySelection(presContext);
302 if (displaySelection) {
303 nsRefPtr<nsFrameSelection> frameSelection =
304 presContext->PresShell()->FrameSelection();
306 if (frameSelection->GetTableCellSelection()) {
307 nscolor bordercolor;
308 if (displaySelection == nsISelectionController::SELECTION_DISABLED) {
309 bordercolor = NS_RGB(176,176,176);// disabled color
310 }
311 else {
312 bordercolor =
313 LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectBackground);
314 }
315 nscoord threePx = nsPresContext::CSSPixelsToAppUnits(3);
316 if ((mRect.width > threePx) && (mRect.height > threePx))
317 {
318 //compare bordercolor to ((nsStyleColor *)myColor)->mBackgroundColor)
319 bordercolor = EnsureDifferentColors(bordercolor,
320 StyleBackground()->mBackgroundColor);
321 nsRenderingContext::AutoPushTranslation
322 translate(&aRenderingContext, aPt);
323 nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
325 aRenderingContext.SetColor(bordercolor);
326 aRenderingContext.DrawLine(onePixel, 0, mRect.width, 0);
327 aRenderingContext.DrawLine(0, onePixel, 0, mRect.height);
328 aRenderingContext.DrawLine(onePixel, mRect.height, mRect.width, mRect.height);
329 aRenderingContext.DrawLine(mRect.width, onePixel, mRect.width, mRect.height);
330 //middle
331 aRenderingContext.DrawRect(onePixel, onePixel, mRect.width-onePixel,
332 mRect.height-onePixel);
333 //shading
334 aRenderingContext.DrawLine(2*onePixel, mRect.height-2*onePixel,
335 mRect.width-onePixel, mRect.height- (2*onePixel));
336 aRenderingContext.DrawLine(mRect.width - (2*onePixel), 2*onePixel,
337 mRect.width - (2*onePixel), mRect.height-onePixel);
338 }
339 }
340 }
341 }
343 void
344 nsTableCellFrame::PaintBackground(nsRenderingContext& aRenderingContext,
345 const nsRect& aDirtyRect,
346 nsPoint aPt,
347 uint32_t aFlags)
348 {
349 nsRect rect(aPt, GetSize());
350 nsCSSRendering::PaintBackground(PresContext(), aRenderingContext, this,
351 aDirtyRect, rect, aFlags);
352 }
354 // Called by nsTablePainter
355 void
356 nsTableCellFrame::PaintCellBackground(nsRenderingContext& aRenderingContext,
357 const nsRect& aDirtyRect, nsPoint aPt,
358 uint32_t aFlags)
359 {
360 if (!StyleVisibility()->IsVisible())
361 return;
363 PaintBackground(aRenderingContext, aDirtyRect, aPt, aFlags);
364 }
366 nsresult
367 nsTableCellFrame::ProcessBorders(nsTableFrame* aFrame,
368 nsDisplayListBuilder* aBuilder,
369 const nsDisplayListSet& aLists)
370 {
371 const nsStyleBorder* borderStyle = StyleBorder();
372 if (aFrame->IsBorderCollapse() || !borderStyle->HasBorder())
373 return NS_OK;
375 if (!GetContentEmpty() ||
376 StyleTableBorder()->mEmptyCells == NS_STYLE_TABLE_EMPTY_CELLS_SHOW) {
377 aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
378 nsDisplayBorder(aBuilder, this));
379 }
381 return NS_OK;
382 }
384 class nsDisplayTableCellBackground : public nsDisplayTableItem {
385 public:
386 nsDisplayTableCellBackground(nsDisplayListBuilder* aBuilder,
387 nsTableCellFrame* aFrame) :
388 nsDisplayTableItem(aBuilder, aFrame) {
389 MOZ_COUNT_CTOR(nsDisplayTableCellBackground);
390 }
391 #ifdef NS_BUILD_REFCNT_LOGGING
392 virtual ~nsDisplayTableCellBackground() {
393 MOZ_COUNT_DTOR(nsDisplayTableCellBackground);
394 }
395 #endif
397 virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
398 HitTestState* aState,
399 nsTArray<nsIFrame*> *aOutFrames) MOZ_OVERRIDE {
400 aOutFrames->AppendElement(mFrame);
401 }
402 virtual void Paint(nsDisplayListBuilder* aBuilder,
403 nsRenderingContext* aCtx) MOZ_OVERRIDE;
404 virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
405 bool* aSnap) MOZ_OVERRIDE;
406 virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
407 const nsDisplayItemGeometry* aGeometry,
408 nsRegion *aInvalidRegion) MOZ_OVERRIDE;
410 NS_DISPLAY_DECL_NAME("TableCellBackground", TYPE_TABLE_CELL_BACKGROUND)
411 };
413 void nsDisplayTableCellBackground::Paint(nsDisplayListBuilder* aBuilder,
414 nsRenderingContext* aCtx)
415 {
416 static_cast<nsTableCellFrame*>(mFrame)->
417 PaintBackground(*aCtx, mVisibleRect, ToReferenceFrame(),
418 aBuilder->GetBackgroundPaintFlags());
419 }
421 nsRect
422 nsDisplayTableCellBackground::GetBounds(nsDisplayListBuilder* aBuilder,
423 bool* aSnap)
424 {
425 // revert from nsDisplayTableItem's implementation ... cell backgrounds
426 // don't overflow the cell
427 return nsDisplayItem::GetBounds(aBuilder, aSnap);
428 }
430 void
431 nsDisplayTableCellBackground::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
432 const nsDisplayItemGeometry* aGeometry,
433 nsRegion *aInvalidRegion)
434 {
435 if (aBuilder->ShouldSyncDecodeImages()) {
436 if (!nsCSSRendering::AreAllBackgroundImagesDecodedForFrame(mFrame)) {
437 bool snap;
438 aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
439 }
440 }
442 nsDisplayTableItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
443 }
445 void nsTableCellFrame::InvalidateFrame(uint32_t aDisplayItemKey)
446 {
447 nsIFrame::InvalidateFrame(aDisplayItemKey);
448 GetParent()->InvalidateFrameWithRect(GetVisualOverflowRect() + GetPosition(), aDisplayItemKey);
449 }
451 void nsTableCellFrame::InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItemKey)
452 {
453 nsIFrame::InvalidateFrameWithRect(aRect, aDisplayItemKey);
454 // If we have filters applied that would affects our bounds, then
455 // we get an inactive layer created and this is computed
456 // within FrameLayerBuilder
457 GetParent()->InvalidateFrameWithRect(aRect + GetPosition(), aDisplayItemKey);
458 }
460 static void
461 PaintTableCellSelection(nsIFrame* aFrame, nsRenderingContext* aCtx,
462 const nsRect& aRect, nsPoint aPt)
463 {
464 static_cast<nsTableCellFrame*>(aFrame)->DecorateForSelection(*aCtx, aPt);
465 }
467 void
468 nsTableCellFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
469 const nsRect& aDirtyRect,
470 const nsDisplayListSet& aLists)
471 {
472 DO_GLOBAL_REFLOW_COUNT_DSP("nsTableCellFrame");
473 if (IsVisibleInSelection(aBuilder)) {
474 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
475 int32_t emptyCellStyle = GetContentEmpty() && !tableFrame->IsBorderCollapse() ?
476 StyleTableBorder()->mEmptyCells
477 : NS_STYLE_TABLE_EMPTY_CELLS_SHOW;
478 // take account of 'empty-cells'
479 if (StyleVisibility()->IsVisible() &&
480 (NS_STYLE_TABLE_EMPTY_CELLS_HIDE != emptyCellStyle)) {
483 bool isRoot = aBuilder->IsAtRootOfPseudoStackingContext();
484 if (!isRoot) {
485 nsDisplayTableItem* currentItem = aBuilder->GetCurrentTableItem();
486 if (currentItem) {
487 currentItem->UpdateForFrameBackground(this);
488 }
489 }
491 // display outset box-shadows if we need to.
492 const nsStyleBorder* borderStyle = StyleBorder();
493 bool hasBoxShadow = !!borderStyle->mBoxShadow;
494 if (hasBoxShadow) {
495 aLists.BorderBackground()->AppendNewToTop(
496 new (aBuilder) nsDisplayBoxShadowOuter(aBuilder, this));
497 }
499 // display background if we need to.
500 if (aBuilder->IsForEventDelivery() ||
501 (((!tableFrame->IsBorderCollapse() || isRoot) &&
502 (!StyleBackground()->IsTransparent() || StyleDisplay()->mAppearance)))) {
503 // The cell background was not painted by the nsTablePainter,
504 // so we need to do it. We have special background processing here
505 // so we need to duplicate some code from nsFrame::DisplayBorderBackgroundOutline
506 nsDisplayTableItem* item =
507 new (aBuilder) nsDisplayTableCellBackground(aBuilder, this);
508 aLists.BorderBackground()->AppendNewToTop(item);
509 item->UpdateForFrameBackground(this);
510 }
512 // display inset box-shadows if we need to.
513 if (hasBoxShadow) {
514 aLists.BorderBackground()->AppendNewToTop(
515 new (aBuilder) nsDisplayBoxShadowInner(aBuilder, this));
516 }
518 // display borders if we need to
519 ProcessBorders(tableFrame, aBuilder, aLists);
521 // and display the selection border if we need to
522 if (IsSelected()) {
523 aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
524 nsDisplayGeneric(aBuilder, this, ::PaintTableCellSelection,
525 "TableCellSelection",
526 nsDisplayItem::TYPE_TABLE_CELL_SELECTION));
527 }
528 }
530 // the 'empty-cells' property has no effect on 'outline'
531 DisplayOutline(aBuilder, aLists);
532 }
534 // Push a null 'current table item' so that descendant tables can't
535 // accidentally mess with our table
536 nsAutoPushCurrentTableItem pushTableItem;
537 pushTableItem.Push(aBuilder, nullptr);
539 nsIFrame* kid = mFrames.FirstChild();
540 NS_ASSERTION(kid && !kid->GetNextSibling(), "Table cells should have just one child");
541 // The child's background will go in our BorderBackground() list.
542 // This isn't a problem since it won't have a real background except for
543 // event handling. We do not call BuildDisplayListForNonBlockChildren
544 // because that/ would put the child's background in the Content() list
545 // which isn't right (e.g., would end up on top of our child floats for
546 // event handling).
547 BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists);
548 }
550 int
551 nsTableCellFrame::GetLogicalSkipSides(const nsHTMLReflowState* aReflowState) const
552 {
553 int skip = 0;
554 if (nullptr != GetPrevInFlow()) {
555 skip |= LOGICAL_SIDE_B_START;
556 }
557 if (nullptr != GetNextInFlow()) {
558 skip |= LOGICAL_SIDE_B_END;
559 }
560 return skip;
561 }
563 /* virtual */ nsMargin
564 nsTableCellFrame::GetBorderOverflow()
565 {
566 return nsMargin(0, 0, 0, 0);
567 }
569 // Align the cell's child frame within the cell
571 void nsTableCellFrame::VerticallyAlignChild(nscoord aMaxAscent)
572 {
573 /* It's the 'border-collapse' on the table that matters */
574 nsMargin borderPadding = GetUsedBorderAndPadding();
576 nscoord topInset = borderPadding.top;
577 nscoord bottomInset = borderPadding.bottom;
579 uint8_t verticalAlignFlags = GetVerticalAlign();
581 nscoord height = mRect.height;
582 nsIFrame* firstKid = mFrames.FirstChild();
583 NS_ASSERTION(firstKid, "Frame construction error, a table cell always has an inner cell frame");
584 nsRect kidRect = firstKid->GetRect();
585 nscoord childHeight = kidRect.height;
587 // Vertically align the child
588 nscoord kidYTop = 0;
589 switch (verticalAlignFlags)
590 {
591 case NS_STYLE_VERTICAL_ALIGN_BASELINE:
592 // Align the baselines of the child frame with the baselines of
593 // other children in the same row which have 'vertical-align: baseline'
594 kidYTop = topInset + aMaxAscent - GetCellBaseline();
595 break;
597 case NS_STYLE_VERTICAL_ALIGN_TOP:
598 // Align the top of the child frame with the top of the content area,
599 kidYTop = topInset;
600 break;
602 case NS_STYLE_VERTICAL_ALIGN_BOTTOM:
603 // Align the bottom of the child frame with the bottom of the content area,
604 kidYTop = height - childHeight - bottomInset;
605 break;
607 default:
608 case NS_STYLE_VERTICAL_ALIGN_MIDDLE:
609 // Align the middle of the child frame with the middle of the content area,
610 kidYTop = (height - childHeight - bottomInset + topInset) / 2;
611 }
612 // if the content is larger than the cell height align from top
613 kidYTop = std::max(0, kidYTop);
615 if (kidYTop != kidRect.y) {
616 // Invalidate at the old position first
617 firstKid->InvalidateFrameSubtree();
618 }
620 firstKid->SetPosition(nsPoint(kidRect.x, kidYTop));
621 nsHTMLReflowMetrics desiredSize(GetWritingMode()); // ???
622 desiredSize.Width() = mRect.width;
623 desiredSize.Height() = mRect.height;
625 nsRect overflow(nsPoint(0,0), GetSize());
626 overflow.Inflate(GetBorderOverflow());
627 desiredSize.mOverflowAreas.SetAllTo(overflow);
628 ConsiderChildOverflow(desiredSize.mOverflowAreas, firstKid);
629 FinishAndStoreOverflow(&desiredSize);
630 if (kidYTop != kidRect.y) {
631 // Make sure any child views are correctly positioned. We know the inner table
632 // cell won't have a view
633 nsContainerFrame::PositionChildViews(firstKid);
635 // Invalidate new overflow rect
636 firstKid->InvalidateFrameSubtree();
637 }
638 if (HasView()) {
639 nsContainerFrame::SyncFrameViewAfterReflow(PresContext(), this,
640 GetView(),
641 desiredSize.VisualOverflow(), 0);
642 }
643 }
645 bool
646 nsTableCellFrame::UpdateOverflow()
647 {
648 nsRect bounds(nsPoint(0,0), GetSize());
649 bounds.Inflate(GetBorderOverflow());
650 nsOverflowAreas overflowAreas(bounds, bounds);
652 nsLayoutUtils::UnionChildOverflow(this, overflowAreas);
654 return FinishAndStoreOverflow(overflowAreas, GetSize());
655 }
657 // Per CSS 2.1, we map 'sub', 'super', 'text-top', 'text-bottom',
658 // length, percentage, and calc() values to 'baseline'.
659 uint8_t
660 nsTableCellFrame::GetVerticalAlign() const
661 {
662 const nsStyleCoord& verticalAlign = StyleTextReset()->mVerticalAlign;
663 if (verticalAlign.GetUnit() == eStyleUnit_Enumerated) {
664 uint8_t value = verticalAlign.GetIntValue();
665 if (value == NS_STYLE_VERTICAL_ALIGN_TOP ||
666 value == NS_STYLE_VERTICAL_ALIGN_MIDDLE ||
667 value == NS_STYLE_VERTICAL_ALIGN_BOTTOM) {
668 return value;
669 }
670 }
671 return NS_STYLE_VERTICAL_ALIGN_BASELINE;
672 }
674 bool
675 nsTableCellFrame::CellHasVisibleContent(nscoord height,
676 nsTableFrame* tableFrame,
677 nsIFrame* kidFrame)
678 {
679 // see http://www.w3.org/TR/CSS21/tables.html#empty-cells
680 if (height > 0)
681 return true;
682 if (tableFrame->IsBorderCollapse())
683 return true;
684 nsIFrame* innerFrame = kidFrame->GetFirstPrincipalChild();
685 while(innerFrame) {
686 nsIAtom* frameType = innerFrame->GetType();
687 if (nsGkAtoms::textFrame == frameType) {
688 nsTextFrame* textFrame = static_cast<nsTextFrame*>(innerFrame);
689 if (textFrame->HasNoncollapsedCharacters())
690 return true;
691 }
692 else if (nsGkAtoms::placeholderFrame != frameType) {
693 return true;
694 }
695 else {
696 nsIFrame *floatFrame = nsLayoutUtils::GetFloatFromPlaceholder(innerFrame);
697 if (floatFrame)
698 return true;
699 }
700 innerFrame = innerFrame->GetNextSibling();
701 }
702 return false;
703 }
705 nscoord
706 nsTableCellFrame::GetCellBaseline() const
707 {
708 // Ignore the position of the inner frame relative to the cell frame
709 // since we want the position as though the inner were top-aligned.
710 nsIFrame *inner = mFrames.FirstChild();
711 nscoord borderPadding = GetUsedBorderAndPadding().top;
712 nscoord result;
713 if (nsLayoutUtils::GetFirstLineBaseline(inner, &result))
714 return result + borderPadding;
715 return inner->GetContentRect().YMost() - inner->GetPosition().y +
716 borderPadding;
717 }
719 int32_t nsTableCellFrame::GetRowSpan()
720 {
721 int32_t rowSpan=1;
722 nsGenericHTMLElement *hc = nsGenericHTMLElement::FromContent(mContent);
724 // Don't look at the content's rowspan if we're a pseudo cell
725 if (hc && !StyleContext()->GetPseudo()) {
726 const nsAttrValue* attr = hc->GetParsedAttr(nsGkAtoms::rowspan);
727 // Note that we don't need to check the tag name, because only table cells
728 // and table headers parse the "rowspan" attribute into an integer.
729 if (attr && attr->Type() == nsAttrValue::eInteger) {
730 rowSpan = attr->GetIntegerValue();
731 }
732 }
733 return rowSpan;
734 }
736 int32_t nsTableCellFrame::GetColSpan()
737 {
738 int32_t colSpan=1;
739 nsGenericHTMLElement *hc = nsGenericHTMLElement::FromContent(mContent);
741 // Don't look at the content's colspan if we're a pseudo cell
742 if (hc && !StyleContext()->GetPseudo()) {
743 const nsAttrValue* attr = hc->GetParsedAttr(nsGkAtoms::colspan);
744 // Note that we don't need to check the tag name, because only table cells
745 // and table headers parse the "colspan" attribute into an integer.
746 if (attr && attr->Type() == nsAttrValue::eInteger) {
747 colSpan = attr->GetIntegerValue();
748 }
749 }
750 return colSpan;
751 }
753 /* virtual */ nscoord
754 nsTableCellFrame::GetMinWidth(nsRenderingContext *aRenderingContext)
755 {
756 nscoord result = 0;
757 DISPLAY_MIN_WIDTH(this, result);
759 nsIFrame *inner = mFrames.FirstChild();
760 result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, inner,
761 nsLayoutUtils::MIN_WIDTH);
762 return result;
763 }
765 /* virtual */ nscoord
766 nsTableCellFrame::GetPrefWidth(nsRenderingContext *aRenderingContext)
767 {
768 nscoord result = 0;
769 DISPLAY_PREF_WIDTH(this, result);
771 nsIFrame *inner = mFrames.FirstChild();
772 result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, inner,
773 nsLayoutUtils::PREF_WIDTH);
774 return result;
775 }
777 /* virtual */ nsIFrame::IntrinsicWidthOffsetData
778 nsTableCellFrame::IntrinsicWidthOffsets(nsRenderingContext* aRenderingContext)
779 {
780 IntrinsicWidthOffsetData result =
781 nsContainerFrame::IntrinsicWidthOffsets(aRenderingContext);
783 result.hMargin = 0;
784 result.hPctMargin = 0;
786 nsMargin border;
787 GetBorderWidth(border);
788 result.hBorder = border.LeftRight();
790 return result;
791 }
793 #ifdef DEBUG
794 #define PROBABLY_TOO_LARGE 1000000
795 static
796 void DebugCheckChildSize(nsIFrame* aChild,
797 nsHTMLReflowMetrics& aMet,
798 nsSize& aAvailSize)
799 {
800 if ((aMet.Width() < 0) || (aMet.Width() > PROBABLY_TOO_LARGE)) {
801 printf("WARNING: cell content %p has large width %d \n",
802 static_cast<void*>(aChild), int32_t(aMet.Width()));
803 }
804 }
805 #endif
807 // the computed height for the cell, which descendants use for percent height calculations
808 // it is the height (minus border, padding) of the cell's first in flow during its final
809 // reflow without an unconstrained height.
810 static nscoord
811 CalcUnpaginagedHeight(nsPresContext* aPresContext,
812 nsTableCellFrame& aCellFrame,
813 nsTableFrame& aTableFrame,
814 nscoord aVerticalBorderPadding)
815 {
816 const nsTableCellFrame* firstCellInFlow =
817 static_cast<nsTableCellFrame*>(aCellFrame.FirstInFlow());
818 nsTableFrame* firstTableInFlow =
819 static_cast<nsTableFrame*>(aTableFrame.FirstInFlow());
820 nsTableRowFrame* row =
821 static_cast<nsTableRowFrame*>(firstCellInFlow->GetParent());
822 nsTableRowGroupFrame* firstRGInFlow =
823 static_cast<nsTableRowGroupFrame*>(row->GetParent());
825 int32_t rowIndex;
826 firstCellInFlow->GetRowIndex(rowIndex);
827 int32_t rowSpan = aTableFrame.GetEffectiveRowSpan(*firstCellInFlow);
828 nscoord cellSpacing = firstTableInFlow->GetCellSpacingX();
830 nscoord computedHeight = ((rowSpan - 1) * cellSpacing) - aVerticalBorderPadding;
831 int32_t rowX;
832 for (row = firstRGInFlow->GetFirstRow(), rowX = 0; row; row = row->GetNextRow(), rowX++) {
833 if (rowX > rowIndex + rowSpan - 1) {
834 break;
835 }
836 else if (rowX >= rowIndex) {
837 computedHeight += row->GetUnpaginatedHeight(aPresContext);
838 }
839 }
840 return computedHeight;
841 }
843 nsresult nsTableCellFrame::Reflow(nsPresContext* aPresContext,
844 nsHTMLReflowMetrics& aDesiredSize,
845 const nsHTMLReflowState& aReflowState,
846 nsReflowStatus& aStatus)
847 {
848 DO_GLOBAL_REFLOW_COUNT("nsTableCellFrame");
849 DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
851 if (aReflowState.mFlags.mSpecialHeightReflow) {
852 FirstInFlow()->AddStateBits(NS_TABLE_CELL_HAD_SPECIAL_REFLOW);
853 }
855 // see if a special height reflow needs to occur due to having a pct height
856 nsTableFrame::CheckRequestSpecialHeightReflow(aReflowState);
858 aStatus = NS_FRAME_COMPLETE;
859 nsSize availSize(aReflowState.AvailableWidth(), aReflowState.AvailableHeight());
861 nsMargin borderPadding = aReflowState.ComputedPhysicalPadding();
862 nsMargin border;
863 GetBorderWidth(border);
864 borderPadding += border;
866 nscoord topInset = borderPadding.top;
867 nscoord rightInset = borderPadding.right;
868 nscoord bottomInset = borderPadding.bottom;
869 nscoord leftInset = borderPadding.left;
871 // reduce available space by insets, if we're in a constrained situation
872 availSize.width -= leftInset + rightInset;
873 if (NS_UNCONSTRAINEDSIZE != availSize.height)
874 availSize.height -= topInset + bottomInset;
876 // Try to reflow the child into the available space. It might not
877 // fit or might need continuing.
878 if (availSize.height < 0)
879 availSize.height = 1;
881 nsHTMLReflowMetrics kidSize(aReflowState.GetWritingMode(), aDesiredSize.mFlags);
882 kidSize.Width() = kidSize.Height() = 0;
883 SetPriorAvailWidth(aReflowState.AvailableWidth());
884 nsIFrame* firstKid = mFrames.FirstChild();
885 NS_ASSERTION(firstKid, "Frame construction error, a table cell always has an inner cell frame");
886 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
888 if (aReflowState.mFlags.mSpecialHeightReflow) {
889 const_cast<nsHTMLReflowState&>(aReflowState).SetComputedHeight(mRect.height - topInset - bottomInset);
890 DISPLAY_REFLOW_CHANGE();
891 }
892 else if (aPresContext->IsPaginated()) {
893 nscoord computedUnpaginatedHeight =
894 CalcUnpaginagedHeight(aPresContext, (nsTableCellFrame&)*this,
895 *tableFrame, topInset + bottomInset);
896 if (computedUnpaginatedHeight > 0) {
897 const_cast<nsHTMLReflowState&>(aReflowState).SetComputedHeight(computedUnpaginatedHeight);
898 DISPLAY_REFLOW_CHANGE();
899 }
900 }
901 else {
902 SetHasPctOverHeight(false);
903 }
905 nsHTMLReflowState kidReflowState(aPresContext, aReflowState, firstKid,
906 availSize);
908 // Don't be a percent height observer if we're in the middle of
909 // special-height reflow, in case we get an accidental NotifyPercentHeight()
910 // call (which we shouldn't honor during special-height reflow)
911 if (!aReflowState.mFlags.mSpecialHeightReflow) {
912 // mPercentHeightObserver is for children of cells in quirks mode,
913 // but only those than are tables in standards mode. NeedsToObserve
914 // will determine how far this is propagated to descendants.
915 kidReflowState.mPercentHeightObserver = this;
916 }
917 // Don't propagate special height reflow state to our kids
918 kidReflowState.mFlags.mSpecialHeightReflow = false;
920 if (aReflowState.mFlags.mSpecialHeightReflow ||
921 (FirstInFlow()->GetStateBits() & NS_TABLE_CELL_HAD_SPECIAL_REFLOW)) {
922 // We need to force the kid to have mVResize set if we've had a
923 // special reflow in the past, since the non-special reflow needs to
924 // resize back to what it was without the special height reflow.
925 kidReflowState.mFlags.mVResize = true;
926 }
928 nsPoint kidOrigin(leftInset, topInset);
929 nsRect origRect = firstKid->GetRect();
930 nsRect origVisualOverflow = firstKid->GetVisualOverflowRect();
931 bool firstReflow = (firstKid->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0;
933 ReflowChild(firstKid, aPresContext, kidSize, kidReflowState,
934 kidOrigin.x, kidOrigin.y, 0, aStatus);
935 if (NS_FRAME_OVERFLOW_IS_INCOMPLETE(aStatus)) {
936 // Don't pass OVERFLOW_INCOMPLETE through tables until they can actually handle it
937 //XXX should paginate overflow as overflow, but not in this patch (bug 379349)
938 NS_FRAME_SET_INCOMPLETE(aStatus);
939 printf("Set table cell incomplete %p\n", static_cast<void*>(this));
940 }
942 // XXXbz is this invalidate actually needed, really?
943 if (GetStateBits() & NS_FRAME_IS_DIRTY) {
944 InvalidateFrameSubtree();
945 }
947 #ifdef DEBUG
948 DebugCheckChildSize(firstKid, kidSize, availSize);
949 #endif
951 // 0 dimensioned cells need to be treated specially in Standard/NavQuirks mode
952 // see testcase "emptyCells.html"
953 nsIFrame* prevInFlow = GetPrevInFlow();
954 bool isEmpty;
955 if (prevInFlow) {
956 isEmpty = static_cast<nsTableCellFrame*>(prevInFlow)->GetContentEmpty();
957 } else {
958 isEmpty = !CellHasVisibleContent(kidSize.Height(), tableFrame, firstKid);
959 }
960 SetContentEmpty(isEmpty);
962 // Place the child
963 FinishReflowChild(firstKid, aPresContext, kidSize, &kidReflowState,
964 kidOrigin.x, kidOrigin.y, 0);
966 nsTableFrame::InvalidateTableFrame(firstKid, origRect, origVisualOverflow,
967 firstReflow);
969 // first, compute the height which can be set w/o being restricted by aMaxSize.height
970 nscoord cellHeight = kidSize.Height();
972 if (NS_UNCONSTRAINEDSIZE != cellHeight) {
973 cellHeight += topInset + bottomInset;
974 }
976 // next determine the cell's width
977 nscoord cellWidth = kidSize.Width(); // at this point, we've factored in the cell's style attributes
979 // factor in border and padding
980 if (NS_UNCONSTRAINEDSIZE != cellWidth) {
981 cellWidth += leftInset + rightInset;
982 }
984 // set the cell's desired size and max element size
985 aDesiredSize.Width() = cellWidth;
986 aDesiredSize.Height() = cellHeight;
988 // the overflow area will be computed when the child will be vertically aligned
990 if (aReflowState.mFlags.mSpecialHeightReflow) {
991 if (aDesiredSize.Height() > mRect.height) {
992 // set a bit indicating that the pct height contents exceeded
993 // the height that they could honor in the pass 2 reflow
994 SetHasPctOverHeight(true);
995 }
996 if (NS_UNCONSTRAINEDSIZE == aReflowState.AvailableHeight()) {
997 aDesiredSize.Height() = mRect.height;
998 }
999 }
1001 // If our parent is in initial reflow, it'll handle invalidating our
1002 // entire overflow rect.
1003 if (!(GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW) &&
1004 nsSize(aDesiredSize.Width(), aDesiredSize.Height()) != mRect.Size()) {
1005 InvalidateFrame();
1006 }
1008 // remember the desired size for this reflow
1009 SetDesiredSize(aDesiredSize);
1011 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
1012 return NS_OK;
1013 }
1015 /* ----- global methods ----- */
1017 NS_QUERYFRAME_HEAD(nsTableCellFrame)
1018 NS_QUERYFRAME_ENTRY(nsTableCellFrame)
1019 NS_QUERYFRAME_ENTRY(nsITableCellLayout)
1020 NS_QUERYFRAME_ENTRY(nsIPercentHeightObserver)
1021 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
1023 #ifdef ACCESSIBILITY
1024 a11y::AccType
1025 nsTableCellFrame::AccessibleType()
1026 {
1027 return a11y::eHTMLTableCellType;
1028 }
1029 #endif
1031 /* This is primarily for editor access via nsITableLayout */
1032 NS_IMETHODIMP
1033 nsTableCellFrame::GetCellIndexes(int32_t &aRowIndex, int32_t &aColIndex)
1034 {
1035 nsresult res = GetRowIndex(aRowIndex);
1036 if (NS_FAILED(res))
1037 {
1038 aColIndex = 0;
1039 return res;
1040 }
1041 aColIndex = mColIndex;
1042 return NS_OK;
1043 }
1045 nsIFrame*
1046 NS_NewTableCellFrame(nsIPresShell* aPresShell,
1047 nsStyleContext* aContext,
1048 bool aIsBorderCollapse)
1049 {
1050 if (aIsBorderCollapse)
1051 return new (aPresShell) nsBCTableCellFrame(aContext);
1052 else
1053 return new (aPresShell) nsTableCellFrame(aContext);
1054 }
1056 NS_IMPL_FRAMEARENA_HELPERS(nsBCTableCellFrame)
1058 nsMargin*
1059 nsTableCellFrame::GetBorderWidth(nsMargin& aBorder) const
1060 {
1061 aBorder = StyleBorder()->GetComputedBorder();
1062 return &aBorder;
1063 }
1065 nsIAtom*
1066 nsTableCellFrame::GetType() const
1067 {
1068 return nsGkAtoms::tableCellFrame;
1069 }
1071 #ifdef DEBUG_FRAME_DUMP
1072 nsresult
1073 nsTableCellFrame::GetFrameName(nsAString& aResult) const
1074 {
1075 return MakeFrameName(NS_LITERAL_STRING("TableCell"), aResult);
1076 }
1077 #endif
1079 // nsBCTableCellFrame
1081 nsBCTableCellFrame::nsBCTableCellFrame(nsStyleContext* aContext)
1082 :nsTableCellFrame(aContext)
1083 {
1084 mTopBorder = mRightBorder = mBottomBorder = mLeftBorder = 0;
1085 }
1087 nsBCTableCellFrame::~nsBCTableCellFrame()
1088 {
1089 }
1091 nsIAtom*
1092 nsBCTableCellFrame::GetType() const
1093 {
1094 return nsGkAtoms::bcTableCellFrame;
1095 }
1097 /* virtual */ nsMargin
1098 nsBCTableCellFrame::GetUsedBorder() const
1099 {
1100 nsMargin result;
1101 GetBorderWidth(result);
1102 return result;
1103 }
1105 /* virtual */ bool
1106 nsBCTableCellFrame::GetBorderRadii(nscoord aRadii[8]) const
1107 {
1108 NS_FOR_CSS_HALF_CORNERS(corner) {
1109 aRadii[corner] = 0;
1110 }
1111 return false;
1112 }
1114 #ifdef DEBUG_FRAME_DUMP
1115 nsresult
1116 nsBCTableCellFrame::GetFrameName(nsAString& aResult) const
1117 {
1118 return MakeFrameName(NS_LITERAL_STRING("BCTableCell"), aResult);
1119 }
1120 #endif
1122 nsMargin*
1123 nsBCTableCellFrame::GetBorderWidth(nsMargin& aBorder) const
1124 {
1125 int32_t aPixelsToTwips = nsPresContext::AppUnitsPerCSSPixel();
1126 aBorder.top = BC_BORDER_BOTTOM_HALF_COORD(aPixelsToTwips, mTopBorder);
1127 aBorder.right = BC_BORDER_LEFT_HALF_COORD(aPixelsToTwips, mRightBorder);
1128 aBorder.bottom = BC_BORDER_TOP_HALF_COORD(aPixelsToTwips, mBottomBorder);
1129 aBorder.left = BC_BORDER_RIGHT_HALF_COORD(aPixelsToTwips, mLeftBorder);
1130 return &aBorder;
1131 }
1133 BCPixelSize
1134 nsBCTableCellFrame::GetBorderWidth(mozilla::css::Side aSide) const
1135 {
1136 switch(aSide) {
1137 case NS_SIDE_TOP:
1138 return BC_BORDER_BOTTOM_HALF(mTopBorder);
1139 case NS_SIDE_RIGHT:
1140 return BC_BORDER_LEFT_HALF(mRightBorder);
1141 case NS_SIDE_BOTTOM:
1142 return BC_BORDER_TOP_HALF(mBottomBorder);
1143 default:
1144 return BC_BORDER_RIGHT_HALF(mLeftBorder);
1145 }
1146 }
1148 void
1149 nsBCTableCellFrame::SetBorderWidth(mozilla::css::Side aSide,
1150 BCPixelSize aValue)
1151 {
1152 switch(aSide) {
1153 case NS_SIDE_TOP:
1154 mTopBorder = aValue;
1155 break;
1156 case NS_SIDE_RIGHT:
1157 mRightBorder = aValue;
1158 break;
1159 case NS_SIDE_BOTTOM:
1160 mBottomBorder = aValue;
1161 break;
1162 default:
1163 mLeftBorder = aValue;
1164 }
1165 }
1167 /* virtual */ nsMargin
1168 nsBCTableCellFrame::GetBorderOverflow()
1169 {
1170 nsMargin halfBorder;
1171 int32_t p2t = nsPresContext::AppUnitsPerCSSPixel();
1172 halfBorder.top = BC_BORDER_TOP_HALF_COORD(p2t, mTopBorder);
1173 halfBorder.right = BC_BORDER_RIGHT_HALF_COORD(p2t, mRightBorder);
1174 halfBorder.bottom = BC_BORDER_BOTTOM_HALF_COORD(p2t, mBottomBorder);
1175 halfBorder.left = BC_BORDER_LEFT_HALF_COORD(p2t, mLeftBorder);
1176 return halfBorder;
1177 }
1180 void
1181 nsBCTableCellFrame::PaintBackground(nsRenderingContext& aRenderingContext,
1182 const nsRect& aDirtyRect,
1183 nsPoint aPt,
1184 uint32_t aFlags)
1185 {
1186 // make border-width reflect the half of the border-collapse
1187 // assigned border that's inside the cell
1188 nsMargin borderWidth;
1189 GetBorderWidth(borderWidth);
1191 nsStyleBorder myBorder(*StyleBorder());
1193 NS_FOR_CSS_SIDES(side) {
1194 myBorder.SetBorderWidth(side, borderWidth.Side(side));
1195 }
1197 nsRect rect(aPt, GetSize());
1198 // bypassing nsCSSRendering::PaintBackground is safe because this kind
1199 // of frame cannot be used for the root element
1200 nsCSSRendering::PaintBackgroundWithSC(PresContext(), aRenderingContext, this,
1201 aDirtyRect, rect,
1202 StyleContext(), myBorder,
1203 aFlags, nullptr);
1204 }