1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=2 sw=2 et tw=80: */
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is mozilla.org code.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Netscape Communications Corporation.
20 : * Portions created by the Initial Developer are Copyright (C) 1998
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Pierre Phaneuf <pp@ludusdesign.com>
25 : * Mats Palmgren <matspal@gmail.com>
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either of the GNU General Public License Version 2 or later (the "GPL"),
29 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 : #include "nsCOMPtr.h"
41 : #include "nsTableFrame.h"
42 : #include "nsRenderingContext.h"
43 : #include "nsStyleContext.h"
44 : #include "nsStyleConsts.h"
45 : #include "nsIContent.h"
46 : #include "nsCellMap.h"
47 : #include "nsTableCellFrame.h"
48 : #include "nsHTMLParts.h"
49 : #include "nsTableColFrame.h"
50 : #include "nsTableColGroupFrame.h"
51 : #include "nsTableRowFrame.h"
52 : #include "nsTableRowGroupFrame.h"
53 : #include "nsTableOuterFrame.h"
54 : #include "nsTablePainter.h"
55 :
56 : #include "BasicTableLayoutStrategy.h"
57 : #include "FixedTableLayoutStrategy.h"
58 :
59 : #include "nsPresContext.h"
60 : #include "nsCSSRendering.h"
61 : #include "nsGkAtoms.h"
62 : #include "nsCSSAnonBoxes.h"
63 : #include "nsIPresShell.h"
64 : #include "nsIDOMElement.h"
65 : #include "nsIDOMHTMLElement.h"
66 : #include "nsIDOMHTMLBodyElement.h"
67 : #include "nsFrameManager.h"
68 : #include "nsLayoutErrors.h"
69 : #include "nsAutoPtr.h"
70 : #include "nsCSSFrameConstructor.h"
71 : #include "nsStyleSet.h"
72 : #include "nsDisplayList.h"
73 : #include "nsIScrollableFrame.h"
74 : #include "nsCSSProps.h"
75 :
76 : using namespace mozilla;
77 : using namespace mozilla::layout;
78 :
79 : /********************************************************************************
80 : ** nsTableReflowState **
81 : ********************************************************************************/
82 :
83 : struct nsTableReflowState {
84 :
85 : // the real reflow state
86 : const nsHTMLReflowState& reflowState;
87 :
88 : // The table's available size
89 : nsSize availSize;
90 :
91 : // Stationary x-offset
92 : nscoord x;
93 :
94 : // Running y-offset
95 : nscoord y;
96 :
97 0 : nsTableReflowState(nsPresContext& aPresContext,
98 : const nsHTMLReflowState& aReflowState,
99 : nsTableFrame& aTableFrame,
100 : nscoord aAvailWidth,
101 : nscoord aAvailHeight)
102 0 : : reflowState(aReflowState)
103 : {
104 0 : Init(aPresContext, aTableFrame, aAvailWidth, aAvailHeight);
105 0 : }
106 :
107 0 : void Init(nsPresContext& aPresContext,
108 : nsTableFrame& aTableFrame,
109 : nscoord aAvailWidth,
110 : nscoord aAvailHeight)
111 : {
112 0 : nsTableFrame* table = (nsTableFrame*)aTableFrame.GetFirstInFlow();
113 0 : nsMargin borderPadding = table->GetChildAreaOffset(&reflowState);
114 0 : nscoord cellSpacingX = table->GetCellSpacingX();
115 :
116 0 : x = borderPadding.left + cellSpacingX;
117 0 : y = borderPadding.top; //cellspacing added during reflow
118 :
119 0 : availSize.width = aAvailWidth;
120 0 : if (NS_UNCONSTRAINEDSIZE != availSize.width) {
121 : availSize.width -= borderPadding.left + borderPadding.right
122 0 : + (2 * cellSpacingX);
123 0 : availSize.width = NS_MAX(0, availSize.width);
124 : }
125 :
126 0 : availSize.height = aAvailHeight;
127 0 : if (NS_UNCONSTRAINEDSIZE != availSize.height) {
128 : availSize.height -= borderPadding.top + borderPadding.bottom
129 0 : + (2 * table->GetCellSpacingY());
130 0 : availSize.height = NS_MAX(0, availSize.height);
131 : }
132 0 : }
133 :
134 : nsTableReflowState(nsPresContext& aPresContext,
135 : const nsHTMLReflowState& aReflowState,
136 : nsTableFrame& aTableFrame)
137 : : reflowState(aReflowState)
138 : {
139 : Init(aPresContext, aTableFrame, aReflowState.availableWidth, aReflowState.availableHeight);
140 : }
141 :
142 : };
143 :
144 : /********************************************************************************
145 : ** nsTableFrame **
146 : ********************************************************************************/
147 :
148 : struct BCPropertyData
149 : {
150 0 : BCPropertyData() : mTopBorderWidth(0), mRightBorderWidth(0),
151 : mBottomBorderWidth(0), mLeftBorderWidth(0),
152 0 : mLeftCellBorderWidth(0), mRightCellBorderWidth(0) {}
153 : nsIntRect mDamageArea;
154 : BCPixelSize mTopBorderWidth;
155 : BCPixelSize mRightBorderWidth;
156 : BCPixelSize mBottomBorderWidth;
157 : BCPixelSize mLeftBorderWidth;
158 : BCPixelSize mLeftCellBorderWidth;
159 : BCPixelSize mRightCellBorderWidth;
160 : };
161 :
162 : nsIFrame*
163 0 : nsTableFrame::GetParentStyleContextFrame() const
164 : {
165 : // Since our parent, the table outer frame, returned this frame, we
166 : // must return whatever our parent would normally have returned.
167 :
168 0 : NS_PRECONDITION(mParent, "table constructed without outer table");
169 0 : if (!mContent->GetParent() && !GetStyleContext()->GetPseudo()) {
170 : // We're the root. We have no style context parent.
171 0 : return nsnull;
172 : }
173 :
174 0 : return static_cast<nsFrame*>(GetParent())->DoGetParentStyleContextFrame();
175 : }
176 :
177 :
178 : nsIAtom*
179 0 : nsTableFrame::GetType() const
180 : {
181 0 : return nsGkAtoms::tableFrame;
182 : }
183 :
184 :
185 0 : nsTableFrame::nsTableFrame(nsStyleContext* aContext)
186 : : nsContainerFrame(aContext),
187 : mCellMap(nsnull),
188 0 : mTableLayoutStrategy(nsnull)
189 : {
190 0 : memset(&mBits, 0, sizeof(mBits));
191 0 : }
192 :
193 0 : NS_QUERYFRAME_HEAD(nsTableFrame)
194 0 : NS_QUERYFRAME_ENTRY(nsITableLayout)
195 0 : NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
196 :
197 : NS_IMETHODIMP
198 0 : nsTableFrame::Init(nsIContent* aContent,
199 : nsIFrame* aParent,
200 : nsIFrame* aPrevInFlow)
201 : {
202 0 : NS_PRECONDITION(!mCellMap, "Init called twice");
203 0 : NS_PRECONDITION(!aPrevInFlow ||
204 : aPrevInFlow->GetType() == nsGkAtoms::tableFrame,
205 : "prev-in-flow must be of same type");
206 :
207 : // Let the base class do its processing
208 0 : nsresult rv = nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
209 :
210 : // see if border collapse is on, if so set it
211 0 : const nsStyleTableBorder* tableStyle = GetStyleTableBorder();
212 0 : bool borderCollapse = (NS_STYLE_BORDER_COLLAPSE == tableStyle->mBorderCollapse);
213 0 : SetBorderCollapse(borderCollapse);
214 :
215 : // Transforms need to affect the outer frame, not the inner frame (bug 722777)
216 0 : mState &= ~NS_FRAME_MAY_BE_TRANSFORMED;
217 :
218 : // Create the cell map if this frame is the first-in-flow.
219 0 : if (!aPrevInFlow) {
220 0 : mCellMap = new nsTableCellMap(*this, borderCollapse);
221 0 : if (!mCellMap)
222 0 : return NS_ERROR_OUT_OF_MEMORY;
223 : }
224 :
225 0 : if (aPrevInFlow) {
226 : // set my width, because all frames in a table flow are the same width and
227 : // code in nsTableOuterFrame depends on this being set
228 0 : mRect.width = aPrevInFlow->GetSize().width;
229 : }
230 : else {
231 0 : NS_ASSERTION(!mTableLayoutStrategy, "strategy was created before Init was called");
232 : // create the strategy
233 0 : if (IsAutoLayout())
234 0 : mTableLayoutStrategy = new BasicTableLayoutStrategy(this);
235 : else
236 0 : mTableLayoutStrategy = new FixedTableLayoutStrategy(this);
237 0 : if (!mTableLayoutStrategy)
238 0 : return NS_ERROR_OUT_OF_MEMORY;
239 : }
240 :
241 0 : return rv;
242 : }
243 :
244 0 : nsTableFrame::~nsTableFrame()
245 : {
246 0 : delete mCellMap;
247 0 : delete mTableLayoutStrategy;
248 0 : }
249 :
250 : void
251 0 : nsTableFrame::DestroyFrom(nsIFrame* aDestructRoot)
252 : {
253 0 : mColGroups.DestroyFramesFrom(aDestructRoot);
254 0 : nsContainerFrame::DestroyFrom(aDestructRoot);
255 0 : }
256 :
257 : // Make sure any views are positioned properly
258 : void
259 0 : nsTableFrame::RePositionViews(nsIFrame* aFrame)
260 : {
261 0 : nsContainerFrame::PositionFrameView(aFrame);
262 0 : nsContainerFrame::PositionChildViews(aFrame);
263 0 : }
264 :
265 : static bool
266 0 : IsRepeatedFrame(nsIFrame* kidFrame)
267 : {
268 0 : return (kidFrame->GetType() == nsGkAtoms::tableRowFrame ||
269 0 : kidFrame->GetType() == nsGkAtoms::tableRowGroupFrame) &&
270 0 : (kidFrame->GetStateBits() & NS_REPEATED_ROW_OR_ROWGROUP);
271 : }
272 :
273 : bool
274 0 : nsTableFrame::PageBreakAfter(nsIFrame* aSourceFrame,
275 : nsIFrame* aNextFrame)
276 : {
277 0 : const nsStyleDisplay* display = aSourceFrame->GetStyleDisplay();
278 0 : nsTableRowGroupFrame* prevRg = do_QueryFrame(aSourceFrame);
279 : // don't allow a page break after a repeated element ...
280 0 : if ((display->mBreakAfter || (prevRg && prevRg->HasInternalBreakAfter())) &&
281 0 : !IsRepeatedFrame(aSourceFrame)) {
282 0 : return !(aNextFrame && IsRepeatedFrame(aNextFrame)); // or before
283 : }
284 :
285 0 : if (aNextFrame) {
286 0 : display = aNextFrame->GetStyleDisplay();
287 : // don't allow a page break before a repeated element ...
288 0 : nsTableRowGroupFrame* nextRg = do_QueryFrame(aNextFrame);
289 0 : if ((display->mBreakBefore ||
290 0 : (nextRg && nextRg->HasInternalBreakBefore())) &&
291 0 : !IsRepeatedFrame(aNextFrame)) {
292 0 : return !IsRepeatedFrame(aSourceFrame); // or after
293 : }
294 : }
295 0 : return false;
296 : }
297 :
298 : // XXX this needs to be cleaned up so that the frame constructor breaks out col group
299 : // frames into a separate child list, bug 343048.
300 : NS_IMETHODIMP
301 0 : nsTableFrame::SetInitialChildList(ChildListID aListID,
302 : nsFrameList& aChildList)
303 : {
304 :
305 0 : if (!mFrames.IsEmpty() || !mColGroups.IsEmpty()) {
306 : // We already have child frames which means we've already been
307 : // initialized
308 0 : NS_NOTREACHED("unexpected second call to SetInitialChildList");
309 0 : return NS_ERROR_UNEXPECTED;
310 : }
311 0 : if (aListID != kPrincipalList) {
312 : // All we know about is the principal child list.
313 0 : NS_NOTREACHED("unknown frame list");
314 0 : return NS_ERROR_INVALID_ARG;
315 : }
316 :
317 : // XXXbz the below code is an icky cesspit that's only needed in its current
318 : // form for two reasons:
319 : // 1) Both rowgroups and column groups come in on the principal child list.
320 0 : while (aChildList.NotEmpty()) {
321 0 : nsIFrame* childFrame = aChildList.FirstChild();
322 0 : aChildList.RemoveFirstChild();
323 0 : const nsStyleDisplay* childDisplay = childFrame->GetStyleDisplay();
324 :
325 0 : if (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == childDisplay->mDisplay) {
326 0 : NS_ASSERTION(nsGkAtoms::tableColGroupFrame == childFrame->GetType(),
327 : "This is not a colgroup");
328 0 : mColGroups.AppendFrame(nsnull, childFrame);
329 : }
330 : else { // row groups and unknown frames go on the main list for now
331 0 : mFrames.AppendFrame(nsnull, childFrame);
332 : }
333 : }
334 :
335 : // If we have a prev-in-flow, then we're a table that has been split and
336 : // so don't treat this like an append
337 0 : if (!GetPrevInFlow()) {
338 : // process col groups first so that real cols get constructed before
339 : // anonymous ones due to cells in rows.
340 0 : InsertColGroups(0, mColGroups);
341 0 : InsertRowGroups(mFrames);
342 : // calc collapsing borders
343 0 : if (IsBorderCollapse()) {
344 0 : SetFullBCDamageArea();
345 : }
346 : }
347 :
348 0 : return NS_OK;
349 : }
350 :
351 0 : void nsTableFrame::AttributeChangedFor(nsIFrame* aFrame,
352 : nsIContent* aContent,
353 : nsIAtom* aAttribute)
354 : {
355 0 : nsTableCellFrame *cellFrame = do_QueryFrame(aFrame);
356 0 : if (cellFrame) {
357 0 : if ((nsGkAtoms::rowspan == aAttribute) ||
358 : (nsGkAtoms::colspan == aAttribute)) {
359 0 : nsTableCellMap* cellMap = GetCellMap();
360 0 : if (cellMap) {
361 : // for now just remove the cell from the map and reinsert it
362 : PRInt32 rowIndex, colIndex;
363 0 : cellFrame->GetRowIndex(rowIndex);
364 0 : cellFrame->GetColIndex(colIndex);
365 0 : RemoveCell(cellFrame, rowIndex);
366 0 : nsAutoTArray<nsTableCellFrame*, 1> cells;
367 0 : cells.AppendElement(cellFrame);
368 0 : InsertCells(cells, rowIndex, colIndex - 1);
369 :
370 : // XXX Should this use eStyleChange? It currently doesn't need
371 : // to, but it might given more optimization.
372 0 : PresContext()->PresShell()->
373 0 : FrameNeedsReflow(this, nsIPresShell::eTreeChange, NS_FRAME_IS_DIRTY);
374 : }
375 : }
376 : }
377 0 : }
378 :
379 :
380 : /* ****** CellMap methods ******* */
381 :
382 : /* return the effective col count */
383 0 : PRInt32 nsTableFrame::GetEffectiveColCount() const
384 : {
385 0 : PRInt32 colCount = GetColCount();
386 0 : if (LayoutStrategy()->GetType() == nsITableLayoutStrategy::Auto) {
387 0 : nsTableCellMap* cellMap = GetCellMap();
388 0 : if (!cellMap) {
389 0 : return 0;
390 : }
391 : // don't count cols at the end that don't have originating cells
392 0 : for (PRInt32 colX = colCount - 1; colX >= 0; colX--) {
393 0 : if (cellMap->GetNumCellsOriginatingInCol(colX) > 0) {
394 0 : break;
395 : }
396 0 : colCount--;
397 : }
398 : }
399 0 : return colCount;
400 : }
401 :
402 0 : PRInt32 nsTableFrame::GetIndexOfLastRealCol()
403 : {
404 0 : PRInt32 numCols = mColFrames.Length();
405 0 : if (numCols > 0) {
406 0 : for (PRInt32 colX = numCols - 1; colX >= 0; colX--) {
407 0 : nsTableColFrame* colFrame = GetColFrame(colX);
408 0 : if (colFrame) {
409 0 : if (eColAnonymousCell != colFrame->GetColType()) {
410 0 : return colX;
411 : }
412 : }
413 : }
414 : }
415 0 : return -1;
416 : }
417 :
418 : nsTableColFrame*
419 0 : nsTableFrame::GetColFrame(PRInt32 aColIndex) const
420 : {
421 0 : NS_ASSERTION(!GetPrevInFlow(), "GetColFrame called on next in flow");
422 0 : PRInt32 numCols = mColFrames.Length();
423 0 : if ((aColIndex >= 0) && (aColIndex < numCols)) {
424 0 : return mColFrames.ElementAt(aColIndex);
425 : }
426 : else {
427 0 : NS_ERROR("invalid col index");
428 0 : return nsnull;
429 : }
430 : }
431 :
432 0 : PRInt32 nsTableFrame::GetEffectiveRowSpan(PRInt32 aRowIndex,
433 : const nsTableCellFrame& aCell) const
434 : {
435 0 : nsTableCellMap* cellMap = GetCellMap();
436 0 : NS_PRECONDITION (nsnull != cellMap, "bad call, cellMap not yet allocated.");
437 :
438 : PRInt32 colIndex;
439 0 : aCell.GetColIndex(colIndex);
440 0 : return cellMap->GetEffectiveRowSpan(aRowIndex, colIndex);
441 : }
442 :
443 0 : PRInt32 nsTableFrame::GetEffectiveRowSpan(const nsTableCellFrame& aCell,
444 : nsCellMap* aCellMap)
445 : {
446 0 : nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT1(1);
447 :
448 : PRInt32 colIndex, rowIndex;
449 0 : aCell.GetColIndex(colIndex);
450 0 : aCell.GetRowIndex(rowIndex);
451 :
452 0 : if (aCellMap)
453 0 : return aCellMap->GetRowSpan(rowIndex, colIndex, true);
454 : else
455 0 : return tableCellMap->GetEffectiveRowSpan(rowIndex, colIndex);
456 : }
457 :
458 0 : PRInt32 nsTableFrame::GetEffectiveColSpan(const nsTableCellFrame& aCell,
459 : nsCellMap* aCellMap) const
460 : {
461 0 : nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT1(1);
462 :
463 : PRInt32 colIndex, rowIndex;
464 0 : aCell.GetColIndex(colIndex);
465 0 : aCell.GetRowIndex(rowIndex);
466 : bool ignore;
467 :
468 0 : if (aCellMap)
469 0 : return aCellMap->GetEffectiveColSpan(*tableCellMap, rowIndex, colIndex, ignore);
470 : else
471 0 : return tableCellMap->GetEffectiveColSpan(rowIndex, colIndex);
472 : }
473 :
474 0 : bool nsTableFrame::HasMoreThanOneCell(PRInt32 aRowIndex) const
475 : {
476 0 : nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT1(1);
477 0 : return tableCellMap->HasMoreThanOneCell(aRowIndex);
478 : }
479 :
480 0 : void nsTableFrame::AdjustRowIndices(PRInt32 aRowIndex,
481 : PRInt32 aAdjustment)
482 : {
483 : // Iterate over the row groups and adjust the row indices of all rows
484 : // whose index is >= aRowIndex.
485 0 : RowGroupArray rowGroups;
486 0 : OrderRowGroups(rowGroups);
487 :
488 0 : for (PRUint32 rgX = 0; rgX < rowGroups.Length(); rgX++) {
489 0 : rowGroups[rgX]->AdjustRowIndices(aRowIndex, aAdjustment);
490 : }
491 0 : }
492 :
493 :
494 0 : void nsTableFrame::ResetRowIndices(const nsFrameList::Slice& aRowGroupsToExclude)
495 : {
496 : // Iterate over the row groups and adjust the row indices of all rows
497 : // omit the rowgroups that will be inserted later
498 0 : RowGroupArray rowGroups;
499 0 : OrderRowGroups(rowGroups);
500 :
501 0 : PRInt32 rowIndex = 0;
502 0 : nsTHashtable<nsPtrHashKey<nsTableRowGroupFrame> > excludeRowGroups;
503 0 : if (!excludeRowGroups.Init()) {
504 0 : NS_ERROR("Failed to initialize excludeRowGroups hash.");
505 : return;
506 : }
507 0 : nsFrameList::Enumerator excludeRowGroupsEnumerator(aRowGroupsToExclude);
508 0 : while (!excludeRowGroupsEnumerator.AtEnd()) {
509 0 : excludeRowGroups.PutEntry(static_cast<nsTableRowGroupFrame*>(excludeRowGroupsEnumerator.get()));
510 0 : excludeRowGroupsEnumerator.Next();
511 : }
512 :
513 0 : for (PRUint32 rgX = 0; rgX < rowGroups.Length(); rgX++) {
514 0 : nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
515 0 : if (!excludeRowGroups.GetEntry(rgFrame)) {
516 0 : const nsFrameList& rowFrames = rgFrame->PrincipalChildList();
517 0 : for (nsFrameList::Enumerator rows(rowFrames); !rows.AtEnd(); rows.Next()) {
518 0 : if (NS_STYLE_DISPLAY_TABLE_ROW==rows.get()->GetStyleDisplay()->mDisplay) {
519 0 : ((nsTableRowFrame *)rows.get())->SetRowIndex(rowIndex);
520 0 : rowIndex++;
521 : }
522 : }
523 : }
524 : }
525 : }
526 0 : void nsTableFrame::InsertColGroups(PRInt32 aStartColIndex,
527 : const nsFrameList::Slice& aColGroups)
528 : {
529 0 : PRInt32 colIndex = aStartColIndex;
530 0 : nsFrameList::Enumerator colGroups(aColGroups);
531 0 : for (; !colGroups.AtEnd(); colGroups.Next()) {
532 : nsTableColGroupFrame* cgFrame =
533 0 : static_cast<nsTableColGroupFrame*>(colGroups.get());
534 0 : cgFrame->SetStartColumnIndex(colIndex);
535 : // XXXbz this sucks. AddColsToTable will actually remove colgroups from
536 : // the list we're traversing! Need to fix things here. :( I guess this is
537 : // why the old code used pointer-to-last-frame as opposed to
538 : // pointer-to-frame-after-last....
539 :
540 : // How about dealing with this by storing a const reference to the
541 : // mNextSibling of the framelist's last frame, instead of storing a pointer
542 : // to the first-after-next frame? Will involve making nsFrameList friend
543 : // of nsIFrame, but it's time for that anyway.
544 : cgFrame->AddColsToTable(colIndex, false,
545 0 : colGroups.get()->PrincipalChildList());
546 0 : PRInt32 numCols = cgFrame->GetColCount();
547 0 : colIndex += numCols;
548 : }
549 :
550 0 : nsFrameList::Enumerator remainingColgroups = colGroups.GetUnlimitedEnumerator();
551 0 : if (!remainingColgroups.AtEnd()) {
552 : nsTableColGroupFrame::ResetColIndices(
553 0 : static_cast<nsTableColGroupFrame*>(remainingColgroups.get()), colIndex);
554 : }
555 0 : }
556 :
557 0 : void nsTableFrame::InsertCol(nsTableColFrame& aColFrame,
558 : PRInt32 aColIndex)
559 : {
560 0 : mColFrames.InsertElementAt(aColIndex, &aColFrame);
561 0 : nsTableColType insertedColType = aColFrame.GetColType();
562 0 : PRInt32 numCacheCols = mColFrames.Length();
563 0 : nsTableCellMap* cellMap = GetCellMap();
564 0 : if (cellMap) {
565 0 : PRInt32 numMapCols = cellMap->GetColCount();
566 0 : if (numCacheCols > numMapCols) {
567 0 : bool removedFromCache = false;
568 0 : if (eColAnonymousCell != insertedColType) {
569 0 : nsTableColFrame* lastCol = mColFrames.ElementAt(numCacheCols - 1);
570 0 : if (lastCol) {
571 0 : nsTableColType lastColType = lastCol->GetColType();
572 0 : if (eColAnonymousCell == lastColType) {
573 : // remove the col from the cache
574 0 : mColFrames.RemoveElementAt(numCacheCols - 1);
575 : // remove the col from the eColGroupAnonymousCell col group
576 0 : nsTableColGroupFrame* lastColGroup = (nsTableColGroupFrame *)mColGroups.LastChild();
577 0 : if (lastColGroup) {
578 0 : lastColGroup->RemoveChild(*lastCol, false);
579 :
580 : // remove the col group if it is empty
581 0 : if (lastColGroup->GetColCount() <= 0) {
582 0 : mColGroups.DestroyFrame((nsIFrame*)lastColGroup);
583 : }
584 : }
585 0 : removedFromCache = true;
586 : }
587 : }
588 : }
589 0 : if (!removedFromCache) {
590 0 : cellMap->AddColsAtEnd(1);
591 : }
592 : }
593 : }
594 : // for now, just bail and recalc all of the collapsing borders
595 0 : if (IsBorderCollapse()) {
596 0 : nsIntRect damageArea(aColIndex, 0, 1, GetRowCount());
597 0 : AddBCDamageArea(damageArea);
598 : }
599 0 : }
600 :
601 0 : void nsTableFrame::RemoveCol(nsTableColGroupFrame* aColGroupFrame,
602 : PRInt32 aColIndex,
603 : bool aRemoveFromCache,
604 : bool aRemoveFromCellMap)
605 : {
606 0 : if (aRemoveFromCache) {
607 0 : mColFrames.RemoveElementAt(aColIndex);
608 : }
609 0 : if (aRemoveFromCellMap) {
610 0 : nsTableCellMap* cellMap = GetCellMap();
611 0 : if (cellMap) {
612 0 : AppendAnonymousColFrames(1);
613 : }
614 : }
615 : // for now, just bail and recalc all of the collapsing borders
616 0 : if (IsBorderCollapse()) {
617 0 : nsIntRect damageArea(0, 0, GetColCount(), GetRowCount());
618 0 : AddBCDamageArea(damageArea);
619 : }
620 0 : }
621 :
622 : /** Get the cell map for this table frame. It is not always mCellMap.
623 : * Only the firstInFlow has a legit cell map
624 : */
625 0 : nsTableCellMap* nsTableFrame::GetCellMap() const
626 : {
627 0 : nsTableFrame* firstInFlow = (nsTableFrame *)GetFirstInFlow();
628 0 : return firstInFlow->mCellMap;
629 : }
630 :
631 : // XXX this needs to be moved to nsCSSFrameConstructor
632 : nsTableColGroupFrame*
633 0 : nsTableFrame::CreateAnonymousColGroupFrame(nsTableColGroupType aColGroupType)
634 : {
635 0 : nsIContent* colGroupContent = GetContent();
636 0 : nsPresContext* presContext = PresContext();
637 0 : nsIPresShell *shell = presContext->PresShell();
638 :
639 0 : nsRefPtr<nsStyleContext> colGroupStyle;
640 : colGroupStyle = shell->StyleSet()->
641 0 : ResolveAnonymousBoxStyle(nsCSSAnonBoxes::tableColGroup, mStyleContext);
642 : // Create a col group frame
643 0 : nsIFrame* newFrame = NS_NewTableColGroupFrame(shell, colGroupStyle);
644 0 : if (newFrame) {
645 0 : ((nsTableColGroupFrame *)newFrame)->SetColType(aColGroupType);
646 0 : newFrame->Init(colGroupContent, this, nsnull);
647 : }
648 0 : return (nsTableColGroupFrame *)newFrame;
649 : }
650 :
651 : void
652 0 : nsTableFrame::AppendAnonymousColFrames(PRInt32 aNumColsToAdd)
653 : {
654 : // get the last col group frame
655 : nsTableColGroupFrame* colGroupFrame =
656 0 : static_cast<nsTableColGroupFrame*>(mColGroups.LastChild());
657 :
658 0 : if (!colGroupFrame ||
659 0 : (colGroupFrame->GetColType() != eColGroupAnonymousCell)) {
660 : PRInt32 colIndex = (colGroupFrame) ?
661 0 : colGroupFrame->GetStartColumnIndex() +
662 0 : colGroupFrame->GetColCount() : 0;
663 0 : colGroupFrame = CreateAnonymousColGroupFrame(eColGroupAnonymousCell);
664 0 : if (!colGroupFrame) {
665 0 : return;
666 : }
667 : // add the new frame to the child list
668 0 : mColGroups.AppendFrame(this, colGroupFrame);
669 0 : colGroupFrame->SetStartColumnIndex(colIndex);
670 : }
671 : AppendAnonymousColFrames(colGroupFrame, aNumColsToAdd, eColAnonymousCell,
672 0 : true);
673 :
674 : }
675 :
676 : // XXX this needs to be moved to nsCSSFrameConstructor
677 : // Right now it only creates the col frames at the end
678 : void
679 0 : nsTableFrame::AppendAnonymousColFrames(nsTableColGroupFrame* aColGroupFrame,
680 : PRInt32 aNumColsToAdd,
681 : nsTableColType aColType,
682 : bool aAddToTable)
683 : {
684 0 : NS_PRECONDITION(aColGroupFrame, "null frame");
685 0 : NS_PRECONDITION(aColType != eColAnonymousCol, "Shouldn't happen");
686 :
687 0 : nsIPresShell *shell = PresContext()->PresShell();
688 :
689 : // Get the last col frame
690 0 : nsFrameList newColFrames;
691 :
692 0 : PRInt32 startIndex = mColFrames.Length();
693 0 : PRInt32 lastIndex = startIndex + aNumColsToAdd - 1;
694 :
695 0 : for (PRInt32 childX = startIndex; childX <= lastIndex; childX++) {
696 : nsIContent* iContent;
697 0 : nsRefPtr<nsStyleContext> styleContext;
698 : nsStyleContext* parentStyleContext;
699 :
700 : // all anonymous cols that we create here use a pseudo style context of the
701 : // col group
702 0 : iContent = aColGroupFrame->GetContent();
703 0 : parentStyleContext = aColGroupFrame->GetStyleContext();
704 : styleContext = shell->StyleSet()->
705 0 : ResolveAnonymousBoxStyle(nsCSSAnonBoxes::tableCol, parentStyleContext);
706 : // ASSERTION to check for bug 54454 sneaking back in...
707 0 : NS_ASSERTION(iContent, "null content in CreateAnonymousColFrames");
708 :
709 : // create the new col frame
710 0 : nsIFrame* colFrame = NS_NewTableColFrame(shell, styleContext);
711 0 : ((nsTableColFrame *) colFrame)->SetColType(aColType);
712 0 : colFrame->Init(iContent, aColGroupFrame, nsnull);
713 :
714 0 : newColFrames.AppendFrame(nsnull, colFrame);
715 : }
716 0 : nsFrameList& cols = aColGroupFrame->GetWritableChildList();
717 0 : nsIFrame* oldLastCol = cols.LastChild();
718 : const nsFrameList::Slice& newCols =
719 0 : cols.InsertFrames(nsnull, oldLastCol, newColFrames);
720 0 : if (aAddToTable) {
721 : // get the starting col index in the cache
722 : PRInt32 startColIndex;
723 0 : if (oldLastCol) {
724 : startColIndex =
725 0 : static_cast<nsTableColFrame*>(oldLastCol)->GetColIndex() + 1;
726 : } else {
727 0 : startColIndex = aColGroupFrame->GetStartColumnIndex();
728 : }
729 :
730 0 : aColGroupFrame->AddColsToTable(startColIndex, true, newCols);
731 : }
732 0 : }
733 :
734 : void
735 0 : nsTableFrame::MatchCellMapToColCache(nsTableCellMap* aCellMap)
736 : {
737 0 : PRInt32 numColsInMap = GetColCount();
738 0 : PRInt32 numColsInCache = mColFrames.Length();
739 0 : PRInt32 numColsToAdd = numColsInMap - numColsInCache;
740 0 : if (numColsToAdd > 0) {
741 : // this sets the child list, updates the col cache and cell map
742 0 : AppendAnonymousColFrames(numColsToAdd);
743 : }
744 0 : if (numColsToAdd < 0) {
745 0 : PRInt32 numColsNotRemoved = DestroyAnonymousColFrames(-numColsToAdd);
746 : // if the cell map has fewer cols than the cache, correct it
747 0 : if (numColsNotRemoved > 0) {
748 0 : aCellMap->AddColsAtEnd(numColsNotRemoved);
749 : }
750 : }
751 0 : if (numColsToAdd && HasZeroColSpans()) {
752 0 : SetNeedColSpanExpansion(true);
753 : }
754 0 : if (NeedColSpanExpansion()) {
755 : // This flag can be set in two ways -- either by changing
756 : // the number of columns (that happens in the block above),
757 : // or by adding a cell with colspan="0" to the cellmap. To
758 : // handle the latter case we need to explicitly check the
759 : // flag here -- it may be set even if the number of columns
760 : // did not change.
761 : //
762 : // @see nsCellMap::AppendCell
763 :
764 0 : aCellMap->ExpandZeroColSpans();
765 : }
766 0 : }
767 :
768 : void
769 0 : nsTableFrame::DidResizeColumns()
770 : {
771 0 : NS_PRECONDITION(!GetPrevInFlow(),
772 : "should only be called on first-in-flow");
773 0 : if (mBits.mResizedColumns)
774 0 : return; // already marked
775 :
776 0 : for (nsTableFrame *f = this; f;
777 0 : f = static_cast<nsTableFrame*>(f->GetNextInFlow()))
778 0 : f->mBits.mResizedColumns = true;
779 : }
780 :
781 : void
782 0 : nsTableFrame::AppendCell(nsTableCellFrame& aCellFrame,
783 : PRInt32 aRowIndex)
784 : {
785 0 : nsTableCellMap* cellMap = GetCellMap();
786 0 : if (cellMap) {
787 0 : nsIntRect damageArea(0,0,0,0);
788 0 : cellMap->AppendCell(aCellFrame, aRowIndex, true, damageArea);
789 0 : MatchCellMapToColCache(cellMap);
790 0 : if (IsBorderCollapse()) {
791 0 : AddBCDamageArea(damageArea);
792 : }
793 : }
794 0 : }
795 :
796 0 : void nsTableFrame::InsertCells(nsTArray<nsTableCellFrame*>& aCellFrames,
797 : PRInt32 aRowIndex,
798 : PRInt32 aColIndexBefore)
799 : {
800 0 : nsTableCellMap* cellMap = GetCellMap();
801 0 : if (cellMap) {
802 0 : nsIntRect damageArea(0,0,0,0);
803 0 : cellMap->InsertCells(aCellFrames, aRowIndex, aColIndexBefore, damageArea);
804 0 : MatchCellMapToColCache(cellMap);
805 0 : if (IsBorderCollapse()) {
806 0 : AddBCDamageArea(damageArea);
807 : }
808 : }
809 0 : }
810 :
811 : // this removes the frames from the col group and table, but not the cell map
812 : PRInt32
813 0 : nsTableFrame::DestroyAnonymousColFrames(PRInt32 aNumFrames)
814 : {
815 : // only remove cols that are of type eTypeAnonymous cell (they are at the end)
816 0 : PRInt32 endIndex = mColFrames.Length() - 1;
817 0 : PRInt32 startIndex = (endIndex - aNumFrames) + 1;
818 0 : PRInt32 numColsRemoved = 0;
819 0 : for (PRInt32 colX = endIndex; colX >= startIndex; colX--) {
820 0 : nsTableColFrame* colFrame = GetColFrame(colX);
821 0 : if (colFrame && (eColAnonymousCell == colFrame->GetColType())) {
822 : nsTableColGroupFrame* cgFrame =
823 0 : static_cast<nsTableColGroupFrame*>(colFrame->GetParent());
824 : // remove the frame from the colgroup
825 0 : cgFrame->RemoveChild(*colFrame, false);
826 : // remove the frame from the cache, but not the cell map
827 0 : RemoveCol(nsnull, colX, true, false);
828 0 : numColsRemoved++;
829 : }
830 : else {
831 0 : break;
832 : }
833 : }
834 0 : return (aNumFrames - numColsRemoved);
835 : }
836 :
837 0 : void nsTableFrame::RemoveCell(nsTableCellFrame* aCellFrame,
838 : PRInt32 aRowIndex)
839 : {
840 0 : nsTableCellMap* cellMap = GetCellMap();
841 0 : if (cellMap) {
842 0 : nsIntRect damageArea(0,0,0,0);
843 0 : cellMap->RemoveCell(aCellFrame, aRowIndex, damageArea);
844 0 : MatchCellMapToColCache(cellMap);
845 0 : if (IsBorderCollapse()) {
846 0 : AddBCDamageArea(damageArea);
847 : }
848 : }
849 0 : }
850 :
851 : PRInt32
852 0 : nsTableFrame::GetStartRowIndex(nsTableRowGroupFrame* aRowGroupFrame)
853 : {
854 0 : RowGroupArray orderedRowGroups;
855 0 : OrderRowGroups(orderedRowGroups);
856 :
857 0 : PRInt32 rowIndex = 0;
858 0 : for (PRUint32 rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) {
859 0 : nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgIndex];
860 0 : if (rgFrame == aRowGroupFrame) {
861 0 : break;
862 : }
863 0 : PRInt32 numRows = rgFrame->GetRowCount();
864 0 : rowIndex += numRows;
865 : }
866 0 : return rowIndex;
867 : }
868 :
869 : // this cannot extend beyond a single row group
870 0 : void nsTableFrame::AppendRows(nsTableRowGroupFrame* aRowGroupFrame,
871 : PRInt32 aRowIndex,
872 : nsTArray<nsTableRowFrame*>& aRowFrames)
873 : {
874 0 : nsTableCellMap* cellMap = GetCellMap();
875 0 : if (cellMap) {
876 0 : PRInt32 absRowIndex = GetStartRowIndex(aRowGroupFrame) + aRowIndex;
877 0 : InsertRows(aRowGroupFrame, aRowFrames, absRowIndex, true);
878 : }
879 0 : }
880 :
881 : // this cannot extend beyond a single row group
882 : PRInt32
883 0 : nsTableFrame::InsertRows(nsTableRowGroupFrame* aRowGroupFrame,
884 : nsTArray<nsTableRowFrame*>& aRowFrames,
885 : PRInt32 aRowIndex,
886 : bool aConsiderSpans)
887 : {
888 : #ifdef DEBUG_TABLE_CELLMAP
889 : printf("=== insertRowsBefore firstRow=%d \n", aRowIndex);
890 : Dump(true, false, true);
891 : #endif
892 :
893 0 : PRInt32 numColsToAdd = 0;
894 0 : nsTableCellMap* cellMap = GetCellMap();
895 0 : if (cellMap) {
896 0 : nsIntRect damageArea(0,0,0,0);
897 0 : PRInt32 origNumRows = cellMap->GetRowCount();
898 0 : PRInt32 numNewRows = aRowFrames.Length();
899 0 : cellMap->InsertRows(aRowGroupFrame, aRowFrames, aRowIndex, aConsiderSpans, damageArea);
900 0 : MatchCellMapToColCache(cellMap);
901 0 : if (aRowIndex < origNumRows) {
902 0 : AdjustRowIndices(aRowIndex, numNewRows);
903 : }
904 : // assign the correct row indices to the new rows. If they were adjusted above
905 : // it may not have been done correctly because each row is constructed with index 0
906 0 : for (PRInt32 rowY = 0; rowY < numNewRows; rowY++) {
907 0 : nsTableRowFrame* rowFrame = aRowFrames.ElementAt(rowY);
908 0 : rowFrame->SetRowIndex(aRowIndex + rowY);
909 : }
910 0 : if (IsBorderCollapse()) {
911 0 : AddBCDamageArea(damageArea);
912 : }
913 : }
914 : #ifdef DEBUG_TABLE_CELLMAP
915 : printf("=== insertRowsAfter \n");
916 : Dump(true, false, true);
917 : #endif
918 :
919 0 : return numColsToAdd;
920 : }
921 :
922 : // this cannot extend beyond a single row group
923 0 : void nsTableFrame::RemoveRows(nsTableRowFrame& aFirstRowFrame,
924 : PRInt32 aNumRowsToRemove,
925 : bool aConsiderSpans)
926 : {
927 : #ifdef TBD_OPTIMIZATION
928 : // decide if we need to rebalance. we have to do this here because the row group
929 : // cannot do it when it gets the dirty reflow corresponding to the frame being destroyed
930 : bool stopTelling = false;
931 : for (nsIFrame* kidFrame = aFirstFrame.FirstChild(); (kidFrame && !stopAsking);
932 : kidFrame = kidFrame->GetNextSibling()) {
933 : nsTableCellFrame *cellFrame = do_QueryFrame(kidFrame);
934 : if (cellFrame) {
935 : stopTelling = tableFrame->CellChangedWidth(*cellFrame, cellFrame->GetPass1MaxElementWidth(),
936 : cellFrame->GetMaximumWidth(), true);
937 : }
938 : }
939 : // XXX need to consider what happens if there are cells that have rowspans
940 : // into the deleted row. Need to consider moving rows if a rebalance doesn't happen
941 : #endif
942 :
943 0 : PRInt32 firstRowIndex = aFirstRowFrame.GetRowIndex();
944 : #ifdef DEBUG_TABLE_CELLMAP
945 : printf("=== removeRowsBefore firstRow=%d numRows=%d\n", firstRowIndex, aNumRowsToRemove);
946 : Dump(true, false, true);
947 : #endif
948 0 : nsTableCellMap* cellMap = GetCellMap();
949 0 : if (cellMap) {
950 0 : nsIntRect damageArea(0,0,0,0);
951 0 : cellMap->RemoveRows(firstRowIndex, aNumRowsToRemove, aConsiderSpans, damageArea);
952 0 : MatchCellMapToColCache(cellMap);
953 0 : if (IsBorderCollapse()) {
954 0 : AddBCDamageArea(damageArea);
955 : }
956 : }
957 0 : AdjustRowIndices(firstRowIndex, -aNumRowsToRemove);
958 : #ifdef DEBUG_TABLE_CELLMAP
959 : printf("=== removeRowsAfter\n");
960 : Dump(true, true, true);
961 : #endif
962 0 : }
963 :
964 : // collect the rows ancestors of aFrame
965 : PRInt32
966 0 : nsTableFrame::CollectRows(nsIFrame* aFrame,
967 : nsTArray<nsTableRowFrame*>& aCollection)
968 : {
969 0 : NS_PRECONDITION(aFrame, "null frame");
970 0 : PRInt32 numRows = 0;
971 0 : nsIFrame* childFrame = aFrame->GetFirstPrincipalChild();
972 0 : while (childFrame) {
973 0 : aCollection.AppendElement(static_cast<nsTableRowFrame*>(childFrame));
974 0 : numRows++;
975 0 : childFrame = childFrame->GetNextSibling();
976 : }
977 0 : return numRows;
978 : }
979 :
980 : void
981 0 : nsTableFrame::InsertRowGroups(const nsFrameList::Slice& aRowGroups)
982 : {
983 : #ifdef DEBUG_TABLE_CELLMAP
984 : printf("=== insertRowGroupsBefore\n");
985 : Dump(true, false, true);
986 : #endif
987 0 : nsTableCellMap* cellMap = GetCellMap();
988 0 : if (cellMap) {
989 0 : RowGroupArray orderedRowGroups;
990 0 : OrderRowGroups(orderedRowGroups);
991 :
992 0 : nsAutoTArray<nsTableRowFrame*, 8> rows;
993 : // Loop over the rowgroups and check if some of them are new, if they are
994 : // insert cellmaps in the order that is predefined by OrderRowGroups,
995 : // XXXbz this code is O(N*M) where N is number of new rowgroups
996 : // and M is number of rowgroups we have!
997 : PRUint32 rgIndex;
998 0 : for (rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) {
999 0 : for (nsFrameList::Enumerator rowgroups(aRowGroups); !rowgroups.AtEnd();
1000 0 : rowgroups.Next()) {
1001 0 : if (orderedRowGroups[rgIndex] == rowgroups.get()) {
1002 : nsTableRowGroupFrame* priorRG =
1003 0 : (0 == rgIndex) ? nsnull : orderedRowGroups[rgIndex - 1];
1004 : // create and add the cell map for the row group
1005 0 : cellMap->InsertGroupCellMap(orderedRowGroups[rgIndex], priorRG);
1006 :
1007 0 : break;
1008 : }
1009 : }
1010 : }
1011 0 : cellMap->Synchronize(this);
1012 0 : ResetRowIndices(aRowGroups);
1013 :
1014 : //now that the cellmaps are reordered too insert the rows
1015 0 : for (rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) {
1016 0 : for (nsFrameList::Enumerator rowgroups(aRowGroups); !rowgroups.AtEnd();
1017 0 : rowgroups.Next()) {
1018 0 : if (orderedRowGroups[rgIndex] == rowgroups.get()) {
1019 : nsTableRowGroupFrame* priorRG =
1020 0 : (0 == rgIndex) ? nsnull : orderedRowGroups[rgIndex - 1];
1021 : // collect the new row frames in an array and add them to the table
1022 0 : PRInt32 numRows = CollectRows(rowgroups.get(), rows);
1023 0 : if (numRows > 0) {
1024 0 : PRInt32 rowIndex = 0;
1025 0 : if (priorRG) {
1026 0 : PRInt32 priorNumRows = priorRG->GetRowCount();
1027 0 : rowIndex = priorRG->GetStartRowIndex() + priorNumRows;
1028 : }
1029 0 : InsertRows(orderedRowGroups[rgIndex], rows, rowIndex, true);
1030 0 : rows.Clear();
1031 : }
1032 0 : break;
1033 : }
1034 : }
1035 : }
1036 :
1037 : }
1038 : #ifdef DEBUG_TABLE_CELLMAP
1039 : printf("=== insertRowGroupsAfter\n");
1040 : Dump(true, true, true);
1041 : #endif
1042 0 : }
1043 :
1044 :
1045 : /////////////////////////////////////////////////////////////////////////////
1046 : // Child frame enumeration
1047 :
1048 : const nsFrameList&
1049 0 : nsTableFrame::GetChildList(ChildListID aListID) const
1050 : {
1051 0 : if (aListID == kColGroupList) {
1052 0 : return mColGroups;
1053 : }
1054 0 : return nsContainerFrame::GetChildList(aListID);
1055 : }
1056 :
1057 : void
1058 0 : nsTableFrame::GetChildLists(nsTArray<ChildList>* aLists) const
1059 : {
1060 0 : nsContainerFrame::GetChildLists(aLists);
1061 0 : mColGroups.AppendIfNonempty(aLists, kColGroupList);
1062 0 : }
1063 :
1064 : nsRect
1065 0 : nsDisplayTableItem::GetBounds(nsDisplayListBuilder* aBuilder) {
1066 0 : return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
1067 : }
1068 :
1069 : bool
1070 0 : nsDisplayTableItem::IsVaryingRelativeToMovingFrame(nsDisplayListBuilder* aBuilder,
1071 : nsIFrame* aFrame)
1072 : {
1073 0 : if (!mPartHasFixedBackground)
1074 0 : return false;
1075 :
1076 : // If aFrame is mFrame or an ancestor in this document, and aFrame is
1077 : // not the viewport frame, then moving aFrame will move mFrame
1078 : // relative to the viewport, so our fixed-pos background will change.
1079 : return mFrame == aFrame ||
1080 0 : nsLayoutUtils::IsProperAncestorFrame(aFrame, mFrame);
1081 : }
1082 :
1083 : /* static */ void
1084 0 : nsDisplayTableItem::UpdateForFrameBackground(nsIFrame* aFrame)
1085 : {
1086 : nsStyleContext *bgSC;
1087 0 : if (!nsCSSRendering::FindBackground(aFrame->PresContext(), aFrame, &bgSC))
1088 0 : return;
1089 0 : if (!bgSC->GetStyleBackground()->HasFixedBackground())
1090 0 : return;
1091 :
1092 0 : mPartHasFixedBackground = true;
1093 : }
1094 :
1095 : class nsDisplayTableBorderBackground : public nsDisplayTableItem {
1096 : public:
1097 0 : nsDisplayTableBorderBackground(nsDisplayListBuilder* aBuilder,
1098 : nsTableFrame* aFrame) :
1099 0 : nsDisplayTableItem(aBuilder, aFrame) {
1100 0 : MOZ_COUNT_CTOR(nsDisplayTableBorderBackground);
1101 0 : }
1102 : #ifdef NS_BUILD_REFCNT_LOGGING
1103 0 : virtual ~nsDisplayTableBorderBackground() {
1104 0 : MOZ_COUNT_DTOR(nsDisplayTableBorderBackground);
1105 0 : }
1106 : #endif
1107 :
1108 : virtual void Paint(nsDisplayListBuilder* aBuilder,
1109 : nsRenderingContext* aCtx);
1110 0 : NS_DISPLAY_DECL_NAME("TableBorderBackground", TYPE_TABLE_BORDER_BACKGROUND)
1111 : };
1112 :
1113 : void
1114 0 : nsDisplayTableBorderBackground::Paint(nsDisplayListBuilder* aBuilder,
1115 : nsRenderingContext* aCtx)
1116 : {
1117 : static_cast<nsTableFrame*>(mFrame)->
1118 : PaintTableBorderBackground(*aCtx, mVisibleRect,
1119 0 : ToReferenceFrame(),
1120 0 : aBuilder->GetBackgroundPaintFlags());
1121 0 : }
1122 :
1123 0 : static PRInt32 GetTablePartRank(nsDisplayItem* aItem)
1124 : {
1125 0 : nsIAtom* type = aItem->GetUnderlyingFrame()->GetType();
1126 0 : if (type == nsGkAtoms::tableFrame)
1127 0 : return 0;
1128 0 : if (type == nsGkAtoms::tableRowGroupFrame)
1129 0 : return 1;
1130 0 : if (type == nsGkAtoms::tableRowFrame)
1131 0 : return 2;
1132 0 : return 3;
1133 : }
1134 :
1135 0 : static bool CompareByTablePartRank(nsDisplayItem* aItem1, nsDisplayItem* aItem2,
1136 : void* aClosure)
1137 : {
1138 0 : return GetTablePartRank(aItem1) <= GetTablePartRank(aItem2);
1139 : }
1140 :
1141 : /* static */ nsresult
1142 0 : nsTableFrame::GenericTraversal(nsDisplayListBuilder* aBuilder, nsFrame* aFrame,
1143 : const nsRect& aDirtyRect, const nsDisplayListSet& aLists)
1144 : {
1145 : // This is similar to what nsContainerFrame::BuildDisplayListForNonBlockChildren
1146 : // does, except that we allow the children's background and borders to go
1147 : // in our BorderBackground list. This doesn't really affect background
1148 : // painting --- the children won't actually draw their own backgrounds
1149 : // because the nsTableFrame already drew them, unless a child has its own
1150 : // stacking context, in which case the child won't use its passed-in
1151 : // BorderBackground list anyway. It does affect cell borders though; this
1152 : // lets us get cell borders into the nsTableFrame's BorderBackground list.
1153 0 : nsIFrame* kid = aFrame->GetFirstPrincipalChild();
1154 0 : while (kid) {
1155 0 : nsresult rv = aFrame->BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists);
1156 0 : NS_ENSURE_SUCCESS(rv, rv);
1157 0 : kid = kid->GetNextSibling();
1158 : }
1159 0 : return NS_OK;
1160 : }
1161 :
1162 : /* static */ nsresult
1163 0 : nsTableFrame::DisplayGenericTablePart(nsDisplayListBuilder* aBuilder,
1164 : nsFrame* aFrame,
1165 : const nsRect& aDirtyRect,
1166 : const nsDisplayListSet& aLists,
1167 : nsDisplayTableItem* aDisplayItem,
1168 : DisplayGenericTablePartTraversal aTraversal)
1169 : {
1170 0 : nsDisplayList eventsBorderBackground;
1171 : // If we need to sort the event backgrounds, then we'll put descendants'
1172 : // display items into their own set of lists.
1173 0 : bool sortEventBackgrounds = aDisplayItem && aBuilder->IsForEventDelivery();
1174 0 : nsDisplayListCollection separatedCollection;
1175 0 : const nsDisplayListSet* lists = sortEventBackgrounds ? &separatedCollection : &aLists;
1176 :
1177 0 : nsAutoPushCurrentTableItem pushTableItem;
1178 0 : if (aDisplayItem) {
1179 0 : pushTableItem.Push(aBuilder, aDisplayItem);
1180 : }
1181 :
1182 0 : if (aFrame->IsVisibleForPainting(aBuilder)) {
1183 0 : nsDisplayTableItem* currentItem = aBuilder->GetCurrentTableItem();
1184 : // currentItem may be null, when none of the table parts have a
1185 : // background or border
1186 0 : if (currentItem) {
1187 0 : currentItem->UpdateForFrameBackground(aFrame);
1188 : }
1189 :
1190 : // Paint the outset box-shadows for the table frames
1191 0 : bool hasBoxShadow = aFrame->GetStyleBorder()->mBoxShadow != nsnull;
1192 0 : if (hasBoxShadow) {
1193 : nsresult rv = lists->BorderBackground()->AppendNewToTop(
1194 0 : new (aBuilder) nsDisplayBoxShadowOuter(aBuilder, aFrame));
1195 0 : NS_ENSURE_SUCCESS(rv, rv);
1196 : }
1197 :
1198 : // Create dedicated background display items per-frame when we're
1199 : // handling events.
1200 : // XXX how to handle collapsed borders?
1201 0 : if (aBuilder->IsForEventDelivery()) {
1202 : nsresult rv = lists->BorderBackground()->AppendNewToTop(
1203 0 : new (aBuilder) nsDisplayBackground(aBuilder, aFrame));
1204 0 : NS_ENSURE_SUCCESS(rv, rv);
1205 : }
1206 :
1207 : // Paint the inset box-shadows for the table frames
1208 0 : if (hasBoxShadow) {
1209 : nsresult rv = lists->BorderBackground()->AppendNewToTop(
1210 0 : new (aBuilder) nsDisplayBoxShadowInner(aBuilder, aFrame));
1211 0 : NS_ENSURE_SUCCESS(rv, rv);
1212 : }
1213 : }
1214 :
1215 0 : nsresult rv = aTraversal(aBuilder, aFrame, aDirtyRect, *lists);
1216 0 : NS_ENSURE_SUCCESS(rv, rv);
1217 :
1218 0 : if (sortEventBackgrounds) {
1219 : // Ensure that the table frame event background goes before the
1220 : // table rowgroups event backgrounds, before the table row event backgrounds,
1221 : // before everything else (cells and their blocks)
1222 0 : separatedCollection.BorderBackground()->Sort(aBuilder, CompareByTablePartRank, nsnull);
1223 0 : separatedCollection.MoveTo(aLists);
1224 : }
1225 :
1226 0 : return aFrame->DisplayOutline(aBuilder, aLists);
1227 : }
1228 :
1229 : #ifdef DEBUG
1230 : static bool
1231 0 : IsFrameAllowedInTable(nsIAtom* aType)
1232 : {
1233 0 : return IS_TABLE_CELL(aType) ||
1234 : nsGkAtoms::tableRowFrame == aType ||
1235 : nsGkAtoms::tableRowGroupFrame == aType ||
1236 : nsGkAtoms::scrollFrame == aType ||
1237 : nsGkAtoms::tableFrame == aType ||
1238 : nsGkAtoms::tableColFrame == aType ||
1239 0 : nsGkAtoms::tableColGroupFrame == aType;
1240 : }
1241 : #endif
1242 :
1243 : static bool
1244 0 : AnyTablePartHasBorderOrBackground(nsIFrame* aStart, nsIFrame* aEnd)
1245 : {
1246 0 : for (nsIFrame* f = aStart; f != aEnd; f = f->GetNextSibling()) {
1247 0 : NS_ASSERTION(IsFrameAllowedInTable(f->GetType()), "unexpected frame type");
1248 :
1249 0 : if (f->GetStyleVisibility()->IsVisible() &&
1250 0 : (!f->GetStyleBackground()->IsTransparent() ||
1251 0 : f->GetStyleDisplay()->mAppearance ||
1252 0 : f->HasBorder()))
1253 0 : return true;
1254 :
1255 0 : nsTableCellFrame *cellFrame = do_QueryFrame(f);
1256 0 : if (cellFrame)
1257 0 : continue;
1258 :
1259 0 : if (AnyTablePartHasBorderOrBackground(f->PrincipalChildList().FirstChild(), nsnull))
1260 0 : return true;
1261 : }
1262 :
1263 0 : return false;
1264 : }
1265 :
1266 : // table paint code is concerned primarily with borders and bg color
1267 : // SEC: TODO: adjust the rect for captions
1268 : NS_IMETHODIMP
1269 0 : nsTableFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
1270 : const nsRect& aDirtyRect,
1271 : const nsDisplayListSet& aLists)
1272 : {
1273 0 : DO_GLOBAL_REFLOW_COUNT_DSP_COLOR("nsTableFrame", NS_RGB(255,128,255));
1274 :
1275 0 : nsDisplayTableItem* item = nsnull;
1276 0 : if (IsVisibleInSelection(aBuilder)) {
1277 0 : if (GetStyleVisibility()->IsVisible()) {
1278 0 : nsMargin deflate = GetDeflationForBackground(PresContext());
1279 : // If 'deflate' is (0,0,0,0) then we can paint the table background
1280 : // in its own display item, so do that to take advantage of
1281 : // opacity and visibility optimizations
1282 0 : if (deflate == nsMargin(0, 0, 0, 0)) {
1283 0 : nsresult rv = DisplayBackgroundUnconditional(aBuilder, aLists, false);
1284 0 : NS_ENSURE_SUCCESS(rv, rv);
1285 : }
1286 : }
1287 :
1288 : // This background is created if any of the table parts are visible,
1289 : // or if we're doing event handling (since DisplayGenericTablePart
1290 : // needs the item for the |sortEventBackgrounds|-dependent code).
1291 : // Specific visibility decisions are delegated to the table background
1292 : // painter, which handles borders and backgrounds for the table.
1293 0 : if (aBuilder->IsForEventDelivery() ||
1294 0 : AnyTablePartHasBorderOrBackground(this, GetNextSibling()) ||
1295 0 : AnyTablePartHasBorderOrBackground(mColGroups.FirstChild(), nsnull)) {
1296 0 : item = new (aBuilder) nsDisplayTableBorderBackground(aBuilder, this);
1297 0 : nsresult rv = aLists.BorderBackground()->AppendNewToTop(item);
1298 0 : NS_ENSURE_SUCCESS(rv, rv);
1299 : }
1300 : }
1301 0 : return DisplayGenericTablePart(aBuilder, this, aDirtyRect, aLists, item);
1302 : }
1303 :
1304 : nsMargin
1305 0 : nsTableFrame::GetDeflationForBackground(nsPresContext* aPresContext) const
1306 : {
1307 0 : if (eCompatibility_NavQuirks != aPresContext->CompatibilityMode() ||
1308 0 : !IsBorderCollapse())
1309 0 : return nsMargin(0,0,0,0);
1310 :
1311 0 : return GetOuterBCBorder();
1312 : }
1313 :
1314 : // XXX We don't put the borders and backgrounds in tree order like we should.
1315 : // That requires some major surgery which we aren't going to do right now.
1316 : void
1317 0 : nsTableFrame::PaintTableBorderBackground(nsRenderingContext& aRenderingContext,
1318 : const nsRect& aDirtyRect,
1319 : nsPoint aPt, PRUint32 aBGPaintFlags)
1320 : {
1321 0 : nsPresContext* presContext = PresContext();
1322 :
1323 : TableBackgroundPainter painter(this, TableBackgroundPainter::eOrigin_Table,
1324 : presContext, aRenderingContext,
1325 0 : aDirtyRect, aPt, aBGPaintFlags);
1326 0 : nsMargin deflate = GetDeflationForBackground(presContext);
1327 : // If 'deflate' is (0,0,0,0) then we'll paint the table background
1328 : // in a separate display item, so don't do it here.
1329 0 : nsresult rv = painter.PaintTable(this, deflate, deflate != nsMargin(0, 0, 0, 0));
1330 0 : if (NS_FAILED(rv)) return;
1331 :
1332 0 : if (GetStyleVisibility()->IsVisible()) {
1333 0 : if (!IsBorderCollapse()) {
1334 0 : PRIntn skipSides = GetSkipSides();
1335 0 : nsRect rect(aPt, mRect.Size());
1336 : nsCSSRendering::PaintBorder(presContext, aRenderingContext, this,
1337 0 : aDirtyRect, rect, mStyleContext, skipSides);
1338 : }
1339 : else {
1340 : // XXX we should probably get rid of this translation at some stage
1341 : // But that would mean modifying PaintBCBorders, ugh
1342 0 : nsRenderingContext::AutoPushTranslation translate(&aRenderingContext, aPt);
1343 0 : PaintBCBorders(aRenderingContext, aDirtyRect - aPt);
1344 : }
1345 : }
1346 : }
1347 :
1348 : PRIntn
1349 0 : nsTableFrame::GetSkipSides() const
1350 : {
1351 0 : PRIntn skip = 0;
1352 : // frame attribute was accounted for in nsHTMLTableElement::MapTableBorderInto
1353 : // account for pagination
1354 0 : if (nsnull != GetPrevInFlow()) {
1355 0 : skip |= 1 << NS_SIDE_TOP;
1356 : }
1357 0 : if (nsnull != GetNextInFlow()) {
1358 0 : skip |= 1 << NS_SIDE_BOTTOM;
1359 : }
1360 0 : return skip;
1361 : }
1362 :
1363 : void
1364 0 : nsTableFrame::SetColumnDimensions(nscoord aHeight,
1365 : const nsMargin& aBorderPadding)
1366 : {
1367 0 : nscoord cellSpacingX = GetCellSpacingX();
1368 0 : nscoord cellSpacingY = GetCellSpacingY();
1369 : nscoord colHeight = aHeight -= aBorderPadding.top + aBorderPadding.bottom +
1370 0 : 2* cellSpacingY;
1371 :
1372 0 : nsTableIterator iter(mColGroups);
1373 0 : nsIFrame* colGroupFrame = iter.First();
1374 0 : bool tableIsLTR = GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR;
1375 0 : PRInt32 colX =tableIsLTR ? 0 : NS_MAX(0, GetColCount() - 1);
1376 0 : PRInt32 tableColIncr = tableIsLTR ? 1 : -1;
1377 : nsPoint colGroupOrigin(aBorderPadding.left + cellSpacingX,
1378 0 : aBorderPadding.top + cellSpacingY);
1379 0 : while (nsnull != colGroupFrame) {
1380 0 : nscoord colGroupWidth = 0;
1381 0 : nsTableIterator iterCol(*colGroupFrame);
1382 0 : nsIFrame* colFrame = iterCol.First();
1383 0 : nsPoint colOrigin(0,0);
1384 0 : while (nsnull != colFrame) {
1385 0 : if (NS_STYLE_DISPLAY_TABLE_COLUMN ==
1386 0 : colFrame->GetStyleDisplay()->mDisplay) {
1387 0 : NS_ASSERTION(colX < GetColCount(), "invalid number of columns");
1388 0 : nscoord colWidth = GetColumnWidth(colX);
1389 0 : nsRect colRect(colOrigin.x, colOrigin.y, colWidth, colHeight);
1390 0 : colFrame->SetRect(colRect);
1391 0 : colOrigin.x += colWidth + cellSpacingX;
1392 0 : colGroupWidth += colWidth + cellSpacingX;
1393 0 : colX += tableColIncr;
1394 : }
1395 0 : colFrame = iterCol.Next();
1396 : }
1397 0 : if (colGroupWidth) {
1398 0 : colGroupWidth -= cellSpacingX;
1399 : }
1400 :
1401 0 : nsRect colGroupRect(colGroupOrigin.x, colGroupOrigin.y, colGroupWidth, colHeight);
1402 0 : colGroupFrame->SetRect(colGroupRect);
1403 0 : colGroupFrame = iter.Next();
1404 0 : colGroupOrigin.x += colGroupWidth + cellSpacingX;
1405 : }
1406 0 : }
1407 :
1408 : // SEC: TODO need to worry about continuing frames prev/next in flow for splitting across pages.
1409 :
1410 : // XXX this could be made more general to handle row modifications that change the
1411 : // table height, but first we need to scrutinize every Invalidate
1412 : void
1413 0 : nsTableFrame::ProcessRowInserted(nscoord aNewHeight)
1414 : {
1415 0 : SetRowInserted(false); // reset the bit that got us here
1416 0 : nsTableFrame::RowGroupArray rowGroups;
1417 0 : OrderRowGroups(rowGroups);
1418 : // find the row group containing the inserted row
1419 0 : for (PRUint32 rgX = 0; rgX < rowGroups.Length(); rgX++) {
1420 0 : nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
1421 0 : NS_ASSERTION(rgFrame, "Must have rgFrame here");
1422 0 : nsIFrame* childFrame = rgFrame->GetFirstPrincipalChild();
1423 : // find the row that was inserted first
1424 0 : while (childFrame) {
1425 0 : nsTableRowFrame *rowFrame = do_QueryFrame(childFrame);
1426 0 : if (rowFrame) {
1427 0 : if (rowFrame->IsFirstInserted()) {
1428 0 : rowFrame->SetFirstInserted(false);
1429 : // damage the table from the 1st row inserted to the end of the table
1430 0 : nscoord damageY = rgFrame->GetPosition().y + rowFrame->GetPosition().y;
1431 0 : nsRect damageRect(0, damageY, GetSize().width, aNewHeight - damageY);
1432 :
1433 0 : Invalidate(damageRect);
1434 : // XXXbz didn't we do this up front? Why do we need to do it again?
1435 0 : SetRowInserted(false);
1436 : return; // found it, so leave
1437 : }
1438 : }
1439 0 : childFrame = childFrame->GetNextSibling();
1440 : }
1441 : }
1442 : }
1443 :
1444 : /* virtual */ void
1445 0 : nsTableFrame::MarkIntrinsicWidthsDirty()
1446 : {
1447 0 : nsITableLayoutStrategy* tls = LayoutStrategy();
1448 0 : if (NS_UNLIKELY(!tls)) {
1449 : // This is a FrameNeedsReflow() from nsBlockFrame::RemoveFrame()
1450 : // walking up the ancestor chain in a table next-in-flow. In this case
1451 : // our original first-in-flow (which owns the TableLayoutStrategy) has
1452 : // already been destroyed and unhooked from the flow chain and thusly
1453 : // LayoutStrategy() returns null. All the frames in the flow will be
1454 : // destroyed so no need to mark anything dirty here. See bug 595758.
1455 0 : return;
1456 : }
1457 0 : tls->MarkIntrinsicWidthsDirty();
1458 :
1459 : // XXXldb Call SetBCDamageArea?
1460 :
1461 0 : nsContainerFrame::MarkIntrinsicWidthsDirty();
1462 : }
1463 :
1464 : /* virtual */ nscoord
1465 0 : nsTableFrame::GetMinWidth(nsRenderingContext *aRenderingContext)
1466 : {
1467 0 : if (NeedToCalcBCBorders())
1468 0 : CalcBCBorders();
1469 :
1470 0 : ReflowColGroups(aRenderingContext);
1471 :
1472 0 : return LayoutStrategy()->GetMinWidth(aRenderingContext);
1473 : }
1474 :
1475 : /* virtual */ nscoord
1476 0 : nsTableFrame::GetPrefWidth(nsRenderingContext *aRenderingContext)
1477 : {
1478 0 : if (NeedToCalcBCBorders())
1479 0 : CalcBCBorders();
1480 :
1481 0 : ReflowColGroups(aRenderingContext);
1482 :
1483 0 : return LayoutStrategy()->GetPrefWidth(aRenderingContext, false);
1484 : }
1485 :
1486 : /* virtual */ nsIFrame::IntrinsicWidthOffsetData
1487 0 : nsTableFrame::IntrinsicWidthOffsets(nsRenderingContext* aRenderingContext)
1488 : {
1489 : IntrinsicWidthOffsetData result =
1490 0 : nsContainerFrame::IntrinsicWidthOffsets(aRenderingContext);
1491 :
1492 0 : result.hMargin = 0;
1493 0 : result.hPctMargin = 0;
1494 :
1495 0 : if (IsBorderCollapse()) {
1496 0 : result.hPadding = 0;
1497 0 : result.hPctPadding = 0;
1498 :
1499 0 : nsMargin outerBC = GetIncludedOuterBCBorder();
1500 0 : result.hBorder = outerBC.LeftRight();
1501 : }
1502 :
1503 : return result;
1504 : }
1505 :
1506 : /* virtual */ nsSize
1507 0 : nsTableFrame::ComputeSize(nsRenderingContext *aRenderingContext,
1508 : nsSize aCBSize, nscoord aAvailableWidth,
1509 : nsSize aMargin, nsSize aBorder, nsSize aPadding,
1510 : bool aShrinkWrap)
1511 : {
1512 : nsSize result =
1513 : nsContainerFrame::ComputeSize(aRenderingContext, aCBSize, aAvailableWidth,
1514 0 : aMargin, aBorder, aPadding, aShrinkWrap);
1515 :
1516 : // If we're a container for font size inflation, then shrink
1517 : // wrapping inside of us should not apply font size inflation.
1518 0 : AutoMaybeNullInflationContainer an(this);
1519 :
1520 : // Tables never shrink below their min width.
1521 0 : nscoord minWidth = GetMinWidth(aRenderingContext);
1522 0 : if (minWidth > result.width)
1523 0 : result.width = minWidth;
1524 :
1525 : return result;
1526 : }
1527 :
1528 : nscoord
1529 0 : nsTableFrame::TableShrinkWidthToFit(nsRenderingContext *aRenderingContext,
1530 : nscoord aWidthInCB)
1531 : {
1532 : // If we're a container for font size inflation, then shrink
1533 : // wrapping inside of us should not apply font size inflation.
1534 0 : AutoMaybeNullInflationContainer an(this);
1535 :
1536 : nscoord result;
1537 0 : nscoord minWidth = GetMinWidth(aRenderingContext);
1538 0 : if (minWidth > aWidthInCB) {
1539 0 : result = minWidth;
1540 : } else {
1541 : // Tables shrink width to fit with a slightly different algorithm
1542 : // from the one they use for their intrinsic widths (the difference
1543 : // relates to handling of percentage widths on columns). So this
1544 : // function differs from nsFrame::ShrinkWidthToFit by only the
1545 : // following line.
1546 : // Since we've already called GetMinWidth, we don't need to do any
1547 : // of the other stuff GetPrefWidth does.
1548 : nscoord prefWidth =
1549 0 : LayoutStrategy()->GetPrefWidth(aRenderingContext, true);
1550 0 : if (prefWidth > aWidthInCB) {
1551 0 : result = aWidthInCB;
1552 : } else {
1553 0 : result = prefWidth;
1554 : }
1555 : }
1556 0 : return result;
1557 : }
1558 :
1559 : /* virtual */ nsSize
1560 0 : nsTableFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext,
1561 : nsSize aCBSize, nscoord aAvailableWidth,
1562 : nsSize aMargin, nsSize aBorder, nsSize aPadding,
1563 : bool aShrinkWrap)
1564 : {
1565 : // Tables always shrink-wrap.
1566 : nscoord cbBased = aAvailableWidth - aMargin.width - aBorder.width -
1567 0 : aPadding.width;
1568 : return nsSize(TableShrinkWidthToFit(aRenderingContext, cbBased),
1569 0 : NS_UNCONSTRAINEDSIZE);
1570 : }
1571 :
1572 : // Return true if aParentReflowState.frame or any of its ancestors within
1573 : // the containing table have non-auto height. (e.g. pct or fixed height)
1574 : bool
1575 0 : nsTableFrame::AncestorsHaveStyleHeight(const nsHTMLReflowState& aParentReflowState)
1576 : {
1577 0 : for (const nsHTMLReflowState* rs = &aParentReflowState;
1578 : rs && rs->frame; rs = rs->parentReflowState) {
1579 0 : nsIAtom* frameType = rs->frame->GetType();
1580 0 : if (IS_TABLE_CELL(frameType) ||
1581 : (nsGkAtoms::tableRowFrame == frameType) ||
1582 : (nsGkAtoms::tableRowGroupFrame == frameType)) {
1583 0 : const nsStyleCoord &height = rs->mStylePosition->mHeight;
1584 : // calc() treated like 'auto' on internal table elements
1585 0 : if (height.GetUnit() != eStyleUnit_Auto && !height.IsCalcUnit()) {
1586 0 : return true;
1587 : }
1588 : }
1589 0 : else if (nsGkAtoms::tableFrame == frameType) {
1590 : // we reached the containing table, so always return
1591 0 : if (rs->mStylePosition->mHeight.GetUnit() != eStyleUnit_Auto) {
1592 0 : return true;
1593 : }
1594 0 : else return false;
1595 : }
1596 : }
1597 0 : return false;
1598 : }
1599 :
1600 : // See if a special height reflow needs to occur and if so, call RequestSpecialHeightReflow
1601 : void
1602 0 : nsTableFrame::CheckRequestSpecialHeightReflow(const nsHTMLReflowState& aReflowState)
1603 : {
1604 0 : NS_ASSERTION(IS_TABLE_CELL(aReflowState.frame->GetType()) ||
1605 : aReflowState.frame->GetType() == nsGkAtoms::tableRowFrame ||
1606 : aReflowState.frame->GetType() == nsGkAtoms::tableRowGroupFrame ||
1607 : aReflowState.frame->GetType() == nsGkAtoms::tableFrame,
1608 : "unexpected frame type");
1609 0 : if (!aReflowState.frame->GetPrevInFlow() && // 1st in flow
1610 0 : (NS_UNCONSTRAINEDSIZE == aReflowState.ComputedHeight() || // no computed height
1611 0 : 0 == aReflowState.ComputedHeight()) &&
1612 0 : eStyleUnit_Percent == aReflowState.mStylePosition->mHeight.GetUnit() && // pct height
1613 0 : nsTableFrame::AncestorsHaveStyleHeight(*aReflowState.parentReflowState)) {
1614 0 : nsTableFrame::RequestSpecialHeightReflow(aReflowState);
1615 : }
1616 0 : }
1617 :
1618 : // Notify the frame and its ancestors (up to the containing table) that a special
1619 : // height reflow will occur. During a special height reflow, a table, row group,
1620 : // row, or cell returns the last size it was reflowed at. However, the table may
1621 : // change the height of row groups, rows, cells in DistributeHeightToRows after.
1622 : // And the row group can change the height of rows, cells in CalculateRowHeights.
1623 : void
1624 0 : nsTableFrame::RequestSpecialHeightReflow(const nsHTMLReflowState& aReflowState)
1625 : {
1626 : // notify the frame and its ancestors of the special reflow, stopping at the containing table
1627 0 : for (const nsHTMLReflowState* rs = &aReflowState; rs && rs->frame; rs = rs->parentReflowState) {
1628 0 : nsIAtom* frameType = rs->frame->GetType();
1629 0 : NS_ASSERTION(IS_TABLE_CELL(frameType) ||
1630 : nsGkAtoms::tableRowFrame == frameType ||
1631 : nsGkAtoms::tableRowGroupFrame == frameType ||
1632 : nsGkAtoms::tableFrame == frameType,
1633 : "unexpected frame type");
1634 :
1635 0 : rs->frame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT);
1636 0 : if (nsGkAtoms::tableFrame == frameType) {
1637 0 : NS_ASSERTION(rs != &aReflowState,
1638 : "should not request special height reflow for table");
1639 : // always stop when we reach a table
1640 0 : break;
1641 : }
1642 : }
1643 0 : }
1644 :
1645 : /******************************************************************************************
1646 : * Before reflow, intrinsic width calculation is done using GetMinWidth
1647 : * and GetPrefWidth. This used to be known as pass 1 reflow.
1648 : *
1649 : * After the intrinsic width calculation, the table determines the
1650 : * column widths using BalanceColumnWidths() and
1651 : * then reflows each child again with a constrained avail width. This reflow is referred to
1652 : * as the pass 2 reflow.
1653 : *
1654 : * A special height reflow (pass 3 reflow) can occur during an initial or resize reflow
1655 : * if (a) a row group, row, cell, or a frame inside a cell has a percent height but no computed
1656 : * height or (b) in paginated mode, a table has a height. (a) supports percent nested tables
1657 : * contained inside cells whose heights aren't known until after the pass 2 reflow. (b) is
1658 : * necessary because the table cannot split until after the pass 2 reflow. The mechanics of
1659 : * the special height reflow (variety a) are as follows:
1660 : *
1661 : * 1) Each table related frame (table, row group, row, cell) implements NeedsSpecialReflow()
1662 : * to indicate that it should get the reflow. It does this when it has a percent height but
1663 : * no computed height by calling CheckRequestSpecialHeightReflow(). This method calls
1664 : * RequestSpecialHeightReflow() which calls SetNeedSpecialReflow() on its ancestors until
1665 : * it reaches the containing table and calls SetNeedToInitiateSpecialReflow() on it. For
1666 : * percent height frames inside cells, during DidReflow(), the cell's NotifyPercentHeight()
1667 : * is called (the cell is the reflow state's mPercentHeightObserver in this case).
1668 : * NotifyPercentHeight() calls RequestSpecialHeightReflow().
1669 : *
1670 : * 2) After the pass 2 reflow, if the table's NeedToInitiateSpecialReflow(true) was called, it
1671 : * will do the special height reflow, setting the reflow state's mFlags.mSpecialHeightReflow
1672 : * to true and mSpecialHeightInitiator to itself. It won't do this if IsPrematureSpecialHeightReflow()
1673 : * returns true because in that case another special height reflow will be coming along with the
1674 : * containing table as the mSpecialHeightInitiator. It is only relevant to do the reflow when
1675 : * the mSpecialHeightInitiator is the containing table, because if it is a remote ancestor, then
1676 : * appropriate heights will not be known.
1677 : *
1678 : * 3) Since the heights of the table, row groups, rows, and cells was determined during the pass 2
1679 : * reflow, they return their last desired sizes during the special height reflow. The reflow only
1680 : * permits percent height frames inside the cells to resize based on the cells height and that height
1681 : * was determined during the pass 2 reflow.
1682 : *
1683 : * So, in the case of deeply nested tables, all of the tables that were told to initiate a special
1684 : * reflow will do so, but if a table is already in a special reflow, it won't inititate the reflow
1685 : * until the current initiator is its containing table. Since these reflows are only received by
1686 : * frames that need them and they don't cause any rebalancing of tables, the extra overhead is minimal.
1687 : *
1688 : * The type of special reflow that occurs during printing (variety b) follows the same mechanism except
1689 : * that all frames will receive the reflow even if they don't really need them.
1690 : *
1691 : * Open issues with the special height reflow:
1692 : *
1693 : * 1) At some point there should be 2 kinds of special height reflows because (a) and (b) above are
1694 : * really quite different. This would avoid unnecessary reflows during printing.
1695 : * 2) When a cell contains frames whose percent heights > 100%, there is data loss (see bug 115245).
1696 : * However, this can also occur if a cell has a fixed height and there is no special height reflow.
1697 : *
1698 : * XXXldb Special height reflow should really be its own method, not
1699 : * part of nsIFrame::Reflow. It should then call nsIFrame::Reflow on
1700 : * the contents of the cells to do the necessary vertical resizing.
1701 : *
1702 : ******************************************************************************************/
1703 :
1704 : /* Layout the entire inner table. */
1705 0 : NS_METHOD nsTableFrame::Reflow(nsPresContext* aPresContext,
1706 : nsHTMLReflowMetrics& aDesiredSize,
1707 : const nsHTMLReflowState& aReflowState,
1708 : nsReflowStatus& aStatus)
1709 : {
1710 0 : DO_GLOBAL_REFLOW_COUNT("nsTableFrame");
1711 0 : DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
1712 0 : bool isPaginated = aPresContext->IsPaginated();
1713 :
1714 0 : aStatus = NS_FRAME_COMPLETE;
1715 0 : if (!GetPrevInFlow() && !mTableLayoutStrategy) {
1716 0 : NS_ASSERTION(false, "strategy should have been created in Init");
1717 0 : return NS_ERROR_NULL_POINTER;
1718 : }
1719 0 : nsresult rv = NS_OK;
1720 :
1721 : // see if collapsing borders need to be calculated
1722 0 : if (!GetPrevInFlow() && IsBorderCollapse() && NeedToCalcBCBorders()) {
1723 0 : CalcBCBorders();
1724 : }
1725 :
1726 0 : aDesiredSize.width = aReflowState.availableWidth;
1727 :
1728 : // Check for an overflow list, and append any row group frames being pushed
1729 0 : MoveOverflowToChildList(aPresContext);
1730 :
1731 0 : bool haveDesiredHeight = false;
1732 0 : SetHaveReflowedColGroups(false);
1733 :
1734 : // Reflow the entire table (pass 2 and possibly pass 3). This phase is necessary during a
1735 : // constrained initial reflow and other reflows which require either a strategy init or balance.
1736 : // This isn't done during an unconstrained reflow, because it will occur later when the parent
1737 : // reflows with a constrained width.
1738 0 : if (NS_SUBTREE_DIRTY(this) ||
1739 0 : aReflowState.ShouldReflowAllKids() ||
1740 0 : IsGeometryDirty() ||
1741 : aReflowState.mFlags.mVResize) {
1742 :
1743 0 : if (aReflowState.ComputedHeight() != NS_UNCONSTRAINEDSIZE ||
1744 : // Also check mVResize, to handle the first Reflow preceding a
1745 : // special height Reflow, when we've already had a special height
1746 : // Reflow (where mComputedHeight would not be
1747 : // NS_UNCONSTRAINEDSIZE, but without a style change in between).
1748 : aReflowState.mFlags.mVResize) {
1749 : // XXX Eventually, we should modify DistributeHeightToRows to use
1750 : // nsTableRowFrame::GetHeight instead of nsIFrame::GetSize().height.
1751 : // That way, it will make its calculations based on internal table
1752 : // frame heights as they are before they ever had any extra height
1753 : // distributed to them. In the meantime, this reflows all the
1754 : // internal table frames, which restores them to their state before
1755 : // DistributeHeightToRows was called.
1756 0 : SetGeometryDirty();
1757 : }
1758 :
1759 : bool needToInitiateSpecialReflow =
1760 0 : !!(GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT);
1761 : // see if an extra reflow will be necessary in pagination mode when there is a specified table height
1762 0 : if (isPaginated && !GetPrevInFlow() && (NS_UNCONSTRAINEDSIZE != aReflowState.availableHeight)) {
1763 0 : nscoord tableSpecifiedHeight = CalcBorderBoxHeight(aReflowState);
1764 0 : if ((tableSpecifiedHeight > 0) &&
1765 : (tableSpecifiedHeight != NS_UNCONSTRAINEDSIZE)) {
1766 0 : needToInitiateSpecialReflow = true;
1767 : }
1768 : }
1769 0 : nsIFrame* lastChildReflowed = nsnull;
1770 :
1771 0 : NS_ASSERTION(!aReflowState.mFlags.mSpecialHeightReflow,
1772 : "Shouldn't be in special height reflow here!");
1773 :
1774 : // do the pass 2 reflow unless this is a special height reflow and we will be
1775 : // initiating a special height reflow
1776 : // XXXldb I changed this. Should I change it back?
1777 :
1778 : // if we need to initiate a special height reflow, then don't constrain the
1779 : // height of the reflow before that
1780 : nscoord availHeight = needToInitiateSpecialReflow
1781 0 : ? NS_UNCONSTRAINEDSIZE : aReflowState.availableHeight;
1782 :
1783 : ReflowTable(aDesiredSize, aReflowState, availHeight,
1784 0 : lastChildReflowed, aStatus);
1785 :
1786 : // reevaluate special height reflow conditions
1787 0 : if (GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT)
1788 0 : needToInitiateSpecialReflow = true;
1789 :
1790 : // XXXldb Are all these conditions correct?
1791 0 : if (needToInitiateSpecialReflow && NS_FRAME_IS_COMPLETE(aStatus)) {
1792 : // XXXldb Do we need to set the mVResize flag on any reflow states?
1793 :
1794 : nsHTMLReflowState &mutable_rs =
1795 0 : const_cast<nsHTMLReflowState&>(aReflowState);
1796 :
1797 : // distribute extra vertical space to rows
1798 0 : CalcDesiredHeight(aReflowState, aDesiredSize);
1799 0 : mutable_rs.mFlags.mSpecialHeightReflow = true;
1800 :
1801 : ReflowTable(aDesiredSize, aReflowState, aReflowState.availableHeight,
1802 0 : lastChildReflowed, aStatus);
1803 :
1804 0 : if (lastChildReflowed && NS_FRAME_IS_NOT_COMPLETE(aStatus)) {
1805 : // if there is an incomplete child, then set the desired height to include it but not the next one
1806 0 : nsMargin borderPadding = GetChildAreaOffset(&aReflowState);
1807 0 : aDesiredSize.height = borderPadding.bottom + GetCellSpacingY() +
1808 0 : lastChildReflowed->GetRect().YMost();
1809 : }
1810 0 : haveDesiredHeight = true;
1811 :
1812 0 : mutable_rs.mFlags.mSpecialHeightReflow = false;
1813 : }
1814 : }
1815 : else {
1816 : // Calculate the overflow area contribution from our children.
1817 0 : for (nsIFrame* kid = GetFirstPrincipalChild(); kid; kid = kid->GetNextSibling()) {
1818 0 : ConsiderChildOverflow(aDesiredSize.mOverflowAreas, kid);
1819 : }
1820 : }
1821 :
1822 0 : aDesiredSize.width = aReflowState.ComputedWidth() +
1823 0 : aReflowState.mComputedBorderPadding.LeftRight();
1824 0 : if (!haveDesiredHeight) {
1825 0 : CalcDesiredHeight(aReflowState, aDesiredSize);
1826 : }
1827 0 : if (IsRowInserted()) {
1828 0 : ProcessRowInserted(aDesiredSize.height);
1829 : }
1830 :
1831 0 : nsMargin borderPadding = GetChildAreaOffset(&aReflowState);
1832 0 : SetColumnDimensions(aDesiredSize.height, borderPadding);
1833 0 : if (NeedToCollapse() &&
1834 : (NS_UNCONSTRAINEDSIZE != aReflowState.availableWidth)) {
1835 0 : AdjustForCollapsingRowsCols(aDesiredSize, borderPadding);
1836 : }
1837 :
1838 : // make sure the table overflow area does include the table rect.
1839 0 : nsRect tableRect(0, 0, aDesiredSize.width, aDesiredSize.height) ;
1840 :
1841 0 : if (!ApplyOverflowClipping(this, aReflowState.mStyleDisplay)) {
1842 : // collapsed border may leak out
1843 0 : nsMargin bcMargin = GetExcludedOuterBCBorder();
1844 0 : tableRect.Inflate(bcMargin);
1845 : }
1846 0 : aDesiredSize.mOverflowAreas.UnionAllWith(tableRect);
1847 :
1848 0 : if (GetStateBits() & NS_FRAME_FIRST_REFLOW) {
1849 : // Fulfill the promise InvalidateFrame makes.
1850 0 : Invalidate(aDesiredSize.VisualOverflow());
1851 : } else {
1852 0 : CheckInvalidateSizeChange(aDesiredSize);
1853 : }
1854 :
1855 0 : FinishAndStoreOverflow(&aDesiredSize);
1856 0 : NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
1857 0 : return rv;
1858 : }
1859 :
1860 : bool
1861 0 : nsTableFrame::UpdateOverflow()
1862 : {
1863 0 : nsRect bounds(nsPoint(0, 0), GetSize());
1864 :
1865 : // As above in Reflow, make sure the table overflow area includes the table
1866 : // rect, and check for collapsed borders leaking out.
1867 0 : if (!ApplyOverflowClipping(this, GetStyleDisplay())) {
1868 0 : nsMargin bcMargin = GetExcludedOuterBCBorder();
1869 0 : bounds.Inflate(bcMargin);
1870 : }
1871 :
1872 0 : nsOverflowAreas overflowAreas(bounds, bounds);
1873 0 : nsLayoutUtils::UnionChildOverflow(this, overflowAreas);
1874 :
1875 0 : return FinishAndStoreOverflow(overflowAreas, GetSize());
1876 : }
1877 :
1878 : nsresult
1879 0 : nsTableFrame::ReflowTable(nsHTMLReflowMetrics& aDesiredSize,
1880 : const nsHTMLReflowState& aReflowState,
1881 : nscoord aAvailHeight,
1882 : nsIFrame*& aLastChildReflowed,
1883 : nsReflowStatus& aStatus)
1884 : {
1885 0 : nsresult rv = NS_OK;
1886 0 : aLastChildReflowed = nsnull;
1887 :
1888 0 : if (!GetPrevInFlow()) {
1889 0 : mTableLayoutStrategy->ComputeColumnWidths(aReflowState);
1890 : }
1891 : // Constrain our reflow width to the computed table width (of the 1st in flow).
1892 : // and our reflow height to our avail height minus border, padding, cellspacing
1893 0 : aDesiredSize.width = aReflowState.ComputedWidth() +
1894 0 : aReflowState.mComputedBorderPadding.LeftRight();
1895 0 : nsTableReflowState reflowState(*PresContext(), aReflowState, *this,
1896 0 : aDesiredSize.width, aAvailHeight);
1897 : ReflowChildren(reflowState, aStatus, aLastChildReflowed,
1898 0 : aDesiredSize.mOverflowAreas);
1899 :
1900 0 : ReflowColGroups(aReflowState.rendContext);
1901 0 : return rv;
1902 : }
1903 :
1904 : nsIFrame*
1905 0 : nsTableFrame::GetFirstBodyRowGroupFrame()
1906 : {
1907 0 : nsIFrame* headerFrame = nsnull;
1908 0 : nsIFrame* footerFrame = nsnull;
1909 :
1910 0 : for (nsIFrame* kidFrame = mFrames.FirstChild(); nsnull != kidFrame; ) {
1911 0 : const nsStyleDisplay* childDisplay = kidFrame->GetStyleDisplay();
1912 :
1913 : // We expect the header and footer row group frames to be first, and we only
1914 : // allow one header and one footer
1915 0 : if (NS_STYLE_DISPLAY_TABLE_HEADER_GROUP == childDisplay->mDisplay) {
1916 0 : if (headerFrame) {
1917 : // We already have a header frame and so this header frame is treated
1918 : // like an ordinary body row group frame
1919 0 : return kidFrame;
1920 : }
1921 0 : headerFrame = kidFrame;
1922 :
1923 0 : } else if (NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP == childDisplay->mDisplay) {
1924 0 : if (footerFrame) {
1925 : // We already have a footer frame and so this footer frame is treated
1926 : // like an ordinary body row group frame
1927 0 : return kidFrame;
1928 : }
1929 0 : footerFrame = kidFrame;
1930 :
1931 0 : } else if (NS_STYLE_DISPLAY_TABLE_ROW_GROUP == childDisplay->mDisplay) {
1932 0 : return kidFrame;
1933 : }
1934 :
1935 : // Get the next child
1936 0 : kidFrame = kidFrame->GetNextSibling();
1937 : }
1938 :
1939 0 : return nsnull;
1940 : }
1941 :
1942 : // Table specific version that takes into account repeated header and footer
1943 : // frames when continuing table frames
1944 : void
1945 0 : nsTableFrame::PushChildren(const RowGroupArray& aRowGroups,
1946 : PRInt32 aPushFrom)
1947 : {
1948 0 : NS_PRECONDITION(aPushFrom > 0, "pushing first child");
1949 :
1950 : // extract the frames from the array into a sibling list
1951 0 : nsFrameList frames;
1952 : PRUint32 childX;
1953 0 : for (childX = aPushFrom; childX < aRowGroups.Length(); ++childX) {
1954 0 : nsTableRowGroupFrame* rgFrame = aRowGroups[childX];
1955 0 : if (!rgFrame->IsRepeatable()) {
1956 0 : mFrames.RemoveFrame(rgFrame);
1957 0 : frames.AppendFrame(nsnull, rgFrame);
1958 : }
1959 : }
1960 :
1961 0 : if (frames.IsEmpty()) {
1962 : return;
1963 : }
1964 :
1965 0 : nsTableFrame* nextInFlow = static_cast<nsTableFrame*>(GetNextInFlow());
1966 0 : if (nextInFlow) {
1967 : // Insert the frames after any repeated header and footer frames.
1968 0 : nsIFrame* firstBodyFrame = nextInFlow->GetFirstBodyRowGroupFrame();
1969 0 : nsIFrame* prevSibling = nsnull;
1970 0 : if (firstBodyFrame) {
1971 0 : prevSibling = firstBodyFrame->GetPrevSibling();
1972 : }
1973 : // When pushing and pulling frames we need to check for whether any
1974 : // views need to be reparented.
1975 0 : ReparentFrameViewList(PresContext(), frames, this, nextInFlow);
1976 : nextInFlow->mFrames.InsertFrames(nextInFlow, prevSibling,
1977 0 : frames);
1978 : }
1979 : else {
1980 : // Add the frames to our overflow list.
1981 0 : SetOverflowFrames(PresContext(), frames);
1982 : }
1983 : }
1984 :
1985 : // collapsing row groups, rows, col groups and cols are accounted for after both passes of
1986 : // reflow so that it has no effect on the calculations of reflow.
1987 : void
1988 0 : nsTableFrame::AdjustForCollapsingRowsCols(nsHTMLReflowMetrics& aDesiredSize,
1989 : nsMargin aBorderPadding)
1990 : {
1991 0 : nscoord yTotalOffset = 0; // total offset among all rows in all row groups
1992 :
1993 : // reset the bit, it will be set again if row/rowgroup or col/colgroup are
1994 : // collapsed
1995 0 : SetNeedToCollapse(false);
1996 :
1997 : // collapse the rows and/or row groups as necessary
1998 : // Get the ordered children
1999 0 : RowGroupArray rowGroups;
2000 0 : OrderRowGroups(rowGroups);
2001 :
2002 0 : nsTableFrame* firstInFlow = static_cast<nsTableFrame*> (GetFirstInFlow());
2003 0 : nscoord width = firstInFlow->GetCollapsedWidth(aBorderPadding);
2004 0 : nscoord rgWidth = width - 2 * GetCellSpacingX();
2005 0 : nsOverflowAreas overflow;
2006 : // Walk the list of children
2007 0 : for (PRUint32 childX = 0; childX < rowGroups.Length(); childX++) {
2008 0 : nsTableRowGroupFrame* rgFrame = rowGroups[childX];
2009 0 : NS_ASSERTION(rgFrame, "Must have row group frame here");
2010 0 : yTotalOffset += rgFrame->CollapseRowGroupIfNecessary(yTotalOffset, rgWidth);
2011 0 : ConsiderChildOverflow(overflow, rgFrame);
2012 : }
2013 :
2014 0 : aDesiredSize.height -= yTotalOffset;
2015 0 : aDesiredSize.width = width;
2016 0 : overflow.UnionAllWith(nsRect(0, 0, aDesiredSize.width, aDesiredSize.height));
2017 : FinishAndStoreOverflow(overflow,
2018 0 : nsSize(aDesiredSize.width, aDesiredSize.height));
2019 0 : }
2020 :
2021 :
2022 : nscoord
2023 0 : nsTableFrame::GetCollapsedWidth(nsMargin aBorderPadding)
2024 : {
2025 0 : NS_ASSERTION(!GetPrevInFlow(), "GetCollapsedWidth called on next in flow");
2026 0 : nscoord cellSpacingX = GetCellSpacingX();
2027 0 : nscoord width = cellSpacingX;
2028 0 : width += aBorderPadding.left + aBorderPadding.right;
2029 0 : for (nsIFrame* groupFrame = mColGroups.FirstChild(); groupFrame;
2030 : groupFrame = groupFrame->GetNextSibling()) {
2031 0 : const nsStyleVisibility* groupVis = groupFrame->GetStyleVisibility();
2032 0 : bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE == groupVis->mVisible);
2033 0 : nsTableColGroupFrame* cgFrame = (nsTableColGroupFrame*)groupFrame;
2034 0 : for (nsTableColFrame* colFrame = cgFrame->GetFirstColumn(); colFrame;
2035 : colFrame = colFrame->GetNextCol()) {
2036 0 : const nsStyleDisplay* colDisplay = colFrame->GetStyleDisplay();
2037 0 : PRInt32 colX = colFrame->GetColIndex();
2038 0 : if (NS_STYLE_DISPLAY_TABLE_COLUMN == colDisplay->mDisplay) {
2039 0 : const nsStyleVisibility* colVis = colFrame->GetStyleVisibility();
2040 0 : bool collapseCol = (NS_STYLE_VISIBILITY_COLLAPSE == colVis->mVisible);
2041 0 : PRInt32 colWidth = GetColumnWidth(colX);
2042 0 : if (!collapseGroup && !collapseCol) {
2043 0 : width += colWidth;
2044 0 : if (ColumnHasCellSpacingBefore(colX))
2045 0 : width += cellSpacingX;
2046 : }
2047 : else {
2048 0 : SetNeedToCollapse(true);
2049 : }
2050 : }
2051 : }
2052 : }
2053 0 : return width;
2054 : }
2055 :
2056 : /* virtual */ void
2057 0 : nsTableFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
2058 : {
2059 0 : if (!aOldStyleContext) //avoid this on init
2060 0 : return;
2061 :
2062 0 : if (IsBorderCollapse() &&
2063 0 : BCRecalcNeeded(aOldStyleContext, GetStyleContext())) {
2064 0 : SetFullBCDamageArea();
2065 : }
2066 :
2067 : //avoid this on init or nextinflow
2068 0 : if (!mTableLayoutStrategy || GetPrevInFlow())
2069 0 : return;
2070 :
2071 0 : bool isAuto = IsAutoLayout();
2072 0 : if (isAuto != (LayoutStrategy()->GetType() == nsITableLayoutStrategy::Auto)) {
2073 : nsITableLayoutStrategy* temp;
2074 0 : if (isAuto)
2075 0 : temp = new BasicTableLayoutStrategy(this);
2076 : else
2077 0 : temp = new FixedTableLayoutStrategy(this);
2078 :
2079 0 : if (temp) {
2080 0 : delete mTableLayoutStrategy;
2081 0 : mTableLayoutStrategy = temp;
2082 : }
2083 : }
2084 : }
2085 :
2086 :
2087 :
2088 : NS_IMETHODIMP
2089 0 : nsTableFrame::AppendFrames(ChildListID aListID,
2090 : nsFrameList& aFrameList)
2091 : {
2092 0 : NS_ASSERTION(aListID == kPrincipalList || aListID == kColGroupList,
2093 : "unexpected child list");
2094 :
2095 : // Because we actually have two child lists, one for col group frames and one
2096 : // for everything else, we need to look at each frame individually
2097 : // XXX The frame construction code should be separating out child frames
2098 : // based on the type, bug 343048.
2099 0 : while (!aFrameList.IsEmpty()) {
2100 0 : nsIFrame* f = aFrameList.FirstChild();
2101 0 : aFrameList.RemoveFrame(f);
2102 :
2103 : // See what kind of frame we have
2104 0 : const nsStyleDisplay* display = f->GetStyleDisplay();
2105 :
2106 0 : if (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == display->mDisplay) {
2107 : nsTableColGroupFrame* lastColGroup =
2108 0 : nsTableColGroupFrame::GetLastRealColGroup(this);
2109 : PRInt32 startColIndex = (lastColGroup)
2110 0 : ? lastColGroup->GetStartColumnIndex() + lastColGroup->GetColCount() : 0;
2111 0 : mColGroups.InsertFrame(nsnull, lastColGroup, f);
2112 : // Insert the colgroup and its cols into the table
2113 : InsertColGroups(startColIndex,
2114 0 : nsFrameList::Slice(mColGroups, f, f->GetNextSibling()));
2115 0 : } else if (IsRowGroup(display->mDisplay)) {
2116 : // Append the new row group frame to the sibling chain
2117 0 : mFrames.AppendFrame(nsnull, f);
2118 :
2119 : // insert the row group and its rows into the table
2120 0 : InsertRowGroups(nsFrameList::Slice(mFrames, f, nsnull));
2121 : } else {
2122 : // Nothing special to do, just add the frame to our child list
2123 0 : NS_NOTREACHED("How did we get here? Frame construction screwed up");
2124 0 : mFrames.AppendFrame(nsnull, f);
2125 : }
2126 : }
2127 :
2128 : #ifdef DEBUG_TABLE_CELLMAP
2129 : printf("=== TableFrame::AppendFrames\n");
2130 : Dump(true, true, true);
2131 : #endif
2132 0 : PresContext()->PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange,
2133 0 : NS_FRAME_HAS_DIRTY_CHILDREN);
2134 0 : SetGeometryDirty();
2135 :
2136 0 : return NS_OK;
2137 : }
2138 :
2139 : NS_IMETHODIMP
2140 0 : nsTableFrame::InsertFrames(ChildListID aListID,
2141 : nsIFrame* aPrevFrame,
2142 : nsFrameList& aFrameList)
2143 : {
2144 : // Asssume there's only one frame being inserted. The problem is that
2145 : // row group frames and col group frames go in separate child lists and
2146 : // so if there's more than one type of frames this gets messy...
2147 : // XXX The frame construction code should be separating out child frames
2148 : // based on the type, bug 343048.
2149 :
2150 0 : NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
2151 : "inserting after sibling frame with different parent");
2152 :
2153 0 : if ((aPrevFrame && !aPrevFrame->GetNextSibling()) ||
2154 0 : (!aPrevFrame && GetChildList(aListID).IsEmpty())) {
2155 : // Treat this like an append; still a workaround for bug 343048.
2156 0 : return AppendFrames(aListID, aFrameList);
2157 : }
2158 :
2159 : // See what kind of frame we have
2160 0 : const nsStyleDisplay* display = aFrameList.FirstChild()->GetStyleDisplay();
2161 : #ifdef DEBUG
2162 : // verify that all sibling have the same type, if they do not, expect cellmap issues
2163 0 : for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
2164 0 : const nsStyleDisplay* nextDisplay = e.get()->GetStyleDisplay();
2165 0 : NS_ASSERTION((display->mDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP) ==
2166 : (nextDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP),
2167 : "heterogenous childlist");
2168 : }
2169 : #endif
2170 0 : if (aPrevFrame) {
2171 0 : const nsStyleDisplay* prevDisplay = aPrevFrame->GetStyleDisplay();
2172 : // Make sure they belong on the same frame list
2173 0 : if ((display->mDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP) !=
2174 : (prevDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP)) {
2175 : // the previous frame is not valid, see comment at ::AppendFrames
2176 : // XXXbz Using content indices here means XBL will get screwed
2177 : // over... Oh, well.
2178 0 : nsIFrame* pseudoFrame = aFrameList.FirstChild();
2179 0 : nsIContent* parentContent = GetContent();
2180 : nsIContent* content;
2181 0 : aPrevFrame = nsnull;
2182 0 : while (pseudoFrame && (parentContent ==
2183 : (content = pseudoFrame->GetContent()))) {
2184 0 : pseudoFrame = pseudoFrame->GetFirstPrincipalChild();
2185 : }
2186 0 : nsCOMPtr<nsIContent> container = content->GetParent();
2187 0 : if (NS_LIKELY(container)) { // XXX need this null-check, see bug 411823.
2188 0 : PRInt32 newIndex = container->IndexOf(content);
2189 : nsIFrame* kidFrame;
2190 : bool isColGroup = (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP ==
2191 0 : display->mDisplay);
2192 : nsTableColGroupFrame* lastColGroup;
2193 0 : if (isColGroup) {
2194 0 : kidFrame = mColGroups.FirstChild();
2195 0 : lastColGroup = nsTableColGroupFrame::GetLastRealColGroup(this);
2196 : }
2197 : else {
2198 0 : kidFrame = mFrames.FirstChild();
2199 : }
2200 : // Important: need to start at a value smaller than all valid indices
2201 0 : PRInt32 lastIndex = -1;
2202 0 : while (kidFrame) {
2203 0 : if (isColGroup) {
2204 0 : if (kidFrame == lastColGroup) {
2205 0 : aPrevFrame = kidFrame; // there is no real colgroup after this one
2206 0 : break;
2207 : }
2208 : }
2209 0 : pseudoFrame = kidFrame;
2210 0 : while (pseudoFrame && (parentContent ==
2211 : (content = pseudoFrame->GetContent()))) {
2212 0 : pseudoFrame = pseudoFrame->GetFirstPrincipalChild();
2213 : }
2214 0 : PRInt32 index = container->IndexOf(content);
2215 0 : if (index > lastIndex && index < newIndex) {
2216 0 : lastIndex = index;
2217 0 : aPrevFrame = kidFrame;
2218 : }
2219 0 : kidFrame = kidFrame->GetNextSibling();
2220 : }
2221 : }
2222 : }
2223 : }
2224 0 : if (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == display->mDisplay) {
2225 0 : NS_ASSERTION(aListID == kPrincipalList || aListID == kColGroupList,
2226 : "unexpected child list");
2227 : // Insert the column group frames
2228 : const nsFrameList::Slice& newColgroups =
2229 0 : mColGroups.InsertFrames(nsnull, aPrevFrame, aFrameList);
2230 : // find the starting col index for the first new col group
2231 0 : PRInt32 startColIndex = 0;
2232 0 : if (aPrevFrame) {
2233 : nsTableColGroupFrame* prevColGroup =
2234 : (nsTableColGroupFrame*)GetFrameAtOrBefore(this, aPrevFrame,
2235 0 : nsGkAtoms::tableColGroupFrame);
2236 0 : if (prevColGroup) {
2237 0 : startColIndex = prevColGroup->GetStartColumnIndex() + prevColGroup->GetColCount();
2238 : }
2239 : }
2240 0 : InsertColGroups(startColIndex, newColgroups);
2241 0 : } else if (IsRowGroup(display->mDisplay)) {
2242 0 : NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
2243 : // Insert the frames in the sibling chain
2244 : const nsFrameList::Slice& newRowGroups =
2245 0 : mFrames.InsertFrames(nsnull, aPrevFrame, aFrameList);
2246 :
2247 0 : InsertRowGroups(newRowGroups);
2248 : } else {
2249 0 : NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
2250 0 : NS_NOTREACHED("How did we even get here?");
2251 : // Just insert the frame and don't worry about reflowing it
2252 0 : mFrames.InsertFrames(nsnull, aPrevFrame, aFrameList);
2253 0 : return NS_OK;
2254 : }
2255 :
2256 0 : PresContext()->PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange,
2257 0 : NS_FRAME_HAS_DIRTY_CHILDREN);
2258 0 : SetGeometryDirty();
2259 : #ifdef DEBUG_TABLE_CELLMAP
2260 : printf("=== TableFrame::InsertFrames\n");
2261 : Dump(true, true, true);
2262 : #endif
2263 0 : return NS_OK;
2264 : }
2265 :
2266 : NS_IMETHODIMP
2267 0 : nsTableFrame::RemoveFrame(ChildListID aListID,
2268 : nsIFrame* aOldFrame)
2269 : {
2270 0 : NS_ASSERTION(aListID == kColGroupList ||
2271 : NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP !=
2272 : aOldFrame->GetStyleDisplay()->mDisplay,
2273 : "Wrong list name; use kColGroupList iff colgroup");
2274 0 : if (aListID == kColGroupList) {
2275 0 : nsIFrame* nextColGroupFrame = aOldFrame->GetNextSibling();
2276 0 : nsTableColGroupFrame* colGroup = (nsTableColGroupFrame*)aOldFrame;
2277 0 : PRInt32 firstColIndex = colGroup->GetStartColumnIndex();
2278 0 : PRInt32 lastColIndex = firstColIndex + colGroup->GetColCount() - 1;
2279 0 : mColGroups.DestroyFrame(aOldFrame);
2280 0 : nsTableColGroupFrame::ResetColIndices(nextColGroupFrame, firstColIndex);
2281 : // remove the cols from the table
2282 : PRInt32 colX;
2283 0 : for (colX = lastColIndex; colX >= firstColIndex; colX--) {
2284 0 : nsTableColFrame* colFrame = mColFrames.SafeElementAt(colX);
2285 0 : if (colFrame) {
2286 0 : RemoveCol(colGroup, colX, true, false);
2287 : }
2288 : }
2289 :
2290 0 : PRInt32 numAnonymousColsToAdd = GetColCount() - mColFrames.Length();
2291 0 : if (numAnonymousColsToAdd > 0) {
2292 : // this sets the child list, updates the col cache and cell map
2293 0 : AppendAnonymousColFrames(numAnonymousColsToAdd);
2294 : }
2295 :
2296 : } else {
2297 0 : NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
2298 : nsTableRowGroupFrame* rgFrame =
2299 0 : static_cast<nsTableRowGroupFrame*>(aOldFrame);
2300 : // remove the row group from the cell map
2301 0 : nsTableCellMap* cellMap = GetCellMap();
2302 0 : if (cellMap) {
2303 0 : cellMap->RemoveGroupCellMap(rgFrame);
2304 : }
2305 :
2306 : // remove the row group frame from the sibling chain
2307 0 : mFrames.DestroyFrame(aOldFrame);
2308 :
2309 : // the removal of a row group changes the cellmap, the columns might change
2310 0 : if (cellMap) {
2311 0 : cellMap->Synchronize(this);
2312 : // Create an empty slice
2313 0 : ResetRowIndices(nsFrameList::Slice(mFrames, nsnull, nsnull));
2314 0 : nsIntRect damageArea;
2315 0 : cellMap->RebuildConsideringCells(nsnull, nsnull, 0, 0, false, damageArea);
2316 :
2317 0 : MatchCellMapToColCache(cellMap);
2318 : }
2319 : }
2320 : // for now, just bail and recalc all of the collapsing borders
2321 : // as the cellmap changes we need to recalc
2322 0 : if (IsBorderCollapse()) {
2323 0 : SetFullBCDamageArea();
2324 : }
2325 0 : PresContext()->PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange,
2326 0 : NS_FRAME_HAS_DIRTY_CHILDREN);
2327 0 : SetGeometryDirty();
2328 : #ifdef DEBUG_TABLE_CELLMAP
2329 : printf("=== TableFrame::RemoveFrame\n");
2330 : Dump(true, true, true);
2331 : #endif
2332 0 : return NS_OK;
2333 : }
2334 :
2335 : /* virtual */ nsMargin
2336 0 : nsTableFrame::GetUsedBorder() const
2337 : {
2338 0 : if (!IsBorderCollapse())
2339 0 : return nsContainerFrame::GetUsedBorder();
2340 :
2341 0 : return GetIncludedOuterBCBorder();
2342 : }
2343 :
2344 : /* virtual */ nsMargin
2345 0 : nsTableFrame::GetUsedPadding() const
2346 : {
2347 0 : if (!IsBorderCollapse())
2348 0 : return nsContainerFrame::GetUsedPadding();
2349 :
2350 0 : return nsMargin(0,0,0,0);
2351 : }
2352 :
2353 : /* virtual */ nsMargin
2354 0 : nsTableFrame::GetUsedMargin() const
2355 : {
2356 : // The margin is inherited to the outer table frame via
2357 : // the ::-moz-table-outer rule in ua.css.
2358 0 : return nsMargin(0, 0, 0, 0);
2359 : }
2360 :
2361 : // Destructor function for BCPropertyData properties
2362 : static void
2363 0 : DestroyBCProperty(void* aPropertyValue)
2364 : {
2365 : delete static_cast<BCPropertyData*>(aPropertyValue);
2366 0 : }
2367 :
2368 0 : NS_DECLARE_FRAME_PROPERTY(TableBCProperty, DestroyBCProperty)
2369 :
2370 : BCPropertyData*
2371 0 : nsTableFrame::GetBCProperty(bool aCreateIfNecessary) const
2372 : {
2373 0 : FrameProperties props = Properties();
2374 : BCPropertyData* value = static_cast<BCPropertyData*>
2375 0 : (props.Get(TableBCProperty()));
2376 0 : if (!value && aCreateIfNecessary) {
2377 0 : value = new BCPropertyData();
2378 0 : props.Set(TableBCProperty(), value);
2379 : }
2380 :
2381 0 : return value;
2382 : }
2383 :
2384 : static void
2385 0 : DivideBCBorderSize(BCPixelSize aPixelSize,
2386 : BCPixelSize& aSmallHalf,
2387 : BCPixelSize& aLargeHalf)
2388 : {
2389 0 : aSmallHalf = aPixelSize / 2;
2390 0 : aLargeHalf = aPixelSize - aSmallHalf;
2391 0 : }
2392 :
2393 : nsMargin
2394 0 : nsTableFrame::GetOuterBCBorder() const
2395 : {
2396 0 : if (NeedToCalcBCBorders())
2397 0 : const_cast<nsTableFrame*>(this)->CalcBCBorders();
2398 :
2399 0 : nsMargin border(0, 0, 0, 0);
2400 0 : PRInt32 p2t = nsPresContext::AppUnitsPerCSSPixel();
2401 0 : BCPropertyData* propData = GetBCProperty();
2402 0 : if (propData) {
2403 0 : border.top = BC_BORDER_TOP_HALF_COORD(p2t, propData->mTopBorderWidth);
2404 0 : border.right = BC_BORDER_RIGHT_HALF_COORD(p2t, propData->mRightBorderWidth);
2405 0 : border.bottom = BC_BORDER_BOTTOM_HALF_COORD(p2t, propData->mBottomBorderWidth);
2406 0 : border.left = BC_BORDER_LEFT_HALF_COORD(p2t, propData->mLeftBorderWidth);
2407 : }
2408 : return border;
2409 : }
2410 :
2411 : nsMargin
2412 0 : nsTableFrame::GetIncludedOuterBCBorder() const
2413 : {
2414 0 : if (NeedToCalcBCBorders())
2415 0 : const_cast<nsTableFrame*>(this)->CalcBCBorders();
2416 :
2417 0 : nsMargin border(0, 0, 0, 0);
2418 0 : PRInt32 p2t = nsPresContext::AppUnitsPerCSSPixel();
2419 0 : BCPropertyData* propData = GetBCProperty();
2420 0 : if (propData) {
2421 0 : border.top += BC_BORDER_TOP_HALF_COORD(p2t, propData->mTopBorderWidth);
2422 0 : border.right += BC_BORDER_RIGHT_HALF_COORD(p2t, propData->mRightCellBorderWidth);
2423 0 : border.bottom += BC_BORDER_BOTTOM_HALF_COORD(p2t, propData->mBottomBorderWidth);
2424 0 : border.left += BC_BORDER_LEFT_HALF_COORD(p2t, propData->mLeftCellBorderWidth);
2425 : }
2426 : return border;
2427 : }
2428 :
2429 : nsMargin
2430 0 : nsTableFrame::GetExcludedOuterBCBorder() const
2431 : {
2432 0 : return GetOuterBCBorder() - GetIncludedOuterBCBorder();
2433 : }
2434 :
2435 : static
2436 0 : void GetSeparateModelBorderPadding(const nsHTMLReflowState* aReflowState,
2437 : nsStyleContext& aStyleContext,
2438 : nsMargin& aBorderPadding)
2439 : {
2440 : // XXXbz Either we _do_ have a reflow state and then we can use its
2441 : // mComputedBorderPadding or we don't and then we get the padding
2442 : // wrong!
2443 0 : const nsStyleBorder* border = aStyleContext.GetStyleBorder();
2444 0 : aBorderPadding = border->GetActualBorder();
2445 0 : if (aReflowState) {
2446 0 : aBorderPadding += aReflowState->mComputedPadding;
2447 : }
2448 0 : }
2449 :
2450 : nsMargin
2451 0 : nsTableFrame::GetChildAreaOffset(const nsHTMLReflowState* aReflowState) const
2452 : {
2453 0 : nsMargin offset(0,0,0,0);
2454 0 : if (IsBorderCollapse()) {
2455 0 : offset = GetIncludedOuterBCBorder();
2456 : }
2457 : else {
2458 0 : GetSeparateModelBorderPadding(aReflowState, *mStyleContext, offset);
2459 : }
2460 : return offset;
2461 : }
2462 :
2463 : void
2464 0 : nsTableFrame::InitChildReflowState(nsHTMLReflowState& aReflowState)
2465 : {
2466 0 : nsMargin collapseBorder;
2467 0 : nsMargin padding(0,0,0,0);
2468 0 : nsMargin* pCollapseBorder = nsnull;
2469 0 : nsPresContext* presContext = PresContext();
2470 0 : if (IsBorderCollapse()) {
2471 : nsTableRowGroupFrame* rgFrame =
2472 0 : static_cast<nsTableRowGroupFrame*>(aReflowState.frame);
2473 0 : pCollapseBorder = rgFrame->GetBCBorderWidth(collapseBorder);
2474 : }
2475 0 : aReflowState.Init(presContext, -1, -1, pCollapseBorder, &padding);
2476 :
2477 0 : NS_ASSERTION(!mBits.mResizedColumns ||
2478 : !aReflowState.parentReflowState->mFlags.mSpecialHeightReflow,
2479 : "should not resize columns on special height reflow");
2480 0 : if (mBits.mResizedColumns) {
2481 0 : aReflowState.mFlags.mHResize = true;
2482 : }
2483 0 : }
2484 :
2485 : // Position and size aKidFrame and update our reflow state. The origin of
2486 : // aKidRect is relative to the upper-left origin of our frame
2487 0 : void nsTableFrame::PlaceChild(nsTableReflowState& aReflowState,
2488 : nsIFrame* aKidFrame,
2489 : nsHTMLReflowMetrics& aKidDesiredSize,
2490 : const nsRect& aOriginalKidRect,
2491 : const nsRect& aOriginalKidVisualOverflow)
2492 : {
2493 : bool isFirstReflow =
2494 0 : (aKidFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0;
2495 :
2496 : // Place and size the child
2497 : FinishReflowChild(aKidFrame, PresContext(), nsnull, aKidDesiredSize,
2498 0 : aReflowState.x, aReflowState.y, 0);
2499 :
2500 : InvalidateFrame(aKidFrame, aOriginalKidRect, aOriginalKidVisualOverflow,
2501 0 : isFirstReflow);
2502 :
2503 : // Adjust the running y-offset
2504 0 : aReflowState.y += aKidDesiredSize.height;
2505 :
2506 : // If our height is constrained, then update the available height
2507 0 : if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) {
2508 0 : aReflowState.availSize.height -= aKidDesiredSize.height;
2509 : }
2510 0 : }
2511 :
2512 : void
2513 0 : nsTableFrame::OrderRowGroups(RowGroupArray& aChildren,
2514 : nsTableRowGroupFrame** aHead,
2515 : nsTableRowGroupFrame** aFoot) const
2516 : {
2517 0 : aChildren.Clear();
2518 0 : nsTableRowGroupFrame* head = nsnull;
2519 0 : nsTableRowGroupFrame* foot = nsnull;
2520 :
2521 0 : nsIFrame* kidFrame = mFrames.FirstChild();
2522 0 : while (kidFrame) {
2523 0 : const nsStyleDisplay* kidDisplay = kidFrame->GetStyleDisplay();
2524 : nsTableRowGroupFrame* rowGroup =
2525 0 : static_cast<nsTableRowGroupFrame*>(kidFrame);
2526 :
2527 0 : switch (kidDisplay->mDisplay) {
2528 : case NS_STYLE_DISPLAY_TABLE_HEADER_GROUP:
2529 0 : if (head) { // treat additional thead like tbody
2530 0 : aChildren.AppendElement(rowGroup);
2531 : }
2532 : else {
2533 0 : head = rowGroup;
2534 : }
2535 0 : break;
2536 : case NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP:
2537 0 : if (foot) { // treat additional tfoot like tbody
2538 0 : aChildren.AppendElement(rowGroup);
2539 : }
2540 : else {
2541 0 : foot = rowGroup;
2542 : }
2543 0 : break;
2544 : case NS_STYLE_DISPLAY_TABLE_ROW_GROUP:
2545 0 : aChildren.AppendElement(rowGroup);
2546 0 : break;
2547 : default:
2548 0 : NS_NOTREACHED("How did this produce an nsTableRowGroupFrame?");
2549 : // Just ignore it
2550 0 : break;
2551 : }
2552 : // Get the next sibling but skip it if it's also the next-in-flow, since
2553 : // a next-in-flow will not be part of the current table.
2554 0 : while (kidFrame) {
2555 0 : nsIFrame* nif = kidFrame->GetNextInFlow();
2556 0 : kidFrame = kidFrame->GetNextSibling();
2557 0 : if (kidFrame != nif)
2558 0 : break;
2559 : }
2560 : }
2561 :
2562 : // put the thead first
2563 0 : if (head) {
2564 0 : aChildren.InsertElementAt(0, head);
2565 : }
2566 0 : if (aHead)
2567 0 : *aHead = head;
2568 : // put the tfoot after the last tbody
2569 0 : if (foot) {
2570 0 : aChildren.AppendElement(foot);
2571 : }
2572 0 : if (aFoot)
2573 0 : *aFoot = foot;
2574 0 : }
2575 :
2576 : nsTableRowGroupFrame*
2577 0 : nsTableFrame::GetTHead() const
2578 : {
2579 0 : nsIFrame* kidFrame = mFrames.FirstChild();
2580 0 : while (kidFrame) {
2581 0 : if (kidFrame->GetStyleDisplay()->mDisplay ==
2582 : NS_STYLE_DISPLAY_TABLE_HEADER_GROUP) {
2583 0 : return static_cast<nsTableRowGroupFrame*>(kidFrame);
2584 : }
2585 :
2586 : // Get the next sibling but skip it if it's also the next-in-flow, since
2587 : // a next-in-flow will not be part of the current table.
2588 0 : while (kidFrame) {
2589 0 : nsIFrame* nif = kidFrame->GetNextInFlow();
2590 0 : kidFrame = kidFrame->GetNextSibling();
2591 0 : if (kidFrame != nif)
2592 0 : break;
2593 : }
2594 : }
2595 :
2596 0 : return nsnull;
2597 : }
2598 :
2599 : nsTableRowGroupFrame*
2600 0 : nsTableFrame::GetTFoot() const
2601 : {
2602 0 : nsIFrame* kidFrame = mFrames.FirstChild();
2603 0 : while (kidFrame) {
2604 0 : if (kidFrame->GetStyleDisplay()->mDisplay ==
2605 : NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP) {
2606 0 : return static_cast<nsTableRowGroupFrame*>(kidFrame);
2607 : }
2608 :
2609 : // Get the next sibling but skip it if it's also the next-in-flow, since
2610 : // a next-in-flow will not be part of the current table.
2611 0 : while (kidFrame) {
2612 0 : nsIFrame* nif = kidFrame->GetNextInFlow();
2613 0 : kidFrame = kidFrame->GetNextSibling();
2614 0 : if (kidFrame != nif)
2615 0 : break;
2616 : }
2617 : }
2618 :
2619 0 : return nsnull;
2620 : }
2621 :
2622 : static bool
2623 0 : IsRepeatable(nscoord aFrameHeight, nscoord aPageHeight)
2624 : {
2625 0 : return aFrameHeight < (aPageHeight / 4);
2626 : }
2627 :
2628 : nsresult
2629 0 : nsTableFrame::SetupHeaderFooterChild(const nsTableReflowState& aReflowState,
2630 : nsTableRowGroupFrame* aFrame,
2631 : nscoord* aDesiredHeight)
2632 : {
2633 0 : nsPresContext* presContext = PresContext();
2634 0 : nscoord pageHeight = presContext->GetPageSize().height;
2635 :
2636 : // Reflow the child with unconstrainted height
2637 : nsHTMLReflowState kidReflowState(presContext, aReflowState.reflowState,
2638 : aFrame,
2639 : nsSize(aReflowState.availSize.width, NS_UNCONSTRAINEDSIZE),
2640 0 : -1, -1, false);
2641 0 : InitChildReflowState(kidReflowState);
2642 0 : kidReflowState.mFlags.mIsTopOfPage = true;
2643 0 : nsHTMLReflowMetrics desiredSize;
2644 0 : desiredSize.width = desiredSize.height = 0;
2645 : nsReflowStatus status;
2646 : nsresult rv = ReflowChild(aFrame, presContext, desiredSize, kidReflowState,
2647 0 : aReflowState.x, aReflowState.y, 0, status);
2648 0 : NS_ENSURE_SUCCESS(rv, rv);
2649 : // The child will be reflowed again "for real" so no need to place it now
2650 :
2651 0 : aFrame->SetRepeatable(IsRepeatable(desiredSize.height, pageHeight));
2652 0 : *aDesiredHeight = desiredSize.height;
2653 0 : return NS_OK;
2654 : }
2655 :
2656 : void
2657 0 : nsTableFrame::PlaceRepeatedFooter(nsTableReflowState& aReflowState,
2658 : nsTableRowGroupFrame *aTfoot,
2659 : nscoord aFooterHeight)
2660 : {
2661 0 : nsPresContext* presContext = PresContext();
2662 0 : nsSize kidAvailSize(aReflowState.availSize);
2663 0 : kidAvailSize.height = aFooterHeight;
2664 : nsHTMLReflowState footerReflowState(presContext,
2665 : aReflowState.reflowState,
2666 : aTfoot, kidAvailSize,
2667 0 : -1, -1, false);
2668 0 : InitChildReflowState(footerReflowState);
2669 0 : aReflowState.y += GetCellSpacingY();
2670 :
2671 0 : nsRect origTfootRect = aTfoot->GetRect();
2672 0 : nsRect origTfootVisualOverflow = aTfoot->GetVisualOverflowRect();
2673 :
2674 : nsReflowStatus footerStatus;
2675 0 : nsHTMLReflowMetrics desiredSize;
2676 0 : desiredSize.width = desiredSize.height = 0;
2677 : ReflowChild(aTfoot, presContext, desiredSize, footerReflowState,
2678 : aReflowState.x, aReflowState.y,
2679 0 : NS_FRAME_INVALIDATE_ON_MOVE, footerStatus);
2680 : PlaceChild(aReflowState, aTfoot, desiredSize, origTfootRect,
2681 0 : origTfootVisualOverflow);
2682 0 : }
2683 :
2684 : // Reflow the children based on the avail size and reason in aReflowState
2685 : // update aReflowMetrics a aStatus
2686 : nsresult
2687 0 : nsTableFrame::ReflowChildren(nsTableReflowState& aReflowState,
2688 : nsReflowStatus& aStatus,
2689 : nsIFrame*& aLastChildReflowed,
2690 : nsOverflowAreas& aOverflowAreas)
2691 : {
2692 0 : aStatus = NS_FRAME_COMPLETE;
2693 0 : aLastChildReflowed = nsnull;
2694 :
2695 0 : nsIFrame* prevKidFrame = nsnull;
2696 0 : nsresult rv = NS_OK;
2697 0 : nscoord cellSpacingY = GetCellSpacingY();
2698 :
2699 0 : nsPresContext* presContext = PresContext();
2700 : // XXXldb Should we be checking constrained height instead?
2701 : // tables are not able to pull back children from its next inflow, so even
2702 : // under paginated contexts tables are should not paginate if they are inside
2703 : // column set
2704 0 : bool isPaginated = presContext->IsPaginated() &&
2705 : NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height &&
2706 0 : aReflowState.reflowState.mFlags.mTableIsSplittable;
2707 :
2708 0 : aOverflowAreas.Clear();
2709 :
2710 0 : bool reflowAllKids = aReflowState.reflowState.ShouldReflowAllKids() ||
2711 : mBits.mResizedColumns ||
2712 0 : IsGeometryDirty();
2713 :
2714 0 : RowGroupArray rowGroups;
2715 : nsTableRowGroupFrame *thead, *tfoot;
2716 0 : OrderRowGroups(rowGroups, &thead, &tfoot);
2717 0 : bool pageBreak = false;
2718 0 : nscoord footerHeight = 0;
2719 :
2720 : // Determine the repeatablility of headers and footers, and also the desired
2721 : // height of any repeatable footer.
2722 : // The repeatability of headers on continued tables is handled
2723 : // when they are created in nsCSSFrameConstructor::CreateContinuingTableFrame.
2724 : // We handle the repeatability of footers again here because we need to
2725 : // determine the footer's height anyway. We could perhaps optimize by
2726 : // using the footer's prev-in-flow's height instead of reflowing it again,
2727 : // but there's no real need.
2728 0 : if (isPaginated) {
2729 0 : if (thead && !GetPrevInFlow()) {
2730 : nscoord desiredHeight;
2731 0 : rv = SetupHeaderFooterChild(aReflowState, thead, &desiredHeight);
2732 0 : if (NS_FAILED(rv))
2733 0 : return rv;
2734 : }
2735 0 : if (tfoot) {
2736 0 : rv = SetupHeaderFooterChild(aReflowState, tfoot, &footerHeight);
2737 0 : if (NS_FAILED(rv))
2738 0 : return rv;
2739 : }
2740 : }
2741 : // if the child is a tbody in paginated mode reduce the height by a repeated footer
2742 0 : bool allowRepeatedFooter = false;
2743 0 : for (PRUint32 childX = 0; childX < rowGroups.Length(); childX++) {
2744 0 : nsIFrame* kidFrame = rowGroups[childX];
2745 : // Get the frame state bits
2746 : // See if we should only reflow the dirty child frames
2747 0 : if (reflowAllKids ||
2748 0 : NS_SUBTREE_DIRTY(kidFrame) ||
2749 : (aReflowState.reflowState.mFlags.mSpecialHeightReflow &&
2750 0 : (isPaginated || (kidFrame->GetStateBits() &
2751 : NS_FRAME_CONTAINS_RELATIVE_HEIGHT)))) {
2752 0 : if (pageBreak) {
2753 0 : if (allowRepeatedFooter) {
2754 0 : PlaceRepeatedFooter(aReflowState, tfoot, footerHeight);
2755 : }
2756 0 : else if (tfoot && tfoot->IsRepeatable()) {
2757 0 : tfoot->SetRepeatable(false);
2758 : }
2759 0 : PushChildren(rowGroups, childX);
2760 0 : aStatus = NS_FRAME_NOT_COMPLETE;
2761 0 : break;
2762 : }
2763 :
2764 0 : nsSize kidAvailSize(aReflowState.availSize);
2765 0 : allowRepeatedFooter = false;
2766 0 : if (isPaginated && (NS_UNCONSTRAINEDSIZE != kidAvailSize.height)) {
2767 : nsTableRowGroupFrame* kidRG =
2768 0 : static_cast<nsTableRowGroupFrame*>(kidFrame);
2769 0 : if (kidRG != thead && kidRG != tfoot && tfoot && tfoot->IsRepeatable()) {
2770 : // the child is a tbody and there is a repeatable footer
2771 0 : NS_ASSERTION(tfoot == rowGroups[rowGroups.Length() - 1], "Missing footer!");
2772 0 : if (footerHeight + cellSpacingY < kidAvailSize.height) {
2773 0 : allowRepeatedFooter = true;
2774 0 : kidAvailSize.height -= footerHeight + cellSpacingY;
2775 : }
2776 : }
2777 : }
2778 :
2779 0 : nsRect oldKidRect = kidFrame->GetRect();
2780 0 : nsRect oldKidVisualOverflow = kidFrame->GetVisualOverflowRect();
2781 :
2782 0 : nsHTMLReflowMetrics desiredSize;
2783 0 : desiredSize.width = desiredSize.height = 0;
2784 :
2785 : // Reflow the child into the available space
2786 : nsHTMLReflowState kidReflowState(presContext, aReflowState.reflowState,
2787 : kidFrame, kidAvailSize,
2788 0 : -1, -1, false);
2789 0 : InitChildReflowState(kidReflowState);
2790 :
2791 : // If this isn't the first row group, and the previous row group has a
2792 : // nonzero YMost, then we can't be at the top of the page.
2793 : // We ignore the head row group in this check, because a head row group
2794 : // may be automatically added at the top of *every* page. This prevents
2795 : // infinite loops in some circumstances - see bug 344883.
2796 0 : if (childX > (thead ? 1 : 0) &&
2797 0 : (rowGroups[childX - 1]->GetRect().YMost() > 0)) {
2798 0 : kidReflowState.mFlags.mIsTopOfPage = false;
2799 : }
2800 0 : aReflowState.y += cellSpacingY;
2801 0 : if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) {
2802 0 : aReflowState.availSize.height -= cellSpacingY;
2803 : }
2804 : // record the presence of a next in flow, it might get destroyed so we
2805 : // need to reorder the row group array
2806 0 : bool reorder = false;
2807 0 : if (kidFrame->GetNextInFlow())
2808 0 : reorder = true;
2809 :
2810 : rv = ReflowChild(kidFrame, presContext, desiredSize, kidReflowState,
2811 : aReflowState.x, aReflowState.y,
2812 0 : NS_FRAME_INVALIDATE_ON_MOVE, aStatus);
2813 :
2814 0 : if (reorder) {
2815 : // reorder row groups the reflow may have changed the nextinflows
2816 0 : OrderRowGroups(rowGroups, &thead, &tfoot);
2817 0 : childX = rowGroups.IndexOf(kidFrame);
2818 0 : if (childX == RowGroupArray::NoIndex) {
2819 : // XXXbz can this happen?
2820 0 : childX = rowGroups.Length();
2821 : }
2822 : }
2823 : // see if the rowgroup did not fit on this page might be pushed on
2824 : // the next page
2825 0 : if (NS_FRAME_IS_COMPLETE(aStatus) && isPaginated &&
2826 : (NS_UNCONSTRAINEDSIZE != kidReflowState.availableHeight) &&
2827 : kidReflowState.availableHeight < desiredSize.height) {
2828 : // if we are on top of the page place with dataloss
2829 0 : if (kidReflowState.mFlags.mIsTopOfPage) {
2830 0 : if (childX+1 < rowGroups.Length()) {
2831 0 : nsIFrame* nextRowGroupFrame = rowGroups[childX + 1];
2832 0 : if (nextRowGroupFrame) {
2833 : PlaceChild(aReflowState, kidFrame, desiredSize, oldKidRect,
2834 0 : oldKidVisualOverflow);
2835 0 : if (allowRepeatedFooter) {
2836 0 : PlaceRepeatedFooter(aReflowState, tfoot, footerHeight);
2837 : }
2838 0 : else if (tfoot && tfoot->IsRepeatable()) {
2839 0 : tfoot->SetRepeatable(false);
2840 : }
2841 0 : aStatus = NS_FRAME_NOT_COMPLETE;
2842 0 : PushChildren(rowGroups, childX + 1);
2843 0 : aLastChildReflowed = kidFrame;
2844 : break;
2845 : }
2846 : }
2847 : }
2848 : else { // we are not on top, push this rowgroup onto the next page
2849 0 : if (prevKidFrame) { // we had a rowgroup before so push this
2850 0 : if (allowRepeatedFooter) {
2851 0 : PlaceRepeatedFooter(aReflowState, tfoot, footerHeight);
2852 : }
2853 0 : else if (tfoot && tfoot->IsRepeatable()) {
2854 0 : tfoot->SetRepeatable(false);
2855 : }
2856 0 : aStatus = NS_FRAME_NOT_COMPLETE;
2857 0 : PushChildren(rowGroups, childX);
2858 0 : aLastChildReflowed = prevKidFrame;
2859 : break;
2860 : }
2861 : else { // we can't push so lets make clear how much space we need
2862 : PlaceChild(aReflowState, kidFrame, desiredSize, oldKidRect,
2863 0 : oldKidVisualOverflow);
2864 0 : aLastChildReflowed = kidFrame;
2865 0 : if (allowRepeatedFooter) {
2866 0 : PlaceRepeatedFooter(aReflowState, tfoot, footerHeight);
2867 0 : aLastChildReflowed = tfoot;
2868 : }
2869 : break;
2870 : }
2871 : }
2872 : }
2873 :
2874 0 : aLastChildReflowed = kidFrame;
2875 :
2876 0 : pageBreak = false;
2877 : // see if there is a page break after this row group or before the next one
2878 0 : if (NS_FRAME_IS_COMPLETE(aStatus) && isPaginated &&
2879 : (NS_UNCONSTRAINEDSIZE != kidReflowState.availableHeight)) {
2880 : nsIFrame* nextKid =
2881 0 : (childX + 1 < rowGroups.Length()) ? rowGroups[childX + 1] : nsnull;
2882 0 : pageBreak = PageBreakAfter(kidFrame, nextKid);
2883 : }
2884 :
2885 : // Place the child
2886 : PlaceChild(aReflowState, kidFrame, desiredSize, oldKidRect,
2887 0 : oldKidVisualOverflow);
2888 :
2889 : // Remember where we just were in case we end up pushing children
2890 0 : prevKidFrame = kidFrame;
2891 :
2892 : // Special handling for incomplete children
2893 0 : if (NS_FRAME_IS_NOT_COMPLETE(aStatus)) {
2894 0 : nsIFrame* kidNextInFlow = kidFrame->GetNextInFlow();
2895 0 : if (!kidNextInFlow) {
2896 : // The child doesn't have a next-in-flow so create a continuing
2897 : // frame. This hooks the child into the flow
2898 : rv = presContext->PresShell()->FrameConstructor()->
2899 0 : CreateContinuingFrame(presContext, kidFrame, this, &kidNextInFlow);
2900 0 : if (NS_FAILED(rv)) {
2901 0 : aStatus = NS_FRAME_COMPLETE;
2902 : break;
2903 : }
2904 :
2905 : // Insert the continuing frame into the sibling list.
2906 0 : mFrames.InsertFrame(nsnull, kidFrame, kidNextInFlow);
2907 :
2908 : // Fall through and update |rowGroups| with the new rowgroup, just as
2909 : // it would have been if we had called OrderRowGroups again.
2910 : // Note that rowGroups doesn't get used again after we PushChildren
2911 : // below, anyway.
2912 : }
2913 :
2914 : // Put the nextinflow so that it will get pushed
2915 : rowGroups.InsertElementAt(childX + 1,
2916 0 : static_cast <nsTableRowGroupFrame*>(kidNextInFlow));
2917 :
2918 : // We've used up all of our available space so push the remaining
2919 : // children to the next-in-flow
2920 0 : if (allowRepeatedFooter) {
2921 0 : PlaceRepeatedFooter(aReflowState, tfoot, footerHeight);
2922 : }
2923 0 : else if (tfoot && tfoot->IsRepeatable()) {
2924 0 : tfoot->SetRepeatable(false);
2925 : }
2926 0 : nsIFrame* nextSibling = kidFrame->GetNextSibling();
2927 0 : if (nsnull != nextSibling) {
2928 0 : PushChildren(rowGroups, childX + 1);
2929 : }
2930 : break;
2931 : }
2932 : }
2933 : else { // it isn't being reflowed
2934 0 : aReflowState.y += cellSpacingY;
2935 0 : nsRect kidRect = kidFrame->GetRect();
2936 0 : if (kidRect.y != aReflowState.y) {
2937 : // invalidate the old position
2938 0 : kidFrame->InvalidateFrameSubtree();
2939 0 : kidRect.y = aReflowState.y;
2940 0 : kidFrame->SetRect(kidRect); // move to the new position
2941 0 : RePositionViews(kidFrame);
2942 : // invalidate the new position
2943 0 : kidFrame->InvalidateFrameSubtree();
2944 : }
2945 0 : aReflowState.y += kidRect.height;
2946 :
2947 : // If our height is constrained then update the available height.
2948 0 : if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) {
2949 0 : aReflowState.availSize.height -= cellSpacingY + kidRect.height;
2950 : }
2951 : }
2952 0 : ConsiderChildOverflow(aOverflowAreas, kidFrame);
2953 : }
2954 :
2955 : // We've now propagated the column resizes and geometry changes to all
2956 : // the children.
2957 0 : mBits.mResizedColumns = false;
2958 0 : ClearGeometryDirty();
2959 :
2960 0 : return rv;
2961 : }
2962 :
2963 : void
2964 0 : nsTableFrame::ReflowColGroups(nsRenderingContext *aRenderingContext)
2965 : {
2966 0 : if (!GetPrevInFlow() && !HaveReflowedColGroups()) {
2967 0 : nsHTMLReflowMetrics kidMet;
2968 0 : nsPresContext *presContext = PresContext();
2969 0 : for (nsIFrame* kidFrame = mColGroups.FirstChild(); kidFrame;
2970 : kidFrame = kidFrame->GetNextSibling()) {
2971 0 : if (NS_SUBTREE_DIRTY(kidFrame)) {
2972 : // The column groups don't care about dimensions or reflow states.
2973 : nsHTMLReflowState kidReflowState(presContext, kidFrame,
2974 0 : aRenderingContext, nsSize(0,0));
2975 : nsReflowStatus cgStatus;
2976 : ReflowChild(kidFrame, presContext, kidMet, kidReflowState, 0, 0, 0,
2977 0 : cgStatus);
2978 0 : FinishReflowChild(kidFrame, presContext, nsnull, kidMet, 0, 0, 0);
2979 : }
2980 : }
2981 0 : SetHaveReflowedColGroups(true);
2982 : }
2983 0 : }
2984 :
2985 : void
2986 0 : nsTableFrame::CalcDesiredHeight(const nsHTMLReflowState& aReflowState, nsHTMLReflowMetrics& aDesiredSize)
2987 : {
2988 0 : nsTableCellMap* cellMap = GetCellMap();
2989 0 : if (!cellMap) {
2990 0 : NS_ASSERTION(false, "never ever call me until the cell map is built!");
2991 0 : aDesiredSize.height = 0;
2992 0 : return;
2993 : }
2994 0 : nscoord cellSpacingY = GetCellSpacingY();
2995 0 : nsMargin borderPadding = GetChildAreaOffset(&aReflowState);
2996 :
2997 : // get the natural height based on the last child's (row group) rect
2998 0 : RowGroupArray rowGroups;
2999 0 : OrderRowGroups(rowGroups);
3000 0 : if (rowGroups.IsEmpty()) {
3001 : // tables can be used as rectangular items without content
3002 0 : nscoord tableSpecifiedHeight = CalcBorderBoxHeight(aReflowState);
3003 0 : if ((NS_UNCONSTRAINEDSIZE != tableSpecifiedHeight) &&
3004 : (tableSpecifiedHeight > 0) &&
3005 0 : eCompatibility_NavQuirks != PresContext()->CompatibilityMode()) {
3006 : // empty tables should not have a size in quirks mode
3007 0 : aDesiredSize.height = tableSpecifiedHeight;
3008 : }
3009 : else
3010 0 : aDesiredSize.height = 0;
3011 : return;
3012 : }
3013 0 : PRInt32 rowCount = cellMap->GetRowCount();
3014 0 : PRInt32 colCount = cellMap->GetColCount();
3015 0 : nscoord desiredHeight = borderPadding.top + borderPadding.bottom;
3016 0 : if (rowCount > 0 && colCount > 0) {
3017 0 : desiredHeight += cellSpacingY;
3018 0 : for (PRUint32 rgX = 0; rgX < rowGroups.Length(); rgX++) {
3019 0 : desiredHeight += rowGroups[rgX]->GetSize().height + cellSpacingY;
3020 : }
3021 : }
3022 :
3023 : // see if a specified table height requires dividing additional space to rows
3024 0 : if (!GetPrevInFlow()) {
3025 0 : nscoord tableSpecifiedHeight = CalcBorderBoxHeight(aReflowState);
3026 0 : if ((tableSpecifiedHeight > 0) &&
3027 : (tableSpecifiedHeight != NS_UNCONSTRAINEDSIZE) &&
3028 : (tableSpecifiedHeight > desiredHeight)) {
3029 : // proportionately distribute the excess height to unconstrained rows in each
3030 : // unconstrained row group.
3031 0 : DistributeHeightToRows(aReflowState, tableSpecifiedHeight - desiredHeight);
3032 : // this might have changed the overflow area incorporate the childframe overflow area.
3033 0 : for (nsIFrame* kidFrame = mFrames.FirstChild(); kidFrame; kidFrame = kidFrame->GetNextSibling()) {
3034 0 : ConsiderChildOverflow(aDesiredSize.mOverflowAreas, kidFrame);
3035 : }
3036 0 : desiredHeight = tableSpecifiedHeight;
3037 : }
3038 : }
3039 0 : aDesiredSize.height = desiredHeight;
3040 : }
3041 :
3042 : static
3043 0 : void ResizeCells(nsTableFrame& aTableFrame)
3044 : {
3045 0 : nsTableFrame::RowGroupArray rowGroups;
3046 0 : aTableFrame.OrderRowGroups(rowGroups);
3047 0 : nsHTMLReflowMetrics tableDesiredSize;
3048 0 : nsRect tableRect = aTableFrame.GetRect();
3049 0 : tableDesiredSize.width = tableRect.width;
3050 0 : tableDesiredSize.height = tableRect.height;
3051 0 : tableDesiredSize.SetOverflowAreasToDesiredBounds();
3052 :
3053 0 : for (PRUint32 rgX = 0; rgX < rowGroups.Length(); rgX++) {
3054 0 : nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
3055 :
3056 0 : nsRect rowGroupRect = rgFrame->GetRect();
3057 0 : nsHTMLReflowMetrics groupDesiredSize;
3058 0 : groupDesiredSize.width = rowGroupRect.width;
3059 0 : groupDesiredSize.height = rowGroupRect.height;
3060 0 : groupDesiredSize.SetOverflowAreasToDesiredBounds();
3061 :
3062 0 : nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
3063 0 : while (rowFrame) {
3064 0 : rowFrame->DidResize();
3065 0 : rgFrame->ConsiderChildOverflow(groupDesiredSize.mOverflowAreas, rowFrame);
3066 0 : rowFrame = rowFrame->GetNextRow();
3067 : }
3068 0 : rgFrame->FinishAndStoreOverflow(&groupDesiredSize);
3069 : tableDesiredSize.mOverflowAreas.UnionWith(groupDesiredSize.mOverflowAreas +
3070 0 : rgFrame->GetPosition());
3071 : }
3072 0 : aTableFrame.FinishAndStoreOverflow(&tableDesiredSize);
3073 0 : }
3074 :
3075 : void
3076 0 : nsTableFrame::DistributeHeightToRows(const nsHTMLReflowState& aReflowState,
3077 : nscoord aAmount)
3078 : {
3079 0 : nscoord cellSpacingY = GetCellSpacingY();
3080 :
3081 0 : nsMargin borderPadding = GetChildAreaOffset(&aReflowState);
3082 :
3083 0 : RowGroupArray rowGroups;
3084 0 : OrderRowGroups(rowGroups);
3085 :
3086 0 : nscoord amountUsed = 0;
3087 : // distribute space to each pct height row whose row group doesn't have a computed
3088 : // height, and base the pct on the table height. If the row group had a computed
3089 : // height, then this was already done in nsTableRowGroupFrame::CalculateRowHeights
3090 0 : nscoord pctBasis = aReflowState.ComputedHeight() - (GetCellSpacingY() * (GetRowCount() + 1));
3091 0 : nscoord yOriginRG = borderPadding.top + GetCellSpacingY();
3092 0 : nscoord yEndRG = yOriginRG;
3093 : PRUint32 rgX;
3094 0 : for (rgX = 0; rgX < rowGroups.Length(); rgX++) {
3095 0 : nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
3096 0 : nscoord amountUsedByRG = 0;
3097 0 : nscoord yOriginRow = 0;
3098 0 : nsRect rgRect = rgFrame->GetRect();
3099 0 : if (!rgFrame->HasStyleHeight()) {
3100 0 : nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
3101 0 : while (rowFrame) {
3102 0 : nsRect rowRect = rowFrame->GetRect();
3103 0 : if ((amountUsed < aAmount) && rowFrame->HasPctHeight()) {
3104 0 : nscoord pctHeight = rowFrame->GetHeight(pctBasis);
3105 0 : nscoord amountForRow = NS_MIN(aAmount - amountUsed, pctHeight - rowRect.height);
3106 0 : if (amountForRow > 0) {
3107 0 : nsRect oldRowRect = rowRect;
3108 0 : rowRect.height += amountForRow;
3109 : // XXXbz we don't need to change rowRect.y to be yOriginRow?
3110 0 : rowFrame->SetRect(rowRect);
3111 0 : yOriginRow += rowRect.height + cellSpacingY;
3112 0 : yEndRG += rowRect.height + cellSpacingY;
3113 0 : amountUsed += amountForRow;
3114 0 : amountUsedByRG += amountForRow;
3115 : //rowFrame->DidResize();
3116 0 : nsTableFrame::RePositionViews(rowFrame);
3117 :
3118 0 : rgFrame->InvalidateRectDifference(oldRowRect, rowRect);
3119 : }
3120 : }
3121 : else {
3122 0 : if (amountUsed > 0 && yOriginRow != rowRect.y &&
3123 0 : !(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
3124 0 : rowFrame->InvalidateFrameSubtree();
3125 0 : rowFrame->SetPosition(nsPoint(rowRect.x, yOriginRow));
3126 0 : nsTableFrame::RePositionViews(rowFrame);
3127 0 : rowFrame->InvalidateFrameSubtree();
3128 : }
3129 0 : yOriginRow += rowRect.height + cellSpacingY;
3130 0 : yEndRG += rowRect.height + cellSpacingY;
3131 : }
3132 0 : rowFrame = rowFrame->GetNextRow();
3133 : }
3134 0 : if (amountUsed > 0) {
3135 0 : if (rgRect.y != yOriginRG) {
3136 0 : rgFrame->InvalidateFrameSubtree();
3137 : }
3138 :
3139 0 : nsRect origRgRect = rgRect;
3140 0 : nsRect origRgVisualOverflow = rgFrame->GetVisualOverflowRect();
3141 :
3142 0 : rgRect.y = yOriginRG;
3143 0 : rgRect.height += amountUsedByRG;
3144 :
3145 0 : rgFrame->SetRect(rgRect);
3146 :
3147 : nsTableFrame::InvalidateFrame(rgFrame, origRgRect,
3148 0 : origRgVisualOverflow, false);
3149 : }
3150 : }
3151 0 : else if (amountUsed > 0 && yOriginRG != rgRect.y) {
3152 0 : rgFrame->InvalidateFrameSubtree();
3153 0 : rgFrame->SetPosition(nsPoint(rgRect.x, yOriginRG));
3154 : // Make sure child views are properly positioned
3155 0 : nsTableFrame::RePositionViews(rgFrame);
3156 0 : rgFrame->InvalidateFrameSubtree();
3157 : }
3158 0 : yOriginRG = yEndRG;
3159 : }
3160 :
3161 0 : if (amountUsed >= aAmount) {
3162 0 : ResizeCells(*this);
3163 : return;
3164 : }
3165 :
3166 : // get the first row without a style height where its row group has an
3167 : // unconstrained height
3168 0 : nsTableRowGroupFrame* firstUnStyledRG = nsnull;
3169 0 : nsTableRowFrame* firstUnStyledRow = nsnull;
3170 0 : for (rgX = 0; rgX < rowGroups.Length() && !firstUnStyledRG; rgX++) {
3171 0 : nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
3172 0 : if (!rgFrame->HasStyleHeight()) {
3173 0 : nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
3174 0 : while (rowFrame) {
3175 0 : if (!rowFrame->HasStyleHeight()) {
3176 0 : firstUnStyledRG = rgFrame;
3177 0 : firstUnStyledRow = rowFrame;
3178 0 : break;
3179 : }
3180 0 : rowFrame = rowFrame->GetNextRow();
3181 : }
3182 : }
3183 : }
3184 :
3185 0 : nsTableRowFrame* lastEligibleRow = nsnull;
3186 : // Accumulate the correct divisor. This will be the total total height of all
3187 : // unstyled rows inside unstyled row groups, unless there are none, in which
3188 : // case, it will be number of all rows. If the unstyled rows don't have a
3189 : // height, divide the space equally among them.
3190 0 : nscoord divisor = 0;
3191 0 : PRInt32 eligibleRows = 0;
3192 0 : bool expandEmptyRows = false;
3193 :
3194 0 : if (!firstUnStyledRow) {
3195 : // there is no unstyled row
3196 0 : divisor = GetRowCount();
3197 : }
3198 : else {
3199 0 : for (rgX = 0; rgX < rowGroups.Length(); rgX++) {
3200 0 : nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
3201 0 : if (!firstUnStyledRG || !rgFrame->HasStyleHeight()) {
3202 0 : nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
3203 0 : while (rowFrame) {
3204 0 : if (!firstUnStyledRG || !rowFrame->HasStyleHeight()) {
3205 0 : NS_ASSERTION(rowFrame->GetSize().height >= 0,
3206 : "negative row frame height");
3207 0 : divisor += rowFrame->GetSize().height;
3208 0 : eligibleRows++;
3209 0 : lastEligibleRow = rowFrame;
3210 : }
3211 0 : rowFrame = rowFrame->GetNextRow();
3212 : }
3213 : }
3214 : }
3215 0 : if (divisor <= 0) {
3216 0 : if (eligibleRows > 0) {
3217 0 : expandEmptyRows = true;
3218 : }
3219 : else {
3220 0 : NS_ERROR("invalid divisor");
3221 : return;
3222 : }
3223 : }
3224 : }
3225 : // allocate the extra height to the unstyled row groups and rows
3226 0 : nscoord heightToDistribute = aAmount - amountUsed;
3227 0 : yOriginRG = borderPadding.top + cellSpacingY;
3228 0 : yEndRG = yOriginRG;
3229 0 : for (rgX = 0; rgX < rowGroups.Length(); rgX++) {
3230 0 : nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
3231 0 : nscoord amountUsedByRG = 0;
3232 0 : nscoord yOriginRow = 0;
3233 0 : nsRect rgRect = rgFrame->GetRect();
3234 0 : nsRect rgVisualOverflow = rgFrame->GetVisualOverflowRect();
3235 : // see if there is an eligible row group or we distribute to all rows
3236 0 : if (!firstUnStyledRG || !rgFrame->HasStyleHeight() || !eligibleRows) {
3237 0 : nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
3238 0 : while (rowFrame) {
3239 0 : nsRect rowRect = rowFrame->GetRect();
3240 0 : nsRect rowVisualOverflow = rowFrame->GetVisualOverflowRect();
3241 : // see if there is an eligible row or we distribute to all rows
3242 0 : if (!firstUnStyledRow || !rowFrame->HasStyleHeight() || !eligibleRows) {
3243 : float ratio;
3244 0 : if (eligibleRows) {
3245 0 : if (!expandEmptyRows) {
3246 : // The amount of additional space each row gets is proportional to
3247 : // its height
3248 0 : ratio = float(rowRect.height) / float(divisor);
3249 : } else {
3250 : // empty rows get all the same additional space
3251 0 : ratio = 1.0f / float(eligibleRows);
3252 : }
3253 : }
3254 : else {
3255 : // all rows get the same additional space
3256 0 : ratio = 1.0f / float(divisor);
3257 : }
3258 : // give rows their additional space, except for the last row which
3259 : // gets the remainder
3260 : nscoord amountForRow = (rowFrame == lastEligibleRow)
3261 0 : ? aAmount - amountUsed : NSToCoordRound(((float)(heightToDistribute)) * ratio);
3262 0 : amountForRow = NS_MIN(amountForRow, aAmount - amountUsed);
3263 :
3264 0 : if (yOriginRow != rowRect.y) {
3265 0 : rowFrame->InvalidateFrameSubtree();
3266 : }
3267 :
3268 : // update the row height
3269 : nsRect newRowRect(rowRect.x, yOriginRow, rowRect.width,
3270 0 : rowRect.height + amountForRow);
3271 0 : rowFrame->SetRect(newRowRect);
3272 :
3273 0 : yOriginRow += newRowRect.height + cellSpacingY;
3274 0 : yEndRG += newRowRect.height + cellSpacingY;
3275 :
3276 0 : amountUsed += amountForRow;
3277 0 : amountUsedByRG += amountForRow;
3278 0 : NS_ASSERTION((amountUsed <= aAmount), "invalid row allocation");
3279 : //rowFrame->DidResize();
3280 0 : nsTableFrame::RePositionViews(rowFrame);
3281 :
3282 : nsTableFrame::InvalidateFrame(rowFrame, rowRect, rowVisualOverflow,
3283 0 : false);
3284 : }
3285 : else {
3286 0 : if (amountUsed > 0 && yOriginRow != rowRect.y) {
3287 0 : rowFrame->InvalidateFrameSubtree();
3288 0 : rowFrame->SetPosition(nsPoint(rowRect.x, yOriginRow));
3289 0 : nsTableFrame::RePositionViews(rowFrame);
3290 0 : rowFrame->InvalidateFrameSubtree();
3291 : }
3292 0 : yOriginRow += rowRect.height + cellSpacingY;
3293 0 : yEndRG += rowRect.height + cellSpacingY;
3294 : }
3295 0 : rowFrame = rowFrame->GetNextRow();
3296 : }
3297 0 : if (amountUsed > 0) {
3298 0 : if (rgRect.y != yOriginRG) {
3299 0 : rgFrame->InvalidateFrameSubtree();
3300 : }
3301 :
3302 : rgFrame->SetRect(nsRect(rgRect.x, yOriginRG, rgRect.width,
3303 0 : rgRect.height + amountUsedByRG));
3304 :
3305 : nsTableFrame::InvalidateFrame(rgFrame, rgRect, rgVisualOverflow,
3306 0 : false);
3307 : }
3308 : // Make sure child views are properly positioned
3309 : }
3310 0 : else if (amountUsed > 0 && yOriginRG != rgRect.y) {
3311 0 : rgFrame->InvalidateFrameSubtree();
3312 0 : rgFrame->SetPosition(nsPoint(rgRect.x, yOriginRG));
3313 : // Make sure child views are properly positioned
3314 0 : nsTableFrame::RePositionViews(rgFrame);
3315 0 : rgFrame->InvalidateFrameSubtree();
3316 : }
3317 0 : yOriginRG = yEndRG;
3318 : }
3319 :
3320 0 : ResizeCells(*this);
3321 : }
3322 :
3323 0 : PRInt32 nsTableFrame::GetColumnWidth(PRInt32 aColIndex)
3324 : {
3325 0 : nsTableFrame* firstInFlow = static_cast<nsTableFrame*>(GetFirstInFlow());
3326 0 : if (this == firstInFlow) {
3327 0 : nsTableColFrame* colFrame = GetColFrame(aColIndex);
3328 0 : return colFrame ? colFrame->GetFinalWidth() : 0;
3329 : }
3330 0 : return firstInFlow->GetColumnWidth(aColIndex);
3331 : }
3332 :
3333 : // XXX: could cache this. But be sure to check style changes if you do!
3334 0 : nscoord nsTableFrame::GetCellSpacingX()
3335 : {
3336 0 : if (IsBorderCollapse())
3337 0 : return 0;
3338 :
3339 0 : return GetStyleTableBorder()->mBorderSpacingX;
3340 : }
3341 :
3342 : // XXX: could cache this. But be sure to check style changes if you do!
3343 0 : nscoord nsTableFrame::GetCellSpacingY()
3344 : {
3345 0 : if (IsBorderCollapse())
3346 0 : return 0;
3347 :
3348 0 : return GetStyleTableBorder()->mBorderSpacingY;
3349 : }
3350 :
3351 :
3352 : /* virtual */ nscoord
3353 0 : nsTableFrame::GetBaseline() const
3354 : {
3355 0 : nscoord ascent = 0;
3356 0 : RowGroupArray orderedRowGroups;
3357 0 : OrderRowGroups(orderedRowGroups);
3358 0 : nsTableRowFrame* firstRow = nsnull;
3359 0 : for (PRUint32 rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) {
3360 0 : nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgIndex];
3361 0 : if (rgFrame->GetRowCount()) {
3362 0 : firstRow = rgFrame->GetFirstRow();
3363 0 : ascent = rgFrame->GetRect().y + firstRow->GetRect().y + firstRow->GetRowBaseline();
3364 0 : break;
3365 : }
3366 : }
3367 0 : if (!firstRow)
3368 0 : ascent = GetRect().height;
3369 0 : return ascent;
3370 : }
3371 : /* ----- global methods ----- */
3372 :
3373 : nsIFrame*
3374 0 : NS_NewTableFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
3375 : {
3376 0 : return new (aPresShell) nsTableFrame(aContext);
3377 : }
3378 :
3379 0 : NS_IMPL_FRAMEARENA_HELPERS(nsTableFrame)
3380 :
3381 : nsTableFrame*
3382 0 : nsTableFrame::GetTableFrame(nsIFrame* aFrame)
3383 : {
3384 0 : for (nsIFrame* ancestor = aFrame->GetParent(); ancestor;
3385 : ancestor = ancestor->GetParent()) {
3386 0 : if (nsGkAtoms::tableFrame == ancestor->GetType()) {
3387 0 : return static_cast<nsTableFrame*>(ancestor);
3388 : }
3389 : }
3390 0 : NS_RUNTIMEABORT("unable to find table parent");
3391 0 : return nsnull;
3392 : }
3393 :
3394 : bool
3395 0 : nsTableFrame::IsAutoHeight()
3396 : {
3397 0 : const nsStyleCoord &height = GetStylePosition()->mHeight;
3398 : // Don't consider calc() here like this quirk for percent.
3399 0 : return height.GetUnit() == eStyleUnit_Auto ||
3400 0 : (height.GetUnit() == eStyleUnit_Percent &&
3401 0 : height.GetPercentValue() <= 0.0f);
3402 : }
3403 :
3404 : nscoord
3405 0 : nsTableFrame::CalcBorderBoxHeight(const nsHTMLReflowState& aState)
3406 : {
3407 0 : nscoord height = aState.ComputedHeight();
3408 0 : if (NS_AUTOHEIGHT != height) {
3409 0 : nsMargin borderPadding = GetChildAreaOffset(&aState);
3410 0 : height += borderPadding.top + borderPadding.bottom;
3411 : }
3412 0 : height = NS_MAX(0, height);
3413 :
3414 0 : return height;
3415 : }
3416 :
3417 : bool
3418 0 : nsTableFrame::IsAutoLayout()
3419 : {
3420 0 : if (GetStyleTable()->mLayoutStrategy == NS_STYLE_TABLE_LAYOUT_AUTO)
3421 0 : return true;
3422 : // a fixed-layout inline-table must have a width
3423 : // and tables with 'width: -moz-max-content' must be auto-layout
3424 : // (at least as long as FixedTableLayoutStrategy::GetPrefWidth returns
3425 : // nscoord_MAX)
3426 0 : const nsStyleCoord &width = GetStylePosition()->mWidth;
3427 0 : return (width.GetUnit() == eStyleUnit_Auto) ||
3428 0 : (width.GetUnit() == eStyleUnit_Enumerated &&
3429 0 : width.GetIntValue() == NS_STYLE_WIDTH_MAX_CONTENT);
3430 : }
3431 :
3432 : #ifdef DEBUG
3433 : NS_IMETHODIMP
3434 0 : nsTableFrame::GetFrameName(nsAString& aResult) const
3435 : {
3436 0 : return MakeFrameName(NS_LITERAL_STRING("Table"), aResult);
3437 : }
3438 : #endif
3439 :
3440 : // Find the closet sibling before aPriorChildFrame (including aPriorChildFrame) that
3441 : // is of type aChildType
3442 : nsIFrame*
3443 0 : nsTableFrame::GetFrameAtOrBefore(nsIFrame* aParentFrame,
3444 : nsIFrame* aPriorChildFrame,
3445 : nsIAtom* aChildType)
3446 : {
3447 0 : nsIFrame* result = nsnull;
3448 0 : if (!aPriorChildFrame) {
3449 0 : return result;
3450 : }
3451 0 : if (aChildType == aPriorChildFrame->GetType()) {
3452 0 : return aPriorChildFrame;
3453 : }
3454 :
3455 : // aPriorChildFrame is not of type aChildType, so we need start from
3456 : // the beginnng and find the closest one
3457 0 : nsIFrame* lastMatchingFrame = nsnull;
3458 0 : nsIFrame* childFrame = aParentFrame->GetFirstPrincipalChild();
3459 0 : while (childFrame && (childFrame != aPriorChildFrame)) {
3460 0 : if (aChildType == childFrame->GetType()) {
3461 0 : lastMatchingFrame = childFrame;
3462 : }
3463 0 : childFrame = childFrame->GetNextSibling();
3464 : }
3465 0 : return lastMatchingFrame;
3466 : }
3467 :
3468 : #ifdef DEBUG
3469 : void
3470 0 : nsTableFrame::DumpRowGroup(nsIFrame* aKidFrame)
3471 : {
3472 0 : if (!aKidFrame)
3473 0 : return;
3474 :
3475 0 : nsIFrame* cFrame = aKidFrame->GetFirstPrincipalChild();
3476 0 : while (cFrame) {
3477 0 : nsTableRowFrame *rowFrame = do_QueryFrame(cFrame);
3478 0 : if (rowFrame) {
3479 0 : printf("row(%d)=%p ", rowFrame->GetRowIndex(),
3480 0 : static_cast<void*>(rowFrame));
3481 0 : nsIFrame* childFrame = cFrame->GetFirstPrincipalChild();
3482 0 : while (childFrame) {
3483 0 : nsTableCellFrame *cellFrame = do_QueryFrame(childFrame);
3484 0 : if (cellFrame) {
3485 : PRInt32 colIndex;
3486 0 : cellFrame->GetColIndex(colIndex);
3487 0 : printf("cell(%d)=%p ", colIndex, static_cast<void*>(childFrame));
3488 : }
3489 0 : childFrame = childFrame->GetNextSibling();
3490 : }
3491 0 : printf("\n");
3492 : }
3493 : else {
3494 0 : DumpRowGroup(rowFrame);
3495 : }
3496 0 : cFrame = cFrame->GetNextSibling();
3497 : }
3498 : }
3499 :
3500 : void
3501 0 : nsTableFrame::Dump(bool aDumpRows,
3502 : bool aDumpCols,
3503 : bool aDumpCellMap)
3504 : {
3505 0 : printf("***START TABLE DUMP*** \n");
3506 : // dump the columns widths array
3507 0 : printf("mColWidths=");
3508 0 : PRInt32 numCols = GetColCount();
3509 : PRInt32 colX;
3510 0 : for (colX = 0; colX < numCols; colX++) {
3511 0 : printf("%d ", GetColumnWidth(colX));
3512 : }
3513 0 : printf("\n");
3514 :
3515 0 : if (aDumpRows) {
3516 0 : nsIFrame* kidFrame = mFrames.FirstChild();
3517 0 : while (kidFrame) {
3518 0 : DumpRowGroup(kidFrame);
3519 0 : kidFrame = kidFrame->GetNextSibling();
3520 : }
3521 : }
3522 :
3523 0 : if (aDumpCols) {
3524 : // output col frame cache
3525 0 : printf("\n col frame cache ->");
3526 0 : for (colX = 0; colX < numCols; colX++) {
3527 0 : nsTableColFrame* colFrame = mColFrames.ElementAt(colX);
3528 0 : if (0 == (colX % 8)) {
3529 0 : printf("\n");
3530 : }
3531 0 : printf ("%d=%p ", colX, static_cast<void*>(colFrame));
3532 0 : nsTableColType colType = colFrame->GetColType();
3533 0 : switch (colType) {
3534 : case eColContent:
3535 0 : printf(" content ");
3536 0 : break;
3537 : case eColAnonymousCol:
3538 0 : printf(" anonymous-column ");
3539 0 : break;
3540 : case eColAnonymousColGroup:
3541 0 : printf(" anonymous-colgroup ");
3542 0 : break;
3543 : case eColAnonymousCell:
3544 0 : printf(" anonymous-cell ");
3545 0 : break;
3546 : }
3547 : }
3548 0 : printf("\n colgroups->");
3549 0 : for (nsIFrame* childFrame = mColGroups.FirstChild(); childFrame;
3550 : childFrame = childFrame->GetNextSibling()) {
3551 0 : if (nsGkAtoms::tableColGroupFrame == childFrame->GetType()) {
3552 0 : nsTableColGroupFrame* colGroupFrame = (nsTableColGroupFrame *)childFrame;
3553 0 : colGroupFrame->Dump(1);
3554 : }
3555 : }
3556 0 : for (colX = 0; colX < numCols; colX++) {
3557 0 : printf("\n");
3558 0 : nsTableColFrame* colFrame = GetColFrame(colX);
3559 0 : colFrame->Dump(1);
3560 : }
3561 : }
3562 0 : if (aDumpCellMap) {
3563 0 : nsTableCellMap* cellMap = GetCellMap();
3564 0 : cellMap->Dump();
3565 : }
3566 0 : printf(" ***END TABLE DUMP*** \n");
3567 0 : }
3568 : #endif
3569 :
3570 : // nsTableIterator
3571 0 : nsTableIterator::nsTableIterator(nsIFrame& aSource)
3572 : {
3573 0 : nsIFrame* firstChild = aSource.GetFirstPrincipalChild();
3574 0 : Init(firstChild);
3575 0 : }
3576 :
3577 0 : nsTableIterator::nsTableIterator(nsFrameList& aSource)
3578 : {
3579 0 : nsIFrame* firstChild = aSource.FirstChild();
3580 0 : Init(firstChild);
3581 0 : }
3582 :
3583 0 : void nsTableIterator::Init(nsIFrame* aFirstChild)
3584 : {
3585 0 : mFirstListChild = aFirstChild;
3586 0 : mFirstChild = aFirstChild;
3587 0 : mCurrentChild = nsnull;
3588 0 : mLeftToRight = true;
3589 0 : mCount = -1;
3590 :
3591 0 : if (!mFirstChild) {
3592 0 : return;
3593 : }
3594 :
3595 0 : nsTableFrame* table = nsTableFrame::GetTableFrame(mFirstChild);
3596 : mLeftToRight = (NS_STYLE_DIRECTION_LTR ==
3597 0 : table->GetStyleVisibility()->mDirection);
3598 :
3599 0 : if (!mLeftToRight) {
3600 0 : mCount = 0;
3601 0 : nsIFrame* nextChild = mFirstChild->GetNextSibling();
3602 0 : while (nsnull != nextChild) {
3603 0 : mCount++;
3604 0 : mFirstChild = nextChild;
3605 0 : nextChild = nextChild->GetNextSibling();
3606 : }
3607 : }
3608 : }
3609 :
3610 0 : nsIFrame* nsTableIterator::First()
3611 : {
3612 0 : mCurrentChild = mFirstChild;
3613 0 : return mCurrentChild;
3614 : }
3615 :
3616 0 : nsIFrame* nsTableIterator::Next()
3617 : {
3618 0 : if (!mCurrentChild) {
3619 0 : return nsnull;
3620 : }
3621 :
3622 0 : if (mLeftToRight) {
3623 0 : mCurrentChild = mCurrentChild->GetNextSibling();
3624 0 : return mCurrentChild;
3625 : }
3626 : else {
3627 0 : nsIFrame* targetChild = mCurrentChild;
3628 0 : mCurrentChild = nsnull;
3629 0 : nsIFrame* child = mFirstListChild;
3630 0 : while (child && (child != targetChild)) {
3631 0 : mCurrentChild = child;
3632 0 : child = child->GetNextSibling();
3633 : }
3634 0 : return mCurrentChild;
3635 : }
3636 : }
3637 :
3638 0 : bool nsTableIterator::IsLeftToRight()
3639 : {
3640 0 : return mLeftToRight;
3641 : }
3642 :
3643 0 : PRInt32 nsTableIterator::Count()
3644 : {
3645 0 : if (-1 == mCount) {
3646 0 : mCount = 0;
3647 0 : nsIFrame* child = mFirstListChild;
3648 0 : while (nsnull != child) {
3649 0 : mCount++;
3650 0 : child = child->GetNextSibling();
3651 : }
3652 : }
3653 0 : return mCount;
3654 : }
3655 :
3656 : /*------------------ nsITableLayout methods ------------------------------*/
3657 : NS_IMETHODIMP
3658 0 : nsTableFrame::GetCellDataAt(PRInt32 aRowIndex,
3659 : PRInt32 aColIndex,
3660 : nsIDOMElement* &aCell, //out params
3661 : PRInt32& aStartRowIndex,
3662 : PRInt32& aStartColIndex,
3663 : PRInt32& aRowSpan,
3664 : PRInt32& aColSpan,
3665 : PRInt32& aActualRowSpan,
3666 : PRInt32& aActualColSpan,
3667 : bool& aIsSelected)
3668 : {
3669 : // Initialize out params
3670 0 : aCell = nsnull;
3671 0 : aStartRowIndex = 0;
3672 0 : aStartColIndex = 0;
3673 0 : aRowSpan = 0;
3674 0 : aColSpan = 0;
3675 0 : aIsSelected = false;
3676 :
3677 0 : nsTableCellMap* cellMap = GetCellMap();
3678 0 : if (!cellMap) { return NS_ERROR_NOT_INITIALIZED;}
3679 :
3680 : bool originates;
3681 : PRInt32 colSpan; // Is this the "effective" or "html" value?
3682 :
3683 0 : nsTableCellFrame *cellFrame = cellMap->GetCellInfoAt(aRowIndex, aColIndex, &originates, &colSpan);
3684 0 : if (!cellFrame) return NS_TABLELAYOUT_CELL_NOT_FOUND;
3685 :
3686 0 : nsresult result= cellFrame->GetRowIndex(aStartRowIndex);
3687 0 : if (NS_FAILED(result)) return result;
3688 0 : result = cellFrame->GetColIndex(aStartColIndex);
3689 0 : if (NS_FAILED(result)) return result;
3690 : //This returns HTML value, which may be 0
3691 0 : aRowSpan = cellFrame->GetRowSpan();
3692 0 : aColSpan = cellFrame->GetColSpan();
3693 0 : aActualRowSpan = GetEffectiveRowSpan(*cellFrame);
3694 0 : aActualColSpan = GetEffectiveColSpan(*cellFrame);
3695 :
3696 : // If these aren't at least 1, we have a cellmap error
3697 0 : if (aActualRowSpan == 0 || aActualColSpan == 0)
3698 0 : return NS_ERROR_FAILURE;
3699 :
3700 0 : aIsSelected = cellFrame->IsSelected();
3701 :
3702 : // do this last, because it addrefs,
3703 : // and we don't want the caller leaking it on error
3704 0 : nsIContent* content = cellFrame->GetContent();
3705 0 : if (!content) return NS_ERROR_FAILURE;
3706 :
3707 0 : return CallQueryInterface(content, &aCell);
3708 : }
3709 :
3710 0 : NS_IMETHODIMP nsTableFrame::GetTableSize(PRInt32& aRowCount, PRInt32& aColCount)
3711 : {
3712 0 : nsTableCellMap* cellMap = GetCellMap();
3713 : // Initialize out params
3714 0 : aRowCount = 0;
3715 0 : aColCount = 0;
3716 0 : if (!cellMap) { return NS_ERROR_NOT_INITIALIZED;}
3717 :
3718 0 : aRowCount = cellMap->GetRowCount();
3719 0 : aColCount = cellMap->GetColCount();
3720 0 : return NS_OK;
3721 : }
3722 :
3723 : NS_IMETHODIMP
3724 0 : nsTableFrame::GetIndexByRowAndColumn(PRInt32 aRow, PRInt32 aColumn,
3725 : PRInt32 *aIndex)
3726 : {
3727 0 : NS_ENSURE_ARG_POINTER(aIndex);
3728 0 : *aIndex = -1;
3729 :
3730 0 : nsTableCellMap* cellMap = GetCellMap();
3731 0 : if (!cellMap)
3732 0 : return NS_ERROR_NOT_INITIALIZED;
3733 :
3734 0 : *aIndex = cellMap->GetIndexByRowAndColumn(aRow, aColumn);
3735 0 : return (*aIndex == -1) ? NS_TABLELAYOUT_CELL_NOT_FOUND : NS_OK;
3736 : }
3737 :
3738 : NS_IMETHODIMP
3739 0 : nsTableFrame::GetRowAndColumnByIndex(PRInt32 aIndex,
3740 : PRInt32 *aRow, PRInt32 *aColumn)
3741 : {
3742 0 : NS_ENSURE_ARG_POINTER(aRow);
3743 0 : *aRow = -1;
3744 :
3745 0 : NS_ENSURE_ARG_POINTER(aColumn);
3746 0 : *aColumn = -1;
3747 :
3748 0 : nsTableCellMap* cellMap = GetCellMap();
3749 0 : if (!cellMap)
3750 0 : return NS_ERROR_NOT_INITIALIZED;
3751 :
3752 0 : cellMap->GetRowAndColumnByIndex(aIndex, aRow, aColumn);
3753 0 : return NS_OK;
3754 : }
3755 :
3756 : /*---------------- end of nsITableLayout implementation ------------------*/
3757 :
3758 : bool
3759 0 : nsTableFrame::ColumnHasCellSpacingBefore(PRInt32 aColIndex) const
3760 : {
3761 : // Since fixed-layout tables should not have their column sizes change
3762 : // as they load, we assume that all columns are significant.
3763 0 : if (LayoutStrategy()->GetType() == nsITableLayoutStrategy::Fixed)
3764 0 : return true;
3765 : // the first column is always significant
3766 0 : if (aColIndex == 0)
3767 0 : return true;
3768 0 : nsTableCellMap* cellMap = GetCellMap();
3769 0 : if (!cellMap)
3770 0 : return false;
3771 0 : return cellMap->GetNumCellsOriginatingInCol(aColIndex) > 0;
3772 : }
3773 :
3774 : /********************************************************************************
3775 : * Collapsing Borders
3776 : *
3777 : * The CSS spec says to resolve border conflicts in this order:
3778 : * 1) any border with the style HIDDEN wins
3779 : * 2) the widest border with a style that is not NONE wins
3780 : * 3) the border styles are ranked in this order, highest to lowest precedence:
3781 : * double, solid, dashed, dotted, ridge, outset, groove, inset
3782 : * 4) borders that are of equal width and style (differ only in color) have this precedence:
3783 : * cell, row, rowgroup, col, colgroup, table
3784 : * 5) if all border styles are NONE, then that's the computed border style.
3785 : *******************************************************************************/
3786 :
3787 : #ifdef DEBUG
3788 : #define VerifyNonNegativeDamageRect(r) \
3789 : NS_ASSERTION((r).x >= 0, "negative col index"); \
3790 : NS_ASSERTION((r).y >= 0, "negative row index"); \
3791 : NS_ASSERTION((r).width >= 0, "negative horizontal damage"); \
3792 : NS_ASSERTION((r).height >= 0, "negative vertical damage");
3793 : #define VerifyDamageRect(r) \
3794 : VerifyNonNegativeDamageRect(r); \
3795 : NS_ASSERTION((r).XMost() <= GetColCount(), \
3796 : "horizontal damage extends outside table"); \
3797 : NS_ASSERTION((r).YMost() <= GetRowCount(), \
3798 : "vertical damage extends outside table");
3799 : #endif
3800 :
3801 : void
3802 0 : nsTableFrame::AddBCDamageArea(const nsIntRect& aValue)
3803 : {
3804 0 : NS_ASSERTION(IsBorderCollapse(), "invalid AddBCDamageArea call");
3805 : #ifdef DEBUG
3806 0 : VerifyDamageRect(aValue);
3807 : #endif
3808 :
3809 0 : SetNeedToCalcBCBorders(true);
3810 : // Get the property
3811 0 : BCPropertyData* value = GetBCProperty(true);
3812 0 : if (value) {
3813 : #ifdef DEBUG
3814 0 : VerifyNonNegativeDamageRect(value->mDamageArea);
3815 : #endif
3816 : // Clamp the old damage area to the current table area in case it shrunk.
3817 0 : PRInt32 cols = GetColCount();
3818 0 : if (value->mDamageArea.XMost() > cols) {
3819 0 : if (value->mDamageArea.x > cols) {
3820 0 : value->mDamageArea.x = cols;
3821 0 : value->mDamageArea.width = 0;
3822 : }
3823 : else {
3824 0 : value->mDamageArea.width = cols - value->mDamageArea.x;
3825 : }
3826 : }
3827 0 : PRInt32 rows = GetRowCount();
3828 0 : if (value->mDamageArea.YMost() > rows) {
3829 0 : if (value->mDamageArea.y > rows) {
3830 0 : value->mDamageArea.y = rows;
3831 0 : value->mDamageArea.height = 0;
3832 : }
3833 : else {
3834 0 : value->mDamageArea.height = rows - value->mDamageArea.y;
3835 : }
3836 : }
3837 :
3838 : // Construct a union of the new and old damage areas.
3839 0 : value->mDamageArea.UnionRect(value->mDamageArea, aValue);
3840 : }
3841 0 : }
3842 :
3843 :
3844 : void
3845 0 : nsTableFrame::SetFullBCDamageArea()
3846 : {
3847 0 : NS_ASSERTION(IsBorderCollapse(), "invalid SetFullBCDamageArea call");
3848 :
3849 0 : SetNeedToCalcBCBorders(true);
3850 :
3851 0 : BCPropertyData* value = GetBCProperty(true);
3852 0 : if (value) {
3853 0 : value->mDamageArea = nsIntRect(0, 0, GetColCount(), GetRowCount());
3854 : }
3855 0 : }
3856 :
3857 :
3858 : /* BCCellBorder represents a border segment which can be either a horizontal
3859 : * or a vertical segment. For each segment we need to know the color, width,
3860 : * style, who owns it and how long it is in cellmap coordinates.
3861 : * Ownership of these segments is important to calculate which corners should
3862 : * be bevelled. This structure has dual use, its used first to compute the
3863 : * dominant border for horizontal and vertical segments and to store the
3864 : * preliminary computed border results in the BCCellBorders structure.
3865 : * This temporary storage is not symmetric with respect to horizontal and
3866 : * vertical border segments, its always column oriented. For each column in
3867 : * the cellmap there is a temporary stored vertical and horizontal segment.
3868 : * XXX_Bernd this asymmetry is the root of those rowspan bc border errors
3869 : */
3870 : struct BCCellBorder
3871 : {
3872 0 : BCCellBorder() { Reset(0, 1); }
3873 : void Reset(PRUint32 aRowIndex, PRUint32 aRowSpan);
3874 : nscolor color; // border segment color
3875 : BCPixelSize width; // border segment width in pixel coordinates !!
3876 : PRUint8 style; // border segment style, possible values are defined
3877 : // in nsStyleConsts.h as NS_STYLE_BORDER_STYLE_*
3878 : BCBorderOwner owner; // border segment owner, possible values are defined
3879 : // in celldata.h. In the cellmap for each border
3880 : // segment we store the owner and later when
3881 : // painting we know the owner and can retrieve the
3882 : // style info from the corresponding frame
3883 : PRInt32 rowIndex; // rowIndex of temporary stored horizontal border
3884 : // segments relative to the table
3885 : PRInt32 rowSpan; // row span of temporary stored horizontal border
3886 : // segments
3887 : };
3888 :
3889 : void
3890 0 : BCCellBorder::Reset(PRUint32 aRowIndex,
3891 : PRUint32 aRowSpan)
3892 : {
3893 0 : style = NS_STYLE_BORDER_STYLE_NONE;
3894 0 : color = 0;
3895 0 : width = 0;
3896 0 : owner = eTableOwner;
3897 0 : rowIndex = aRowIndex;
3898 0 : rowSpan = aRowSpan;
3899 0 : }
3900 :
3901 : class BCMapCellIterator;
3902 :
3903 : /*****************************************************************
3904 : * BCMapCellInfo
3905 : * This structure stores information about the cellmap and all involved
3906 : * table related frames that are used during the computation of winning borders
3907 : * in CalcBCBorders so that they do need to be looked up again and again when
3908 : * iterating over the cells.
3909 : ****************************************************************/
3910 : struct BCMapCellInfo
3911 : {
3912 : BCMapCellInfo(nsTableFrame* aTableFrame);
3913 : void ResetCellInfo();
3914 : void SetInfo(nsTableRowFrame* aNewRow,
3915 : PRInt32 aColIndex,
3916 : BCCellData* aCellData,
3917 : BCMapCellIterator* aIter,
3918 : nsCellMap* aCellMap = nsnull);
3919 : // The BCMapCellInfo has functions to set the continous
3920 : // border widths (see nsTablePainter.cpp for a description of the continous
3921 : // borders concept). The widths are computed inside these functions based on
3922 : // the current position inside the table and the cached frames that correspond
3923 : // to this position. The widths are stored in member variables of the internal
3924 : // table frames.
3925 : void SetTableTopLeftContBCBorder();
3926 : void SetRowGroupLeftContBCBorder();
3927 : void SetRowGroupRightContBCBorder();
3928 : void SetRowGroupBottomContBCBorder();
3929 : void SetRowLeftContBCBorder();
3930 : void SetRowRightContBCBorder();
3931 : void SetColumnTopRightContBCBorder();
3932 : void SetColumnBottomContBCBorder();
3933 : void SetColGroupBottomContBCBorder();
3934 : void SetInnerRowGroupBottomContBCBorder(const nsIFrame* aNextRowGroup,
3935 : nsTableRowFrame* aNextRow);
3936 :
3937 : // functions to set the border widths on the table related frames, where the
3938 : // knowledge about the current position in the table is used.
3939 : void SetTableTopBorderWidth(BCPixelSize aWidth);
3940 : void SetTableLeftBorderWidth(PRInt32 aRowY, BCPixelSize aWidth);
3941 : void SetTableRightBorderWidth(PRInt32 aRowY, BCPixelSize aWidth);
3942 : void SetTableBottomBorderWidth(BCPixelSize aWidth);
3943 : void SetLeftBorderWidths(BCPixelSize aWidth);
3944 : void SetRightBorderWidths(BCPixelSize aWidth);
3945 : void SetTopBorderWidths(BCPixelSize aWidth);
3946 : void SetBottomBorderWidths(BCPixelSize aWidth);
3947 :
3948 : // functions to compute the borders; they depend on the
3949 : // knowledge about the current position in the table. The edge functions
3950 : // should be called if a table edge is involved, otherwise the internal
3951 : // functions should be called.
3952 : BCCellBorder GetTopEdgeBorder();
3953 : BCCellBorder GetBottomEdgeBorder();
3954 : BCCellBorder GetLeftEdgeBorder();
3955 : BCCellBorder GetRightEdgeBorder();
3956 : BCCellBorder GetRightInternalBorder();
3957 : BCCellBorder GetLeftInternalBorder();
3958 : BCCellBorder GetTopInternalBorder();
3959 : BCCellBorder GetBottomInternalBorder();
3960 :
3961 : // functions to set the interal position information
3962 : void SetColumn(PRInt32 aColX);
3963 : // Increment the row as we loop over the rows of a rowspan
3964 : void IncrementRow(bool aResetToTopRowOfCell = false);
3965 :
3966 : // Helper functions to get extent of the cell
3967 : PRInt32 GetCellEndRowIndex() const;
3968 : PRInt32 GetCellEndColIndex() const;
3969 :
3970 : // storage of table information
3971 : nsTableFrame* mTableFrame;
3972 : PRInt32 mNumTableRows;
3973 : PRInt32 mNumTableCols;
3974 : BCPropertyData* mTableBCData;
3975 :
3976 : // storage of table ltr information, the border collapse code swaps the sides
3977 : // to account for rtl tables, this is done through mStartSide and mEndSide
3978 : bool mTableIsLTR;
3979 : mozilla::css::Side mStartSide;
3980 : mozilla::css::Side mEndSide;
3981 :
3982 : // a cell can only belong to one rowgroup
3983 : nsTableRowGroupFrame* mRowGroup;
3984 :
3985 : // a cell with a rowspan has a top and a bottom row, and rows in between
3986 : nsTableRowFrame* mTopRow;
3987 : nsTableRowFrame* mBottomRow;
3988 : nsTableRowFrame* mCurrentRowFrame;
3989 :
3990 : // a cell with a colspan has a left and right column and columns in between
3991 : // they can belong to different colgroups
3992 : nsTableColGroupFrame* mColGroup;
3993 : nsTableColGroupFrame* mCurrentColGroupFrame;
3994 :
3995 : nsTableColFrame* mLeftCol;
3996 : nsTableColFrame* mRightCol;
3997 : nsTableColFrame* mCurrentColFrame;
3998 :
3999 : // cell information
4000 : BCCellData* mCellData;
4001 : nsBCTableCellFrame* mCell;
4002 :
4003 : PRInt32 mRowIndex;
4004 : PRInt32 mRowSpan;
4005 : PRInt32 mColIndex;
4006 : PRInt32 mColSpan;
4007 :
4008 : // flags to describe the position of the cell with respect to the row- and
4009 : // colgroups, for instance mRgAtTop documents that the top cell border hits
4010 : // a rowgroup border
4011 : bool mRgAtTop;
4012 : bool mRgAtBottom;
4013 : bool mCgAtLeft;
4014 : bool mCgAtRight;
4015 :
4016 : };
4017 :
4018 :
4019 0 : BCMapCellInfo::BCMapCellInfo(nsTableFrame* aTableFrame)
4020 : {
4021 0 : mTableFrame = aTableFrame;
4022 : mTableIsLTR =
4023 0 : aTableFrame->GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR;
4024 0 : if (mTableIsLTR) {
4025 0 : mStartSide = NS_SIDE_LEFT;
4026 0 : mEndSide = NS_SIDE_RIGHT;
4027 : }
4028 : else {
4029 0 : mStartSide = NS_SIDE_RIGHT;
4030 0 : mEndSide = NS_SIDE_LEFT;
4031 : }
4032 0 : mNumTableRows = mTableFrame->GetRowCount();
4033 0 : mNumTableCols = mTableFrame->GetColCount();
4034 : mTableBCData = static_cast<BCPropertyData*>
4035 0 : (mTableFrame->Properties().Get(TableBCProperty()));
4036 :
4037 0 : ResetCellInfo();
4038 0 : }
4039 :
4040 0 : void BCMapCellInfo::ResetCellInfo()
4041 : {
4042 0 : mCellData = nsnull;
4043 0 : mRowGroup = nsnull;
4044 0 : mTopRow = nsnull;
4045 0 : mBottomRow = nsnull;
4046 0 : mColGroup = nsnull;
4047 0 : mLeftCol = nsnull;
4048 0 : mRightCol = nsnull;
4049 0 : mCell = nsnull;
4050 0 : mRowIndex = mRowSpan = mColIndex = mColSpan = 0;
4051 0 : mRgAtTop = mRgAtBottom = mCgAtLeft = mCgAtRight = false;
4052 0 : }
4053 :
4054 0 : inline PRInt32 BCMapCellInfo::GetCellEndRowIndex() const
4055 : {
4056 0 : return mRowIndex + mRowSpan - 1;
4057 : }
4058 :
4059 0 : inline PRInt32 BCMapCellInfo::GetCellEndColIndex() const
4060 : {
4061 0 : return mColIndex + mColSpan - 1;
4062 : }
4063 :
4064 :
4065 : class BCMapCellIterator
4066 0 : {
4067 : public:
4068 : BCMapCellIterator(nsTableFrame* aTableFrame,
4069 : const nsIntRect& aDamageArea);
4070 :
4071 : void First(BCMapCellInfo& aMapCellInfo);
4072 :
4073 : void Next(BCMapCellInfo& aMapCellInfo);
4074 :
4075 : void PeekRight(BCMapCellInfo& aRefInfo,
4076 : PRUint32 aRowIndex,
4077 : BCMapCellInfo& aAjaInfo);
4078 :
4079 : void PeekBottom(BCMapCellInfo& aRefInfo,
4080 : PRUint32 aColIndex,
4081 : BCMapCellInfo& aAjaInfo);
4082 :
4083 0 : bool IsNewRow() { return mIsNewRow; }
4084 :
4085 : nsTableRowFrame* GetPrevRow() const { return mPrevRow; }
4086 0 : nsTableRowFrame* GetCurrentRow() const { return mRow; }
4087 0 : nsTableRowGroupFrame* GetCurrentRowGroup() const { return mRowGroup;}
4088 :
4089 : PRInt32 mRowGroupStart;
4090 : PRInt32 mRowGroupEnd;
4091 : bool mAtEnd;
4092 : nsCellMap* mCellMap;
4093 :
4094 : private:
4095 : bool SetNewRow(nsTableRowFrame* row = nsnull);
4096 : bool SetNewRowGroup(bool aFindFirstDamagedRow);
4097 :
4098 : nsTableFrame* mTableFrame;
4099 : nsTableCellMap* mTableCellMap;
4100 : nsTableFrame::RowGroupArray mRowGroups;
4101 : nsTableRowGroupFrame* mRowGroup;
4102 : PRInt32 mRowGroupIndex;
4103 : PRUint32 mNumTableRows;
4104 : nsTableRowFrame* mRow;
4105 : nsTableRowFrame* mPrevRow;
4106 : bool mIsNewRow;
4107 : PRInt32 mRowIndex;
4108 : PRUint32 mNumTableCols;
4109 : PRInt32 mColIndex;
4110 : nsPoint mAreaStart;
4111 : nsPoint mAreaEnd;
4112 : };
4113 :
4114 0 : BCMapCellIterator::BCMapCellIterator(nsTableFrame* aTableFrame,
4115 : const nsIntRect& aDamageArea)
4116 0 : :mTableFrame(aTableFrame)
4117 : {
4118 0 : mTableCellMap = aTableFrame->GetCellMap();
4119 :
4120 0 : mAreaStart.x = aDamageArea.x;
4121 0 : mAreaStart.y = aDamageArea.y;
4122 0 : mAreaEnd.y = aDamageArea.y + aDamageArea.height - 1;
4123 0 : mAreaEnd.x = aDamageArea.x + aDamageArea.width - 1;
4124 :
4125 0 : mNumTableRows = mTableFrame->GetRowCount();
4126 0 : mRow = nsnull;
4127 0 : mRowIndex = 0;
4128 0 : mNumTableCols = mTableFrame->GetColCount();
4129 0 : mColIndex = 0;
4130 0 : mRowGroupIndex = -1;
4131 :
4132 : // Get the ordered row groups
4133 0 : aTableFrame->OrderRowGroups(mRowGroups);
4134 :
4135 0 : mAtEnd = true; // gets reset when First() is called
4136 0 : }
4137 :
4138 : // fill fields that we need for border collapse computation on a given cell
4139 : void
4140 0 : BCMapCellInfo::SetInfo(nsTableRowFrame* aNewRow,
4141 : PRInt32 aColIndex,
4142 : BCCellData* aCellData,
4143 : BCMapCellIterator* aIter,
4144 : nsCellMap* aCellMap)
4145 : {
4146 : // fill the cell information
4147 0 : mCellData = aCellData;
4148 0 : mColIndex = aColIndex;
4149 :
4150 : // initialize the row information if it was not previously set for cells in
4151 : // this row
4152 0 : mRowIndex = 0;
4153 0 : if (aNewRow) {
4154 0 : mTopRow = aNewRow;
4155 0 : mRowIndex = aNewRow->GetRowIndex();
4156 : }
4157 :
4158 : // fill cell frame info and row information
4159 0 : mCell = nsnull;
4160 0 : mRowSpan = 1;
4161 0 : mColSpan = 1;
4162 0 : if (aCellData) {
4163 0 : mCell = static_cast<nsBCTableCellFrame*>(aCellData->GetCellFrame());
4164 0 : if (mCell) {
4165 0 : if (!mTopRow) {
4166 0 : mTopRow = static_cast<nsTableRowFrame*>(mCell->GetParent());
4167 0 : if (!mTopRow) ABORT0();
4168 0 : mRowIndex = mTopRow->GetRowIndex();
4169 : }
4170 0 : mColSpan = mTableFrame->GetEffectiveColSpan(*mCell, aCellMap);
4171 0 : mRowSpan = mTableFrame->GetEffectiveRowSpan(*mCell, aCellMap);
4172 : }
4173 : }
4174 :
4175 0 : if (!mTopRow) {
4176 0 : mTopRow = aIter->GetCurrentRow();
4177 : }
4178 0 : if (1 == mRowSpan) {
4179 0 : mBottomRow = mTopRow;
4180 : }
4181 : else {
4182 0 : mBottomRow = mTopRow->GetNextRow();
4183 0 : if (mBottomRow) {
4184 0 : for (PRInt32 spanY = 2; mBottomRow && (spanY < mRowSpan); spanY++) {
4185 0 : mBottomRow = mBottomRow->GetNextRow();
4186 : }
4187 0 : NS_ASSERTION(mBottomRow, "spanned row not found");
4188 : }
4189 : else {
4190 0 : NS_ASSERTION(false, "error in cell map");
4191 0 : mRowSpan = 1;
4192 0 : mBottomRow = mTopRow;
4193 : }
4194 : }
4195 : // row group frame info
4196 : // try to reuse the rgStart and rgEnd from the iterator as calls to
4197 : // GetRowCount() are computationally expensive and should be avoided if
4198 : // possible
4199 0 : PRUint32 rgStart = aIter->mRowGroupStart;
4200 0 : PRUint32 rgEnd = aIter->mRowGroupEnd;
4201 0 : mRowGroup = static_cast<nsTableRowGroupFrame*>(mTopRow->GetParent());
4202 0 : if (mRowGroup != aIter->GetCurrentRowGroup()) {
4203 0 : rgStart = mRowGroup->GetStartRowIndex();
4204 0 : rgEnd = rgStart + mRowGroup->GetRowCount() - 1;
4205 : }
4206 0 : PRUint32 rowIndex = mTopRow->GetRowIndex();
4207 0 : mRgAtTop = (rgStart == rowIndex);
4208 0 : mRgAtBottom = (rgEnd == rowIndex + mRowSpan - 1);
4209 :
4210 : // col frame info
4211 0 : mLeftCol = mTableFrame->GetColFrame(aColIndex);
4212 0 : if (!mLeftCol) ABORT0();
4213 :
4214 0 : mRightCol = mLeftCol;
4215 0 : if (mColSpan > 1) {
4216 : nsTableColFrame* colFrame = mTableFrame->GetColFrame(aColIndex +
4217 0 : mColSpan -1);
4218 0 : if (!colFrame) ABORT0();
4219 0 : mRightCol = colFrame;
4220 : }
4221 :
4222 : // col group frame info
4223 0 : mColGroup = static_cast<nsTableColGroupFrame*>(mLeftCol->GetParent());
4224 0 : PRInt32 cgStart = mColGroup->GetStartColumnIndex();
4225 0 : PRInt32 cgEnd = NS_MAX(0, cgStart + mColGroup->GetColCount() - 1);
4226 0 : mCgAtLeft = (cgStart == aColIndex);
4227 0 : mCgAtRight = (cgEnd == aColIndex + mColSpan - 1);
4228 : }
4229 :
4230 : bool
4231 0 : BCMapCellIterator::SetNewRow(nsTableRowFrame* aRow)
4232 : {
4233 0 : mAtEnd = true;
4234 0 : mPrevRow = mRow;
4235 0 : if (aRow) {
4236 0 : mRow = aRow;
4237 : }
4238 0 : else if (mRow) {
4239 0 : mRow = mRow->GetNextRow();
4240 : }
4241 0 : if (mRow) {
4242 0 : mRowIndex = mRow->GetRowIndex();
4243 : // get to the first entry with an originating cell
4244 0 : PRInt32 rgRowIndex = mRowIndex - mRowGroupStart;
4245 0 : if (PRUint32(rgRowIndex) >= mCellMap->mRows.Length())
4246 0 : ABORT1(false);
4247 0 : const nsCellMap::CellDataArray& row = mCellMap->mRows[rgRowIndex];
4248 :
4249 0 : for (mColIndex = mAreaStart.x; mColIndex <= mAreaEnd.x; mColIndex++) {
4250 0 : CellData* cellData = row.SafeElementAt(mColIndex);
4251 0 : if (!cellData) { // add a dead cell data
4252 0 : nsIntRect damageArea;
4253 : cellData = mCellMap->AppendCell(*mTableCellMap, nsnull, rgRowIndex,
4254 0 : false, 0, damageArea);
4255 0 : if (!cellData) ABORT1(false);
4256 : }
4257 0 : if (cellData && (cellData->IsOrig() || cellData->IsDead())) {
4258 0 : break;
4259 : }
4260 : }
4261 0 : mIsNewRow = true;
4262 0 : mAtEnd = false;
4263 : }
4264 0 : else ABORT1(false);
4265 :
4266 0 : return !mAtEnd;
4267 : }
4268 :
4269 : bool
4270 0 : BCMapCellIterator::SetNewRowGroup(bool aFindFirstDamagedRow)
4271 : {
4272 0 : mAtEnd = true;
4273 0 : PRInt32 numRowGroups = mRowGroups.Length();
4274 0 : mCellMap = nsnull;
4275 0 : for (mRowGroupIndex++; mRowGroupIndex < numRowGroups; mRowGroupIndex++) {
4276 0 : mRowGroup = mRowGroups[mRowGroupIndex];
4277 0 : PRInt32 rowCount = mRowGroup->GetRowCount();
4278 0 : mRowGroupStart = mRowGroup->GetStartRowIndex();
4279 0 : mRowGroupEnd = mRowGroupStart + rowCount - 1;
4280 0 : if (rowCount > 0) {
4281 0 : mCellMap = mTableCellMap->GetMapFor(mRowGroup, mCellMap);
4282 0 : if (!mCellMap) ABORT1(false);
4283 0 : nsTableRowFrame* firstRow = mRowGroup->GetFirstRow();
4284 0 : if (aFindFirstDamagedRow) {
4285 0 : if ((mAreaStart.y >= mRowGroupStart) && (mAreaStart.y <= mRowGroupEnd)) {
4286 : // the damage area starts in the row group
4287 0 : if (aFindFirstDamagedRow) {
4288 : // find the correct first damaged row
4289 0 : PRInt32 numRows = mAreaStart.y - mRowGroupStart;
4290 0 : for (PRInt32 i = 0; i < numRows; i++) {
4291 0 : firstRow = firstRow->GetNextRow();
4292 0 : if (!firstRow) ABORT1(false);
4293 : }
4294 : }
4295 : }
4296 : else {
4297 0 : continue;
4298 : }
4299 : }
4300 0 : if (SetNewRow(firstRow)) { // sets mAtEnd
4301 0 : break;
4302 : }
4303 : }
4304 : }
4305 :
4306 0 : return !mAtEnd;
4307 : }
4308 :
4309 : void
4310 0 : BCMapCellIterator::First(BCMapCellInfo& aMapInfo)
4311 : {
4312 0 : aMapInfo.ResetCellInfo();
4313 :
4314 0 : SetNewRowGroup(true); // sets mAtEnd
4315 0 : while (!mAtEnd) {
4316 0 : if ((mAreaStart.y >= mRowGroupStart) && (mAreaStart.y <= mRowGroupEnd)) {
4317 : BCCellData* cellData =
4318 : static_cast<BCCellData*>(mCellMap->GetDataAt(mAreaStart.y -
4319 : mRowGroupStart,
4320 0 : mAreaStart.x));
4321 0 : if (cellData && (cellData->IsOrig() || cellData->IsDead())) {
4322 0 : aMapInfo.SetInfo(mRow, mAreaStart.x, cellData, this);
4323 0 : return;
4324 : }
4325 : else {
4326 0 : NS_ASSERTION(((0 == mAreaStart.x) && (mRowGroupStart == mAreaStart.y)) ,
4327 : "damage area expanded incorrectly");
4328 : }
4329 : }
4330 0 : SetNewRowGroup(true); // sets mAtEnd
4331 : }
4332 : }
4333 :
4334 : void
4335 0 : BCMapCellIterator::Next(BCMapCellInfo& aMapInfo)
4336 : {
4337 0 : if (mAtEnd) ABORT0();
4338 0 : aMapInfo.ResetCellInfo();
4339 :
4340 0 : mIsNewRow = false;
4341 0 : mColIndex++;
4342 0 : while ((mRowIndex <= mAreaEnd.y) && !mAtEnd) {
4343 0 : for (; mColIndex <= mAreaEnd.x; mColIndex++) {
4344 0 : PRInt32 rgRowIndex = mRowIndex - mRowGroupStart;
4345 : BCCellData* cellData =
4346 0 : static_cast<BCCellData*>(mCellMap->GetDataAt(rgRowIndex, mColIndex));
4347 0 : if (!cellData) { // add a dead cell data
4348 0 : nsIntRect damageArea;
4349 : cellData =
4350 : static_cast<BCCellData*>(mCellMap->AppendCell(*mTableCellMap, nsnull,
4351 : rgRowIndex, false, 0,
4352 0 : damageArea));
4353 0 : if (!cellData) ABORT0();
4354 : }
4355 0 : if (cellData && (cellData->IsOrig() || cellData->IsDead())) {
4356 0 : aMapInfo.SetInfo(mRow, mColIndex, cellData, this);
4357 0 : return;
4358 : }
4359 : }
4360 0 : if (mRowIndex >= mRowGroupEnd) {
4361 0 : SetNewRowGroup(false); // could set mAtEnd
4362 : }
4363 : else {
4364 0 : SetNewRow(); // could set mAtEnd
4365 : }
4366 : }
4367 0 : mAtEnd = true;
4368 : }
4369 :
4370 : void
4371 0 : BCMapCellIterator::PeekRight(BCMapCellInfo& aRefInfo,
4372 : PRUint32 aRowIndex,
4373 : BCMapCellInfo& aAjaInfo)
4374 : {
4375 0 : aAjaInfo.ResetCellInfo();
4376 0 : PRInt32 colIndex = aRefInfo.mColIndex + aRefInfo.mColSpan;
4377 0 : PRUint32 rgRowIndex = aRowIndex - mRowGroupStart;
4378 :
4379 : BCCellData* cellData =
4380 0 : static_cast<BCCellData*>(mCellMap->GetDataAt(rgRowIndex, colIndex));
4381 0 : if (!cellData) { // add a dead cell data
4382 0 : NS_ASSERTION(colIndex < mTableCellMap->GetColCount(), "program error");
4383 0 : nsIntRect damageArea;
4384 : cellData =
4385 : static_cast<BCCellData*>(mCellMap->AppendCell(*mTableCellMap, nsnull,
4386 : rgRowIndex, false, 0,
4387 0 : damageArea));
4388 0 : if (!cellData) ABORT0();
4389 : }
4390 0 : nsTableRowFrame* row = nsnull;
4391 0 : if (cellData->IsRowSpan()) {
4392 0 : rgRowIndex -= cellData->GetRowSpanOffset();
4393 : cellData =
4394 0 : static_cast<BCCellData*>(mCellMap->GetDataAt(rgRowIndex, colIndex));
4395 0 : if (!cellData)
4396 0 : ABORT0();
4397 : }
4398 : else {
4399 0 : row = mRow;
4400 : }
4401 0 : aAjaInfo.SetInfo(row, colIndex, cellData, this);
4402 : }
4403 :
4404 : void
4405 0 : BCMapCellIterator::PeekBottom(BCMapCellInfo& aRefInfo,
4406 : PRUint32 aColIndex,
4407 : BCMapCellInfo& aAjaInfo)
4408 : {
4409 0 : aAjaInfo.ResetCellInfo();
4410 0 : PRInt32 rowIndex = aRefInfo.mRowIndex + aRefInfo.mRowSpan;
4411 0 : PRInt32 rgRowIndex = rowIndex - mRowGroupStart;
4412 0 : nsTableRowGroupFrame* rg = mRowGroup;
4413 0 : nsCellMap* cellMap = mCellMap;
4414 0 : nsTableRowFrame* nextRow = nsnull;
4415 0 : if (rowIndex > mRowGroupEnd) {
4416 0 : PRInt32 nextRgIndex = mRowGroupIndex;
4417 0 : do {
4418 0 : nextRgIndex++;
4419 0 : rg = mRowGroups.SafeElementAt(nextRgIndex);
4420 0 : if (rg) {
4421 0 : cellMap = mTableCellMap->GetMapFor(rg, cellMap); if (!cellMap) ABORT0();
4422 0 : rgRowIndex = 0;
4423 0 : nextRow = rg->GetFirstRow();
4424 : }
4425 : }
4426 : while (rg && !nextRow);
4427 0 : if(!rg) return;
4428 : }
4429 : else {
4430 : // get the row within the same row group
4431 0 : nextRow = mRow;
4432 0 : for (PRInt32 i = 0; i < aRefInfo.mRowSpan; i++) {
4433 0 : nextRow = nextRow->GetNextRow(); if (!nextRow) ABORT0();
4434 : }
4435 : }
4436 :
4437 : BCCellData* cellData =
4438 0 : static_cast<BCCellData*>(cellMap->GetDataAt(rgRowIndex, aColIndex));
4439 0 : if (!cellData) { // add a dead cell data
4440 0 : NS_ASSERTION(rgRowIndex < cellMap->GetRowCount(), "program error");
4441 0 : nsIntRect damageArea;
4442 : cellData =
4443 : static_cast<BCCellData*>(cellMap->AppendCell(*mTableCellMap, nsnull,
4444 : rgRowIndex, false, 0,
4445 0 : damageArea));
4446 0 : if (!cellData) ABORT0();
4447 : }
4448 0 : if (cellData->IsColSpan()) {
4449 0 : aColIndex -= cellData->GetColSpanOffset();
4450 : cellData =
4451 0 : static_cast<BCCellData*>(cellMap->GetDataAt(rgRowIndex, aColIndex));
4452 : }
4453 0 : aAjaInfo.SetInfo(nextRow, aColIndex, cellData, this, cellMap);
4454 : }
4455 :
4456 : // Assign priorities to border styles. For example, styleToPriority(NS_STYLE_BORDER_STYLE_SOLID)
4457 : // will return the priority of NS_STYLE_BORDER_STYLE_SOLID.
4458 : static PRUint8 styleToPriority[13] = { 0, // NS_STYLE_BORDER_STYLE_NONE
4459 : 2, // NS_STYLE_BORDER_STYLE_GROOVE
4460 : 4, // NS_STYLE_BORDER_STYLE_RIDGE
4461 : 5, // NS_STYLE_BORDER_STYLE_DOTTED
4462 : 6, // NS_STYLE_BORDER_STYLE_DASHED
4463 : 7, // NS_STYLE_BORDER_STYLE_SOLID
4464 : 8, // NS_STYLE_BORDER_STYLE_DOUBLE
4465 : 1, // NS_STYLE_BORDER_STYLE_INSET
4466 : 3, // NS_STYLE_BORDER_STYLE_OUTSET
4467 : 9 };// NS_STYLE_BORDER_STYLE_HIDDEN
4468 : // priority rules follow CSS 2.1 spec
4469 : // 'hidden', 'double', 'solid', 'dashed', 'dotted', 'ridge', 'outset', 'groove',
4470 : // and the lowest: 'inset'. none is even weaker
4471 : #define CELL_CORNER true
4472 :
4473 : /** return the border style, border color for a given frame and side
4474 : * @param aFrame - query the info for this frame
4475 : * @param aSide - the side of the frame
4476 : * @param aStyle - the border style
4477 : * @param aColor - the border color
4478 : * @param aTableIsLTR - table direction is LTR
4479 : */
4480 : static void
4481 0 : GetColorAndStyle(const nsIFrame* aFrame,
4482 : mozilla::css::Side aSide,
4483 : PRUint8& aStyle,
4484 : nscolor& aColor,
4485 : bool aTableIsLTR)
4486 : {
4487 0 : NS_PRECONDITION(aFrame, "null frame");
4488 : // initialize out arg
4489 0 : aColor = 0;
4490 0 : const nsStyleBorder* styleData = aFrame->GetStyleBorder();
4491 0 : if(!aTableIsLTR) { // revert the directions
4492 0 : if (NS_SIDE_RIGHT == aSide) {
4493 0 : aSide = NS_SIDE_LEFT;
4494 : }
4495 0 : else if (NS_SIDE_LEFT == aSide) {
4496 0 : aSide = NS_SIDE_RIGHT;
4497 : }
4498 : }
4499 0 : aStyle = styleData->GetBorderStyle(aSide);
4500 :
4501 0 : if ((NS_STYLE_BORDER_STYLE_NONE == aStyle) ||
4502 : (NS_STYLE_BORDER_STYLE_HIDDEN == aStyle)) {
4503 0 : return;
4504 : }
4505 : aColor = aFrame->GetStyleContext()->GetVisitedDependentColor(
4506 0 : nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_color)[aSide]);
4507 : }
4508 :
4509 : /** coerce the paint style as required by CSS2.1
4510 : * @param aFrame - query the info for this frame
4511 : * @param aSide - the side of the frame
4512 : * @param aStyle - the border style
4513 : * @param aColor - the border color
4514 : * @param aTableIsLTR - table direction is LTR
4515 : */
4516 : static void
4517 0 : GetPaintStyleInfo(const nsIFrame* aFrame,
4518 : mozilla::css::Side aSide,
4519 : PRUint8& aStyle,
4520 : nscolor& aColor,
4521 : bool aTableIsLTR)
4522 : {
4523 0 : GetColorAndStyle(aFrame, aSide, aStyle, aColor, aTableIsLTR);
4524 0 : if (NS_STYLE_BORDER_STYLE_INSET == aStyle) {
4525 0 : aStyle = NS_STYLE_BORDER_STYLE_RIDGE;
4526 : }
4527 0 : else if (NS_STYLE_BORDER_STYLE_OUTSET == aStyle) {
4528 0 : aStyle = NS_STYLE_BORDER_STYLE_GROOVE;
4529 : }
4530 0 : }
4531 :
4532 : /** return the border style, border color and the width in pixel for a given
4533 : * frame and side
4534 : * @param aFrame - query the info for this frame
4535 : * @param aSide - the side of the frame
4536 : * @param aStyle - the border style
4537 : * @param aColor - the border color
4538 : * @param aTableIsLTR - table direction is LTR
4539 : * @param aWidth - the border width in px.
4540 : * @param aTwipsToPixels - conversion factor from twips to pixel
4541 : */
4542 : static void
4543 0 : GetColorAndStyle(const nsIFrame* aFrame,
4544 : mozilla::css::Side aSide,
4545 : PRUint8& aStyle,
4546 : nscolor& aColor,
4547 : bool aTableIsLTR,
4548 : BCPixelSize& aWidth)
4549 : {
4550 0 : GetColorAndStyle(aFrame, aSide, aStyle, aColor, aTableIsLTR);
4551 0 : if ((NS_STYLE_BORDER_STYLE_NONE == aStyle) ||
4552 : (NS_STYLE_BORDER_STYLE_HIDDEN == aStyle)) {
4553 0 : aWidth = 0;
4554 0 : return;
4555 : }
4556 0 : const nsStyleBorder* styleData = aFrame->GetStyleBorder();
4557 : nscoord width;
4558 0 : if(!aTableIsLTR) { // revert the directions
4559 0 : if (NS_SIDE_RIGHT == aSide) {
4560 0 : aSide = NS_SIDE_LEFT;
4561 : }
4562 0 : else if (NS_SIDE_LEFT == aSide) {
4563 0 : aSide = NS_SIDE_RIGHT;
4564 : }
4565 : }
4566 0 : width = styleData->GetActualBorderWidth(aSide);
4567 0 : aWidth = nsPresContext::AppUnitsToIntCSSPixels(width);
4568 : }
4569 :
4570 0 : class nsDelayedCalcBCBorders : public nsRunnable {
4571 : public:
4572 0 : nsDelayedCalcBCBorders(nsIFrame* aFrame) :
4573 0 : mFrame(aFrame) {}
4574 :
4575 0 : NS_IMETHOD Run() {
4576 0 : if (mFrame) {
4577 0 : nsTableFrame* tableFrame = static_cast <nsTableFrame*>(mFrame.GetFrame());
4578 0 : if (tableFrame->NeedToCalcBCBorders()) {
4579 0 : tableFrame->CalcBCBorders();
4580 : }
4581 : }
4582 0 : return NS_OK;
4583 : }
4584 : private:
4585 : nsWeakFrame mFrame;
4586 : };
4587 :
4588 : bool
4589 0 : nsTableFrame::BCRecalcNeeded(nsStyleContext* aOldStyleContext,
4590 : nsStyleContext* aNewStyleContext)
4591 : {
4592 : // Attention: the old style context is the one we're forgetting,
4593 : // and hence possibly completely bogus for GetStyle* purposes.
4594 : // We use PeekStyleData instead.
4595 :
4596 0 : const nsStyleBorder* oldStyleData = aOldStyleContext->PeekStyleBorder();
4597 0 : if (!oldStyleData)
4598 0 : return false;
4599 :
4600 0 : const nsStyleBorder* newStyleData = aNewStyleContext->GetStyleBorder();
4601 0 : nsChangeHint change = newStyleData->CalcDifference(*oldStyleData);
4602 0 : if (!change)
4603 0 : return false;
4604 0 : if (change & nsChangeHint_ReflowFrame)
4605 0 : return true; // the caller only needs to mark the bc damage area
4606 0 : if (change & nsChangeHint_RepaintFrame) {
4607 : // we need to recompute the borders and the caller needs to mark
4608 : // the bc damage area
4609 : // XXX In principle this should only be necessary for border style changes
4610 : // However the bc painting code tries to maximize the drawn border segments
4611 : // so it stores in the cellmap where a new border segment starts and this
4612 : // introduces a unwanted cellmap data dependence on color
4613 0 : nsCOMPtr<nsIRunnable> evt = new nsDelayedCalcBCBorders(this);
4614 0 : NS_DispatchToCurrentThread(evt);
4615 0 : return true;
4616 : }
4617 0 : return false;
4618 : }
4619 :
4620 :
4621 : // Compare two border segments, this comparison depends whether the two
4622 : // segments meet at a corner and whether the second segment is horizontal.
4623 : // The return value is whichever of aBorder1 or aBorder2 dominates.
4624 : static const BCCellBorder&
4625 0 : CompareBorders(bool aIsCorner, // Pass true for corner calculations
4626 : const BCCellBorder& aBorder1,
4627 : const BCCellBorder& aBorder2,
4628 : bool aSecondIsHorizontal,
4629 : bool* aFirstDominates = nsnull)
4630 : {
4631 0 : bool firstDominates = true;
4632 :
4633 0 : if (NS_STYLE_BORDER_STYLE_HIDDEN == aBorder1.style) {
4634 0 : firstDominates = (aIsCorner) ? false : true;
4635 : }
4636 0 : else if (NS_STYLE_BORDER_STYLE_HIDDEN == aBorder2.style) {
4637 0 : firstDominates = (aIsCorner) ? true : false;
4638 : }
4639 0 : else if (aBorder1.width < aBorder2.width) {
4640 0 : firstDominates = false;
4641 : }
4642 0 : else if (aBorder1.width == aBorder2.width) {
4643 0 : if (styleToPriority[aBorder1.style] < styleToPriority[aBorder2.style]) {
4644 0 : firstDominates = false;
4645 : }
4646 0 : else if (styleToPriority[aBorder1.style] == styleToPriority[aBorder2.style]) {
4647 0 : if (aBorder1.owner == aBorder2.owner) {
4648 0 : firstDominates = !aSecondIsHorizontal;
4649 : }
4650 0 : else if (aBorder1.owner < aBorder2.owner) {
4651 0 : firstDominates = false;
4652 : }
4653 : }
4654 : }
4655 :
4656 0 : if (aFirstDominates)
4657 0 : *aFirstDominates = firstDominates;
4658 :
4659 0 : if (firstDominates)
4660 0 : return aBorder1;
4661 0 : return aBorder2;
4662 : }
4663 :
4664 : /** calc the dominant border by considering the table, row/col group, row/col,
4665 : * cell.
4666 : * Depending on whether the side is vertical or horizontal and whether
4667 : * adjacent frames are taken into account the ownership of a single border
4668 : * segment is defined. The return value is the dominating border
4669 : * The cellmap stores only top and left borders for each cellmap position.
4670 : * If the cell border is owned by the cell that is left of the border
4671 : * it will be an adjacent owner aka eAjaCellOwner. See celldata.h for the other
4672 : * scenarios with a adjacent owner.
4673 : * @param xxxFrame - the frame for style information, might be zero if
4674 : * it should not be considered
4675 : * @param aSide - side of the frames that should be considered
4676 : * @param aAja - the border comparison takes place from the point of
4677 : * a frame that is adjacent to the cellmap entry, for
4678 : * when a cell owns its lower border it will be the
4679 : * adjacent owner as in the cellmap only top and left
4680 : * borders are stored.
4681 : * @param aTwipsToPixels - conversion factor as borders need to be drawn pixel
4682 : * aligned.
4683 : */
4684 : static BCCellBorder
4685 0 : CompareBorders(const nsIFrame* aTableFrame,
4686 : const nsIFrame* aColGroupFrame,
4687 : const nsIFrame* aColFrame,
4688 : const nsIFrame* aRowGroupFrame,
4689 : const nsIFrame* aRowFrame,
4690 : const nsIFrame* aCellFrame,
4691 : bool aTableIsLTR,
4692 : mozilla::css::Side aSide,
4693 : bool aAja)
4694 : {
4695 0 : BCCellBorder border, tempBorder;
4696 0 : bool horizontal = (NS_SIDE_TOP == aSide) || (NS_SIDE_BOTTOM == aSide);
4697 :
4698 : // start with the table as dominant if present
4699 0 : if (aTableFrame) {
4700 0 : GetColorAndStyle(aTableFrame, aSide, border.style, border.color, aTableIsLTR, border.width);
4701 0 : border.owner = eTableOwner;
4702 0 : if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) {
4703 0 : return border;
4704 : }
4705 : }
4706 : // see if the colgroup is dominant
4707 0 : if (aColGroupFrame) {
4708 0 : GetColorAndStyle(aColGroupFrame, aSide, tempBorder.style, tempBorder.color, aTableIsLTR, tempBorder.width);
4709 0 : tempBorder.owner = (aAja && !horizontal) ? eAjaColGroupOwner : eColGroupOwner;
4710 : // pass here and below false for aSecondIsHorizontal as it is only used for corner calculations.
4711 0 : border = CompareBorders(!CELL_CORNER, border, tempBorder, false);
4712 0 : if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) {
4713 0 : return border;
4714 : }
4715 : }
4716 : // see if the col is dominant
4717 0 : if (aColFrame) {
4718 0 : GetColorAndStyle(aColFrame, aSide, tempBorder.style, tempBorder.color, aTableIsLTR, tempBorder.width);
4719 0 : tempBorder.owner = (aAja && !horizontal) ? eAjaColOwner : eColOwner;
4720 0 : border = CompareBorders(!CELL_CORNER, border, tempBorder, false);
4721 0 : if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) {
4722 0 : return border;
4723 : }
4724 : }
4725 : // see if the rowgroup is dominant
4726 0 : if (aRowGroupFrame) {
4727 0 : GetColorAndStyle(aRowGroupFrame, aSide, tempBorder.style, tempBorder.color, aTableIsLTR, tempBorder.width);
4728 0 : tempBorder.owner = (aAja && horizontal) ? eAjaRowGroupOwner : eRowGroupOwner;
4729 0 : border = CompareBorders(!CELL_CORNER, border, tempBorder, false);
4730 0 : if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) {
4731 0 : return border;
4732 : }
4733 : }
4734 : // see if the row is dominant
4735 0 : if (aRowFrame) {
4736 0 : GetColorAndStyle(aRowFrame, aSide, tempBorder.style, tempBorder.color, aTableIsLTR, tempBorder.width);
4737 0 : tempBorder.owner = (aAja && horizontal) ? eAjaRowOwner : eRowOwner;
4738 0 : border = CompareBorders(!CELL_CORNER, border, tempBorder, false);
4739 0 : if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) {
4740 0 : return border;
4741 : }
4742 : }
4743 : // see if the cell is dominant
4744 0 : if (aCellFrame) {
4745 0 : GetColorAndStyle(aCellFrame, aSide, tempBorder.style, tempBorder.color, aTableIsLTR, tempBorder.width);
4746 0 : tempBorder.owner = (aAja) ? eAjaCellOwner : eCellOwner;
4747 0 : border = CompareBorders(!CELL_CORNER, border, tempBorder, false);
4748 : }
4749 0 : return border;
4750 : }
4751 :
4752 : static bool
4753 0 : Perpendicular(mozilla::css::Side aSide1,
4754 : mozilla::css::Side aSide2)
4755 : {
4756 0 : switch (aSide1) {
4757 : case NS_SIDE_TOP:
4758 0 : return (NS_SIDE_BOTTOM != aSide2);
4759 : case NS_SIDE_RIGHT:
4760 0 : return (NS_SIDE_LEFT != aSide2);
4761 : case NS_SIDE_BOTTOM:
4762 0 : return (NS_SIDE_TOP != aSide2);
4763 : default: // NS_SIDE_LEFT
4764 0 : return (NS_SIDE_RIGHT != aSide2);
4765 : }
4766 : }
4767 :
4768 : // XXX allocate this as number-of-cols+1 instead of number-of-cols+1 * number-of-rows+1
4769 : struct BCCornerInfo
4770 : {
4771 0 : BCCornerInfo() { ownerColor = 0; ownerWidth = subWidth = ownerElem = subSide =
4772 0 : subElem = hasDashDot = numSegs = bevel = 0; ownerSide = NS_SIDE_TOP;
4773 0 : ownerStyle = 0xFF; subStyle = NS_STYLE_BORDER_STYLE_SOLID; }
4774 : void Set(mozilla::css::Side aSide,
4775 : BCCellBorder border);
4776 :
4777 : void Update(mozilla::css::Side aSide,
4778 : BCCellBorder border);
4779 :
4780 : nscolor ownerColor; // color of borderOwner
4781 : PRUint16 ownerWidth; // pixel width of borderOwner
4782 : PRUint16 subWidth; // pixel width of the largest border intersecting the border perpendicular
4783 : // to ownerSide
4784 : PRUint32 ownerSide:2; // mozilla::css::Side (e.g NS_SIDE_TOP, NS_SIDE_RIGHT, etc) of the border
4785 : // owning the corner relative to the corner
4786 : PRUint32 ownerElem:3; // elem type (e.g. eTable, eGroup, etc) owning the corner
4787 : PRUint32 ownerStyle:8; // border style of ownerElem
4788 : PRUint32 subSide:2; // side of border with subWidth relative to the corner
4789 : PRUint32 subElem:3; // elem type (e.g. eTable, eGroup, etc) of sub owner
4790 : PRUint32 subStyle:8; // border style of subElem
4791 : PRUint32 hasDashDot:1; // does a dashed, dotted segment enter the corner, they cannot be beveled
4792 : PRUint32 numSegs:3; // number of segments entering corner
4793 : PRUint32 bevel:1; // is the corner beveled (uses the above two fields together with subWidth)
4794 : PRUint32 unused:1;
4795 : };
4796 :
4797 : void
4798 0 : BCCornerInfo::Set(mozilla::css::Side aSide,
4799 : BCCellBorder aBorder)
4800 : {
4801 0 : ownerElem = aBorder.owner;
4802 0 : ownerStyle = aBorder.style;
4803 0 : ownerWidth = aBorder.width;
4804 0 : ownerColor = aBorder.color;
4805 0 : ownerSide = aSide;
4806 0 : hasDashDot = 0;
4807 0 : numSegs = 0;
4808 0 : if (aBorder.width > 0) {
4809 0 : numSegs++;
4810 : hasDashDot = (NS_STYLE_BORDER_STYLE_DASHED == aBorder.style) ||
4811 0 : (NS_STYLE_BORDER_STYLE_DOTTED == aBorder.style);
4812 : }
4813 0 : bevel = 0;
4814 0 : subWidth = 0;
4815 : // the following will get set later
4816 0 : subSide = ((aSide == NS_SIDE_LEFT) || (aSide == NS_SIDE_RIGHT)) ? NS_SIDE_TOP : NS_SIDE_LEFT;
4817 0 : subElem = eTableOwner;
4818 0 : subStyle = NS_STYLE_BORDER_STYLE_SOLID;
4819 0 : }
4820 :
4821 : void
4822 0 : BCCornerInfo::Update(mozilla::css::Side aSide,
4823 : BCCellBorder aBorder)
4824 : {
4825 0 : bool existingWins = false;
4826 0 : if (0xFF == ownerStyle) { // initial value indiating that it hasn't been set yet
4827 0 : Set(aSide, aBorder);
4828 : }
4829 : else {
4830 0 : bool horizontal = (NS_SIDE_LEFT == aSide) || (NS_SIDE_RIGHT == aSide); // relative to the corner
4831 0 : BCCellBorder oldBorder, tempBorder;
4832 0 : oldBorder.owner = (BCBorderOwner) ownerElem;
4833 0 : oldBorder.style = ownerStyle;
4834 0 : oldBorder.width = ownerWidth;
4835 0 : oldBorder.color = ownerColor;
4836 :
4837 0 : mozilla::css::Side oldSide = mozilla::css::Side(ownerSide);
4838 :
4839 0 : tempBorder = CompareBorders(CELL_CORNER, oldBorder, aBorder, horizontal, &existingWins);
4840 :
4841 0 : ownerElem = tempBorder.owner;
4842 0 : ownerStyle = tempBorder.style;
4843 0 : ownerWidth = tempBorder.width;
4844 0 : ownerColor = tempBorder.color;
4845 0 : if (existingWins) { // existing corner is dominant
4846 0 : if (::Perpendicular(mozilla::css::Side(ownerSide), aSide)) {
4847 : // see if the new sub info replaces the old
4848 0 : BCCellBorder subBorder;
4849 0 : subBorder.owner = (BCBorderOwner) subElem;
4850 0 : subBorder.style = subStyle;
4851 0 : subBorder.width = subWidth;
4852 0 : subBorder.color = 0; // we are not interested in subBorder color
4853 : bool firstWins;
4854 :
4855 0 : tempBorder = CompareBorders(CELL_CORNER, subBorder, aBorder, horizontal, &firstWins);
4856 :
4857 0 : subElem = tempBorder.owner;
4858 0 : subStyle = tempBorder.style;
4859 0 : subWidth = tempBorder.width;
4860 0 : if (!firstWins) {
4861 0 : subSide = aSide;
4862 : }
4863 : }
4864 : }
4865 : else { // input args are dominant
4866 0 : ownerSide = aSide;
4867 0 : if (::Perpendicular(oldSide, mozilla::css::Side(ownerSide))) {
4868 0 : subElem = oldBorder.owner;
4869 0 : subStyle = oldBorder.style;
4870 0 : subWidth = oldBorder.width;
4871 0 : subSide = oldSide;
4872 : }
4873 : }
4874 0 : if (aBorder.width > 0) {
4875 0 : numSegs++;
4876 0 : if (!hasDashDot && ((NS_STYLE_BORDER_STYLE_DASHED == aBorder.style) ||
4877 : (NS_STYLE_BORDER_STYLE_DOTTED == aBorder.style))) {
4878 0 : hasDashDot = 1;
4879 : }
4880 : }
4881 :
4882 : // bevel the corner if only two perpendicular non dashed/dotted segments enter the corner
4883 0 : bevel = (2 == numSegs) && (subWidth > 1) && (0 == hasDashDot);
4884 : }
4885 0 : }
4886 :
4887 : struct BCCorners
4888 : {
4889 : BCCorners(PRInt32 aNumCorners,
4890 : PRInt32 aStartIndex);
4891 :
4892 0 : ~BCCorners() { delete [] corners; }
4893 :
4894 0 : BCCornerInfo& operator [](PRInt32 i) const
4895 0 : { NS_ASSERTION((i >= startIndex) && (i <= endIndex), "program error");
4896 0 : return corners[clamped(i, startIndex, endIndex) - startIndex]; }
4897 :
4898 : PRInt32 startIndex;
4899 : PRInt32 endIndex;
4900 : BCCornerInfo* corners;
4901 : };
4902 :
4903 0 : BCCorners::BCCorners(PRInt32 aNumCorners,
4904 : PRInt32 aStartIndex)
4905 : {
4906 0 : NS_ASSERTION((aNumCorners > 0) && (aStartIndex >= 0), "program error");
4907 0 : startIndex = aStartIndex;
4908 0 : endIndex = aStartIndex + aNumCorners - 1;
4909 0 : corners = new BCCornerInfo[aNumCorners];
4910 0 : }
4911 :
4912 :
4913 : struct BCCellBorders
4914 : {
4915 : BCCellBorders(PRInt32 aNumBorders,
4916 : PRInt32 aStartIndex);
4917 :
4918 0 : ~BCCellBorders() { delete [] borders; }
4919 :
4920 0 : BCCellBorder& operator [](PRInt32 i) const
4921 0 : { NS_ASSERTION((i >= startIndex) && (i <= endIndex), "program error");
4922 0 : return borders[clamped(i, startIndex, endIndex) - startIndex]; }
4923 :
4924 : PRInt32 startIndex;
4925 : PRInt32 endIndex;
4926 : BCCellBorder* borders;
4927 : };
4928 :
4929 0 : BCCellBorders::BCCellBorders(PRInt32 aNumBorders,
4930 : PRInt32 aStartIndex)
4931 : {
4932 0 : NS_ASSERTION((aNumBorders > 0) && (aStartIndex >= 0), "program error");
4933 0 : startIndex = aStartIndex;
4934 0 : endIndex = aStartIndex + aNumBorders - 1;
4935 0 : borders = new BCCellBorder[aNumBorders];
4936 0 : }
4937 :
4938 : // this function sets the new border properties and returns true if the border
4939 : // segment will start a new segment and not be accumulated into the previous
4940 : // segment.
4941 : static bool
4942 0 : SetBorder(const BCCellBorder& aNewBorder,
4943 : BCCellBorder& aBorder)
4944 : {
4945 : bool changed = (aNewBorder.style != aBorder.style) ||
4946 : (aNewBorder.width != aBorder.width) ||
4947 0 : (aNewBorder.color != aBorder.color);
4948 0 : aBorder.color = aNewBorder.color;
4949 0 : aBorder.width = aNewBorder.width;
4950 0 : aBorder.style = aNewBorder.style;
4951 0 : aBorder.owner = aNewBorder.owner;
4952 :
4953 0 : return changed;
4954 : }
4955 :
4956 : // this function will set the horizontal border. It will return true if the
4957 : // existing segment will not be continued. Having a vertical owner of a corner
4958 : // should also start a new segment.
4959 : static bool
4960 0 : SetHorBorder(const BCCellBorder& aNewBorder,
4961 : const BCCornerInfo& aCorner,
4962 : BCCellBorder& aBorder)
4963 : {
4964 0 : bool startSeg = ::SetBorder(aNewBorder, aBorder);
4965 0 : if (!startSeg) {
4966 0 : startSeg = ((NS_SIDE_LEFT != aCorner.ownerSide) && (NS_SIDE_RIGHT != aCorner.ownerSide));
4967 : }
4968 0 : return startSeg;
4969 : }
4970 :
4971 : // Make the damage area larger on the top and bottom by at least one row and on the left and right
4972 : // at least one column. This is done so that adjacent elements are part of the border calculations.
4973 : // The extra segments and borders outside the actual damage area will not be updated in the cell map,
4974 : // because they in turn would need info from adjacent segments outside the damage area to be accurate.
4975 : void
4976 0 : nsTableFrame::ExpandBCDamageArea(nsIntRect& aRect) const
4977 : {
4978 0 : PRInt32 numRows = GetRowCount();
4979 0 : PRInt32 numCols = GetColCount();
4980 :
4981 0 : PRInt32 dStartX = aRect.x;
4982 0 : PRInt32 dEndX = aRect.XMost() - 1;
4983 0 : PRInt32 dStartY = aRect.y;
4984 0 : PRInt32 dEndY = aRect.YMost() - 1;
4985 :
4986 : // expand the damage area in each direction
4987 0 : if (dStartX > 0) {
4988 0 : dStartX--;
4989 : }
4990 0 : if (dEndX < (numCols - 1)) {
4991 0 : dEndX++;
4992 : }
4993 0 : if (dStartY > 0) {
4994 0 : dStartY--;
4995 : }
4996 0 : if (dEndY < (numRows - 1)) {
4997 0 : dEndY++;
4998 : }
4999 : // Check the damage area so that there are no cells spanning in or out. If there are any then
5000 : // make the damage area as big as the table, similarly to the way the cell map decides whether
5001 : // to rebuild versus expand. This could be optimized to expand to the smallest area that contains
5002 : // no spanners, but it may not be worth the effort in general, and it would need to be done in the
5003 : // cell map as well.
5004 0 : bool haveSpanner = false;
5005 0 : if ((dStartX > 0) || (dEndX < (numCols - 1)) || (dStartY > 0) || (dEndY < (numRows - 1))) {
5006 0 : nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT0();
5007 : // Get the ordered row groups
5008 0 : RowGroupArray rowGroups;
5009 0 : OrderRowGroups(rowGroups);
5010 :
5011 : // Scope outside loop to be used as hint.
5012 0 : nsCellMap* cellMap = nsnull;
5013 0 : for (PRUint32 rgX = 0; rgX < rowGroups.Length(); rgX++) {
5014 0 : nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
5015 0 : PRInt32 rgStartY = rgFrame->GetStartRowIndex();
5016 0 : PRInt32 rgEndY = rgStartY + rgFrame->GetRowCount() - 1;
5017 0 : if (dEndY < rgStartY)
5018 0 : break;
5019 0 : cellMap = tableCellMap->GetMapFor(rgFrame, cellMap);
5020 0 : if (!cellMap) ABORT0();
5021 : // check for spanners from above and below
5022 0 : if ((dStartY > 0) && (dStartY >= rgStartY) && (dStartY <= rgEndY)) {
5023 0 : if (PRUint32(dStartY - rgStartY) >= cellMap->mRows.Length())
5024 0 : ABORT0();
5025 : const nsCellMap::CellDataArray& row =
5026 0 : cellMap->mRows[dStartY - rgStartY];
5027 0 : for (PRInt32 x = dStartX; x <= dEndX; x++) {
5028 0 : CellData* cellData = row.SafeElementAt(x);
5029 0 : if (cellData && (cellData->IsRowSpan())) {
5030 0 : haveSpanner = true;
5031 0 : break;
5032 : }
5033 : }
5034 0 : if (dEndY < rgEndY) {
5035 0 : if (PRUint32(dEndY + 1 - rgStartY) >= cellMap->mRows.Length())
5036 0 : ABORT0();
5037 : const nsCellMap::CellDataArray& row2 =
5038 0 : cellMap->mRows[dEndY + 1 - rgStartY];
5039 0 : for (PRInt32 x = dStartX; x <= dEndX; x++) {
5040 0 : CellData* cellData = row2.SafeElementAt(x);
5041 0 : if (cellData && (cellData->IsRowSpan())) {
5042 0 : haveSpanner = true;
5043 0 : break;
5044 : }
5045 : }
5046 : }
5047 : }
5048 : // check for spanners on the left and right
5049 0 : PRInt32 iterStartY = -1;
5050 0 : PRInt32 iterEndY = -1;
5051 0 : if ((dStartY >= rgStartY) && (dStartY <= rgEndY)) {
5052 : // the damage area starts in the row group
5053 0 : iterStartY = dStartY;
5054 0 : iterEndY = NS_MIN(dEndY, rgEndY);
5055 : }
5056 0 : else if ((dEndY >= rgStartY) && (dEndY <= rgEndY)) {
5057 : // the damage area ends in the row group
5058 0 : iterStartY = rgStartY;
5059 0 : iterEndY = dEndY;
5060 : }
5061 0 : else if ((rgStartY >= dStartY) && (rgEndY <= dEndY)) {
5062 : // the damage area contains the row group
5063 0 : iterStartY = rgStartY;
5064 0 : iterEndY = rgEndY;
5065 : }
5066 0 : if ((iterStartY >= 0) && (iterEndY >= 0)) {
5067 0 : for (PRInt32 y = iterStartY; y <= iterEndY; y++) {
5068 0 : if (PRUint32(y - rgStartY) >= cellMap->mRows.Length())
5069 0 : ABORT0();
5070 : const nsCellMap::CellDataArray& row =
5071 0 : cellMap->mRows[y - rgStartY];
5072 0 : CellData* cellData = row.SafeElementAt(dStartX);
5073 0 : if (cellData && (cellData->IsColSpan())) {
5074 0 : haveSpanner = true;
5075 0 : break;
5076 : }
5077 0 : if (dEndX < (numCols - 1)) {
5078 0 : cellData = row.SafeElementAt(dEndX + 1);
5079 0 : if (cellData && (cellData->IsColSpan())) {
5080 0 : haveSpanner = true;
5081 0 : break;
5082 : }
5083 : }
5084 : }
5085 : }
5086 : }
5087 : }
5088 0 : if (haveSpanner) {
5089 : // make the damage area the whole table
5090 0 : aRect.x = 0;
5091 0 : aRect.y = 0;
5092 0 : aRect.width = numCols;
5093 0 : aRect.height = numRows;
5094 : }
5095 : else {
5096 0 : aRect.x = dStartX;
5097 0 : aRect.y = dStartY;
5098 0 : aRect.width = 1 + dEndX - dStartX;
5099 0 : aRect.height = 1 + dEndY - dStartY;
5100 : }
5101 : }
5102 :
5103 :
5104 : #define ADJACENT true
5105 : #define HORIZONTAL true
5106 :
5107 : void
5108 0 : BCMapCellInfo::SetTableTopLeftContBCBorder()
5109 : {
5110 0 : BCCellBorder currentBorder;
5111 : //calculate continuous top first row & rowgroup border: special case
5112 : //because it must include the table in the collapse
5113 0 : if (mTopRow) {
5114 : currentBorder = CompareBorders(mTableFrame, nsnull, nsnull, mRowGroup,
5115 : mTopRow, nsnull, mTableIsLTR,
5116 0 : NS_SIDE_TOP, !ADJACENT);
5117 0 : mTopRow->SetContinuousBCBorderWidth(NS_SIDE_TOP, currentBorder.width);
5118 : }
5119 0 : if (mCgAtRight && mColGroup) {
5120 : //calculate continuous top colgroup border once per colgroup
5121 : currentBorder = CompareBorders(mTableFrame, mColGroup, nsnull, mRowGroup,
5122 : mTopRow, nsnull, mTableIsLTR,
5123 0 : NS_SIDE_TOP, !ADJACENT);
5124 0 : mColGroup->SetContinuousBCBorderWidth(NS_SIDE_TOP, currentBorder.width);
5125 : }
5126 0 : if (0 == mColIndex) {
5127 : currentBorder = CompareBorders(mTableFrame, mColGroup, mLeftCol, nsnull,
5128 : nsnull, nsnull, mTableIsLTR, NS_SIDE_LEFT,
5129 0 : !ADJACENT);
5130 0 : mTableFrame->SetContinuousLeftBCBorderWidth(currentBorder.width);
5131 : }
5132 0 : }
5133 :
5134 : void
5135 0 : BCMapCellInfo::SetRowGroupLeftContBCBorder()
5136 : {
5137 0 : BCCellBorder currentBorder;
5138 : //get row group continuous borders
5139 0 : if (mRgAtBottom && mRowGroup) { //once per row group, so check for bottom
5140 : currentBorder = CompareBorders(mTableFrame, mColGroup, mLeftCol, mRowGroup,
5141 : nsnull, nsnull, mTableIsLTR, NS_SIDE_LEFT,
5142 0 : !ADJACENT);
5143 0 : mRowGroup->SetContinuousBCBorderWidth(mStartSide, currentBorder.width);
5144 : }
5145 0 : }
5146 :
5147 : void
5148 0 : BCMapCellInfo::SetRowGroupRightContBCBorder()
5149 : {
5150 0 : BCCellBorder currentBorder;
5151 : //get row group continuous borders
5152 0 : if (mRgAtBottom && mRowGroup) { //once per mRowGroup, so check for bottom
5153 : currentBorder = CompareBorders(mTableFrame, mColGroup, mRightCol, mRowGroup,
5154 : nsnull, nsnull, mTableIsLTR, NS_SIDE_RIGHT,
5155 0 : ADJACENT);
5156 0 : mRowGroup->SetContinuousBCBorderWidth(mEndSide, currentBorder.width);
5157 : }
5158 0 : }
5159 :
5160 : void
5161 0 : BCMapCellInfo::SetColumnTopRightContBCBorder()
5162 : {
5163 0 : BCCellBorder currentBorder;
5164 : //calculate column continuous borders
5165 : //we only need to do this once, so we'll do it only on the first row
5166 : currentBorder = CompareBorders(mTableFrame, mCurrentColGroupFrame,
5167 : mCurrentColFrame, mRowGroup, mTopRow, nsnull,
5168 0 : mTableIsLTR, NS_SIDE_TOP, !ADJACENT);
5169 : ((nsTableColFrame*) mCurrentColFrame)->SetContinuousBCBorderWidth(NS_SIDE_TOP,
5170 0 : currentBorder.width);
5171 0 : if (mNumTableCols == GetCellEndColIndex() + 1) {
5172 : currentBorder = CompareBorders(mTableFrame, mCurrentColGroupFrame,
5173 : mCurrentColFrame, nsnull, nsnull, nsnull,
5174 0 : mTableIsLTR, NS_SIDE_RIGHT, !ADJACENT);
5175 : }
5176 : else {
5177 : currentBorder = CompareBorders(nsnull, mCurrentColGroupFrame,
5178 : mCurrentColFrame, nsnull,nsnull, nsnull,
5179 0 : mTableIsLTR, NS_SIDE_RIGHT, !ADJACENT);
5180 : }
5181 : mCurrentColFrame->SetContinuousBCBorderWidth(NS_SIDE_RIGHT,
5182 0 : currentBorder.width);
5183 0 : }
5184 :
5185 : void
5186 0 : BCMapCellInfo::SetColumnBottomContBCBorder()
5187 : {
5188 0 : BCCellBorder currentBorder;
5189 : //get col continuous border
5190 : currentBorder = CompareBorders(mTableFrame, mCurrentColGroupFrame,
5191 : mCurrentColFrame, mRowGroup, mBottomRow,
5192 0 : nsnull, mTableIsLTR, NS_SIDE_BOTTOM, ADJACENT);
5193 : mCurrentColFrame->SetContinuousBCBorderWidth(NS_SIDE_BOTTOM,
5194 0 : currentBorder.width);
5195 0 : }
5196 :
5197 : void
5198 0 : BCMapCellInfo::SetColGroupBottomContBCBorder()
5199 : {
5200 0 : BCCellBorder currentBorder;
5201 0 : if (mColGroup) {
5202 : currentBorder = CompareBorders(mTableFrame, mColGroup, nsnull, mRowGroup,
5203 : mBottomRow, nsnull, mTableIsLTR,
5204 0 : NS_SIDE_BOTTOM, ADJACENT);
5205 0 : mColGroup->SetContinuousBCBorderWidth(NS_SIDE_BOTTOM, currentBorder.width);
5206 : }
5207 0 : }
5208 :
5209 : void
5210 0 : BCMapCellInfo::SetRowGroupBottomContBCBorder()
5211 : {
5212 0 : BCCellBorder currentBorder;
5213 0 : if (mRowGroup) {
5214 : currentBorder = CompareBorders(mTableFrame, nsnull, nsnull, mRowGroup,
5215 : mBottomRow, nsnull, mTableIsLTR,
5216 0 : NS_SIDE_BOTTOM, ADJACENT);
5217 0 : mRowGroup->SetContinuousBCBorderWidth(NS_SIDE_BOTTOM, currentBorder.width);
5218 : }
5219 0 : }
5220 :
5221 : void
5222 0 : BCMapCellInfo::SetInnerRowGroupBottomContBCBorder(const nsIFrame* aNextRowGroup,
5223 : nsTableRowFrame* aNextRow)
5224 : {
5225 0 : BCCellBorder currentBorder, adjacentBorder;
5226 :
5227 0 : const nsIFrame* rowgroup = (mRgAtBottom) ? mRowGroup : nsnull;
5228 : currentBorder = CompareBorders(nsnull, nsnull, nsnull, rowgroup, mBottomRow,
5229 0 : nsnull, mTableIsLTR, NS_SIDE_BOTTOM, ADJACENT);
5230 :
5231 : adjacentBorder = CompareBorders(nsnull, nsnull, nsnull, aNextRowGroup,
5232 : aNextRow, nsnull, mTableIsLTR, NS_SIDE_TOP,
5233 0 : !ADJACENT);
5234 : currentBorder = CompareBorders(false, currentBorder, adjacentBorder,
5235 0 : HORIZONTAL);
5236 0 : if (aNextRow) {
5237 0 : aNextRow->SetContinuousBCBorderWidth(NS_SIDE_TOP, currentBorder.width);
5238 : }
5239 0 : if (mRgAtBottom && mRowGroup) {
5240 0 : mRowGroup->SetContinuousBCBorderWidth(NS_SIDE_BOTTOM, currentBorder.width);
5241 : }
5242 0 : }
5243 :
5244 : void
5245 0 : BCMapCellInfo::SetRowLeftContBCBorder()
5246 : {
5247 : //get row continuous borders
5248 0 : if (mCurrentRowFrame) {
5249 0 : BCCellBorder currentBorder;
5250 : currentBorder = CompareBorders(mTableFrame, mColGroup, mLeftCol, mRowGroup,
5251 : mCurrentRowFrame, nsnull, mTableIsLTR,
5252 0 : NS_SIDE_LEFT, !ADJACENT);
5253 : mCurrentRowFrame->SetContinuousBCBorderWidth(mStartSide,
5254 0 : currentBorder.width);
5255 : }
5256 0 : }
5257 :
5258 : void
5259 0 : BCMapCellInfo::SetRowRightContBCBorder()
5260 : {
5261 0 : if (mCurrentRowFrame) {
5262 0 : BCCellBorder currentBorder;
5263 : currentBorder = CompareBorders(mTableFrame, mColGroup, mRightCol, mRowGroup,
5264 : mCurrentRowFrame, nsnull, mTableIsLTR,
5265 0 : NS_SIDE_RIGHT, ADJACENT);
5266 : mCurrentRowFrame->SetContinuousBCBorderWidth(mEndSide,
5267 0 : currentBorder.width);
5268 : }
5269 0 : }
5270 : void
5271 0 : BCMapCellInfo::SetTableTopBorderWidth(BCPixelSize aWidth)
5272 : {
5273 0 : mTableBCData->mTopBorderWidth = NS_MAX(mTableBCData->mTopBorderWidth, aWidth);
5274 0 : }
5275 :
5276 : void
5277 0 : BCMapCellInfo::SetTableLeftBorderWidth(PRInt32 aRowY, BCPixelSize aWidth)
5278 : {
5279 : // update the left/right first cell border
5280 0 : if (aRowY == 0) {
5281 0 : if (mTableIsLTR) {
5282 0 : mTableBCData->mLeftCellBorderWidth = aWidth;
5283 : }
5284 : else {
5285 0 : mTableBCData->mRightCellBorderWidth = aWidth;
5286 : }
5287 : }
5288 : mTableBCData->mLeftBorderWidth = NS_MAX(mTableBCData->mLeftBorderWidth,
5289 0 : aWidth);
5290 0 : }
5291 :
5292 : void
5293 0 : BCMapCellInfo::SetTableRightBorderWidth(PRInt32 aRowY, BCPixelSize aWidth)
5294 : {
5295 : // update the left/right first cell border
5296 0 : if (aRowY == 0) {
5297 0 : if (mTableIsLTR) {
5298 0 : mTableBCData->mRightCellBorderWidth = aWidth;
5299 : }
5300 : else {
5301 0 : mTableBCData->mLeftCellBorderWidth = aWidth;
5302 : }
5303 : }
5304 : mTableBCData->mRightBorderWidth = NS_MAX(mTableBCData->mRightBorderWidth,
5305 0 : aWidth);
5306 0 : }
5307 :
5308 : void
5309 0 : BCMapCellInfo::SetRightBorderWidths(BCPixelSize aWidth)
5310 : {
5311 : // update the borders of the cells and cols affected
5312 0 : if (mCell) {
5313 : mCell->SetBorderWidth(mEndSide, NS_MAX(aWidth,
5314 0 : mCell->GetBorderWidth(mEndSide)));
5315 : }
5316 0 : if (mRightCol) {
5317 0 : BCPixelSize half = BC_BORDER_LEFT_HALF(aWidth);
5318 : mRightCol->SetRightBorderWidth(NS_MAX(nscoord(half),
5319 0 : mRightCol->GetRightBorderWidth()));
5320 : }
5321 0 : }
5322 :
5323 : void
5324 0 : BCMapCellInfo::SetBottomBorderWidths(BCPixelSize aWidth)
5325 : {
5326 : // update the borders of the affected cells and rows
5327 0 : if (mCell) {
5328 : mCell->SetBorderWidth(NS_SIDE_BOTTOM, NS_MAX(aWidth,
5329 0 : mCell->GetBorderWidth(NS_SIDE_BOTTOM)));
5330 : }
5331 0 : if (mBottomRow) {
5332 0 : BCPixelSize half = BC_BORDER_TOP_HALF(aWidth);
5333 : mBottomRow->SetBottomBCBorderWidth(NS_MAX(nscoord(half),
5334 0 : mBottomRow->GetBottomBCBorderWidth()));
5335 : }
5336 0 : }
5337 : void
5338 0 : BCMapCellInfo::SetTopBorderWidths(BCPixelSize aWidth)
5339 : {
5340 0 : if (mCell) {
5341 : mCell->SetBorderWidth(NS_SIDE_TOP, NS_MAX(aWidth,
5342 0 : mCell->GetBorderWidth(NS_SIDE_TOP)));
5343 : }
5344 0 : if (mTopRow) {
5345 0 : BCPixelSize half = BC_BORDER_BOTTOM_HALF(aWidth);
5346 : mTopRow->SetTopBCBorderWidth(NS_MAX(nscoord(half),
5347 0 : mTopRow->GetTopBCBorderWidth()));
5348 : }
5349 0 : }
5350 : void
5351 0 : BCMapCellInfo::SetLeftBorderWidths(BCPixelSize aWidth)
5352 : {
5353 0 : if (mCell) {
5354 : mCell->SetBorderWidth(mStartSide, NS_MAX(aWidth,
5355 0 : mCell->GetBorderWidth(mStartSide)));
5356 : }
5357 0 : if (mLeftCol) {
5358 0 : BCPixelSize half = BC_BORDER_RIGHT_HALF(aWidth);
5359 : mLeftCol->SetLeftBorderWidth(NS_MAX(nscoord(half),
5360 0 : mLeftCol->GetLeftBorderWidth()));
5361 : }
5362 0 : }
5363 :
5364 : void
5365 0 : BCMapCellInfo::SetTableBottomBorderWidth(BCPixelSize aWidth)
5366 : {
5367 : mTableBCData->mBottomBorderWidth = NS_MAX(mTableBCData->mBottomBorderWidth,
5368 0 : aWidth);
5369 0 : }
5370 :
5371 : void
5372 0 : BCMapCellInfo::SetColumn(PRInt32 aColX)
5373 : {
5374 0 : mCurrentColFrame = mTableFrame->GetColFrame(aColX);
5375 0 : if (!mCurrentColFrame) {
5376 0 : NS_ERROR("null mCurrentColFrame");
5377 : }
5378 : mCurrentColGroupFrame = static_cast<nsTableColGroupFrame*>
5379 0 : (mCurrentColFrame->GetParent());
5380 0 : if (!mCurrentColGroupFrame) {
5381 0 : NS_ERROR("null mCurrentColGroupFrame");
5382 : }
5383 0 : }
5384 :
5385 : void
5386 0 : BCMapCellInfo::IncrementRow(bool aResetToTopRowOfCell)
5387 : {
5388 : mCurrentRowFrame = (aResetToTopRowOfCell) ? mTopRow :
5389 0 : mCurrentRowFrame->GetNextRow();
5390 0 : }
5391 :
5392 : BCCellBorder
5393 0 : BCMapCellInfo::GetTopEdgeBorder()
5394 : {
5395 : return CompareBorders(mTableFrame, mCurrentColGroupFrame, mCurrentColFrame,
5396 : mRowGroup, mTopRow, mCell, mTableIsLTR, NS_SIDE_TOP,
5397 0 : !ADJACENT);
5398 : }
5399 :
5400 : BCCellBorder
5401 0 : BCMapCellInfo::GetBottomEdgeBorder()
5402 : {
5403 : return CompareBorders(mTableFrame, mCurrentColGroupFrame, mCurrentColFrame,
5404 : mRowGroup, mBottomRow, mCell, mTableIsLTR,
5405 0 : NS_SIDE_BOTTOM, ADJACENT);
5406 : }
5407 : BCCellBorder
5408 0 : BCMapCellInfo::GetLeftEdgeBorder()
5409 : {
5410 : return CompareBorders(mTableFrame, mColGroup, mLeftCol, mRowGroup,
5411 : mCurrentRowFrame, mCell, mTableIsLTR, NS_SIDE_LEFT,
5412 0 : !ADJACENT);
5413 : }
5414 : BCCellBorder
5415 0 : BCMapCellInfo::GetRightEdgeBorder()
5416 : {
5417 : return CompareBorders(mTableFrame, mColGroup, mRightCol, mRowGroup,
5418 : mCurrentRowFrame, mCell, mTableIsLTR, NS_SIDE_RIGHT,
5419 0 : ADJACENT);
5420 : }
5421 : BCCellBorder
5422 0 : BCMapCellInfo::GetRightInternalBorder()
5423 : {
5424 0 : const nsIFrame* cg = (mCgAtRight) ? mColGroup : nsnull;
5425 : return CompareBorders(nsnull, cg, mRightCol, nsnull, nsnull, mCell,
5426 0 : mTableIsLTR, NS_SIDE_RIGHT, ADJACENT);
5427 : }
5428 :
5429 : BCCellBorder
5430 0 : BCMapCellInfo::GetLeftInternalBorder()
5431 : {
5432 0 : const nsIFrame* cg = (mCgAtLeft) ? mColGroup : nsnull;
5433 : return CompareBorders(nsnull, cg, mLeftCol, nsnull, nsnull, mCell,
5434 0 : mTableIsLTR, NS_SIDE_LEFT, !ADJACENT);
5435 : }
5436 :
5437 : BCCellBorder
5438 0 : BCMapCellInfo::GetBottomInternalBorder()
5439 : {
5440 0 : const nsIFrame* rg = (mRgAtBottom) ? mRowGroup : nsnull;
5441 : return CompareBorders(nsnull, nsnull, nsnull, rg, mBottomRow, mCell,
5442 0 : mTableIsLTR, NS_SIDE_BOTTOM, ADJACENT);
5443 : }
5444 :
5445 : BCCellBorder
5446 0 : BCMapCellInfo::GetTopInternalBorder()
5447 : {
5448 0 : const nsIFrame* rg = (mRgAtTop) ? mRowGroup : nsnull;
5449 : return CompareBorders(nsnull, nsnull, nsnull, rg, mTopRow, mCell,
5450 0 : mTableIsLTR, NS_SIDE_TOP, !ADJACENT);
5451 : }
5452 :
5453 : /* Here is the order for storing border edges in the cell map as a cell is processed. There are
5454 : n=colspan top and bottom border edges per cell and n=rowspan left and right border edges per cell.
5455 :
5456 : 1) On the top edge of the table, store the top edge. Never store the top edge otherwise, since
5457 : a bottom edge from a cell above will take care of it.
5458 : 2) On the left edge of the table, store the left edge. Never store the left edge othewise, since
5459 : a right edge from a cell to the left will take care of it.
5460 : 3) Store the right edge (or edges if a row span)
5461 : 4) Store the bottom edge (or edges if a col span)
5462 :
5463 : Since corners are computed with only an array of BCCornerInfo indexed by the number-of-cols, corner
5464 : calculations are somewhat complicated. Using an array with number-of-rows * number-of-col entries
5465 : would simplify this, but at an extra in memory cost of nearly 12 bytes per cell map entry. Collapsing
5466 : borders already have about an extra 8 byte per cell map entry overhead (this could be
5467 : reduced to 4 bytes if we are willing to not store border widths in nsTableCellFrame), Here are the
5468 : rules in priority order for storing cornes in the cell map as a cell is processed. top-left means the
5469 : left endpoint of the border edge on the top of the cell. There are n=colspan top and bottom border
5470 : edges per cell and n=rowspan left and right border edges per cell.
5471 :
5472 : 1) On the top edge of the table, store the top-left corner, unless on the left edge of the table.
5473 : Never store the top-right corner, since it will get stored as a right-top corner.
5474 : 2) On the left edge of the table, store the left-top corner. Never store the left-bottom corner,
5475 : since it will get stored as a bottom-left corner.
5476 : 3) Store the right-top corner if (a) it is the top right corner of the table or (b) it is not on
5477 : the top edge of the table. Never store the right-bottom corner since it will get stored as a
5478 : bottom-right corner.
5479 : 4) Store the bottom-right corner, if it is the bottom right corner of the table. Never store it
5480 : otherwise, since it will get stored as either a right-top corner by a cell below or
5481 : a bottom-left corner from a cell to the right.
5482 : 5) Store the bottom-left corner, if (a) on the bottom edge of the table or (b) if the left edge hits
5483 : the top side of a colspan in its interior. Never store the corner otherwise, since it will
5484 : get stored as a right-top corner by a cell from below.
5485 :
5486 : XXX the BC-RTL hack - The correct fix would be a rewrite as described in bug 203686.
5487 : In order to draw borders in rtl conditions somehow correct, the existing structure which relies
5488 : heavily on the assumption that the next cell sibling will be on the right side, has been modified.
5489 : We flip the border during painting and during style lookup. Look for tableIsLTR for places where
5490 : the flipping is done.
5491 : */
5492 :
5493 :
5494 :
5495 : // Calc the dominant border at every cell edge and corner within the current damage area
5496 : void
5497 0 : nsTableFrame::CalcBCBorders()
5498 : {
5499 0 : NS_ASSERTION(IsBorderCollapse(),
5500 : "calling CalcBCBorders on separated-border table");
5501 0 : nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT0();
5502 0 : PRInt32 numRows = GetRowCount();
5503 0 : PRInt32 numCols = GetColCount();
5504 0 : if (!numRows || !numCols)
5505 0 : return; // nothing to do
5506 :
5507 : // Get the property holding the table damage area and border widths
5508 0 : BCPropertyData* propData = GetBCProperty();
5509 0 : if (!propData) ABORT0();
5510 :
5511 :
5512 :
5513 : // calculate an expanded damage area
5514 0 : nsIntRect damageArea(propData->mDamageArea);
5515 0 : ExpandBCDamageArea(damageArea);
5516 :
5517 : // segments that are on the table border edges need
5518 : // to be initialized only once
5519 : bool tableBorderReset[4];
5520 0 : for (PRUint32 sideX = NS_SIDE_TOP; sideX <= NS_SIDE_LEFT; sideX++) {
5521 0 : tableBorderReset[sideX] = false;
5522 : }
5523 :
5524 : // vertical borders indexed in x-direction (cols)
5525 0 : BCCellBorders lastVerBorders(damageArea.width + 1, damageArea.x);
5526 0 : if (!lastVerBorders.borders) ABORT0();
5527 0 : BCCellBorder lastTopBorder, lastBottomBorder;
5528 : // horizontal borders indexed in x-direction (cols)
5529 0 : BCCellBorders lastBottomBorders(damageArea.width + 1, damageArea.x);
5530 0 : if (!lastBottomBorders.borders) ABORT0();
5531 : bool startSeg;
5532 0 : bool gotRowBorder = false;
5533 :
5534 0 : BCMapCellInfo info(this), ajaInfo(this);
5535 :
5536 0 : BCCellBorder currentBorder, adjacentBorder;
5537 0 : BCCorners topCorners(damageArea.width + 1, damageArea.x);
5538 0 : if (!topCorners.corners) ABORT0();
5539 0 : BCCorners bottomCorners(damageArea.width + 1, damageArea.x);
5540 0 : if (!bottomCorners.corners) ABORT0();
5541 :
5542 0 : BCMapCellIterator iter(this, damageArea);
5543 0 : for (iter.First(info); !iter.mAtEnd; iter.Next(info)) {
5544 : // see if lastTopBorder, lastBottomBorder need to be reset
5545 0 : if (iter.IsNewRow()) {
5546 0 : gotRowBorder = false;
5547 0 : lastTopBorder.Reset(info.mRowIndex, info.mRowSpan);
5548 0 : lastBottomBorder.Reset(info.GetCellEndRowIndex() + 1, info.mRowSpan);
5549 : }
5550 0 : else if (info.mColIndex > damageArea.x) {
5551 0 : lastBottomBorder = lastBottomBorders[info.mColIndex - 1];
5552 0 : if (info.mRowIndex >
5553 : (lastBottomBorder.rowIndex - lastBottomBorder.rowSpan)) {
5554 : // the top border's left edge butts against the middle of a rowspan
5555 0 : lastTopBorder.Reset(info.mRowIndex, info.mRowSpan);
5556 : }
5557 0 : if (lastBottomBorder.rowIndex > (info.GetCellEndRowIndex() + 1)) {
5558 : // the bottom border's left edge butts against the middle of a rowspan
5559 0 : lastBottomBorder.Reset(info.GetCellEndRowIndex() + 1, info.mRowSpan);
5560 : }
5561 : }
5562 :
5563 : // find the dominant border considering the cell's top border and the table,
5564 : // row group, row if the border is at the top of the table, otherwise it was
5565 : // processed in a previous row
5566 0 : if (0 == info.mRowIndex) {
5567 0 : if (!tableBorderReset[NS_SIDE_TOP]) {
5568 0 : propData->mTopBorderWidth = 0;
5569 0 : tableBorderReset[NS_SIDE_TOP] = true;
5570 : }
5571 0 : for (PRInt32 colX = info.mColIndex; colX <= info.GetCellEndColIndex();
5572 : colX++) {
5573 0 : info.SetColumn(colX);
5574 0 : currentBorder = info.GetTopEdgeBorder();
5575 : // update/store the top left & top right corners of the seg
5576 0 : BCCornerInfo& tlCorner = topCorners[colX]; // top left
5577 0 : if (0 == colX) {
5578 : // we are on right hand side of the corner
5579 0 : tlCorner.Set(NS_SIDE_RIGHT, currentBorder);
5580 : }
5581 : else {
5582 0 : tlCorner.Update(NS_SIDE_RIGHT, currentBorder);
5583 : tableCellMap->SetBCBorderCorner(eTopLeft, *iter.mCellMap, 0, 0, colX,
5584 : mozilla::css::Side(tlCorner.ownerSide),
5585 : tlCorner.subWidth,
5586 0 : tlCorner.bevel);
5587 : }
5588 0 : topCorners[colX + 1].Set(NS_SIDE_LEFT, currentBorder); // top right
5589 : // update lastTopBorder and see if a new segment starts
5590 0 : startSeg = SetHorBorder(currentBorder, tlCorner, lastTopBorder);
5591 : // store the border segment in the cell map
5592 : tableCellMap->SetBCBorderEdge(NS_SIDE_TOP, *iter.mCellMap, 0, 0, colX,
5593 : 1, currentBorder.owner,
5594 0 : currentBorder.width, startSeg);
5595 :
5596 0 : info.SetTableTopBorderWidth(currentBorder.width);
5597 0 : info.SetTopBorderWidths(currentBorder.width);
5598 0 : info.SetColumnTopRightContBCBorder();
5599 : }
5600 0 : info.SetTableTopLeftContBCBorder();
5601 : }
5602 : else {
5603 : // see if the top border needs to be the start of a segment due to a
5604 : // vertical border owning the corner
5605 0 : if (info.mColIndex > 0) {
5606 0 : BCData& data = info.mCellData->mData;
5607 0 : if (!data.IsTopStart()) {
5608 : mozilla::css::Side cornerSide;
5609 : bool bevel;
5610 0 : data.GetCorner(cornerSide, bevel);
5611 0 : if ((NS_SIDE_TOP == cornerSide) || (NS_SIDE_BOTTOM == cornerSide)) {
5612 0 : data.SetTopStart(true);
5613 : }
5614 : }
5615 : }
5616 : }
5617 :
5618 : // find the dominant border considering the cell's left border and the
5619 : // table, col group, col if the border is at the left of the table,
5620 : // otherwise it was processed in a previous col
5621 0 : if (0 == info.mColIndex) {
5622 0 : if (!tableBorderReset[NS_SIDE_LEFT]) {
5623 0 : propData->mLeftBorderWidth = 0;
5624 0 : tableBorderReset[NS_SIDE_LEFT] = true;
5625 : }
5626 0 : info.mCurrentRowFrame = nsnull;
5627 0 : for (PRInt32 rowY = info.mRowIndex; rowY <= info.GetCellEndRowIndex();
5628 : rowY++) {
5629 0 : info.IncrementRow(rowY == info.mRowIndex);
5630 0 : currentBorder = info.GetLeftEdgeBorder();
5631 0 : BCCornerInfo& tlCorner = (0 == rowY) ? topCorners[0] : bottomCorners[0];
5632 0 : tlCorner.Update(NS_SIDE_BOTTOM, currentBorder);
5633 : tableCellMap->SetBCBorderCorner(eTopLeft, *iter.mCellMap,
5634 : iter.mRowGroupStart, rowY, 0,
5635 : mozilla::css::Side(tlCorner.ownerSide),
5636 : tlCorner.subWidth,
5637 0 : tlCorner.bevel);
5638 0 : bottomCorners[0].Set(NS_SIDE_TOP, currentBorder); // bottom left
5639 :
5640 : // update lastVerBordersBorder and see if a new segment starts
5641 0 : startSeg = SetBorder(currentBorder, lastVerBorders[0]);
5642 : // store the border segment in the cell map
5643 : tableCellMap->SetBCBorderEdge(NS_SIDE_LEFT, *iter.mCellMap,
5644 : iter.mRowGroupStart, rowY, info.mColIndex,
5645 : 1, currentBorder.owner,
5646 0 : currentBorder.width, startSeg);
5647 0 : info.SetTableLeftBorderWidth(rowY , currentBorder.width);
5648 0 : info.SetLeftBorderWidths(currentBorder.width);
5649 0 : info.SetRowLeftContBCBorder();
5650 : }
5651 0 : info.SetRowGroupLeftContBCBorder();
5652 : }
5653 :
5654 : // find the dominant border considering the cell's right border, adjacent
5655 : // cells and the table, row group, row
5656 0 : if (info.mNumTableCols == info.GetCellEndColIndex() + 1) {
5657 : // touches right edge of table
5658 0 : if (!tableBorderReset[NS_SIDE_RIGHT]) {
5659 0 : propData->mRightBorderWidth = 0;
5660 0 : tableBorderReset[NS_SIDE_RIGHT] = true;
5661 : }
5662 0 : info.mCurrentRowFrame = nsnull;
5663 0 : for (PRInt32 rowY = info.mRowIndex; rowY <= info.GetCellEndRowIndex();
5664 : rowY++) {
5665 0 : info.IncrementRow(rowY == info.mRowIndex);
5666 0 : currentBorder = info.GetRightEdgeBorder();
5667 : // update/store the top right & bottom right corners
5668 : BCCornerInfo& trCorner = (0 == rowY) ?
5669 0 : topCorners[info.GetCellEndColIndex() + 1] :
5670 0 : bottomCorners[info.GetCellEndColIndex() + 1];
5671 0 : trCorner.Update(NS_SIDE_BOTTOM, currentBorder); // top right
5672 : tableCellMap->SetBCBorderCorner(eTopRight, *iter.mCellMap,
5673 : iter.mRowGroupStart, rowY,
5674 0 : info.GetCellEndColIndex(),
5675 : mozilla::css::Side(trCorner.ownerSide),
5676 : trCorner.subWidth,
5677 0 : trCorner.bevel);
5678 0 : BCCornerInfo& brCorner = bottomCorners[info.GetCellEndColIndex() + 1];
5679 0 : brCorner.Set(NS_SIDE_TOP, currentBorder); // bottom right
5680 : tableCellMap->SetBCBorderCorner(eBottomRight, *iter.mCellMap,
5681 : iter.mRowGroupStart, rowY,
5682 0 : info.GetCellEndColIndex(),
5683 : mozilla::css::Side(brCorner.ownerSide),
5684 : brCorner.subWidth,
5685 0 : brCorner.bevel);
5686 : // update lastVerBorders and see if a new segment starts
5687 : startSeg = SetBorder(currentBorder,
5688 0 : lastVerBorders[info.GetCellEndColIndex() + 1]);
5689 : // store the border segment in the cell map and update cellBorders
5690 : tableCellMap->SetBCBorderEdge(NS_SIDE_RIGHT, *iter.mCellMap,
5691 : iter.mRowGroupStart, rowY,
5692 0 : info.GetCellEndColIndex(), 1,
5693 : currentBorder.owner, currentBorder.width,
5694 0 : startSeg);
5695 0 : info.SetTableRightBorderWidth(rowY, currentBorder.width);
5696 0 : info.SetRightBorderWidths(currentBorder.width);
5697 0 : info.SetRowRightContBCBorder();
5698 : }
5699 0 : info.SetRowGroupRightContBCBorder();
5700 : }
5701 : else {
5702 0 : PRInt32 segLength = 0;
5703 0 : BCMapCellInfo priorAjaInfo(this);
5704 0 : for (PRInt32 rowY = info.mRowIndex; rowY <= info.GetCellEndRowIndex();
5705 : rowY += segLength) {
5706 0 : iter.PeekRight(info, rowY, ajaInfo);
5707 0 : currentBorder = info.GetRightInternalBorder();
5708 0 : adjacentBorder = ajaInfo.GetLeftInternalBorder();
5709 : currentBorder = CompareBorders(!CELL_CORNER, currentBorder,
5710 0 : adjacentBorder, !HORIZONTAL);
5711 :
5712 0 : segLength = NS_MAX(1, ajaInfo.mRowIndex + ajaInfo.mRowSpan - rowY);
5713 0 : segLength = NS_MIN(segLength, info.mRowIndex + info.mRowSpan - rowY);
5714 :
5715 : // update lastVerBorders and see if a new segment starts
5716 : startSeg = SetBorder(currentBorder,
5717 0 : lastVerBorders[info.GetCellEndColIndex() + 1]);
5718 : // store the border segment in the cell map and update cellBorders
5719 0 : if (info.GetCellEndColIndex() < damageArea.XMost() &&
5720 0 : rowY >= damageArea.y && rowY < damageArea.YMost()) {
5721 : tableCellMap->SetBCBorderEdge(NS_SIDE_RIGHT, *iter.mCellMap,
5722 : iter.mRowGroupStart, rowY,
5723 0 : info.GetCellEndColIndex(), segLength,
5724 : currentBorder.owner,
5725 0 : currentBorder.width, startSeg);
5726 0 : info.SetRightBorderWidths(currentBorder.width);
5727 0 : ajaInfo.SetLeftBorderWidths(currentBorder.width);
5728 : }
5729 : // update the top right corner
5730 : bool hitsSpanOnRight = (rowY > ajaInfo.mRowIndex) &&
5731 0 : (rowY < ajaInfo.mRowIndex + ajaInfo.mRowSpan);
5732 : BCCornerInfo* trCorner = ((0 == rowY) || hitsSpanOnRight) ?
5733 0 : &topCorners[info.GetCellEndColIndex() + 1] :
5734 0 : &bottomCorners[info.GetCellEndColIndex() + 1];
5735 0 : trCorner->Update(NS_SIDE_BOTTOM, currentBorder);
5736 : // if this is not the first time through,
5737 : // consider the segment to the right
5738 0 : if (rowY != info.mRowIndex) {
5739 0 : currentBorder = priorAjaInfo.GetBottomInternalBorder();
5740 0 : adjacentBorder = ajaInfo.GetTopInternalBorder();
5741 : currentBorder = CompareBorders(!CELL_CORNER, currentBorder,
5742 0 : adjacentBorder, HORIZONTAL);
5743 0 : trCorner->Update(NS_SIDE_RIGHT, currentBorder);
5744 : }
5745 : // store the top right corner in the cell map
5746 0 : if (info.GetCellEndColIndex() < damageArea.XMost() &&
5747 : rowY >= damageArea.y) {
5748 0 : if (0 != rowY) {
5749 : tableCellMap->SetBCBorderCorner(eTopRight, *iter.mCellMap,
5750 : iter.mRowGroupStart, rowY,
5751 0 : info.GetCellEndColIndex(),
5752 : mozilla::css::Side(trCorner->ownerSide),
5753 : trCorner->subWidth,
5754 0 : trCorner->bevel);
5755 : }
5756 : // store any corners this cell spans together with the aja cell
5757 0 : for (PRInt32 rX = rowY + 1; rX < rowY + segLength; rX++) {
5758 : tableCellMap->SetBCBorderCorner(eBottomRight, *iter.mCellMap,
5759 : iter.mRowGroupStart, rX,
5760 0 : info.GetCellEndColIndex(),
5761 : mozilla::css::Side(trCorner->ownerSide),
5762 0 : trCorner->subWidth, false);
5763 : }
5764 : }
5765 : // update bottom right corner, topCorners, bottomCorners
5766 : hitsSpanOnRight = (rowY + segLength <
5767 0 : ajaInfo.mRowIndex + ajaInfo.mRowSpan);
5768 : BCCornerInfo& brCorner = (hitsSpanOnRight) ?
5769 0 : topCorners[info.GetCellEndColIndex() + 1] :
5770 0 : bottomCorners[info.GetCellEndColIndex() + 1];
5771 0 : brCorner.Set(NS_SIDE_TOP, currentBorder);
5772 0 : priorAjaInfo = ajaInfo;
5773 : }
5774 : }
5775 0 : for (PRInt32 colX = info.mColIndex + 1; colX <= info.GetCellEndColIndex();
5776 : colX++) {
5777 0 : lastVerBorders[colX].Reset(0,1);
5778 : }
5779 :
5780 : // find the dominant border considering the cell's bottom border, adjacent
5781 : // cells and the table, row group, row
5782 0 : if (info.mNumTableRows == info.GetCellEndRowIndex() + 1) {
5783 : // touches bottom edge of table
5784 0 : if (!tableBorderReset[NS_SIDE_BOTTOM]) {
5785 0 : propData->mBottomBorderWidth = 0;
5786 0 : tableBorderReset[NS_SIDE_BOTTOM] = true;
5787 : }
5788 0 : for (PRInt32 colX = info.mColIndex; colX <= info.GetCellEndColIndex();
5789 : colX++) {
5790 0 : info.SetColumn(colX);
5791 0 : currentBorder = info.GetBottomEdgeBorder();
5792 : // update/store the bottom left & bottom right corners
5793 0 : BCCornerInfo& blCorner = bottomCorners[colX]; // bottom left
5794 0 : blCorner.Update(NS_SIDE_RIGHT, currentBorder);
5795 : tableCellMap->SetBCBorderCorner(eBottomLeft, *iter.mCellMap,
5796 : iter.mRowGroupStart,
5797 0 : info.GetCellEndRowIndex(),
5798 : colX,
5799 : mozilla::css::Side(blCorner.ownerSide),
5800 0 : blCorner.subWidth, blCorner.bevel);
5801 0 : BCCornerInfo& brCorner = bottomCorners[colX + 1]; // bottom right
5802 0 : brCorner.Update(NS_SIDE_LEFT, currentBorder);
5803 0 : if (info.mNumTableCols == colX + 1) { // lower right corner of the table
5804 : tableCellMap->SetBCBorderCorner(eBottomRight, *iter.mCellMap,
5805 : iter.mRowGroupStart,
5806 0 : info.GetCellEndRowIndex(),colX,
5807 : mozilla::css::Side(brCorner.ownerSide),
5808 : brCorner.subWidth,
5809 0 : brCorner.bevel, true);
5810 : }
5811 : // update lastBottomBorder and see if a new segment starts
5812 0 : startSeg = SetHorBorder(currentBorder, blCorner, lastBottomBorder);
5813 0 : if (!startSeg) {
5814 : // make sure that we did not compare apples to oranges i.e. the
5815 : // current border should be a continuation of the lastBottomBorder,
5816 : // as it is a bottom border
5817 : // add 1 to the info.GetCellEndRowIndex()
5818 : startSeg = (lastBottomBorder.rowIndex !=
5819 0 : (info.GetCellEndRowIndex() + 1));
5820 : }
5821 : // store the border segment in the cell map and update cellBorders
5822 : tableCellMap->SetBCBorderEdge(NS_SIDE_BOTTOM, *iter.mCellMap,
5823 : iter.mRowGroupStart,
5824 0 : info.GetCellEndRowIndex(),
5825 : colX, 1, currentBorder.owner,
5826 0 : currentBorder.width, startSeg);
5827 : // update lastBottomBorders
5828 0 : lastBottomBorder.rowIndex = info.GetCellEndRowIndex() + 1;
5829 0 : lastBottomBorder.rowSpan = info.mRowSpan;
5830 0 : lastBottomBorders[colX] = lastBottomBorder;
5831 :
5832 0 : info.SetBottomBorderWidths(currentBorder.width);
5833 0 : info.SetTableBottomBorderWidth(currentBorder.width);
5834 0 : info.SetColumnBottomContBCBorder();
5835 : }
5836 0 : info.SetRowGroupBottomContBCBorder();
5837 0 : info.SetColGroupBottomContBCBorder();
5838 : }
5839 : else {
5840 0 : PRInt32 segLength = 0;
5841 0 : for (PRInt32 colX = info.mColIndex; colX <= info.GetCellEndColIndex();
5842 : colX += segLength) {
5843 0 : iter.PeekBottom(info, colX, ajaInfo);
5844 0 : currentBorder = info.GetBottomInternalBorder();
5845 0 : adjacentBorder = ajaInfo.GetTopInternalBorder();
5846 : currentBorder = CompareBorders(!CELL_CORNER, currentBorder,
5847 0 : adjacentBorder, HORIZONTAL);
5848 0 : segLength = NS_MAX(1, ajaInfo.mColIndex + ajaInfo.mColSpan - colX);
5849 0 : segLength = NS_MIN(segLength, info.mColIndex + info.mColSpan - colX);
5850 :
5851 : // update, store the bottom left corner
5852 0 : BCCornerInfo& blCorner = bottomCorners[colX]; // bottom left
5853 : bool hitsSpanBelow = (colX > ajaInfo.mColIndex) &&
5854 0 : (colX < ajaInfo.mColIndex + ajaInfo.mColSpan);
5855 0 : bool update = true;
5856 0 : if ((colX == info.mColIndex) && (colX > damageArea.x)) {
5857 0 : PRInt32 prevRowIndex = lastBottomBorders[colX - 1].rowIndex;
5858 0 : if (prevRowIndex > info.GetCellEndRowIndex() + 1) {
5859 : // hits a rowspan on the right
5860 0 : update = false;
5861 : // the corner was taken care of during the cell on the left
5862 : }
5863 0 : else if (prevRowIndex < info.GetCellEndRowIndex() + 1) {
5864 : // spans below the cell to the left
5865 0 : topCorners[colX] = blCorner;
5866 0 : blCorner.Set(NS_SIDE_RIGHT, currentBorder);
5867 0 : update = false;
5868 : }
5869 : }
5870 0 : if (update) {
5871 0 : blCorner.Update(NS_SIDE_RIGHT, currentBorder);
5872 : }
5873 0 : if (info.GetCellEndRowIndex() < damageArea.YMost() &&
5874 : (colX >= damageArea.x)) {
5875 0 : if (hitsSpanBelow) {
5876 : tableCellMap->SetBCBorderCorner(eBottomLeft, *iter.mCellMap,
5877 : iter.mRowGroupStart,
5878 0 : info.GetCellEndRowIndex(), colX,
5879 : mozilla::css::Side(blCorner.ownerSide),
5880 0 : blCorner.subWidth, blCorner.bevel);
5881 : }
5882 : // store any corners this cell spans together with the aja cell
5883 0 : for (PRInt32 cX = colX + 1; cX < colX + segLength; cX++) {
5884 0 : BCCornerInfo& corner = bottomCorners[cX];
5885 0 : corner.Set(NS_SIDE_RIGHT, currentBorder);
5886 : tableCellMap->SetBCBorderCorner(eBottomLeft, *iter.mCellMap,
5887 : iter.mRowGroupStart,
5888 0 : info.GetCellEndRowIndex(), cX,
5889 : mozilla::css::Side(corner.ownerSide),
5890 : corner.subWidth,
5891 0 : false);
5892 : }
5893 : }
5894 : // update lastBottomBorders and see if a new segment starts
5895 0 : startSeg = SetHorBorder(currentBorder, blCorner, lastBottomBorder);
5896 0 : if (!startSeg) {
5897 : // make sure that we did not compare apples to oranges i.e. the
5898 : // current border should be a continuation of the lastBottomBorder,
5899 : // as it is a bottom border
5900 : // add 1 to the info.GetCellEndRowIndex()
5901 : startSeg = (lastBottomBorder.rowIndex !=
5902 0 : info.GetCellEndRowIndex() + 1);
5903 : }
5904 0 : lastBottomBorder.rowIndex = info.GetCellEndRowIndex() + 1;
5905 0 : lastBottomBorder.rowSpan = info.mRowSpan;
5906 0 : for (PRInt32 cX = colX; cX < colX + segLength; cX++) {
5907 0 : lastBottomBorders[cX] = lastBottomBorder;
5908 : }
5909 :
5910 : // store the border segment the cell map and update cellBorders
5911 0 : if (info.GetCellEndRowIndex() < damageArea.YMost() &&
5912 : (colX >= damageArea.x) &&
5913 0 : (colX < damageArea.XMost())) {
5914 : tableCellMap->SetBCBorderEdge(NS_SIDE_BOTTOM, *iter.mCellMap,
5915 : iter.mRowGroupStart,
5916 0 : info.GetCellEndRowIndex(),
5917 : colX, segLength, currentBorder.owner,
5918 0 : currentBorder.width, startSeg);
5919 0 : info.SetBottomBorderWidths(currentBorder.width);
5920 0 : ajaInfo.SetTopBorderWidths(currentBorder.width);
5921 : }
5922 : // update bottom right corner
5923 0 : BCCornerInfo& brCorner = bottomCorners[colX + segLength];
5924 0 : brCorner.Update(NS_SIDE_LEFT, currentBorder);
5925 : }
5926 0 : if (!gotRowBorder && 1 == info.mRowSpan &&
5927 : (ajaInfo.mTopRow || info.mRgAtBottom)) {
5928 : //get continuous row/row group border
5929 : //we need to check the row group's bottom border if this is
5930 : //the last row in the row group, but only a cell with rowspan=1
5931 : //will know whether *this* row is at the bottom
5932 : const nsIFrame* nextRowGroup = (ajaInfo.mRgAtTop) ? ajaInfo.mRowGroup :
5933 0 : nsnull;
5934 0 : info.SetInnerRowGroupBottomContBCBorder(nextRowGroup, ajaInfo.mTopRow);
5935 0 : gotRowBorder = true;
5936 : }
5937 : }
5938 :
5939 : // see if the cell to the right had a rowspan and its lower left border
5940 : // needs be joined with this one's bottom
5941 : // if there is a cell to the right and the cell to right was a rowspan
5942 0 : if ((info.mNumTableCols != info.GetCellEndColIndex() + 1) &&
5943 0 : (lastBottomBorders[info.GetCellEndColIndex() + 1].rowSpan > 1)) {
5944 0 : BCCornerInfo& corner = bottomCorners[info.GetCellEndColIndex() + 1];
5945 0 : if ((NS_SIDE_TOP != corner.ownerSide) &&
5946 : (NS_SIDE_BOTTOM != corner.ownerSide)) {
5947 : // not a vertical owner
5948 0 : BCCellBorder& thisBorder = lastBottomBorder;
5949 0 : BCCellBorder& nextBorder = lastBottomBorders[info.mColIndex + 1];
5950 0 : if ((thisBorder.color == nextBorder.color) &&
5951 : (thisBorder.width == nextBorder.width) &&
5952 : (thisBorder.style == nextBorder.style)) {
5953 : // set the flag on the next border indicating it is not the start of a
5954 : // new segment
5955 0 : if (iter.mCellMap) {
5956 : tableCellMap->ResetTopStart(NS_SIDE_BOTTOM, *iter.mCellMap,
5957 0 : info.GetCellEndRowIndex(),
5958 0 : info.GetCellEndColIndex() + 1);
5959 : }
5960 : }
5961 : }
5962 : }
5963 : } // for (iter.First(info); info.mCell; iter.Next(info)) {
5964 : // reset the bc flag and damage area
5965 0 : SetNeedToCalcBCBorders(false);
5966 0 : propData->mDamageArea = nsIntRect(0,0,0,0);
5967 : #ifdef DEBUG_TABLE_CELLMAP
5968 : mCellMap->Dump();
5969 : #endif
5970 : }
5971 :
5972 : class BCPaintBorderIterator;
5973 :
5974 : struct BCVerticalSeg
5975 : {
5976 : BCVerticalSeg();
5977 :
5978 : void Start(BCPaintBorderIterator& aIter,
5979 : BCBorderOwner aBorderOwner,
5980 : BCPixelSize aVerSegWidth,
5981 : BCPixelSize aHorSegHeight);
5982 :
5983 : void Initialize(BCPaintBorderIterator& aIter);
5984 : void GetBottomCorner(BCPaintBorderIterator& aIter,
5985 : BCPixelSize aHorSegHeight);
5986 :
5987 :
5988 : void Paint(BCPaintBorderIterator& aIter,
5989 : nsRenderingContext& aRenderingContext,
5990 : BCPixelSize aHorSegHeight);
5991 : void AdvanceOffsetY();
5992 : void IncludeCurrentBorder(BCPaintBorderIterator& aIter);
5993 :
5994 :
5995 : union {
5996 : nsTableColFrame* mCol;
5997 : PRInt32 mColWidth;
5998 : };
5999 : nscoord mOffsetX; // x-offset with respect to the table edge
6000 : nscoord mOffsetY; // y-offset with respect to the table edge
6001 : nscoord mLength; // vertical length including corners
6002 : BCPixelSize mWidth; // width in pixels
6003 :
6004 : nsTableCellFrame* mAjaCell; // previous sibling to the first cell
6005 : // where the segment starts, it can be
6006 : // the owner of a segment
6007 : nsTableCellFrame* mFirstCell; // cell at the start of the segment
6008 : nsTableRowGroupFrame* mFirstRowGroup; // row group at the start of the segment
6009 : nsTableRowFrame* mFirstRow; // row at the start of the segment
6010 : nsTableCellFrame* mLastCell; // cell at the current end of the
6011 : // segment
6012 :
6013 :
6014 : PRUint8 mOwner; // owner of the border, defines the
6015 : // style
6016 : mozilla::css::Side mTopBevelSide; // direction to bevel at the top
6017 : nscoord mTopBevelOffset; // how much to bevel at the top
6018 : BCPixelSize mBottomHorSegHeight; // height of the crossing
6019 : //horizontal border
6020 : nscoord mBottomOffset; // how much longer is the segment due
6021 : // to the horizontal border, by this
6022 : // amount the next segment needs to be
6023 : // shifted.
6024 : bool mIsBottomBevel; // should we bevel at the bottom
6025 : };
6026 :
6027 : struct BCHorizontalSeg
6028 : {
6029 : BCHorizontalSeg();
6030 :
6031 : void Start(BCPaintBorderIterator& aIter,
6032 : BCBorderOwner aBorderOwner,
6033 : BCPixelSize aBottomVerSegWidth,
6034 : BCPixelSize aHorSegHeight);
6035 : void GetRightCorner(BCPaintBorderIterator& aIter,
6036 : BCPixelSize aLeftSegWidth);
6037 : void AdvanceOffsetX(PRInt32 aIncrement);
6038 : void IncludeCurrentBorder(BCPaintBorderIterator& aIter);
6039 : void Paint(BCPaintBorderIterator& aIter,
6040 : nsRenderingContext& aRenderingContext);
6041 :
6042 : nscoord mOffsetX; // x-offset with respect to the table edge
6043 : nscoord mOffsetY; // y-offset with respect to the table edge
6044 : nscoord mLength; // horizontal length including corners
6045 : BCPixelSize mWidth; // border width in pixels
6046 : nscoord mLeftBevelOffset; // how much to bevel at the left
6047 : mozilla::css::Side mLeftBevelSide; // direction to bevel at the left
6048 : bool mIsRightBevel; // should we bevel at the right end
6049 : nscoord mRightBevelOffset; // how much to bevel at the right
6050 : mozilla::css::Side mRightBevelSide; // direction to bevel at the right
6051 : nscoord mEndOffset; // how much longer is the segment due
6052 : // to the vertical border, by this
6053 : // amount the next segment needs to be
6054 : // shifted.
6055 : PRUint8 mOwner; // owner of the border, defines the
6056 : // style
6057 : nsTableCellFrame* mFirstCell; // cell at the start of the segment
6058 : nsTableCellFrame* mAjaCell; // neighboring cell to the first cell
6059 : // where the segment starts, it can be
6060 : // the owner of a segment
6061 : };
6062 :
6063 : // Iterates over borders (left border, corner, top border) in the cell map within a damage area
6064 : // from left to right, top to bottom. All members are in terms of the 1st in flow frames, except
6065 : // where suffixed by InFlow.
6066 : class BCPaintBorderIterator
6067 : {
6068 : public:
6069 :
6070 :
6071 : BCPaintBorderIterator(nsTableFrame* aTable);
6072 0 : ~BCPaintBorderIterator() { if (mVerInfo) {
6073 0 : delete [] mVerInfo;
6074 0 : }}
6075 : void Reset();
6076 :
6077 : /**
6078 : * Determine the damage area in terms of rows and columns and finalize
6079 : * mInitialOffsetX and mInitialOffsetY.
6080 : * @param aDirtyRect - dirty rect in table coordinates
6081 : * @return - true if we need to paint something given dirty rect
6082 : */
6083 : bool SetDamageArea(const nsRect& aDamageRect);
6084 : void First();
6085 : void Next();
6086 : void AccumulateOrPaintHorizontalSegment(nsRenderingContext& aRenderingContext);
6087 : void AccumulateOrPaintVerticalSegment(nsRenderingContext& aRenderingContext);
6088 : void ResetVerInfo();
6089 : void StoreColumnWidth(PRInt32 aIndex);
6090 : bool VerticalSegmentOwnsCorner();
6091 :
6092 : nsTableFrame* mTable;
6093 : nsTableFrame* mTableFirstInFlow;
6094 : nsTableCellMap* mTableCellMap;
6095 : nsCellMap* mCellMap;
6096 : bool mTableIsLTR;
6097 : PRInt32 mColInc; // +1 for ltr -1 for rtl
6098 : const nsStyleBackground* mTableBgColor;
6099 : nsTableFrame::RowGroupArray mRowGroups;
6100 :
6101 : nsTableRowGroupFrame* mPrevRg;
6102 : nsTableRowGroupFrame* mRg;
6103 : bool mIsRepeatedHeader;
6104 : bool mIsRepeatedFooter;
6105 : nsTableRowGroupFrame* mStartRg; // first row group in the damagearea
6106 : PRInt32 mRgIndex; // current row group index in the
6107 : // mRowgroups array
6108 : PRInt32 mFifRgFirstRowIndex; // start row index of the first in
6109 : // flow of the row group
6110 : PRInt32 mRgFirstRowIndex; // row index of the first row in the
6111 : // row group
6112 : PRInt32 mRgLastRowIndex; // row index of the last row in the row
6113 : // group
6114 : PRInt32 mNumTableRows; // number of rows in the table and all
6115 : // continuations
6116 : PRInt32 mNumTableCols; // number of columns in the table
6117 : PRInt32 mColIndex; // with respect to the table
6118 : PRInt32 mRowIndex; // with respect to the table
6119 : PRInt32 mRepeatedHeaderRowIndex; // row index in a repeated
6120 : //header, it's equivalent to
6121 : // mRowIndex when we're in a repeated
6122 : // header, and set to the last row
6123 : // index of a repeated header when
6124 : // we're not
6125 : bool mIsNewRow;
6126 : bool mAtEnd; // the iterator cycled over all
6127 : // borders
6128 : nsTableRowFrame* mPrevRow;
6129 : nsTableRowFrame* mRow;
6130 : nsTableRowFrame* mStartRow; //first row in a inside the damagearea
6131 :
6132 :
6133 : // cell properties
6134 : nsTableCellFrame* mPrevCell;
6135 : nsTableCellFrame* mCell;
6136 : BCCellData* mPrevCellData;
6137 : BCCellData* mCellData;
6138 : BCData* mBCData;
6139 :
6140 0 : bool IsTableTopMost() {return (mRowIndex == 0) && !mTable->GetPrevInFlow();}
6141 0 : bool IsTableRightMost() {return (mColIndex >= mNumTableCols);}
6142 0 : bool IsTableBottomMost() {return (mRowIndex >= mNumTableRows) && !mTable->GetNextInFlow();}
6143 0 : bool IsTableLeftMost() {return (mColIndex == 0);}
6144 0 : bool IsDamageAreaTopMost() {return (mRowIndex == mDamageArea.y);}
6145 0 : bool IsDamageAreaRightMost() {return (mColIndex >= mDamageArea.XMost());}
6146 0 : bool IsDamageAreaBottomMost() {return (mRowIndex >= mDamageArea.YMost());}
6147 0 : bool IsDamageAreaLeftMost() {return (mColIndex == mDamageArea.x);}
6148 0 : PRInt32 GetRelativeColIndex() {return (mColIndex - mDamageArea.x);}
6149 :
6150 : nsIntRect mDamageArea; // damageArea in cellmap coordinates
6151 0 : bool IsAfterRepeatedHeader() { return !mIsRepeatedHeader && (mRowIndex == (mRepeatedHeaderRowIndex + 1));}
6152 0 : bool StartRepeatedFooter() {return mIsRepeatedFooter && (mRowIndex == mRgFirstRowIndex) && (mRowIndex != mDamageArea.y);}
6153 : nscoord mInitialOffsetX; // offsetX of the first border with
6154 : // respect to the table
6155 : nscoord mInitialOffsetY; // offsetY of the first border with
6156 : // respect to the table
6157 : nscoord mNextOffsetY; // offsetY of the next segment
6158 : BCVerticalSeg* mVerInfo; // this array is used differently when
6159 : // horizontal and vertical borders are drawn
6160 : // When horizontal border are drawn we cache
6161 : // the column widths and the width of the
6162 : // vertical borders that arrive from top
6163 : // When we draw vertical borders we store
6164 : // lengths and width for vertical borders
6165 : // before they are drawn while we move over
6166 : // the columns in the damage area
6167 : // It has one more elements than columns are
6168 : //in the table.
6169 : BCHorizontalSeg mHorSeg; // the horizontal segment while we
6170 : // move over the colums
6171 : BCPixelSize mPrevHorSegHeight; // the height of the previous
6172 : // horizontal border
6173 :
6174 : private:
6175 :
6176 : bool SetNewRow(nsTableRowFrame* aRow = nsnull);
6177 : bool SetNewRowGroup();
6178 : void SetNewData(PRInt32 aRowIndex, PRInt32 aColIndex);
6179 :
6180 : };
6181 :
6182 :
6183 :
6184 0 : BCPaintBorderIterator::BCPaintBorderIterator(nsTableFrame* aTable)
6185 : {
6186 0 : mTable = aTable;
6187 0 : mVerInfo = nsnull;
6188 0 : nsMargin childAreaOffset = mTable->GetChildAreaOffset(nsnull);
6189 0 : mTableFirstInFlow = (nsTableFrame*) mTable->GetFirstInFlow();
6190 0 : mTableCellMap = mTable->GetCellMap();
6191 : // y position of first row in damage area
6192 0 : mInitialOffsetY = mTable->GetPrevInFlow() ? 0 : childAreaOffset.top;
6193 0 : mNumTableRows = mTable->GetRowCount();
6194 0 : mNumTableCols = mTable->GetColCount();
6195 :
6196 : // Get the ordered row groups
6197 0 : mTable->OrderRowGroups(mRowGroups);
6198 : // initialize to a non existing index
6199 0 : mRepeatedHeaderRowIndex = -99;
6200 :
6201 0 : mTableIsLTR = mTable->GetStyleVisibility()->mDirection ==
6202 0 : NS_STYLE_DIRECTION_LTR;
6203 0 : mColInc = (mTableIsLTR) ? 1 : -1;
6204 :
6205 : nsIFrame* bgFrame =
6206 0 : nsCSSRendering::FindNonTransparentBackgroundFrame(aTable);
6207 0 : mTableBgColor = bgFrame->GetStyleBackground();
6208 0 : }
6209 :
6210 : bool
6211 0 : BCPaintBorderIterator::SetDamageArea(const nsRect& aDirtyRect)
6212 : {
6213 :
6214 : PRUint32 startRowIndex, endRowIndex, startColIndex, endColIndex;
6215 0 : startRowIndex = endRowIndex = startColIndex = endColIndex = 0;
6216 0 : bool done = false;
6217 0 : bool haveIntersect = false;
6218 : // find startRowIndex, endRowIndex
6219 0 : nscoord rowY = mInitialOffsetY;
6220 0 : for (PRUint32 rgX = 0; rgX < mRowGroups.Length() && !done; rgX++) {
6221 0 : nsTableRowGroupFrame* rgFrame = mRowGroups[rgX];
6222 0 : for (nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); rowFrame;
6223 : rowFrame = rowFrame->GetNextRow()) {
6224 : // conservatively estimate the half border widths outside the row
6225 0 : nscoord topBorderHalf = (mTable->GetPrevInFlow()) ? 0 :
6226 0 : nsPresContext::CSSPixelsToAppUnits(rowFrame->GetTopBCBorderWidth() + 1);
6227 0 : nscoord bottomBorderHalf = (mTable->GetNextInFlow()) ? 0 :
6228 0 : nsPresContext::CSSPixelsToAppUnits(rowFrame->GetBottomBCBorderWidth() + 1);
6229 : // get the row rect relative to the table rather than the row group
6230 0 : nsSize rowSize = rowFrame->GetSize();
6231 0 : if (haveIntersect) {
6232 0 : if (aDirtyRect.YMost() >= (rowY - topBorderHalf)) {
6233 : nsTableRowFrame* fifRow =
6234 0 : static_cast<nsTableRowFrame*>(rowFrame->GetFirstInFlow());
6235 0 : endRowIndex = fifRow->GetRowIndex();
6236 : }
6237 0 : else done = true;
6238 : }
6239 : else {
6240 0 : if ((rowY + rowSize.height + bottomBorderHalf) >= aDirtyRect.y) {
6241 0 : mStartRg = rgFrame;
6242 0 : mStartRow = rowFrame;
6243 : nsTableRowFrame* fifRow =
6244 0 : static_cast<nsTableRowFrame*>(rowFrame->GetFirstInFlow());
6245 0 : startRowIndex = endRowIndex = fifRow->GetRowIndex();
6246 0 : haveIntersect = true;
6247 : }
6248 : else {
6249 0 : mInitialOffsetY += rowSize.height;
6250 : }
6251 : }
6252 0 : rowY += rowSize.height;
6253 : }
6254 : }
6255 0 : mNextOffsetY = mInitialOffsetY;
6256 :
6257 : // XXX comment refers to the obsolete NS_FRAME_OUTSIDE_CHILDREN flag
6258 : // XXX but I don't understand it, so not changing it for now
6259 : // outer table borders overflow the table, so the table might be
6260 : // target to other areas as the NS_FRAME_OUTSIDE_CHILDREN is set
6261 : // on the table
6262 0 : if (!haveIntersect)
6263 0 : return false;
6264 : // find startColIndex, endColIndex, startColX
6265 0 : haveIntersect = false;
6266 0 : if (0 == mNumTableCols)
6267 0 : return false;
6268 : PRInt32 leftCol, rightCol; // columns are in the range [leftCol, rightCol)
6269 :
6270 0 : nsMargin childAreaOffset = mTable->GetChildAreaOffset(nsnull);
6271 0 : if (mTableIsLTR) {
6272 0 : mInitialOffsetX = childAreaOffset.left; // x position of first col in
6273 : // damage area
6274 0 : leftCol = 0;
6275 0 : rightCol = mNumTableCols;
6276 : } else {
6277 : // x position of first col in damage area
6278 0 : mInitialOffsetX = mTable->GetRect().width - childAreaOffset.right;
6279 0 : leftCol = mNumTableCols-1;
6280 0 : rightCol = -1;
6281 : }
6282 0 : nscoord x = 0;
6283 : PRInt32 colX;
6284 0 : for (colX = leftCol; colX != rightCol; colX += mColInc) {
6285 0 : nsTableColFrame* colFrame = mTableFirstInFlow->GetColFrame(colX);
6286 0 : if (!colFrame) ABORT1(false);
6287 : // get the col rect relative to the table rather than the col group
6288 0 : nsSize size = colFrame->GetSize();
6289 0 : if (haveIntersect) {
6290 : // conservatively estimate the left half border width outside the col
6291 : nscoord leftBorderHalf =
6292 0 : nsPresContext::CSSPixelsToAppUnits(colFrame->GetLeftBorderWidth() + 1);
6293 0 : if (aDirtyRect.XMost() >= (x - leftBorderHalf)) {
6294 0 : endColIndex = colX;
6295 : }
6296 0 : else break;
6297 : }
6298 : else {
6299 : // conservatively estimate the right half border width outside the col
6300 : nscoord rightBorderHalf =
6301 0 : nsPresContext::CSSPixelsToAppUnits(colFrame->GetRightBorderWidth() + 1);
6302 0 : if ((x + size.width + rightBorderHalf) >= aDirtyRect.x) {
6303 0 : startColIndex = endColIndex = colX;
6304 0 : haveIntersect = true;
6305 : }
6306 : else {
6307 0 : mInitialOffsetX += mColInc * size.width;
6308 : }
6309 : }
6310 0 : x += size.width;
6311 : }
6312 0 : if (!mTableIsLTR) {
6313 : PRUint32 temp;
6314 0 : mInitialOffsetX = mTable->GetRect().width - childAreaOffset.right;
6315 0 : temp = startColIndex; startColIndex = endColIndex; endColIndex = temp;
6316 0 : for (PRUint32 column = 0; column < startColIndex; column++) {
6317 0 : nsTableColFrame* colFrame = mTableFirstInFlow->GetColFrame(column);
6318 0 : if (!colFrame) ABORT1(false);
6319 0 : nsSize size = colFrame->GetSize();
6320 0 : mInitialOffsetX += mColInc * size.width;
6321 : }
6322 : }
6323 0 : if (!haveIntersect)
6324 0 : return false;
6325 : mDamageArea = nsIntRect(startColIndex, startRowIndex,
6326 0 : 1 + NS_ABS(PRInt32(endColIndex - startColIndex)),
6327 0 : 1 + endRowIndex - startRowIndex);
6328 :
6329 0 : Reset();
6330 0 : mVerInfo = new BCVerticalSeg[mDamageArea.width + 1];
6331 0 : if (!mVerInfo)
6332 0 : return false;
6333 0 : return true;
6334 : }
6335 :
6336 : void
6337 0 : BCPaintBorderIterator::Reset()
6338 : {
6339 0 : mAtEnd = true; // gets reset when First() is called
6340 0 : mRg = mStartRg;
6341 0 : mPrevRow = nsnull;
6342 0 : mRow = mStartRow;
6343 0 : mRowIndex = 0;
6344 0 : mColIndex = 0;
6345 0 : mRgIndex = -1;
6346 0 : mPrevCell = nsnull;
6347 0 : mCell = nsnull;
6348 0 : mPrevCellData = nsnull;
6349 0 : mCellData = nsnull;
6350 0 : mBCData = nsnull;
6351 0 : ResetVerInfo();
6352 0 : }
6353 :
6354 : /**
6355 : * Set the iterator data to a new cellmap coordinate
6356 : * @param aRowIndex - the row index
6357 : * @param aColIndex - the col index
6358 : */
6359 : void
6360 0 : BCPaintBorderIterator::SetNewData(PRInt32 aY,
6361 : PRInt32 aX)
6362 : {
6363 0 : if (!mTableCellMap || !mTableCellMap->mBCInfo) ABORT0();
6364 :
6365 0 : mColIndex = aX;
6366 0 : mRowIndex = aY;
6367 0 : mPrevCellData = mCellData;
6368 0 : if (IsTableRightMost() && IsTableBottomMost()) {
6369 0 : mCell = nsnull;
6370 0 : mBCData = &mTableCellMap->mBCInfo->mLowerRightCorner;
6371 : }
6372 0 : else if (IsTableRightMost()) {
6373 0 : mCellData = nsnull;
6374 0 : mBCData = &mTableCellMap->mBCInfo->mRightBorders.ElementAt(aY);
6375 : }
6376 0 : else if (IsTableBottomMost()) {
6377 0 : mCellData = nsnull;
6378 0 : mBCData = &mTableCellMap->mBCInfo->mBottomBorders.ElementAt(aX);
6379 : }
6380 : else {
6381 0 : if (PRUint32(mRowIndex - mFifRgFirstRowIndex) < mCellMap->mRows.Length()) {
6382 0 : mBCData = nsnull;
6383 : mCellData =
6384 0 : (BCCellData*)mCellMap->mRows[mRowIndex - mFifRgFirstRowIndex].SafeElementAt(mColIndex);
6385 0 : if (mCellData) {
6386 0 : mBCData = &mCellData->mData;
6387 0 : if (!mCellData->IsOrig()) {
6388 0 : if (mCellData->IsRowSpan()) {
6389 0 : aY -= mCellData->GetRowSpanOffset();
6390 : }
6391 0 : if (mCellData->IsColSpan()) {
6392 0 : aX -= mCellData->GetColSpanOffset();
6393 : }
6394 0 : if ((aX >= 0) && (aY >= 0)) {
6395 0 : mCellData = (BCCellData*)mCellMap->mRows[aY - mFifRgFirstRowIndex][aX];
6396 : }
6397 : }
6398 0 : if (mCellData->IsOrig()) {
6399 0 : mPrevCell = mCell;
6400 0 : mCell = mCellData->GetCellFrame();
6401 : }
6402 : }
6403 : }
6404 : }
6405 : }
6406 :
6407 : /**
6408 : * Set the iterator to a new row
6409 : * @param aRow - the new row frame, if null the iterator will advance to the
6410 : * next row
6411 : */
6412 : bool
6413 0 : BCPaintBorderIterator::SetNewRow(nsTableRowFrame* aRow)
6414 : {
6415 0 : mPrevRow = mRow;
6416 0 : mRow = (aRow) ? aRow : mRow->GetNextRow();
6417 0 : if (mRow) {
6418 0 : mIsNewRow = true;
6419 0 : mRowIndex = mRow->GetRowIndex();
6420 0 : mColIndex = mDamageArea.x;
6421 0 : mPrevHorSegHeight = 0;
6422 0 : if (mIsRepeatedHeader) {
6423 0 : mRepeatedHeaderRowIndex = mRowIndex;
6424 : }
6425 : }
6426 : else {
6427 0 : mAtEnd = true;
6428 : }
6429 0 : return !mAtEnd;
6430 : }
6431 :
6432 : /**
6433 : * Advance the iterator to the next row group
6434 : */
6435 : bool
6436 0 : BCPaintBorderIterator::SetNewRowGroup()
6437 : {
6438 :
6439 0 : mRgIndex++;
6440 :
6441 0 : mIsRepeatedHeader = false;
6442 0 : mIsRepeatedFooter = false;
6443 :
6444 0 : NS_ASSERTION(mRgIndex >= 0, "mRgIndex out of bounds");
6445 0 : if (PRUint32(mRgIndex) < mRowGroups.Length()) {
6446 0 : mPrevRg = mRg;
6447 0 : mRg = mRowGroups[mRgIndex];
6448 : nsTableRowGroupFrame* fifRg =
6449 0 : static_cast<nsTableRowGroupFrame*>(mRg->GetFirstInFlow());
6450 0 : mFifRgFirstRowIndex = fifRg->GetStartRowIndex();
6451 0 : mRgFirstRowIndex = mRg->GetStartRowIndex();
6452 0 : mRgLastRowIndex = mRgFirstRowIndex + mRg->GetRowCount() - 1;
6453 :
6454 0 : if (SetNewRow(mRg->GetFirstRow())) {
6455 0 : mCellMap = mTableCellMap->GetMapFor(fifRg, nsnull);
6456 0 : if (!mCellMap) ABORT1(false);
6457 : }
6458 0 : if (mRg && mTable->GetPrevInFlow() && !mRg->GetPrevInFlow()) {
6459 : // if mRowGroup doesn't have a prev in flow, then it may be a repeated
6460 : // header or footer
6461 0 : const nsStyleDisplay* display = mRg->GetStyleDisplay();
6462 0 : if (mRowIndex == mDamageArea.y) {
6463 0 : mIsRepeatedHeader = (NS_STYLE_DISPLAY_TABLE_HEADER_GROUP == display->mDisplay);
6464 : }
6465 : else {
6466 0 : mIsRepeatedFooter = (NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP == display->mDisplay);
6467 : }
6468 : }
6469 : }
6470 : else {
6471 0 : mAtEnd = true;
6472 : }
6473 0 : return !mAtEnd;
6474 : }
6475 :
6476 : /**
6477 : * Move the iterator to the first position in the damageArea
6478 : */
6479 : void
6480 0 : BCPaintBorderIterator::First()
6481 : {
6482 0 : if (!mTable || (mDamageArea.x >= mNumTableCols) ||
6483 0 : (mDamageArea.y >= mNumTableRows)) ABORT0();
6484 :
6485 0 : mAtEnd = false;
6486 :
6487 0 : PRUint32 numRowGroups = mRowGroups.Length();
6488 0 : for (PRUint32 rgY = 0; rgY < numRowGroups; rgY++) {
6489 0 : nsTableRowGroupFrame* rowG = mRowGroups[rgY];
6490 0 : PRInt32 start = rowG->GetStartRowIndex();
6491 0 : PRInt32 end = start + rowG->GetRowCount() - 1;
6492 0 : if ((mDamageArea.y >= start) && (mDamageArea.y <= end)) {
6493 0 : mRgIndex = rgY - 1; // SetNewRowGroup increments rowGroupIndex
6494 0 : if (SetNewRowGroup()) {
6495 0 : while ((mRowIndex < mDamageArea.y) && !mAtEnd) {
6496 0 : SetNewRow();
6497 : }
6498 0 : if (!mAtEnd) {
6499 0 : SetNewData(mDamageArea.y, mDamageArea.x);
6500 : }
6501 : }
6502 0 : return;
6503 : }
6504 : }
6505 0 : mAtEnd = true;
6506 : }
6507 :
6508 : /**
6509 : * Advance the iterator to the next position
6510 : */
6511 : void
6512 0 : BCPaintBorderIterator::Next()
6513 : {
6514 0 : if (mAtEnd) ABORT0();
6515 0 : mIsNewRow = false;
6516 :
6517 0 : mColIndex++;
6518 0 : if (mColIndex > mDamageArea.XMost()) {
6519 0 : mRowIndex++;
6520 0 : if (mRowIndex == mDamageArea.YMost()) {
6521 0 : mColIndex = mDamageArea.x;
6522 : }
6523 0 : else if (mRowIndex < mDamageArea.YMost()) {
6524 0 : if (mRowIndex <= mRgLastRowIndex) {
6525 0 : SetNewRow();
6526 : }
6527 : else {
6528 0 : SetNewRowGroup();
6529 : }
6530 : }
6531 : else {
6532 0 : mAtEnd = true;
6533 : }
6534 : }
6535 0 : if (!mAtEnd) {
6536 0 : SetNewData(mRowIndex, mColIndex);
6537 : }
6538 : }
6539 :
6540 : // XXX if CalcVerCornerOffset and CalcHorCornerOffset remain similar, combine
6541 : // them
6542 : /** Compute the vertical offset of a vertical border segment
6543 : * @param aCornerOwnerSide - which side owns the corner
6544 : * @param aCornerSubWidth - how wide is the nonwinning side of the corner
6545 : * @param aHorWidth - how wide is the horizontal edge of the corner
6546 : * @param aIsStartOfSeg - does this corner start a new segment
6547 : * @param aIsBevel - is this corner beveled
6548 : * @return - offset in twips
6549 : */
6550 : static nscoord
6551 0 : CalcVerCornerOffset(mozilla::css::Side aCornerOwnerSide,
6552 : BCPixelSize aCornerSubWidth,
6553 : BCPixelSize aHorWidth,
6554 : bool aIsStartOfSeg,
6555 : bool aIsBevel)
6556 : {
6557 0 : nscoord offset = 0;
6558 : // XXX These should be replaced with appropriate side-specific macros (which?)
6559 : BCPixelSize smallHalf, largeHalf;
6560 0 : if ((NS_SIDE_TOP == aCornerOwnerSide) ||
6561 : (NS_SIDE_BOTTOM == aCornerOwnerSide)) {
6562 0 : DivideBCBorderSize(aCornerSubWidth, smallHalf, largeHalf);
6563 0 : if (aIsBevel) {
6564 0 : offset = (aIsStartOfSeg) ? -largeHalf : smallHalf;
6565 : }
6566 : else {
6567 0 : offset = (NS_SIDE_TOP == aCornerOwnerSide) ? smallHalf : -largeHalf;
6568 : }
6569 : }
6570 : else {
6571 0 : DivideBCBorderSize(aHorWidth, smallHalf, largeHalf);
6572 0 : if (aIsBevel) {
6573 0 : offset = (aIsStartOfSeg) ? -largeHalf : smallHalf;
6574 : }
6575 : else {
6576 0 : offset = (aIsStartOfSeg) ? smallHalf : -largeHalf;
6577 : }
6578 : }
6579 0 : return nsPresContext::CSSPixelsToAppUnits(offset);
6580 : }
6581 :
6582 : /** Compute the horizontal offset of a horizontal border segment
6583 : * @param aCornerOwnerSide - which side owns the corner
6584 : * @param aCornerSubWidth - how wide is the nonwinning side of the corner
6585 : * @param aVerWidth - how wide is the vertical edge of the corner
6586 : * @param aIsStartOfSeg - does this corner start a new segment
6587 : * @param aIsBevel - is this corner beveled
6588 : * @param aTableIsLTR - direction, the computation depends on ltr or rtl
6589 : * @return - offset in twips
6590 : */
6591 : static nscoord
6592 0 : CalcHorCornerOffset(mozilla::css::Side aCornerOwnerSide,
6593 : BCPixelSize aCornerSubWidth,
6594 : BCPixelSize aVerWidth,
6595 : bool aIsStartOfSeg,
6596 : bool aIsBevel,
6597 : bool aTableIsLTR)
6598 : {
6599 0 : nscoord offset = 0;
6600 : // XXX These should be replaced with appropriate side-specific macros (which?)
6601 : BCPixelSize smallHalf, largeHalf;
6602 0 : if ((NS_SIDE_LEFT == aCornerOwnerSide) ||
6603 : (NS_SIDE_RIGHT == aCornerOwnerSide)) {
6604 0 : if (aTableIsLTR) {
6605 0 : DivideBCBorderSize(aCornerSubWidth, smallHalf, largeHalf);
6606 : }
6607 : else {
6608 0 : DivideBCBorderSize(aCornerSubWidth, largeHalf, smallHalf);
6609 : }
6610 0 : if (aIsBevel) {
6611 0 : offset = (aIsStartOfSeg) ? -largeHalf : smallHalf;
6612 : }
6613 : else {
6614 0 : offset = (NS_SIDE_LEFT == aCornerOwnerSide) ? smallHalf : -largeHalf;
6615 : }
6616 : }
6617 : else {
6618 0 : if (aTableIsLTR) {
6619 0 : DivideBCBorderSize(aVerWidth, smallHalf, largeHalf);
6620 : }
6621 : else {
6622 0 : DivideBCBorderSize(aVerWidth, largeHalf, smallHalf);
6623 : }
6624 0 : if (aIsBevel) {
6625 0 : offset = (aIsStartOfSeg) ? -largeHalf : smallHalf;
6626 : }
6627 : else {
6628 0 : offset = (aIsStartOfSeg) ? smallHalf : -largeHalf;
6629 : }
6630 : }
6631 0 : return nsPresContext::CSSPixelsToAppUnits(offset);
6632 : }
6633 :
6634 0 : BCVerticalSeg::BCVerticalSeg()
6635 : {
6636 0 : mCol = nsnull;
6637 0 : mFirstCell = mLastCell = mAjaCell = nsnull;
6638 0 : mOffsetX = mOffsetY = mLength = mWidth = mTopBevelOffset = 0;
6639 0 : mTopBevelSide = NS_SIDE_TOP;
6640 0 : mOwner = eCellOwner;
6641 0 : }
6642 :
6643 : /**
6644 : * Start a new vertical segment
6645 : * @param aIter - iterator containing the structural information
6646 : * @param aBorderOwner - determines the border style
6647 : * @param aVerSegWidth - the width of segment in pixel
6648 : * @param aHorSegHeight - the width of the horizontal segment joining the corner
6649 : * at the start
6650 : */
6651 : void
6652 0 : BCVerticalSeg::Start(BCPaintBorderIterator& aIter,
6653 : BCBorderOwner aBorderOwner,
6654 : BCPixelSize aVerSegWidth,
6655 : BCPixelSize aHorSegHeight)
6656 : {
6657 0 : mozilla::css::Side ownerSide = NS_SIDE_TOP;
6658 0 : bool bevel = false;
6659 :
6660 :
6661 : nscoord cornerSubWidth = (aIter.mBCData) ?
6662 0 : aIter.mBCData->GetCorner(ownerSide, bevel) : 0;
6663 :
6664 0 : bool topBevel = (aVerSegWidth > 0) ? bevel : false;
6665 0 : BCPixelSize maxHorSegHeight = NS_MAX(aIter.mPrevHorSegHeight, aHorSegHeight);
6666 : nscoord offset = CalcVerCornerOffset(ownerSide, cornerSubWidth,
6667 : maxHorSegHeight, true,
6668 0 : topBevel);
6669 :
6670 : mTopBevelOffset = topBevel ?
6671 0 : nsPresContext::CSSPixelsToAppUnits(maxHorSegHeight): 0;
6672 : // XXX this assumes that only corners where 2 segments join can be beveled
6673 0 : mTopBevelSide = (aHorSegHeight > 0) ? NS_SIDE_RIGHT : NS_SIDE_LEFT;
6674 0 : mOffsetY += offset;
6675 0 : mLength = -offset;
6676 0 : mWidth = aVerSegWidth;
6677 0 : mOwner = aBorderOwner;
6678 0 : mFirstCell = aIter.mCell;
6679 0 : mFirstRowGroup = aIter.mRg;
6680 0 : mFirstRow = aIter.mRow;
6681 0 : if (aIter.GetRelativeColIndex() > 0) {
6682 0 : mAjaCell = aIter.mVerInfo[aIter.GetRelativeColIndex() - 1].mLastCell;
6683 : }
6684 0 : }
6685 :
6686 : /**
6687 : * Initialize the vertical segments with information that will persist for any
6688 : * vertical segment in this column
6689 : * @param aIter - iterator containing the structural information
6690 : */
6691 : void
6692 0 : BCVerticalSeg::Initialize(BCPaintBorderIterator& aIter)
6693 : {
6694 0 : PRInt32 relColIndex = aIter.GetRelativeColIndex();
6695 0 : mCol = aIter.IsTableRightMost() ? aIter.mVerInfo[relColIndex - 1].mCol :
6696 0 : aIter.mTableFirstInFlow->GetColFrame(aIter.mColIndex);
6697 0 : if (!mCol) ABORT0();
6698 0 : if (0 == relColIndex) {
6699 0 : mOffsetX = aIter.mInitialOffsetX;
6700 : }
6701 : // set colX for the next column
6702 0 : if (!aIter.IsDamageAreaRightMost()) {
6703 0 : aIter.mVerInfo[relColIndex + 1].mOffsetX = mOffsetX +
6704 0 : aIter.mColInc * mCol->GetSize().width;
6705 : }
6706 0 : mOffsetY = aIter.mInitialOffsetY;
6707 0 : mLastCell = aIter.mCell;
6708 : }
6709 :
6710 : /**
6711 : * Compute the offsets for the bottom corner of a vertical segment
6712 : * @param aIter - iterator containing the structural information
6713 : * @param aHorSegHeight - the width of the horizontal segment joining the corner
6714 : * at the start
6715 : */
6716 : void
6717 0 : BCVerticalSeg::GetBottomCorner(BCPaintBorderIterator& aIter,
6718 : BCPixelSize aHorSegHeight)
6719 : {
6720 0 : mozilla::css::Side ownerSide = NS_SIDE_TOP;
6721 0 : nscoord cornerSubWidth = 0;
6722 0 : bool bevel = false;
6723 0 : if (aIter.mBCData) {
6724 0 : cornerSubWidth = aIter.mBCData->GetCorner(ownerSide, bevel);
6725 : }
6726 0 : mIsBottomBevel = (mWidth > 0) ? bevel : false;
6727 0 : mBottomHorSegHeight = NS_MAX(aIter.mPrevHorSegHeight, aHorSegHeight);
6728 : mBottomOffset = CalcVerCornerOffset(ownerSide, cornerSubWidth,
6729 : mBottomHorSegHeight,
6730 0 : false, mIsBottomBevel);
6731 0 : mLength += mBottomOffset;
6732 0 : }
6733 :
6734 : /**
6735 : * Paint the vertical segment
6736 : * @param aIter - iterator containing the structural information
6737 : * @param aRenderingContext - the rendering context
6738 : * @param aHorSegHeight - the width of the horizontal segment joining the corner
6739 : * at the start
6740 : */
6741 : void
6742 0 : BCVerticalSeg::Paint(BCPaintBorderIterator& aIter,
6743 : nsRenderingContext& aRenderingContext,
6744 : BCPixelSize aHorSegHeight)
6745 : {
6746 : // get the border style, color and paint the segment
6747 0 : mozilla::css::Side side = (aIter.IsDamageAreaRightMost()) ? NS_SIDE_RIGHT :
6748 0 : NS_SIDE_LEFT;
6749 0 : PRInt32 relColIndex = aIter.GetRelativeColIndex();
6750 0 : nsTableColFrame* col = mCol; if (!col) ABORT0();
6751 0 : nsTableCellFrame* cell = mFirstCell; // ???
6752 0 : nsIFrame* owner = nsnull;
6753 0 : PRUint8 style = NS_STYLE_BORDER_STYLE_SOLID;
6754 0 : nscolor color = 0xFFFFFFFF;
6755 :
6756 0 : switch (mOwner) {
6757 : case eTableOwner:
6758 0 : owner = aIter.mTable;
6759 0 : break;
6760 : case eAjaColGroupOwner:
6761 0 : side = NS_SIDE_RIGHT;
6762 0 : if (!aIter.IsTableRightMost() && (relColIndex > 0)) {
6763 0 : col = aIter.mVerInfo[relColIndex - 1].mCol;
6764 : } // and fall through
6765 : case eColGroupOwner:
6766 0 : if (col) {
6767 0 : owner = col->GetParent();
6768 : }
6769 0 : break;
6770 : case eAjaColOwner:
6771 0 : side = NS_SIDE_RIGHT;
6772 0 : if (!aIter.IsTableRightMost() && (relColIndex > 0)) {
6773 0 : col = aIter.mVerInfo[relColIndex - 1].mCol;
6774 : } // and fall through
6775 : case eColOwner:
6776 0 : owner = col;
6777 0 : break;
6778 : case eAjaRowGroupOwner:
6779 0 : NS_ERROR("a neighboring rowgroup can never own a vertical border");
6780 : // and fall through
6781 : case eRowGroupOwner:
6782 0 : NS_ASSERTION(aIter.IsTableLeftMost() || aIter.IsTableRightMost(),
6783 : "row group can own border only at table edge");
6784 0 : owner = mFirstRowGroup;
6785 0 : break;
6786 : case eAjaRowOwner:
6787 0 : NS_ASSERTION(false, "program error"); // and fall through
6788 : case eRowOwner:
6789 0 : NS_ASSERTION(aIter.IsTableLeftMost() || aIter.IsTableRightMost(),
6790 : "row can own border only at table edge");
6791 0 : owner = mFirstRow;
6792 0 : break;
6793 : case eAjaCellOwner:
6794 0 : side = NS_SIDE_RIGHT;
6795 0 : cell = mAjaCell; // and fall through
6796 : case eCellOwner:
6797 0 : owner = cell;
6798 0 : break;
6799 : }
6800 0 : if (owner) {
6801 0 : ::GetPaintStyleInfo(owner, side, style, color, aIter.mTableIsLTR);
6802 : }
6803 : BCPixelSize smallHalf, largeHalf;
6804 0 : DivideBCBorderSize(mWidth, smallHalf, largeHalf);
6805 0 : nsRect segRect(mOffsetX - nsPresContext::CSSPixelsToAppUnits(largeHalf),
6806 : mOffsetY,
6807 0 : nsPresContext::CSSPixelsToAppUnits(mWidth), mLength);
6808 : nscoord bottomBevelOffset = (mIsBottomBevel) ?
6809 0 : nsPresContext::CSSPixelsToAppUnits(mBottomHorSegHeight) : 0;
6810 0 : mozilla::css::Side bottomBevelSide = ((aHorSegHeight > 0) ^ !aIter.mTableIsLTR) ?
6811 0 : NS_SIDE_RIGHT : NS_SIDE_LEFT;
6812 0 : mozilla::css::Side topBevelSide = ((mTopBevelSide == NS_SIDE_RIGHT) ^ !aIter.mTableIsLTR)?
6813 0 : NS_SIDE_RIGHT : NS_SIDE_LEFT;
6814 : nsCSSRendering::DrawTableBorderSegment(aRenderingContext, style, color,
6815 : aIter.mTableBgColor, segRect,
6816 : nsPresContext::AppUnitsPerCSSPixel(),
6817 : topBevelSide, mTopBevelOffset,
6818 0 : bottomBevelSide, bottomBevelOffset);
6819 : }
6820 :
6821 : /**
6822 : * Advance the start point of a segment
6823 : */
6824 : void
6825 0 : BCVerticalSeg::AdvanceOffsetY()
6826 : {
6827 0 : mOffsetY += mLength - mBottomOffset;
6828 0 : }
6829 :
6830 : /**
6831 : * Accumulate the current segment
6832 : */
6833 : void
6834 0 : BCVerticalSeg::IncludeCurrentBorder(BCPaintBorderIterator& aIter)
6835 : {
6836 0 : mLastCell = aIter.mCell;
6837 0 : mLength += aIter.mRow->GetRect().height;
6838 0 : }
6839 :
6840 0 : BCHorizontalSeg::BCHorizontalSeg()
6841 : {
6842 0 : mOffsetX = mOffsetY = mLength = mWidth = mLeftBevelOffset = 0;
6843 0 : mLeftBevelSide = NS_SIDE_TOP;
6844 0 : mFirstCell = mAjaCell = nsnull;
6845 0 : }
6846 :
6847 : /** Initialize a horizontal border segment for painting
6848 : * @param aIter - iterator storing the current and adjacent frames
6849 : * @param aBorderOwner - which frame owns the border
6850 : * @param aBottomVerSegWidth - vertical segment width coming from up
6851 : * @param aHorSegHeight - the height of the segment
6852 : + */
6853 : void
6854 0 : BCHorizontalSeg::Start(BCPaintBorderIterator& aIter,
6855 : BCBorderOwner aBorderOwner,
6856 : BCPixelSize aBottomVerSegWidth,
6857 : BCPixelSize aHorSegHeight)
6858 : {
6859 0 : mozilla::css::Side cornerOwnerSide = NS_SIDE_TOP;
6860 0 : bool bevel = false;
6861 :
6862 0 : mOwner = aBorderOwner;
6863 : nscoord cornerSubWidth = (aIter.mBCData) ?
6864 : aIter.mBCData->GetCorner(cornerOwnerSide,
6865 0 : bevel) : 0;
6866 :
6867 0 : bool leftBevel = (aHorSegHeight > 0) ? bevel : false;
6868 0 : PRInt32 relColIndex = aIter.GetRelativeColIndex();
6869 0 : nscoord maxVerSegWidth = NS_MAX(aIter.mVerInfo[relColIndex].mWidth,
6870 0 : aBottomVerSegWidth);
6871 : nscoord offset = CalcHorCornerOffset(cornerOwnerSide, cornerSubWidth,
6872 : maxVerSegWidth, true, leftBevel,
6873 0 : aIter.mTableIsLTR);
6874 0 : mLeftBevelOffset = (leftBevel && (aHorSegHeight > 0)) ? maxVerSegWidth : 0;
6875 : // XXX this assumes that only corners where 2 segments join can be beveled
6876 0 : mLeftBevelSide = (aBottomVerSegWidth > 0) ? NS_SIDE_BOTTOM : NS_SIDE_TOP;
6877 0 : if (aIter.mTableIsLTR) {
6878 0 : mOffsetX += offset;
6879 : }
6880 : else {
6881 0 : mOffsetX -= offset;
6882 : }
6883 0 : mLength = -offset;
6884 0 : mWidth = aHorSegHeight;
6885 0 : mFirstCell = aIter.mCell;
6886 0 : mAjaCell = (aIter.IsDamageAreaTopMost()) ? nsnull :
6887 0 : aIter.mVerInfo[relColIndex].mLastCell;
6888 0 : }
6889 :
6890 : /**
6891 : * Compute the offsets for the right corner of a horizontal segment
6892 : * @param aIter - iterator containing the structural information
6893 : * @param aLeftSegWidth - the width of the vertical segment joining the corner
6894 : * at the start
6895 : */
6896 : void
6897 0 : BCHorizontalSeg::GetRightCorner(BCPaintBorderIterator& aIter,
6898 : BCPixelSize aLeftSegWidth)
6899 : {
6900 0 : mozilla::css::Side ownerSide = NS_SIDE_TOP;
6901 0 : nscoord cornerSubWidth = 0;
6902 0 : bool bevel = false;
6903 0 : if (aIter.mBCData) {
6904 0 : cornerSubWidth = aIter.mBCData->GetCorner(ownerSide, bevel);
6905 : }
6906 :
6907 0 : mIsRightBevel = (mWidth > 0) ? bevel : 0;
6908 0 : PRInt32 relColIndex = aIter.GetRelativeColIndex();
6909 0 : nscoord verWidth = NS_MAX(aIter.mVerInfo[relColIndex].mWidth, aLeftSegWidth);
6910 : mEndOffset = CalcHorCornerOffset(ownerSide, cornerSubWidth, verWidth,
6911 0 : false, mIsRightBevel, aIter.mTableIsLTR);
6912 0 : mLength += mEndOffset;
6913 : mRightBevelOffset = (mIsRightBevel) ?
6914 0 : nsPresContext::CSSPixelsToAppUnits(verWidth) : 0;
6915 0 : mRightBevelSide = (aLeftSegWidth > 0) ? NS_SIDE_BOTTOM : NS_SIDE_TOP;
6916 0 : }
6917 :
6918 : /**
6919 : * Paint the horizontal segment
6920 : * @param aIter - iterator containing the structural information
6921 : * @param aRenderingContext - the rendering context
6922 : */
6923 : void
6924 0 : BCHorizontalSeg::Paint(BCPaintBorderIterator& aIter,
6925 : nsRenderingContext& aRenderingContext)
6926 : {
6927 : // get the border style, color and paint the segment
6928 0 : mozilla::css::Side side = (aIter.IsDamageAreaBottomMost()) ? NS_SIDE_BOTTOM :
6929 0 : NS_SIDE_TOP;
6930 0 : nsIFrame* rg = aIter.mRg; if (!rg) ABORT0();
6931 0 : nsIFrame* row = aIter.mRow; if (!row) ABORT0();
6932 0 : nsIFrame* cell = mFirstCell;
6933 : nsIFrame* col;
6934 0 : nsIFrame* owner = nsnull;
6935 :
6936 0 : PRUint8 style = NS_STYLE_BORDER_STYLE_SOLID;
6937 0 : nscolor color = 0xFFFFFFFF;
6938 :
6939 :
6940 0 : switch (mOwner) {
6941 : case eTableOwner:
6942 0 : owner = aIter.mTable;
6943 0 : break;
6944 : case eAjaColGroupOwner:
6945 0 : NS_ERROR("neighboring colgroups can never own a horizontal border");
6946 : // and fall through
6947 : case eColGroupOwner:
6948 0 : NS_ASSERTION(aIter.IsTableTopMost() || aIter.IsTableBottomMost(),
6949 : "col group can own border only at the table edge");
6950 0 : col = aIter.mTableFirstInFlow->GetColFrame(aIter.mColIndex - 1);
6951 0 : if (!col) ABORT0();
6952 0 : owner = col->GetParent();
6953 0 : break;
6954 : case eAjaColOwner:
6955 0 : NS_ERROR("neighboring column can never own a horizontal border");
6956 : // and fall through
6957 : case eColOwner:
6958 0 : NS_ASSERTION(aIter.IsTableTopMost() || aIter.IsTableBottomMost(),
6959 : "col can own border only at the table edge");
6960 0 : owner = aIter.mTableFirstInFlow->GetColFrame(aIter.mColIndex - 1);
6961 0 : break;
6962 : case eAjaRowGroupOwner:
6963 0 : side = NS_SIDE_BOTTOM;
6964 0 : rg = (aIter.IsTableBottomMost()) ? aIter.mRg : aIter.mPrevRg;
6965 : // and fall through
6966 : case eRowGroupOwner:
6967 0 : owner = rg;
6968 0 : break;
6969 : case eAjaRowOwner:
6970 0 : side = NS_SIDE_BOTTOM;
6971 0 : row = (aIter.IsTableBottomMost()) ? aIter.mRow : aIter.mPrevRow;
6972 : // and fall through
6973 : case eRowOwner:
6974 0 : owner = row;
6975 0 : break;
6976 : case eAjaCellOwner:
6977 0 : side = NS_SIDE_BOTTOM;
6978 : // if this is null due to the damage area origin-y > 0, then the border
6979 : // won't show up anyway
6980 0 : cell = mAjaCell;
6981 : // and fall through
6982 : case eCellOwner:
6983 0 : owner = cell;
6984 0 : break;
6985 : }
6986 0 : if (owner) {
6987 0 : ::GetPaintStyleInfo(owner, side, style, color, aIter.mTableIsLTR);
6988 : }
6989 : BCPixelSize smallHalf, largeHalf;
6990 0 : DivideBCBorderSize(mWidth, smallHalf, largeHalf);
6991 : nsRect segRect(mOffsetX,
6992 0 : mOffsetY - nsPresContext::CSSPixelsToAppUnits(largeHalf),
6993 : mLength,
6994 0 : nsPresContext::CSSPixelsToAppUnits(mWidth));
6995 0 : if (aIter.mTableIsLTR) {
6996 : nsCSSRendering::DrawTableBorderSegment(aRenderingContext, style, color,
6997 : aIter.mTableBgColor, segRect,
6998 : nsPresContext::AppUnitsPerCSSPixel(),
6999 : mLeftBevelSide,
7000 : nsPresContext::CSSPixelsToAppUnits(mLeftBevelOffset),
7001 0 : mRightBevelSide, mRightBevelOffset);
7002 : }
7003 : else {
7004 0 : segRect.x -= segRect.width;
7005 : nsCSSRendering::DrawTableBorderSegment(aRenderingContext, style, color,
7006 : aIter.mTableBgColor, segRect,
7007 : nsPresContext::AppUnitsPerCSSPixel(),
7008 : mRightBevelSide, mRightBevelOffset,
7009 : mLeftBevelSide,
7010 0 : nsPresContext::CSSPixelsToAppUnits(mLeftBevelOffset));
7011 : }
7012 : }
7013 :
7014 : /**
7015 : * Advance the start point of a segment
7016 : */
7017 : void
7018 0 : BCHorizontalSeg::AdvanceOffsetX(PRInt32 aIncrement)
7019 : {
7020 0 : mOffsetX += aIncrement * (mLength - mEndOffset);
7021 0 : }
7022 :
7023 : /**
7024 : * Accumulate the current segment
7025 : */
7026 : void
7027 0 : BCHorizontalSeg::IncludeCurrentBorder(BCPaintBorderIterator& aIter)
7028 : {
7029 0 : mLength += aIter.mVerInfo[aIter.GetRelativeColIndex()].mColWidth;
7030 0 : }
7031 :
7032 : /**
7033 : * store the column width information while painting horizontal segment
7034 : */
7035 : void
7036 0 : BCPaintBorderIterator::StoreColumnWidth(PRInt32 aIndex)
7037 : {
7038 0 : if (IsTableRightMost()) {
7039 0 : mVerInfo[aIndex].mColWidth = mVerInfo[aIndex - 1].mColWidth;
7040 : }
7041 : else {
7042 0 : nsTableColFrame* col = mTableFirstInFlow->GetColFrame(mColIndex);
7043 0 : if (!col) ABORT0();
7044 0 : mVerInfo[aIndex].mColWidth = col->GetSize().width;
7045 : }
7046 : }
7047 : /**
7048 : * Determine if a vertical segment owns the corder
7049 : */
7050 : bool
7051 0 : BCPaintBorderIterator::VerticalSegmentOwnsCorner()
7052 : {
7053 0 : mozilla::css::Side cornerOwnerSide = NS_SIDE_TOP;
7054 0 : bool bevel = false;
7055 0 : if (mBCData) {
7056 0 : mBCData->GetCorner(cornerOwnerSide, bevel);
7057 : }
7058 : // unitialized ownerside, bevel
7059 : return (NS_SIDE_TOP == cornerOwnerSide) ||
7060 0 : (NS_SIDE_BOTTOM == cornerOwnerSide);
7061 : }
7062 :
7063 : /**
7064 : * Paint if necessary a horizontal segment, otherwise accumulate it
7065 : * @param aRenderingContext - the rendering context
7066 : */
7067 : void
7068 0 : BCPaintBorderIterator::AccumulateOrPaintHorizontalSegment(nsRenderingContext& aRenderingContext)
7069 : {
7070 :
7071 0 : PRInt32 relColIndex = GetRelativeColIndex();
7072 : // store the current col width if it hasn't been already
7073 0 : if (mVerInfo[relColIndex].mColWidth < 0) {
7074 0 : StoreColumnWidth(relColIndex);
7075 : }
7076 :
7077 0 : BCBorderOwner borderOwner = eCellOwner;
7078 : BCBorderOwner ignoreBorderOwner;
7079 0 : bool isSegStart = true;
7080 : bool ignoreSegStart;
7081 :
7082 : nscoord leftSegWidth =
7083 0 : mBCData ? mBCData->GetLeftEdge(ignoreBorderOwner, ignoreSegStart) : 0;
7084 : nscoord topSegHeight =
7085 0 : mBCData ? mBCData->GetTopEdge(borderOwner, isSegStart) : 0;
7086 :
7087 0 : if (mIsNewRow || (IsDamageAreaLeftMost() && IsDamageAreaBottomMost())) {
7088 : // reset for every new row and on the bottom of the last row
7089 0 : mHorSeg.mOffsetY = mNextOffsetY;
7090 0 : mNextOffsetY = mNextOffsetY + mRow->GetSize().height;
7091 0 : mHorSeg.mOffsetX = mInitialOffsetX;
7092 0 : mHorSeg.Start(*this, borderOwner, leftSegWidth, topSegHeight);
7093 : }
7094 :
7095 0 : if (!IsDamageAreaLeftMost() && (isSegStart || IsDamageAreaRightMost() ||
7096 0 : VerticalSegmentOwnsCorner())) {
7097 : // paint the previous seg or the current one if IsDamageAreaRightMost()
7098 0 : if (mHorSeg.mLength > 0) {
7099 0 : mHorSeg.GetRightCorner(*this, leftSegWidth);
7100 0 : if (mHorSeg.mWidth > 0) {
7101 0 : mHorSeg.Paint(*this, aRenderingContext);
7102 : }
7103 0 : mHorSeg.AdvanceOffsetX(mColInc);
7104 : }
7105 0 : mHorSeg.Start(*this, borderOwner, leftSegWidth, topSegHeight);
7106 : }
7107 0 : mHorSeg.IncludeCurrentBorder(*this);
7108 0 : mVerInfo[relColIndex].mWidth = leftSegWidth;
7109 0 : mVerInfo[relColIndex].mLastCell = mCell;
7110 0 : }
7111 : /**
7112 : * Paint if necessary a vertical segment, otherwise it
7113 : * @param aRenderingContext - the rendering context
7114 : */
7115 : void
7116 0 : BCPaintBorderIterator::AccumulateOrPaintVerticalSegment(nsRenderingContext& aRenderingContext)
7117 : {
7118 0 : BCBorderOwner borderOwner = eCellOwner;
7119 : BCBorderOwner ignoreBorderOwner;
7120 0 : bool isSegStart = true;
7121 : bool ignoreSegStart;
7122 :
7123 : nscoord verSegWidth =
7124 0 : mBCData ? mBCData->GetLeftEdge(borderOwner, isSegStart) : 0;
7125 : nscoord horSegHeight =
7126 0 : mBCData ? mBCData->GetTopEdge(ignoreBorderOwner, ignoreSegStart) : 0;
7127 :
7128 0 : PRInt32 relColIndex = GetRelativeColIndex();
7129 0 : BCVerticalSeg& verSeg = mVerInfo[relColIndex];
7130 0 : if (!verSeg.mCol) { // on the first damaged row and the first segment in the
7131 : // col
7132 0 : verSeg.Initialize(*this);
7133 0 : verSeg.Start(*this, borderOwner, verSegWidth, horSegHeight);
7134 : }
7135 :
7136 0 : if (!IsDamageAreaTopMost() && (isSegStart || IsDamageAreaBottomMost() ||
7137 0 : IsAfterRepeatedHeader() ||
7138 0 : StartRepeatedFooter())) {
7139 : // paint the previous seg or the current one if IsDamageAreaBottomMost()
7140 0 : if (verSeg.mLength > 0) {
7141 0 : verSeg.GetBottomCorner(*this, horSegHeight);
7142 0 : if (verSeg.mWidth > 0) {
7143 0 : verSeg.Paint(*this, aRenderingContext, horSegHeight);
7144 : }
7145 0 : verSeg.AdvanceOffsetY();
7146 : }
7147 0 : verSeg.Start(*this, borderOwner, verSegWidth, horSegHeight);
7148 : }
7149 0 : verSeg.IncludeCurrentBorder(*this);
7150 0 : mPrevHorSegHeight = horSegHeight;
7151 0 : }
7152 :
7153 : /**
7154 : * Reset the vertical information cache
7155 : */
7156 : void
7157 0 : BCPaintBorderIterator::ResetVerInfo()
7158 : {
7159 0 : if (mVerInfo) {
7160 0 : memset(mVerInfo, 0, mDamageArea.width * sizeof(BCVerticalSeg));
7161 : // XXX reinitialize properly
7162 0 : for (PRInt32 xIndex = 0; xIndex < mDamageArea.width; xIndex++) {
7163 0 : mVerInfo[xIndex].mColWidth = -1;
7164 : }
7165 : }
7166 0 : }
7167 :
7168 : /**
7169 : * Method to paint BCBorders, this does not use currently display lists although
7170 : * it will do this in future
7171 : * @param aRenderingContext - the rendering context
7172 : * @param aDirtyRect - inside this rectangle the BC Borders will redrawn
7173 : */
7174 : void
7175 0 : nsTableFrame::PaintBCBorders(nsRenderingContext& aRenderingContext,
7176 : const nsRect& aDirtyRect)
7177 : {
7178 : // We first transfer the aDirtyRect into cellmap coordinates to compute which
7179 : // cell borders need to be painted
7180 0 : BCPaintBorderIterator iter(this);
7181 0 : if (!iter.SetDamageArea(aDirtyRect))
7182 : return;
7183 :
7184 : // First, paint all of the vertical borders from top to bottom and left to
7185 : // right as they become complete. They are painted first, since they are less
7186 : // efficient to paint than horizontal segments. They were stored with as few
7187 : // segments as possible (since horizontal borders are painted last and
7188 : // possibly over them). For every cell in a row that fails in the damage are
7189 : // we look up if the current border would start a new segment, if so we paint
7190 : // the previously stored vertical segment and start a new segment. After
7191 : // this we the now active segment with the current border. These
7192 : // segments are stored in mVerInfo to be used on the next row
7193 0 : for (iter.First(); !iter.mAtEnd; iter.Next()) {
7194 0 : iter.AccumulateOrPaintVerticalSegment(aRenderingContext);
7195 : }
7196 :
7197 : // Next, paint all of the horizontal border segments from top to bottom reuse
7198 : // the mVerInfo array to keep track of col widths and vertical segments for
7199 : // corner calculations
7200 0 : iter.Reset();
7201 0 : for (iter.First(); !iter.mAtEnd; iter.Next()) {
7202 0 : iter.AccumulateOrPaintHorizontalSegment(aRenderingContext);
7203 : }
7204 : }
7205 :
7206 0 : bool nsTableFrame::RowHasSpanningCells(PRInt32 aRowIndex, PRInt32 aNumEffCols)
7207 : {
7208 0 : bool result = false;
7209 0 : nsTableCellMap* cellMap = GetCellMap();
7210 0 : NS_PRECONDITION (cellMap, "bad call, cellMap not yet allocated.");
7211 0 : if (cellMap) {
7212 0 : result = cellMap->RowHasSpanningCells(aRowIndex, aNumEffCols);
7213 : }
7214 0 : return result;
7215 : }
7216 :
7217 0 : bool nsTableFrame::RowIsSpannedInto(PRInt32 aRowIndex, PRInt32 aNumEffCols)
7218 : {
7219 0 : bool result = false;
7220 0 : nsTableCellMap* cellMap = GetCellMap();
7221 0 : NS_PRECONDITION (cellMap, "bad call, cellMap not yet allocated.");
7222 0 : if (cellMap) {
7223 0 : result = cellMap->RowIsSpannedInto(aRowIndex, aNumEffCols);
7224 : }
7225 0 : return result;
7226 : }
7227 :
7228 : /* static */
7229 : void
7230 0 : nsTableFrame::InvalidateFrame(nsIFrame* aFrame,
7231 : const nsRect& aOrigRect,
7232 : const nsRect& aOrigVisualOverflow,
7233 : bool aIsFirstReflow)
7234 : {
7235 0 : nsIFrame* parent = aFrame->GetParent();
7236 0 : NS_ASSERTION(parent, "What happened here?");
7237 :
7238 0 : if (parent->GetStateBits() & NS_FRAME_FIRST_REFLOW) {
7239 : // Don't bother; we'll invalidate the parent's overflow rect when
7240 : // we finish reflowing it.
7241 0 : return;
7242 : }
7243 :
7244 : // The part that looks at both the rect and the overflow rect is a
7245 : // bit of a hack. See nsBlockFrame::ReflowLine for an eloquent
7246 : // description of its hackishness.
7247 0 : nsRect visualOverflow = aFrame->GetVisualOverflowRect();
7248 0 : if (aIsFirstReflow ||
7249 0 : aOrigRect.TopLeft() != aFrame->GetPosition() ||
7250 0 : aOrigVisualOverflow.TopLeft() != visualOverflow.TopLeft()) {
7251 : // Invalidate the old and new overflow rects. Note that if the
7252 : // frame moved, we can't just use aOrigVisualOverflow, since it's in
7253 : // coordinates relative to the old position. So invalidate via
7254 : // aFrame's parent, and reposition that overflow rect to the right
7255 : // place.
7256 : // XXXbz this doesn't handle outlines, does it?
7257 0 : aFrame->Invalidate(visualOverflow);
7258 0 : parent->Invalidate(aOrigVisualOverflow + aOrigRect.TopLeft());
7259 : } else {
7260 0 : nsRect rect = aFrame->GetRect();
7261 : aFrame->CheckInvalidateSizeChange(aOrigRect, aOrigVisualOverflow,
7262 0 : rect.Size());
7263 0 : aFrame->InvalidateRectDifference(aOrigVisualOverflow, visualOverflow);
7264 0 : parent->InvalidateRectDifference(aOrigRect, rect);
7265 : }
7266 : }
|