layout/tables/nsTableCellFrame.cpp

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

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

mercurial