Fri, 16 Jan 2015 04:50:19 +0100
Replace accessor implementation with direct member state manipulation, by
request https://trac.torproject.org/projects/tor/ticket/9701#comment:32
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 | } |