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