layout/tables/nsTablePainter.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
michael@0 6 #include "nsTableFrame.h"
michael@0 7 #include "nsTableRowGroupFrame.h"
michael@0 8 #include "nsTableRowFrame.h"
michael@0 9 #include "nsTableColGroupFrame.h"
michael@0 10 #include "nsTableColFrame.h"
michael@0 11 #include "nsTableCellFrame.h"
michael@0 12 #include "nsTablePainter.h"
michael@0 13 #include "nsCSSRendering.h"
michael@0 14 #include "nsDisplayList.h"
michael@0 15
michael@0 16 /* ~*~ Table Background Painting ~*~
michael@0 17
michael@0 18 Mozilla's Table Background painting follows CSS2.1:17.5.1
michael@0 19 That section does not, however, describe the effect of
michael@0 20 borders on background image positioning. What we do is:
michael@0 21
michael@0 22 - in separate borders, the borders are passed in so that
michael@0 23 their width figures in image positioning, even for rows/cols, which
michael@0 24 don't have visible borders. This is done to allow authors
michael@0 25 to position row backgrounds by, for example, aligning the
michael@0 26 top left corner with the top left padding corner of the
michael@0 27 top left table cell in the row in cases where all cells
michael@0 28 have consistent border widths. If we didn't honor these
michael@0 29 invisible borders, there would be no way to align
michael@0 30 backgrounds with the padding edges, and designs would be
michael@0 31 lost underneath the border.
michael@0 32
michael@0 33 - in collapsing borders, because the borders collapse, we
michael@0 34 use the -continuous border- width to synthesize a border
michael@0 35 style and pass that in instead of using the element's
michael@0 36 assigned style directly.
michael@0 37
michael@0 38 The continuous border on a given edge of an element is
michael@0 39 the collapse of all borders guaranteed to be continuous
michael@0 40 along that edge. Cell borders are ignored (because, for
michael@0 41 example, setting a thick border on the leftmost cell
michael@0 42 should not shift the row background over; this way a
michael@0 43 striped background set on <tr> will line up across rows
michael@0 44 even if the cells are assigned arbitrary border widths.
michael@0 45
michael@0 46 For example, the continuous border on the top edge of a
michael@0 47 row group is the collapse of any row group, row, and
michael@0 48 table borders involved. (The first row group's top would
michael@0 49 be [table-top + row group top + first row top]. It's bottom
michael@0 50 would be [row group bottom + last row bottom + next row
michael@0 51 top + next row group top].)
michael@0 52 The top edge of a column group likewise includes the
michael@0 53 table top, row group top, and first row top borders. However,
michael@0 54 it *also* includes its own top border, since that is guaranteed
michael@0 55 to be continuous. It does not include column borders because
michael@0 56 those are not guaranteed to be continuous: there may be two
michael@0 57 columns with different borders in a single column group.
michael@0 58
michael@0 59 An alternative would be to define the continuous border as
michael@0 60 [table? + row group + row] for horizontal
michael@0 61 [table? + col group + col] for vertical
michael@0 62 This makes it easier to line up backgrounds across elements
michael@0 63 despite varying border widths, but it does not give much
michael@0 64 flexibility in aligning /to/ those border widths.
michael@0 65 */
michael@0 66
michael@0 67
michael@0 68 /* ~*~ TableBackgroundPainter ~*~
michael@0 69
michael@0 70 The TableBackgroundPainter is created and destroyed in one painting call.
michael@0 71 Its principal function is PaintTable, which paints all table element
michael@0 72 backgrounds. The initial code in that method sets up an array of column
michael@0 73 data that caches the background styles and the border sizes for the
michael@0 74 columns and colgroups in TableBackgroundData structs in mCols. Data for
michael@0 75 BC borders are calculated and stashed in a synthesized border style struct
michael@0 76 in the data struct since collapsed borders aren't the same width as style-
michael@0 77 assigned borders. The data struct optimizes by only doing this if there's
michael@0 78 an image background; otherwise we don't care. //XXX should also check background-origin
michael@0 79 The class then loops through the row groups, rows, and cells. It uses
michael@0 80 the mRowGroup and mRow TableBackgroundData structs to cache data for
michael@0 81 the current frame in the loop. At the cell level, it paints the backgrounds,
michael@0 82 one over the other, inside the cell rect.
michael@0 83
michael@0 84 The exception to this pattern is when a table element creates a (pseudo)
michael@0 85 stacking context. Elements with stacking contexts (e.g., 'opacity' applied)
michael@0 86 are <dfn>passed through</dfn>, which means their data (and their
michael@0 87 descendants' data) are not cached. The full loop is still executed, however,
michael@0 88 so that underlying layers can get painted at the cell level.
michael@0 89
michael@0 90 The TableBackgroundPainter is then destroyed.
michael@0 91
michael@0 92 Elements with stacking contexts set up their own painter to finish the
michael@0 93 painting process, since they were skipped. They call the appropriate
michael@0 94 sub-part of the loop (e.g. PaintRow) which will paint the frame and
michael@0 95 descendants. Note that it is permissible according to CSS2.1 to ignore'
michael@0 96 'position:relative' (and implicitly, 'opacity') on table parts so that
michael@0 97 table parts can never create stacking contexts; if we want to, we can
michael@0 98 implement that, and then we won't have to deal with TableBackgroundPainter
michael@0 99 being used anywhere but from the nsTableFrame.
michael@0 100
michael@0 101 XXX views are going
michael@0 102 */
michael@0 103
michael@0 104 TableBackgroundPainter::TableBackgroundData::TableBackgroundData()
michael@0 105 : mFrame(nullptr),
michael@0 106 mVisible(false),
michael@0 107 mBorder(nullptr),
michael@0 108 mSynthBorder(nullptr)
michael@0 109 {
michael@0 110 MOZ_COUNT_CTOR(TableBackgroundData);
michael@0 111 }
michael@0 112
michael@0 113 TableBackgroundPainter::TableBackgroundData::~TableBackgroundData()
michael@0 114 {
michael@0 115 NS_ASSERTION(!mSynthBorder, "must call Destroy before dtor");
michael@0 116 MOZ_COUNT_DTOR(TableBackgroundData);
michael@0 117 }
michael@0 118
michael@0 119 void
michael@0 120 TableBackgroundPainter::TableBackgroundData::Destroy(nsPresContext* aPresContext)
michael@0 121 {
michael@0 122 NS_PRECONDITION(aPresContext, "null prescontext");
michael@0 123 if (mSynthBorder) {
michael@0 124 mSynthBorder->Destroy(aPresContext);
michael@0 125 mSynthBorder = nullptr;
michael@0 126 }
michael@0 127 }
michael@0 128
michael@0 129 void
michael@0 130 TableBackgroundPainter::TableBackgroundData::Clear()
michael@0 131 {
michael@0 132 mRect.SetEmpty();
michael@0 133 mFrame = nullptr;
michael@0 134 mBorder = nullptr;
michael@0 135 mVisible = false;
michael@0 136 }
michael@0 137
michael@0 138 void
michael@0 139 TableBackgroundPainter::TableBackgroundData::SetFrame(nsIFrame* aFrame)
michael@0 140 {
michael@0 141 NS_PRECONDITION(aFrame, "null frame");
michael@0 142 mFrame = aFrame;
michael@0 143 mRect = aFrame->GetRect();
michael@0 144 }
michael@0 145
michael@0 146 void
michael@0 147 TableBackgroundPainter::TableBackgroundData::SetData()
michael@0 148 {
michael@0 149 NS_PRECONDITION(mFrame, "null frame");
michael@0 150 if (mFrame->IsVisibleForPainting()) {
michael@0 151 mVisible = true;
michael@0 152 mBorder = mFrame->StyleBorder();
michael@0 153 }
michael@0 154 }
michael@0 155
michael@0 156 void
michael@0 157 TableBackgroundPainter::TableBackgroundData::SetFull(nsIFrame* aFrame)
michael@0 158 {
michael@0 159 NS_PRECONDITION(aFrame, "null frame");
michael@0 160 SetFrame(aFrame);
michael@0 161 SetData();
michael@0 162 }
michael@0 163
michael@0 164 inline bool
michael@0 165 TableBackgroundPainter::TableBackgroundData::ShouldSetBCBorder()
michael@0 166 {
michael@0 167 /* we only need accurate border data when positioning background images*/
michael@0 168 if (!mVisible) {
michael@0 169 return false;
michael@0 170 }
michael@0 171
michael@0 172 const nsStyleBackground *bg = mFrame->StyleBackground();
michael@0 173 NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, bg) {
michael@0 174 if (!bg->mLayers[i].mImage.IsEmpty())
michael@0 175 return true;
michael@0 176 }
michael@0 177 return false;
michael@0 178 }
michael@0 179
michael@0 180 nsresult
michael@0 181 TableBackgroundPainter::TableBackgroundData::SetBCBorder(nsMargin& aBorder,
michael@0 182 TableBackgroundPainter* aPainter)
michael@0 183 {
michael@0 184 NS_PRECONDITION(aPainter, "null painter");
michael@0 185 if (!mSynthBorder) {
michael@0 186 mSynthBorder = new (aPainter->mPresContext)
michael@0 187 nsStyleBorder(aPainter->mZeroBorder);
michael@0 188 if (!mSynthBorder) return NS_ERROR_OUT_OF_MEMORY;
michael@0 189 }
michael@0 190
michael@0 191 NS_FOR_CSS_SIDES(side) {
michael@0 192 mSynthBorder->SetBorderWidth(side, aBorder.Side(side));
michael@0 193 }
michael@0 194
michael@0 195 mBorder = mSynthBorder;
michael@0 196 return NS_OK;
michael@0 197 }
michael@0 198
michael@0 199 TableBackgroundPainter::TableBackgroundPainter(nsTableFrame* aTableFrame,
michael@0 200 Origin aOrigin,
michael@0 201 nsPresContext* aPresContext,
michael@0 202 nsRenderingContext& aRenderingContext,
michael@0 203 const nsRect& aDirtyRect,
michael@0 204 const nsPoint& aRenderPt,
michael@0 205 uint32_t aBGPaintFlags)
michael@0 206 : mPresContext(aPresContext),
michael@0 207 mRenderingContext(aRenderingContext),
michael@0 208 mRenderPt(aRenderPt),
michael@0 209 mDirtyRect(aDirtyRect),
michael@0 210 mOrigin(aOrigin),
michael@0 211 mCols(nullptr),
michael@0 212 mZeroBorder(aPresContext),
michael@0 213 mBGPaintFlags(aBGPaintFlags)
michael@0 214 {
michael@0 215 MOZ_COUNT_CTOR(TableBackgroundPainter);
michael@0 216
michael@0 217 NS_FOR_CSS_SIDES(side) {
michael@0 218 mZeroBorder.SetBorderStyle(side, NS_STYLE_BORDER_STYLE_SOLID);
michael@0 219 mZeroBorder.SetBorderWidth(side, 0);
michael@0 220 }
michael@0 221
michael@0 222 mIsBorderCollapse = aTableFrame->IsBorderCollapse();
michael@0 223 #ifdef DEBUG
michael@0 224 mCompatMode = mPresContext->CompatibilityMode();
michael@0 225 #endif
michael@0 226 mNumCols = aTableFrame->GetColCount();
michael@0 227 }
michael@0 228
michael@0 229 TableBackgroundPainter::~TableBackgroundPainter()
michael@0 230 {
michael@0 231 if (mCols) {
michael@0 232 TableBackgroundData* lastColGroup = nullptr;
michael@0 233 for (uint32_t i = 0; i < mNumCols; i++) {
michael@0 234 if (mCols[i].mColGroup != lastColGroup) {
michael@0 235 lastColGroup = mCols[i].mColGroup;
michael@0 236 NS_ASSERTION(mCols[i].mColGroup, "colgroup data should not be null - bug 237421");
michael@0 237 // we need to wallpaper a over zero pointer deref, bug 237421 will have the real fix
michael@0 238 if(lastColGroup)
michael@0 239 lastColGroup->Destroy(mPresContext);
michael@0 240 delete lastColGroup;
michael@0 241 }
michael@0 242 mCols[i].mColGroup = nullptr;
michael@0 243 mCols[i].mCol.Destroy(mPresContext);
michael@0 244 }
michael@0 245 delete [] mCols;
michael@0 246 }
michael@0 247 mRowGroup.Destroy(mPresContext);
michael@0 248 mRow.Destroy(mPresContext);
michael@0 249 MOZ_COUNT_DTOR(TableBackgroundPainter);
michael@0 250 }
michael@0 251
michael@0 252 nsresult
michael@0 253 TableBackgroundPainter::PaintTableFrame(nsTableFrame* aTableFrame,
michael@0 254 nsTableRowGroupFrame* aFirstRowGroup,
michael@0 255 nsTableRowGroupFrame* aLastRowGroup,
michael@0 256 const nsMargin& aDeflate)
michael@0 257 {
michael@0 258 NS_PRECONDITION(aTableFrame, "null frame");
michael@0 259 TableBackgroundData tableData;
michael@0 260 tableData.SetFull(aTableFrame);
michael@0 261 tableData.mRect.MoveTo(0,0); //using table's coords
michael@0 262 tableData.mRect.Deflate(aDeflate);
michael@0 263 if (mIsBorderCollapse && tableData.ShouldSetBCBorder()) {
michael@0 264 if (aFirstRowGroup && aLastRowGroup && mNumCols > 0) {
michael@0 265 //only handle non-degenerate tables; we need a more robust BC model
michael@0 266 //to make degenerate tables' borders reasonable to deal with
michael@0 267 nsMargin border, tempBorder;
michael@0 268 nsTableColFrame* colFrame = aTableFrame->GetColFrame(mNumCols - 1);
michael@0 269 if (colFrame) {
michael@0 270 colFrame->GetContinuousBCBorderWidth(tempBorder);
michael@0 271 }
michael@0 272 border.right = tempBorder.right;
michael@0 273
michael@0 274 aLastRowGroup->GetContinuousBCBorderWidth(tempBorder);
michael@0 275 border.bottom = tempBorder.bottom;
michael@0 276
michael@0 277 nsTableRowFrame* rowFrame = aFirstRowGroup->GetFirstRow();
michael@0 278 if (rowFrame) {
michael@0 279 rowFrame->GetContinuousBCBorderWidth(tempBorder);
michael@0 280 border.top = tempBorder.top;
michael@0 281 }
michael@0 282
michael@0 283 border.left = aTableFrame->GetContinuousLeftBCBorderWidth();
michael@0 284
michael@0 285 nsresult rv = tableData.SetBCBorder(border, this);
michael@0 286 if (NS_FAILED(rv)) {
michael@0 287 tableData.Destroy(mPresContext);
michael@0 288 return rv;
michael@0 289 }
michael@0 290 }
michael@0 291 }
michael@0 292 if (tableData.IsVisible()) {
michael@0 293 nsCSSRendering::PaintBackgroundWithSC(mPresContext, mRenderingContext,
michael@0 294 tableData.mFrame, mDirtyRect,
michael@0 295 tableData.mRect + mRenderPt,
michael@0 296 tableData.mFrame->StyleContext(),
michael@0 297 *tableData.mBorder,
michael@0 298 mBGPaintFlags);
michael@0 299 }
michael@0 300 tableData.Destroy(mPresContext);
michael@0 301 return NS_OK;
michael@0 302 }
michael@0 303
michael@0 304 void
michael@0 305 TableBackgroundPainter::TranslateContext(nscoord aDX,
michael@0 306 nscoord aDY)
michael@0 307 {
michael@0 308 mRenderPt += nsPoint(aDX, aDY);
michael@0 309 if (mCols) {
michael@0 310 TableBackgroundData* lastColGroup = nullptr;
michael@0 311 for (uint32_t i = 0; i < mNumCols; i++) {
michael@0 312 mCols[i].mCol.mRect.MoveBy(-aDX, -aDY);
michael@0 313 if (lastColGroup != mCols[i].mColGroup) {
michael@0 314 NS_ASSERTION(mCols[i].mColGroup, "colgroup data should not be null - bug 237421");
michael@0 315 // we need to wallpaper a over zero pointer deref, bug 237421 will have the real fix
michael@0 316 if (!mCols[i].mColGroup)
michael@0 317 return;
michael@0 318 mCols[i].mColGroup->mRect.MoveBy(-aDX, -aDY);
michael@0 319 lastColGroup = mCols[i].mColGroup;
michael@0 320 }
michael@0 321 }
michael@0 322 }
michael@0 323 }
michael@0 324
michael@0 325 nsresult
michael@0 326 TableBackgroundPainter::PaintTable(nsTableFrame* aTableFrame,
michael@0 327 const nsMargin& aDeflate,
michael@0 328 bool aPaintTableBackground)
michael@0 329 {
michael@0 330 NS_PRECONDITION(aTableFrame, "null table frame");
michael@0 331
michael@0 332 nsTableFrame::RowGroupArray rowGroups;
michael@0 333 aTableFrame->OrderRowGroups(rowGroups);
michael@0 334
michael@0 335 if (rowGroups.Length() < 1) { //degenerate case
michael@0 336 if (aPaintTableBackground) {
michael@0 337 PaintTableFrame(aTableFrame, nullptr, nullptr, nsMargin(0,0,0,0));
michael@0 338 }
michael@0 339 /* No cells; nothing else to paint */
michael@0 340 return NS_OK;
michael@0 341 }
michael@0 342
michael@0 343 if (aPaintTableBackground) {
michael@0 344 PaintTableFrame(aTableFrame, rowGroups[0], rowGroups[rowGroups.Length() - 1],
michael@0 345 aDeflate);
michael@0 346 }
michael@0 347
michael@0 348 /*Set up column background/border data*/
michael@0 349 if (mNumCols > 0) {
michael@0 350 nsFrameList& colGroupList = aTableFrame->GetColGroups();
michael@0 351 NS_ASSERTION(colGroupList.FirstChild(), "table should have at least one colgroup");
michael@0 352
michael@0 353 mCols = new ColData[mNumCols];
michael@0 354 if (!mCols) return NS_ERROR_OUT_OF_MEMORY;
michael@0 355
michael@0 356 TableBackgroundData* cgData = nullptr;
michael@0 357 nsMargin border;
michael@0 358 /* BC left borders aren't stored on cols, but the previous column's
michael@0 359 right border is the next one's left border.*/
michael@0 360 //Start with table's left border.
michael@0 361 nscoord lastLeftBorder = aTableFrame->GetContinuousLeftBCBorderWidth();
michael@0 362 for (nsTableColGroupFrame* cgFrame = static_cast<nsTableColGroupFrame*>(colGroupList.FirstChild());
michael@0 363 cgFrame; cgFrame = static_cast<nsTableColGroupFrame*>(cgFrame->GetNextSibling())) {
michael@0 364
michael@0 365 if (cgFrame->GetColCount() < 1) {
michael@0 366 //No columns, no cells, so no need for data
michael@0 367 continue;
michael@0 368 }
michael@0 369
michael@0 370 /*Create data struct for column group*/
michael@0 371 cgData = new TableBackgroundData;
michael@0 372 if (!cgData) return NS_ERROR_OUT_OF_MEMORY;
michael@0 373 cgData->SetFull(cgFrame);
michael@0 374 if (mIsBorderCollapse && cgData->ShouldSetBCBorder()) {
michael@0 375 border.left = lastLeftBorder;
michael@0 376 cgFrame->GetContinuousBCBorderWidth(border);
michael@0 377 nsresult rv = cgData->SetBCBorder(border, this);
michael@0 378 if (NS_FAILED(rv)) {
michael@0 379 cgData->Destroy(mPresContext);
michael@0 380 delete cgData;
michael@0 381 return rv;
michael@0 382 }
michael@0 383 }
michael@0 384
michael@0 385 // Boolean that indicates whether mCols took ownership of cgData
michael@0 386 bool cgDataOwnershipTaken = false;
michael@0 387
michael@0 388 /*Loop over columns in this colgroup*/
michael@0 389 for (nsTableColFrame* col = cgFrame->GetFirstColumn(); col;
michael@0 390 col = static_cast<nsTableColFrame*>(col->GetNextSibling())) {
michael@0 391 /*Create data struct for column*/
michael@0 392 uint32_t colIndex = col->GetColIndex();
michael@0 393 NS_ASSERTION(colIndex < mNumCols, "prevent array boundary violation");
michael@0 394 if (mNumCols <= colIndex)
michael@0 395 break;
michael@0 396 mCols[colIndex].mCol.SetFull(col);
michael@0 397 //Bring column mRect into table's coord system
michael@0 398 mCols[colIndex].mCol.mRect.MoveBy(cgData->mRect.x, cgData->mRect.y);
michael@0 399 //link to parent colgroup's data
michael@0 400 mCols[colIndex].mColGroup = cgData;
michael@0 401 cgDataOwnershipTaken = true;
michael@0 402 if (mIsBorderCollapse) {
michael@0 403 border.left = lastLeftBorder;
michael@0 404 lastLeftBorder = col->GetContinuousBCBorderWidth(border);
michael@0 405 if (mCols[colIndex].mCol.ShouldSetBCBorder()) {
michael@0 406 nsresult rv = mCols[colIndex].mCol.SetBCBorder(border, this);
michael@0 407 if (NS_FAILED(rv)) return rv;
michael@0 408 }
michael@0 409 }
michael@0 410 }
michael@0 411
michael@0 412 if (!cgDataOwnershipTaken) {
michael@0 413 cgData->Destroy(mPresContext);
michael@0 414 delete cgData;
michael@0 415 }
michael@0 416 }
michael@0 417 }
michael@0 418
michael@0 419 for (uint32_t i = 0; i < rowGroups.Length(); i++) {
michael@0 420 nsTableRowGroupFrame* rg = rowGroups[i];
michael@0 421 mRowGroup.SetFrame(rg);
michael@0 422 // Need to compute the right rect via GetOffsetTo, since the row
michael@0 423 // group may not be a child of the table.
michael@0 424 mRowGroup.mRect.MoveTo(rg->GetOffsetTo(aTableFrame));
michael@0 425 if (mRowGroup.mRect.Intersects(mDirtyRect - mRenderPt)) {
michael@0 426 nsresult rv = PaintRowGroup(rg, rg->IsPseudoStackingContextFromStyle());
michael@0 427 if (NS_FAILED(rv)) return rv;
michael@0 428 }
michael@0 429 }
michael@0 430 return NS_OK;
michael@0 431 }
michael@0 432
michael@0 433 nsresult
michael@0 434 TableBackgroundPainter::PaintRowGroup(nsTableRowGroupFrame* aFrame,
michael@0 435 bool aPassThrough)
michael@0 436 {
michael@0 437 NS_PRECONDITION(aFrame, "null frame");
michael@0 438
michael@0 439 if (!mRowGroup.mFrame) {
michael@0 440 mRowGroup.SetFrame(aFrame);
michael@0 441 }
michael@0 442
michael@0 443 nsTableRowFrame* firstRow = aFrame->GetFirstRow();
michael@0 444
michael@0 445 /* Load row group data */
michael@0 446 if (!aPassThrough) {
michael@0 447 mRowGroup.SetData();
michael@0 448 if (mIsBorderCollapse && mRowGroup.ShouldSetBCBorder()) {
michael@0 449 nsMargin border;
michael@0 450 if (firstRow) {
michael@0 451 //pick up first row's top border (= rg top border)
michael@0 452 firstRow->GetContinuousBCBorderWidth(border);
michael@0 453 /* (row group doesn't store its top border) */
michael@0 454 }
michael@0 455 //overwrite sides+bottom borders with rg's own
michael@0 456 aFrame->GetContinuousBCBorderWidth(border);
michael@0 457 nsresult res = mRowGroup.SetBCBorder(border, this);
michael@0 458 if (!NS_SUCCEEDED(res)) {
michael@0 459 return res;
michael@0 460 }
michael@0 461 }
michael@0 462 aPassThrough = !mRowGroup.IsVisible();
michael@0 463 }
michael@0 464
michael@0 465 /* translate everything into row group coord system*/
michael@0 466 if (eOrigin_TableRowGroup != mOrigin) {
michael@0 467 TranslateContext(mRowGroup.mRect.x, mRowGroup.mRect.y);
michael@0 468 }
michael@0 469 nsRect rgRect = mRowGroup.mRect;
michael@0 470 mRowGroup.mRect.MoveTo(0, 0);
michael@0 471
michael@0 472 /* Find the right row to start with */
michael@0 473 nscoord ignored; // We don't care about overflow above, since what we really
michael@0 474 // care about are backgrounds and overflow above doesn't
michael@0 475 // correspond to backgrounds, since cells can't span up from
michael@0 476 // their originating row. We do care about overflow below,
michael@0 477 // however, since that can be due to rowspans.
michael@0 478
michael@0 479 // Note that mDirtyRect - mRenderPt is guaranteed to be in the row
michael@0 480 // group's coordinate system here, so passing its .y to
michael@0 481 // GetFirstRowContaining is ok.
michael@0 482 nsIFrame* cursor = aFrame->GetFirstRowContaining(mDirtyRect.y - mRenderPt.y, &ignored);
michael@0 483
michael@0 484 // Sadly, it seems like there may be non-row frames in there... or something?
michael@0 485 // There are certainly null-checks in GetFirstRow() and GetNextRow(). :(
michael@0 486 while (cursor && cursor->GetType() != nsGkAtoms::tableRowFrame) {
michael@0 487 cursor = cursor->GetNextSibling();
michael@0 488 }
michael@0 489
michael@0 490 // It's OK if cursor is null here.
michael@0 491 nsTableRowFrame* row = static_cast<nsTableRowFrame*>(cursor);
michael@0 492 if (!row) {
michael@0 493 // No useful cursor; just start at the top. Don't bother to set up a
michael@0 494 // cursor; if we've gotten this far then we've already built the display
michael@0 495 // list for the rowgroup, so not having a cursor means that there's some
michael@0 496 // good reason we don't have a cursor and we shouldn't create one here.
michael@0 497 row = firstRow;
michael@0 498 }
michael@0 499
michael@0 500 /* Finally paint */
michael@0 501 for (; row; row = row->GetNextRow()) {
michael@0 502 mRow.SetFrame(row);
michael@0 503 if (mDirtyRect.YMost() - mRenderPt.y < mRow.mRect.y) { // Intersect wouldn't handle
michael@0 504 // rowspans.
michael@0 505
michael@0 506 // All done; cells originating in later rows can't intersect mDirtyRect.
michael@0 507 break;
michael@0 508 }
michael@0 509
michael@0 510 nsresult rv = PaintRow(row, aPassThrough || row->IsPseudoStackingContextFromStyle());
michael@0 511 if (NS_FAILED(rv)) return rv;
michael@0 512 }
michael@0 513
michael@0 514 /* translate back into table coord system */
michael@0 515 if (eOrigin_TableRowGroup != mOrigin) {
michael@0 516 TranslateContext(-rgRect.x, -rgRect.y);
michael@0 517 }
michael@0 518
michael@0 519 /* unload rg data */
michael@0 520 mRowGroup.Clear();
michael@0 521
michael@0 522 return NS_OK;
michael@0 523 }
michael@0 524
michael@0 525 nsresult
michael@0 526 TableBackgroundPainter::PaintRow(nsTableRowFrame* aFrame,
michael@0 527 bool aPassThrough)
michael@0 528 {
michael@0 529 NS_PRECONDITION(aFrame, "null frame");
michael@0 530
michael@0 531 if (!mRow.mFrame) {
michael@0 532 mRow.SetFrame(aFrame);
michael@0 533 }
michael@0 534
michael@0 535 /* Load row data */
michael@0 536 if (!aPassThrough) {
michael@0 537 mRow.SetData();
michael@0 538 if (mIsBorderCollapse && mRow.ShouldSetBCBorder()) {
michael@0 539 nsMargin border;
michael@0 540 nsTableRowFrame* nextRow = aFrame->GetNextRow();
michael@0 541 if (nextRow) { //outer top below us is inner bottom for us
michael@0 542 border.bottom = nextRow->GetOuterTopContBCBorderWidth();
michael@0 543 }
michael@0 544 else { //acquire rg's bottom border
michael@0 545 nsTableRowGroupFrame* rowGroup = static_cast<nsTableRowGroupFrame*>(aFrame->GetParent());
michael@0 546 rowGroup->GetContinuousBCBorderWidth(border);
michael@0 547 }
michael@0 548 //get the rest of the borders; will overwrite all but bottom
michael@0 549 aFrame->GetContinuousBCBorderWidth(border);
michael@0 550
michael@0 551 nsresult res = mRow.SetBCBorder(border, this);
michael@0 552 if (!NS_SUCCEEDED(res)) {
michael@0 553 return res;
michael@0 554 }
michael@0 555 }
michael@0 556 aPassThrough = !mRow.IsVisible();
michael@0 557 }
michael@0 558
michael@0 559 /* Translate */
michael@0 560 if (eOrigin_TableRow == mOrigin) {
michael@0 561 /* If we originate from the row, then make the row the origin. */
michael@0 562 mRow.mRect.MoveTo(0, 0);
michael@0 563 }
michael@0 564 //else: Use row group's coord system -> no translation necessary
michael@0 565
michael@0 566 for (nsTableCellFrame* cell = aFrame->GetFirstCell(); cell; cell = cell->GetNextCell()) {
michael@0 567 //Translate to use the same coord system as mRow.
michael@0 568 mCellRect = cell->GetRect() + mRow.mRect.TopLeft() + mRenderPt;
michael@0 569 if (mCellRect.Intersects(mDirtyRect)) {
michael@0 570 nsresult rv = PaintCell(cell, aPassThrough || cell->IsPseudoStackingContextFromStyle());
michael@0 571 if (NS_FAILED(rv)) return rv;
michael@0 572 }
michael@0 573 }
michael@0 574
michael@0 575 /* Unload row data */
michael@0 576 mRow.Clear();
michael@0 577 return NS_OK;
michael@0 578 }
michael@0 579
michael@0 580 nsresult
michael@0 581 TableBackgroundPainter::PaintCell(nsTableCellFrame* aCell,
michael@0 582 bool aPassSelf)
michael@0 583 {
michael@0 584 NS_PRECONDITION(aCell, "null frame");
michael@0 585
michael@0 586 const nsStyleTableBorder* cellTableStyle;
michael@0 587 cellTableStyle = aCell->StyleTableBorder();
michael@0 588 if (!(NS_STYLE_TABLE_EMPTY_CELLS_SHOW == cellTableStyle->mEmptyCells ||
michael@0 589 NS_STYLE_TABLE_EMPTY_CELLS_SHOW_BACKGROUND == cellTableStyle->mEmptyCells)
michael@0 590 && aCell->GetContentEmpty() && !mIsBorderCollapse) {
michael@0 591 return NS_OK;
michael@0 592 }
michael@0 593
michael@0 594 int32_t colIndex;
michael@0 595 aCell->GetColIndex(colIndex);
michael@0 596 NS_ASSERTION(colIndex < int32_t(mNumCols), "prevent array boundary violation");
michael@0 597 if (int32_t(mNumCols) <= colIndex)
michael@0 598 return NS_OK;
michael@0 599
michael@0 600 //Paint column group background
michael@0 601 if (mCols && mCols[colIndex].mColGroup && mCols[colIndex].mColGroup->IsVisible()) {
michael@0 602 nsCSSRendering::PaintBackgroundWithSC(mPresContext, mRenderingContext,
michael@0 603 mCols[colIndex].mColGroup->mFrame, mDirtyRect,
michael@0 604 mCols[colIndex].mColGroup->mRect + mRenderPt,
michael@0 605 mCols[colIndex].mColGroup->mFrame->StyleContext(),
michael@0 606 *mCols[colIndex].mColGroup->mBorder,
michael@0 607 mBGPaintFlags, &mCellRect);
michael@0 608 }
michael@0 609
michael@0 610 //Paint column background
michael@0 611 if (mCols && mCols[colIndex].mCol.IsVisible()) {
michael@0 612 nsCSSRendering::PaintBackgroundWithSC(mPresContext, mRenderingContext,
michael@0 613 mCols[colIndex].mCol.mFrame, mDirtyRect,
michael@0 614 mCols[colIndex].mCol.mRect + mRenderPt,
michael@0 615 mCols[colIndex].mCol.mFrame->StyleContext(),
michael@0 616 *mCols[colIndex].mCol.mBorder,
michael@0 617 mBGPaintFlags, &mCellRect);
michael@0 618 }
michael@0 619
michael@0 620 //Paint row group background
michael@0 621 if (mRowGroup.IsVisible()) {
michael@0 622 nsCSSRendering::PaintBackgroundWithSC(mPresContext, mRenderingContext,
michael@0 623 mRowGroup.mFrame, mDirtyRect,
michael@0 624 mRowGroup.mRect + mRenderPt,
michael@0 625 mRowGroup.mFrame->StyleContext(),
michael@0 626 *mRowGroup.mBorder,
michael@0 627 mBGPaintFlags, &mCellRect);
michael@0 628 }
michael@0 629
michael@0 630 //Paint row background
michael@0 631 if (mRow.IsVisible()) {
michael@0 632 nsCSSRendering::PaintBackgroundWithSC(mPresContext, mRenderingContext,
michael@0 633 mRow.mFrame, mDirtyRect,
michael@0 634 mRow.mRect + mRenderPt,
michael@0 635 mRow.mFrame->StyleContext(),
michael@0 636 *mRow.mBorder,
michael@0 637 mBGPaintFlags, &mCellRect);
michael@0 638 }
michael@0 639
michael@0 640 //Paint cell background in border-collapse unless we're just passing
michael@0 641 if (mIsBorderCollapse && !aPassSelf) {
michael@0 642 aCell->PaintCellBackground(mRenderingContext, mDirtyRect,
michael@0 643 mCellRect.TopLeft(), mBGPaintFlags);
michael@0 644 }
michael@0 645
michael@0 646 return NS_OK;
michael@0 647 }

mercurial