Bug Summary

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