Bug Summary

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