Coverage Report

Created: 2018-09-25 14:53

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