Bug Summary

File:var/lib/jenkins/workspace/firefox-scan-build/layout/tables/nsTableFrame.cpp
Warning:line 4233, column 25
Value stored to 'rg' during its initialization is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

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