Bug Summary

File:var/lib/jenkins/workspace/firefox-scan-build/layout/tables/nsTableFrame.cpp
Warning:line 4260, 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 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/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/x86_64-linux-gnu/c++/13 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/backward -internal-isystem /usr/lib/llvm-18/lib/clang/18/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../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-05-16-034744-15991-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 "nsIScrollableFrame.h"
51#include "nsCSSProps.h"
52#include "nsLayoutUtils.h"
53#include "nsStyleChangeList.h"
54#include <algorithm>
55
56#include "mozilla/layers/StackingContextHelper.h"
57#include "mozilla/layers/RenderRootStateManager.h"
58
59using namespace mozilla;
60using namespace mozilla::image;
61using namespace mozilla::layout;
62
63using mozilla::gfx::AutoRestoreTransform;
64using mozilla::gfx::DrawTarget;
65using mozilla::gfx::Float;
66using mozilla::gfx::ToDeviceColor;
67
68namespace mozilla {
69
70struct TableReflowInput final {
71 TableReflowInput(const ReflowInput& aReflowInput,
72 const LogicalMargin& aBorderPadding, TableReflowMode aMode)
73 : mReflowInput(aReflowInput),
74 mWM(aReflowInput.GetWritingMode()),
75 mAvailSize(mWM) {
76 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"
, 77); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mReflowInput.mFrame->IsTableFrame()"
") (" "TableReflowInput should only be created for nsTableFrame"
")"); do { *((volatile int*)__null) = 77; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
77 "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"
, 77); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mReflowInput.mFrame->IsTableFrame()"
") (" "TableReflowInput should only be created for nsTableFrame"
")"); do { *((volatile int*)__null) = 77; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
78 auto* table = static_cast<nsTableFrame*>(mReflowInput.mFrame);
79
80 mICoord = aBorderPadding.IStart(mWM) + table->GetColSpacing(-1);
81 mAvailSize.ISize(mWM) =
82 std::max(0, mReflowInput.ComputedISize() - table->GetColSpacing(-1) -
83 table->GetColSpacing(table->GetColCount()));
84
85 mAvailSize.BSize(mWM) = aMode == TableReflowMode::Measuring
86 ? NS_UNCONSTRAINEDSIZE
87 : mReflowInput.AvailableBSize();
88 AdvanceBCoord(aBorderPadding.BStart(mWM) +
89 (!table->GetPrevInFlow() ? table->GetRowSpacing(-1) : 0));
90 if (aReflowInput.mStyleBorder->mBoxDecorationBreak ==
91 StyleBoxDecorationBreak::Clone) {
92 // At this point, we're assuming we won't be the last fragment, so we only
93 // reserve space for block-end border-padding if we're cloning it on each
94 // fragment; and we don't need to reserve any row-spacing for this
95 // hypothetical fragmentation, either.
96 ReduceAvailableBSizeBy(aBorderPadding.BEnd(mWM));
97 }
98 }
99
100 // Advance to the next block-offset and reduce the available block-size.
101 void AdvanceBCoord(nscoord aAmount) {
102 mBCoord += aAmount;
103 ReduceAvailableBSizeBy(aAmount);
104 }
105
106 const LogicalSize& AvailableSize() const { return mAvailSize; }
107
108 // The real reflow input of the table frame.
109 const ReflowInput& mReflowInput;
110
111 // Stationary inline-offset, which won't change after the constructor.
112 nscoord mICoord = 0;
113
114 // Running block-offset, which will be adjusted as we reflow children.
115 nscoord mBCoord = 0;
116
117 private:
118 void ReduceAvailableBSizeBy(nscoord aAmount) {
119 if (mAvailSize.BSize(mWM) == NS_UNCONSTRAINEDSIZE) {
120 return;
121 }
122 mAvailSize.BSize(mWM) -= aAmount;
123 mAvailSize.BSize(mWM) = std::max(0, mAvailSize.BSize(mWM));
124 }
125
126 // mReflowInput's (i.e. table frame's) writing-mode.
127 WritingMode mWM;
128
129 // The available size for children. The inline-size is stationary after the
130 // constructor, but the block-size will be adjusted as we reflow children.
131 LogicalSize mAvailSize;
132};
133
134struct TableBCData final {
135 TableArea mDamageArea;
136 nscoord mBStartBorderWidth = 0;
137 nscoord mIEndBorderWidth = 0;
138 nscoord mBEndBorderWidth = 0;
139 nscoord mIStartBorderWidth = 0;
140 nscoord mIStartCellBorderWidth = 0;
141 nscoord mIEndCellBorderWidth = 0;
142};
143
144} // namespace mozilla
145
146/********************************************************************************
147 ** nsTableFrame **
148 ********************************************************************************/
149
150ComputedStyle* nsTableFrame::GetParentComputedStyle(
151 nsIFrame** aProviderFrame) const {
152 // Since our parent, the table wrapper frame, returned this frame, we
153 // must return whatever our parent would normally have returned.
154
155 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"
, 155); AnnotateMozCrashReason("MOZ_ASSERT" "(" "GetParent()"
") (" "table constructed without table wrapper" ")"); do { *
((volatile int*)__null) = 155; __attribute__((nomerge)) ::abort
(); } while (false); } } while (false)
;
156 if (!mContent->GetParent() && !Style()->IsPseudoOrAnonBox()) {
157 // We're the root. We have no ComputedStyle parent.
158 *aProviderFrame = nullptr;
159 return nullptr;
160 }
161
162 return GetParent()->DoGetParentComputedStyle(aProviderFrame);
163}
164
165nsTableFrame::nsTableFrame(ComputedStyle* aStyle, nsPresContext* aPresContext,
166 ClassID aID)
167 : nsContainerFrame(aStyle, aPresContext, aID) {
168 memset(&mBits, 0, sizeof(mBits));
169}
170
171void nsTableFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
172 nsIFrame* aPrevInFlow) {
173 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"
, 173); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!mCellMap" ") ("
"Init called twice" ")"); do { *((volatile int*)__null) = 173
; __attribute__((nomerge)) ::abort(); } while (false); } } while
(false)
;
174 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"
, 174); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!mTableLayoutStrategy"
") (" "Init called twice" ")"); do { *((volatile int*)__null
) = 174; __attribute__((nomerge)) ::abort(); } while (false);
} } while (false)
;
175 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"
, 176); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!aPrevInFlow || aPrevInFlow->IsTableFrame()"
") (" "prev-in-flow must be of same type" ")"); do { *((volatile
int*)__null) = 176; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
176 "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"
, 176); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!aPrevInFlow || aPrevInFlow->IsTableFrame()"
") (" "prev-in-flow must be of same type" ")"); do { *((volatile
int*)__null) = 176; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
;
177
178 // Let the base class do its processing
179 nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
180
181 // see if border collapse is on, if so set it
182 const nsStyleTableBorder* tableStyle = StyleTableBorder();
183 bool borderCollapse =
184 (StyleBorderCollapse::Collapse == tableStyle->mBorderCollapse);
185 SetBorderCollapse(borderCollapse);
186 if (borderCollapse) {
187 SetNeedToCalcHasBCBorders(true);
188 }
189
190 if (!aPrevInFlow) {
191 // If we're the first-in-flow, we manage the cell map & layout strategy that
192 // get used by our continuation chain:
193 mCellMap = MakeUnique<nsTableCellMap>(*this, borderCollapse);
194 if (IsAutoLayout()) {
195 mTableLayoutStrategy = MakeUnique<BasicTableLayoutStrategy>(this);
196 } else {
197 mTableLayoutStrategy = MakeUnique<FixedTableLayoutStrategy>(this);
198 }
199 } else {
200 // Set my isize, because all frames in a table flow are the same isize and
201 // code in nsTableWrapperFrame depends on this being set.
202 WritingMode wm = GetWritingMode();
203 SetSize(LogicalSize(wm, aPrevInFlow->ISize(wm), BSize(wm)));
204 }
205}
206
207// Define here (Rather than in the header), even if it's trival, to avoid
208// UniquePtr members causing compile errors when their destructors are
209// implicitly inserted into this destructor. Destruction requires
210// the full definition of types that these UniquePtrs are managing, and
211// the header only has forward declarations of them.
212nsTableFrame::~nsTableFrame() = default;
213
214void nsTableFrame::Destroy(DestroyContext& aContext) {
215 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"
, 215); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!mBits.mIsDestroying"
")"); do { *((volatile int*)__null) = 215; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
216 mBits.mIsDestroying = true;
217 mColGroups.DestroyFrames(aContext);
218 nsContainerFrame::Destroy(aContext);
219}
220
221// Make sure any views are positioned properly
222void nsTableFrame::RePositionViews(nsIFrame* aFrame) {
223 nsContainerFrame::PositionFrameView(aFrame);
224 nsContainerFrame::PositionChildViews(aFrame);
225}
226
227static bool IsRepeatedFrame(nsIFrame* kidFrame) {
228 return (kidFrame->IsTableRowFrame() || kidFrame->IsTableRowGroupFrame()) &&
229 kidFrame->HasAnyStateBits(NS_REPEATED_ROW_OR_ROWGROUP);
230}
231
232bool nsTableFrame::PageBreakAfter(nsIFrame* aSourceFrame,
233 nsIFrame* aNextFrame) {
234 const nsStyleDisplay* display = aSourceFrame->StyleDisplay();
235 nsTableRowGroupFrame* prevRg = do_QueryFrame(aSourceFrame);
236 // don't allow a page break after a repeated element ...
237 if ((display->BreakAfter() || (prevRg && prevRg->HasInternalBreakAfter())) &&
238 !IsRepeatedFrame(aSourceFrame)) {
239 return !(aNextFrame && IsRepeatedFrame(aNextFrame)); // or before
240 }
241
242 if (aNextFrame) {
243 display = aNextFrame->StyleDisplay();
244 // don't allow a page break before a repeated element ...
245 nsTableRowGroupFrame* nextRg = do_QueryFrame(aNextFrame);
246 if ((display->BreakBefore() ||
247 (nextRg && nextRg->HasInternalBreakBefore())) &&
248 !IsRepeatedFrame(aNextFrame)) {
249 return !IsRepeatedFrame(aSourceFrame); // or after
250 }
251 }
252 return false;
253}
254
255/* static */
256void nsTableFrame::PositionedTablePartMaybeChanged(nsIFrame* aFrame,
257 ComputedStyle* aOldStyle) {
258 const bool wasPositioned =
259 aOldStyle && aOldStyle->IsAbsPosContainingBlock(aFrame);
260 const bool isPositioned = aFrame->IsAbsPosContainingBlock();
261 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"
, 261); AnnotateMozCrashReason("MOZ_ASSERT" "(" "isPositioned == aFrame->Style()->IsAbsPosContainingBlock(aFrame)"
")"); do { *((volatile int*)__null) = 261; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
262 if (wasPositioned == isPositioned) {
263 return;
264 }
265
266 nsTableFrame* tableFrame = GetTableFrame(aFrame);
267 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"
, 267); AnnotateMozCrashReason("MOZ_ASSERT" "(" "tableFrame" ") ("
"Should have a table frame here" ")"); do { *((volatile int*
)__null) = 267; __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
268 tableFrame = static_cast<nsTableFrame*>(tableFrame->FirstContinuation());
269
270 // Retrieve the positioned parts array for this table.
271 FrameTArray* positionedParts =
272 tableFrame->GetProperty(PositionedTablePartArray());
273
274 // Lazily create the array if it doesn't exist yet.
275 if (!positionedParts) {
276 positionedParts = new FrameTArray;
277 tableFrame->SetProperty(PositionedTablePartArray(), positionedParts);
278 }
279
280 if (isPositioned) {
281 // Add this frame to the list.
282 positionedParts->AppendElement(aFrame);
283 } else {
284 positionedParts->RemoveElement(aFrame);
285 }
286}
287
288/* static */
289void nsTableFrame::MaybeUnregisterPositionedTablePart(nsIFrame* aFrame) {
290 if (!aFrame->IsAbsPosContainingBlock()) {
291 return;
292 }
293 nsTableFrame* tableFrame = GetTableFrame(aFrame);
294 tableFrame = static_cast<nsTableFrame*>(tableFrame->FirstContinuation());
295
296 if (tableFrame->IsDestroying()) {
297 return; // We're throwing the table away anyways.
298 }
299
300 // Retrieve the positioned parts array for this table.
301 FrameTArray* positionedParts =
302 tableFrame->GetProperty(PositionedTablePartArray());
303
304 // Remove the frame.
305 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"
, 307); AnnotateMozCrashReason("MOZ_ASSERT" "(" "positionedParts && positionedParts->Contains(aFrame)"
") (" "Asked to unregister a positioned table part that wasn't registered"
")"); do { *((volatile int*)__null) = 307; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
306 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"
, 307); AnnotateMozCrashReason("MOZ_ASSERT" "(" "positionedParts && positionedParts->Contains(aFrame)"
") (" "Asked to unregister a positioned table part that wasn't registered"
")"); do { *((volatile int*)__null) = 307; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
307 "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"
, 307); AnnotateMozCrashReason("MOZ_ASSERT" "(" "positionedParts && positionedParts->Contains(aFrame)"
") (" "Asked to unregister a positioned table part that wasn't registered"
")"); do { *((volatile int*)__null) = 307; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
308 if (positionedParts) {
309 positionedParts->RemoveElement(aFrame);
310 }
311}
312
313// XXX this needs to be cleaned up so that the frame constructor breaks out col
314// group frames into a separate child list, bug 343048.
315void nsTableFrame::SetInitialChildList(ChildListID aListID,
316 nsFrameList&& aChildList) {
317 if (aListID != FrameChildListID::Principal) {
318 nsContainerFrame::SetInitialChildList(aListID, std::move(aChildList));
319 return;
320 }
321
322 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"
, 323); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mFrames.IsEmpty() && mColGroups.IsEmpty()"
") (" "unexpected second call to SetInitialChildList" ")"); do
{ *((volatile int*)__null) = 323; __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
323 "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"
, 323); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mFrames.IsEmpty() && mColGroups.IsEmpty()"
") (" "unexpected second call to SetInitialChildList" ")"); do
{ *((volatile int*)__null) = 323; __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
324#ifdef DEBUG1
325 for (nsIFrame* f : aChildList) {
326 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"
, 326); AnnotateMozCrashReason("MOZ_ASSERT" "(" "f->GetParent() == this"
") (" "Unexpected parent" ")"); do { *((volatile int*)__null
) = 326; __attribute__((nomerge)) ::abort(); } while (false);
} } while (false)
;
327 }
328#endif
329
330 // XXXbz the below code is an icky cesspit that's only needed in its current
331 // form for two reasons:
332 // 1) Both rowgroups and column groups come in on the principal child list.
333 while (aChildList.NotEmpty()) {
334 nsIFrame* childFrame = aChildList.FirstChild();
335 aChildList.RemoveFirstChild();
336 const nsStyleDisplay* childDisplay = childFrame->StyleDisplay();
337
338 if (mozilla::StyleDisplay::TableColumnGroup == childDisplay->mDisplay) {
339 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"
, 340); MOZ_PretendNoReturn(); } } while (0)
340 "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"
, 340); MOZ_PretendNoReturn(); } } while (0)
;
341 mColGroups.AppendFrame(nullptr, childFrame);
342 } else { // row groups and unknown frames go on the main list for now
343 mFrames.AppendFrame(nullptr, childFrame);
344 }
345 }
346
347 // If we have a prev-in-flow, then we're a table that has been split and
348 // so don't treat this like an append
349 if (!GetPrevInFlow()) {
350 // process col groups first so that real cols get constructed before
351 // anonymous ones due to cells in rows.
352 InsertColGroups(0, mColGroups);
353 InsertRowGroups(mFrames);
354 // calc collapsing borders
355 if (IsBorderCollapse()) {
356 SetFullBCDamageArea();
357 }
358 }
359}
360
361void nsTableFrame::RowOrColSpanChanged(nsTableCellFrame* aCellFrame) {
362 if (aCellFrame) {
363 nsTableCellMap* cellMap = GetCellMap();
364 if (cellMap) {
365 // for now just remove the cell from the map and reinsert it
366 uint32_t rowIndex = aCellFrame->RowIndex();
367 uint32_t colIndex = aCellFrame->ColIndex();
368 RemoveCell(aCellFrame, rowIndex);
369 AutoTArray<nsTableCellFrame*, 1> cells;
370 cells.AppendElement(aCellFrame);
371 InsertCells(cells, rowIndex, colIndex - 1);
372
373 // XXX Should this use IntrinsicDirty::FrameAncestorsAndDescendants? It
374 // currently doesn't need to, but it might given more optimization.
375 PresShell()->FrameNeedsReflow(this, IntrinsicDirty::FrameAndAncestors,
376 NS_FRAME_IS_DIRTY);
377 }
378 }
379}
380
381/* ****** CellMap methods ******* */
382
383/* return the effective col count */
384int32_t nsTableFrame::GetEffectiveColCount() const {
385 int32_t colCount = GetColCount();
386 if (LayoutStrategy()->GetType() == nsITableLayoutStrategy::Auto) {
387 nsTableCellMap* cellMap = GetCellMap();
388 if (!cellMap) {
389 return 0;
390 }
391 // don't count cols at the end that don't have originating cells
392 for (int32_t colIdx = colCount - 1; colIdx >= 0; colIdx--) {
393 if (cellMap->GetNumCellsOriginatingInCol(colIdx) > 0) {
394 break;
395 }
396 colCount--;
397 }
398 }
399 return colCount;
400}
401
402int32_t nsTableFrame::GetIndexOfLastRealCol() {
403 int32_t numCols = mColFrames.Length();
404 if (numCols > 0) {
405 for (int32_t colIdx = numCols - 1; colIdx >= 0; colIdx--) {
406 nsTableColFrame* colFrame = GetColFrame(colIdx);
407 if (colFrame) {
408 if (eColAnonymousCell != colFrame->GetColType()) {
409 return colIdx;
410 }
411 }
412 }
413 }
414 return -1;
415}
416
417nsTableColFrame* nsTableFrame::GetColFrame(int32_t aColIndex) const {
418 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"
, 418); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!GetPrevInFlow()"
") (" "GetColFrame called on next in flow" ")"); do { *((volatile
int*)__null) = 418; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
;
419 int32_t numCols = mColFrames.Length();
420 if ((aColIndex >= 0) && (aColIndex < numCols)) {
421 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"
, 421); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mColFrames.ElementAt(aColIndex)"
")"); do { *((volatile int*)__null) = 421; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
422 return mColFrames.ElementAt(aColIndex);
423 } else {
424 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"
, 424); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "invalid col index" ")"); do { *(
(volatile int*)__null) = 424; __attribute__((nomerge)) ::abort
(); } while (false); } } while (false)
;
425 return nullptr;
426 }
427}
428
429int32_t nsTableFrame::GetEffectiveRowSpan(int32_t aRowIndex,
430 const nsTableCellFrame& aCell) const {
431 nsTableCellMap* cellMap = GetCellMap();
432 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"
, 432); AnnotateMozCrashReason("MOZ_ASSERT" "(" "nullptr != cellMap"
") (" "bad call, cellMap not yet allocated." ")"); do { *((volatile
int*)__null) = 432; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
;
433
434 return cellMap->GetEffectiveRowSpan(aRowIndex, aCell.ColIndex());
435}
436
437int32_t nsTableFrame::GetEffectiveRowSpan(const nsTableCellFrame& aCell,
438 nsCellMap* aCellMap) {
439 nsTableCellMap* tableCellMap = GetCellMap();
440 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"
, 440); MOZ_PretendNoReturn(); } } while (0); return 1; }
;
441
442 uint32_t colIndex = aCell.ColIndex();
443 uint32_t rowIndex = aCell.RowIndex();
444
445 if (aCellMap)
446 return aCellMap->GetRowSpan(rowIndex, colIndex, true);
447 else
448 return tableCellMap->GetEffectiveRowSpan(rowIndex, colIndex);
449}
450
451int32_t nsTableFrame::GetEffectiveColSpan(const nsTableCellFrame& aCell,
452 nsCellMap* aCellMap) const {
453 nsTableCellMap* tableCellMap = GetCellMap();
454 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"
, 454); MOZ_PretendNoReturn(); } } while (0); return 1; }
;
455
456 uint32_t colIndex = aCell.ColIndex();
457 uint32_t rowIndex = aCell.RowIndex();
458
459 if (aCellMap)
460 return aCellMap->GetEffectiveColSpan(*tableCellMap, rowIndex, colIndex);
461 else
462 return tableCellMap->GetEffectiveColSpan(rowIndex, colIndex);
463}
464
465bool nsTableFrame::HasMoreThanOneCell(int32_t aRowIndex) const {
466 nsTableCellMap* tableCellMap = GetCellMap();
467 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"
, 467); MOZ_PretendNoReturn(); } } while (0); return 1; }
;
468 return tableCellMap->HasMoreThanOneCell(aRowIndex);
469}
470
471void nsTableFrame::AdjustRowIndices(int32_t aRowIndex, int32_t aAdjustment) {
472 // Iterate over the row groups and adjust the row indices of all rows
473 // whose index is >= aRowIndex.
474 RowGroupArray rowGroups = OrderedRowGroups();
475
476 for (uint32_t rgIdx = 0; rgIdx < rowGroups.Length(); rgIdx++) {
477 rowGroups[rgIdx]->AdjustRowIndices(aRowIndex, aAdjustment);
478 }
479}
480
481void nsTableFrame::ResetRowIndices(
482 const nsFrameList::Slice& aRowGroupsToExclude) {
483 // Iterate over the row groups and adjust the row indices of all rows
484 // omit the rowgroups that will be inserted later
485 mDeletedRowIndexRanges.clear();
486
487 RowGroupArray rowGroups = OrderedRowGroups();
488
489 nsTHashSet<nsTableRowGroupFrame*> excludeRowGroups;
490 for (nsIFrame* excludeRowGroup : aRowGroupsToExclude) {
491 excludeRowGroups.Insert(
492 static_cast<nsTableRowGroupFrame*>(excludeRowGroup));
493#ifdef DEBUG1
494 {
495 // Check to make sure that the row indices of all rows in excluded row
496 // groups are '0' (i.e. the initial value since they haven't been added
497 // yet)
498 const nsFrameList& rowFrames = excludeRowGroup->PrincipalChildList();
499 for (nsIFrame* r : rowFrames) {
500 auto* row = static_cast<nsTableRowFrame*>(r);
501 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"
, 503); 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) = 503; __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
502 "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"
, 503); 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) = 503; __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
503 "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"
, 503); 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) = 503; __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
504 }
505 }
506#endif
507 }
508
509 int32_t rowIndex = 0;
510 for (uint32_t rgIdx = 0; rgIdx < rowGroups.Length(); rgIdx++) {
511 nsTableRowGroupFrame* rgFrame = rowGroups[rgIdx];
512 if (!excludeRowGroups.Contains(rgFrame)) {
513 const nsFrameList& rowFrames = rgFrame->PrincipalChildList();
514 for (nsIFrame* r : rowFrames) {
515 if (mozilla::StyleDisplay::TableRow == r->StyleDisplay()->mDisplay) {
516 auto* row = static_cast<nsTableRowFrame*>(r);
517 row->SetRowIndex(rowIndex);
518 rowIndex++;
519 }
520 }
521 }
522 }
523}
524
525void nsTableFrame::InsertColGroups(int32_t aStartColIndex,
526 const nsFrameList::Slice& aColGroups) {
527 int32_t colIndex = aStartColIndex;
528
529 // XXX: We cannot use range-based for loop because AddColsToTable() can
530 // destroy the nsTableColGroupFrame in the slice we're traversing! Need to
531 // check the validity of *colGroupIter.
532 auto colGroupIter = aColGroups.begin();
533 for (auto colGroupIterEnd = aColGroups.end();
534 *colGroupIter && colGroupIter != colGroupIterEnd; ++colGroupIter) {
535 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"
, 535); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(*colGroupIter)->IsTableColGroupFrame()"
")"); do { *((volatile int*)__null) = 535; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
536 auto* cgFrame = static_cast<nsTableColGroupFrame*>(*colGroupIter);
537 cgFrame->SetStartColumnIndex(colIndex);
538 cgFrame->AddColsToTable(colIndex, false, cgFrame->PrincipalChildList());
539 int32_t numCols = cgFrame->GetColCount();
540 colIndex += numCols;
541 }
542
543 if (*colGroupIter) {
544 nsTableColGroupFrame::ResetColIndices(*colGroupIter, colIndex);
545 }
546}
547
548void nsTableFrame::InsertCol(nsTableColFrame& aColFrame, int32_t aColIndex) {
549 mColFrames.InsertElementAt(aColIndex, &aColFrame);
550 nsTableColType insertedColType = aColFrame.GetColType();
551 int32_t numCacheCols = mColFrames.Length();
552 nsTableCellMap* cellMap = GetCellMap();
553 if (cellMap) {
554 int32_t numMapCols = cellMap->GetColCount();
555 if (numCacheCols > numMapCols) {
556 bool removedFromCache = false;
557 if (eColAnonymousCell != insertedColType) {
558 nsTableColFrame* lastCol = mColFrames.ElementAt(numCacheCols - 1);
559 if (lastCol) {
560 nsTableColType lastColType = lastCol->GetColType();
561 if (eColAnonymousCell == lastColType) {
562 // remove the col from the cache
563 mColFrames.RemoveLastElement();
564 // remove the col from the synthetic col group
565 nsTableColGroupFrame* lastColGroup =
566 (nsTableColGroupFrame*)mColGroups.LastChild();
567 if (lastColGroup) {
568 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"
, 568); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lastColGroup->IsSynthetic()"
")"); do { *((volatile int*)__null) = 568; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
569 DestroyContext context(PresShell());
570 lastColGroup->RemoveChild(context, *lastCol, false);
571
572 // remove the col group if it is empty
573 if (lastColGroup->GetColCount() <= 0) {
574 mColGroups.DestroyFrame(context, (nsIFrame*)lastColGroup);
575 }
576 }
577 removedFromCache = true;
578 }
579 }
580 }
581 if (!removedFromCache) {
582 cellMap->AddColsAtEnd(1);
583 }
584 }
585 }
586 // for now, just bail and recalc all of the collapsing borders
587 if (IsBorderCollapse()) {
588 TableArea damageArea(aColIndex, 0, GetColCount() - aColIndex,
589 GetRowCount());
590 AddBCDamageArea(damageArea);
591 }
592}
593
594void nsTableFrame::RemoveCol(nsTableColGroupFrame* aColGroupFrame,
595 int32_t aColIndex, bool aRemoveFromCache,
596 bool aRemoveFromCellMap) {
597 if (aRemoveFromCache) {
598 mColFrames.RemoveElementAt(aColIndex);
599 }
600 if (aRemoveFromCellMap) {
601 nsTableCellMap* cellMap = GetCellMap();
602 if (cellMap) {
603 // If we have some anonymous cols at the end already, we just
604 // add a new anonymous col.
605 if (!mColFrames.IsEmpty() &&
606 mColFrames.LastElement() && // XXXbz is this ever null?
607 mColFrames.LastElement()->GetColType() == eColAnonymousCell) {
608 AppendAnonymousColFrames(1);
609 } else {
610 // All of our colframes correspond to actual <col> tags. It's possible
611 // that we still have at least as many <col> tags as we have logical
612 // columns from cells, but we might have one less. Handle the latter
613 // case as follows: First ask the cellmap to drop its last col if it
614 // doesn't have any actual cells in it. Then call
615 // MatchCellMapToColCache to append an anonymous column if it's needed;
616 // this needs to be after RemoveColsAtEnd, since it will determine the
617 // need for a new column frame based on the width of the cell map.
618 cellMap->RemoveColsAtEnd();
619 MatchCellMapToColCache(cellMap);
620 }
621 }
622 }
623 // for now, just bail and recalc all of the collapsing borders
624 if (IsBorderCollapse()) {
625 TableArea damageArea(0, 0, GetColCount(), GetRowCount());
626 AddBCDamageArea(damageArea);
627 }
628}
629
630/** Get the cell map for this table frame. It is not always mCellMap.
631 * Only the first-in-flow has a legit cell map.
632 */
633nsTableCellMap* nsTableFrame::GetCellMap() const {
634 return static_cast<nsTableFrame*>(FirstInFlow())->mCellMap.get();
635}
636
637nsTableColGroupFrame* nsTableFrame::CreateSyntheticColGroupFrame() {
638 nsIContent* colGroupContent = GetContent();
639 mozilla::PresShell* presShell = PresShell();
640
641 RefPtr<ComputedStyle> colGroupStyle;
642 colGroupStyle = presShell->StyleSet()->ResolveNonInheritingAnonymousBoxStyle(
643 PseudoStyleType::tableColGroup);
644 // Create a col group frame
645 nsTableColGroupFrame* newFrame =
646 NS_NewTableColGroupFrame(presShell, colGroupStyle);
647 newFrame->SetIsSynthetic();
648 newFrame->Init(colGroupContent, this, nullptr);
649 return newFrame;
650}
651
652void nsTableFrame::AppendAnonymousColFrames(int32_t aNumColsToAdd) {
653 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"
, 653); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aNumColsToAdd > 0"
") (" "We should be adding _something_." ")"); do { *((volatile
int*)__null) = 653; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
;
654 // get the last col group frame
655 nsTableColGroupFrame* colGroupFrame =
656 static_cast<nsTableColGroupFrame*>(mColGroups.LastChild());
657
658 if (!colGroupFrame || !colGroupFrame->IsSynthetic()) {
659 int32_t colIndex = (colGroupFrame) ? colGroupFrame->GetStartColumnIndex() +
660 colGroupFrame->GetColCount()
661 : 0;
662 colGroupFrame = CreateSyntheticColGroupFrame();
663 if (!colGroupFrame) {
664 return;
665 }
666 // add the new frame to the child list
667 mColGroups.AppendFrame(this, colGroupFrame);
668 colGroupFrame->SetStartColumnIndex(colIndex);
669 }
670 AppendAnonymousColFrames(colGroupFrame, aNumColsToAdd, eColAnonymousCell,
671 true);
672}
673
674// XXX this needs to be moved to nsCSSFrameConstructor
675// Right now it only creates the col frames at the end
676void nsTableFrame::AppendAnonymousColFrames(
677 nsTableColGroupFrame* aColGroupFrame, int32_t aNumColsToAdd,
678 nsTableColType aColType, bool aAddToTable) {
679 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"
, 679); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aColGroupFrame"
") (" "null frame" ")"); do { *((volatile int*)__null) = 679
; __attribute__((nomerge)) ::abort(); } while (false); } } while
(false)
;
680 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"
, 680); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aColType != eColAnonymousCol"
") (" "Shouldn't happen" ")"); do { *((volatile int*)__null)
= 680; __attribute__((nomerge)) ::abort(); } while (false); }
} while (false)
;
681 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"
, 681); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aNumColsToAdd > 0"
") (" "We should be adding _something_." ")"); do { *((volatile
int*)__null) = 681; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
;
682
683 mozilla::PresShell* presShell = PresShell();
684
685 // Get the last col frame
686 nsFrameList newColFrames;
687
688 int32_t startIndex = mColFrames.Length();
689 int32_t lastIndex = startIndex + aNumColsToAdd - 1;
690
691 for (int32_t childX = startIndex; childX <= lastIndex; childX++) {
692 // all anonymous cols that we create here use a pseudo ComputedStyle of the
693 // col group
694 nsIContent* iContent = aColGroupFrame->GetContent();
695 RefPtr<ComputedStyle> computedStyle =
696 presShell->StyleSet()->ResolveNonInheritingAnonymousBoxStyle(
697 PseudoStyleType::tableCol);
698 // ASSERTION to check for bug 54454 sneaking back in...
699 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"
, 699); MOZ_PretendNoReturn(); } } while (0)
;
700
701 // create the new col frame
702 nsIFrame* colFrame = NS_NewTableColFrame(presShell, computedStyle);
703 ((nsTableColFrame*)colFrame)->SetColType(aColType);
704 colFrame->Init(iContent, aColGroupFrame, nullptr);
705
706 newColFrames.AppendFrame(nullptr, colFrame);
707 }
708 nsFrameList& cols = aColGroupFrame->GetWritableChildList();
709 nsIFrame* oldLastCol = cols.LastChild();
710 const nsFrameList::Slice& newCols =
711 cols.InsertFrames(nullptr, oldLastCol, std::move(newColFrames));
712 if (aAddToTable) {
713 // get the starting col index in the cache
714 int32_t startColIndex;
715 if (oldLastCol) {
716 startColIndex =
717 static_cast<nsTableColFrame*>(oldLastCol)->GetColIndex() + 1;
718 } else {
719 startColIndex = aColGroupFrame->GetStartColumnIndex();
720 }
721
722 aColGroupFrame->AddColsToTable(startColIndex, true, newCols);
723 }
724}
725
726void nsTableFrame::MatchCellMapToColCache(nsTableCellMap* aCellMap) {
727 int32_t numColsInMap = GetColCount();
728 int32_t numColsInCache = mColFrames.Length();
729 int32_t numColsToAdd = numColsInMap - numColsInCache;
730 if (numColsToAdd > 0) {
731 // this sets the child list, updates the col cache and cell map
732 AppendAnonymousColFrames(numColsToAdd);
733 }
734 if (numColsToAdd < 0) {
735 int32_t numColsNotRemoved = DestroyAnonymousColFrames(-numColsToAdd);
736 // if the cell map has fewer cols than the cache, correct it
737 if (numColsNotRemoved > 0) {
738 aCellMap->AddColsAtEnd(numColsNotRemoved);
739 }
740 }
741}
742
743void nsTableFrame::DidResizeColumns() {
744 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"
, 744); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!GetPrevInFlow()"
") (" "should only be called on first-in-flow" ")"); do { *(
(volatile int*)__null) = 744; __attribute__((nomerge)) ::abort
(); } while (false); } } while (false)
;
745
746 if (mBits.mResizedColumns) return; // already marked
747
748 for (nsTableFrame* f = this; f;
749 f = static_cast<nsTableFrame*>(f->GetNextInFlow()))
750 f->mBits.mResizedColumns = true;
751}
752
753void nsTableFrame::AppendCell(nsTableCellFrame& aCellFrame, int32_t aRowIndex) {
754 nsTableCellMap* cellMap = GetCellMap();
755 if (cellMap) {
756 TableArea damageArea(0, 0, 0, 0);
757 cellMap->AppendCell(aCellFrame, aRowIndex, true, damageArea);
758 MatchCellMapToColCache(cellMap);
759 if (IsBorderCollapse()) {
760 AddBCDamageArea(damageArea);
761 }
762 }
763}
764
765void nsTableFrame::InsertCells(nsTArray<nsTableCellFrame*>& aCellFrames,
766 int32_t aRowIndex, int32_t aColIndexBefore) {
767 nsTableCellMap* cellMap = GetCellMap();
768 if (cellMap) {
769 TableArea damageArea(0, 0, 0, 0);
770 cellMap->InsertCells(aCellFrames, aRowIndex, aColIndexBefore, damageArea);
771 MatchCellMapToColCache(cellMap);
772 if (IsBorderCollapse()) {
773 AddBCDamageArea(damageArea);
774 }
775 }
776}
777
778// this removes the frames from the col group and table, but not the cell map
779int32_t nsTableFrame::DestroyAnonymousColFrames(int32_t aNumFrames) {
780 // only remove cols that are of type eTypeAnonymous cell (they are at the end)
781 int32_t endIndex = mColFrames.Length() - 1;
782 int32_t startIndex = (endIndex - aNumFrames) + 1;
783 int32_t numColsRemoved = 0;
784 DestroyContext context(PresShell());
785 for (int32_t colIdx = endIndex; colIdx >= startIndex; colIdx--) {
786 nsTableColFrame* colFrame = GetColFrame(colIdx);
787 if (colFrame && (eColAnonymousCell == colFrame->GetColType())) {
788 auto* cgFrame = static_cast<nsTableColGroupFrame*>(colFrame->GetParent());
789 // remove the frame from the colgroup
790 cgFrame->RemoveChild(context, *colFrame, false);
791 // remove the frame from the cache, but not the cell map
792 RemoveCol(nullptr, colIdx, true, false);
793 numColsRemoved++;
794 } else {
795 break;
796 }
797 }
798 return (aNumFrames - numColsRemoved);
799}
800
801void nsTableFrame::RemoveCell(nsTableCellFrame* aCellFrame, int32_t aRowIndex) {
802 nsTableCellMap* cellMap = GetCellMap();
803 if (cellMap) {
804 TableArea damageArea(0, 0, 0, 0);
805 cellMap->RemoveCell(aCellFrame, aRowIndex, damageArea);
806 MatchCellMapToColCache(cellMap);
807 if (IsBorderCollapse()) {
808 AddBCDamageArea(damageArea);
809 }
810 }
811}
812
813int32_t nsTableFrame::GetStartRowIndex(
814 const nsTableRowGroupFrame* aRowGroupFrame) const {
815 RowGroupArray orderedRowGroups = OrderedRowGroups();
816
817 int32_t rowIndex = 0;
818 for (uint32_t rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) {
819 nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgIndex];
820 if (rgFrame == aRowGroupFrame) {
821 break;
822 }
823 int32_t numRows = rgFrame->GetRowCount();
824 rowIndex += numRows;
825 }
826 return rowIndex;
827}
828
829// this cannot extend beyond a single row group
830void nsTableFrame::AppendRows(nsTableRowGroupFrame* aRowGroupFrame,
831 int32_t aRowIndex,
832 nsTArray<nsTableRowFrame*>& aRowFrames) {
833 nsTableCellMap* cellMap = GetCellMap();
834 if (cellMap) {
835 int32_t absRowIndex = GetStartRowIndex(aRowGroupFrame) + aRowIndex;
836 InsertRows(aRowGroupFrame, aRowFrames, absRowIndex, true);
837 }
838}
839
840// this cannot extend beyond a single row group
841int32_t nsTableFrame::InsertRows(nsTableRowGroupFrame* aRowGroupFrame,
842 nsTArray<nsTableRowFrame*>& aRowFrames,
843 int32_t aRowIndex, bool aConsiderSpans) {
844#ifdef DEBUG_TABLE_CELLMAP
845 printf("=== insertRowsBefore firstRow=%d \n", aRowIndex);
846 Dump(true, false, true);
847#endif
848
849 int32_t numColsToAdd = 0;
850 nsTableCellMap* cellMap = GetCellMap();
851 if (cellMap) {
852 TableArea damageArea(0, 0, 0, 0);
853 bool shouldRecalculateIndex = !IsDeletedRowIndexRangesEmpty();
854 if (shouldRecalculateIndex) {
855 ResetRowIndices(nsFrameList::Slice(nullptr, nullptr));
856 }
857 int32_t origNumRows = cellMap->GetRowCount();
858 int32_t numNewRows = aRowFrames.Length();
859 cellMap->InsertRows(aRowGroupFrame, aRowFrames, aRowIndex, aConsiderSpans,
860 damageArea);
861 MatchCellMapToColCache(cellMap);
862
863 // Perform row index adjustment only if row indices were not
864 // reset above
865 if (!shouldRecalculateIndex) {
866 if (aRowIndex < origNumRows) {
867 AdjustRowIndices(aRowIndex, numNewRows);
868 }
869
870 // assign the correct row indices to the new rows. If they were
871 // recalculated above it may not have been done correctly because each row
872 // is constructed with index 0
873 for (int32_t rowB = 0; rowB < numNewRows; rowB++) {
874 nsTableRowFrame* rowFrame = aRowFrames.ElementAt(rowB);
875 rowFrame->SetRowIndex(aRowIndex + rowB);
876 }
877 }
878
879 if (IsBorderCollapse()) {
880 AddBCDamageArea(damageArea);
881 }
882 }
883#ifdef DEBUG_TABLE_CELLMAP
884 printf("=== insertRowsAfter \n");
885 Dump(true, false, true);
886#endif
887
888 return numColsToAdd;
889}
890
891void nsTableFrame::AddDeletedRowIndex(int32_t aDeletedRowStoredIndex) {
892 if (mDeletedRowIndexRanges.empty()) {
893 mDeletedRowIndexRanges.insert(std::pair<int32_t, int32_t>(
894 aDeletedRowStoredIndex, aDeletedRowStoredIndex));
895 return;
896 }
897
898 // Find the position of the current deleted row's stored index
899 // among the previous deleted row index ranges and merge ranges if
900 // they are consecutive, else add a new (disjoint) range to the map.
901 // Call to mDeletedRowIndexRanges.upper_bound is
902 // O(log(mDeletedRowIndexRanges.size())) therefore call to
903 // AddDeletedRowIndex is also ~O(log(mDeletedRowIndexRanges.size()))
904
905 // greaterIter = will point to smallest range in the map with lower value
906 // greater than the aDeletedRowStoredIndex.
907 // If no such value exists, point to end of map.
908 // smallerIter = will point to largest range in the map with higher value
909 // smaller than the aDeletedRowStoredIndex
910 // If no such value exists, point to beginning of map.
911 // i.e. when both values exist below is true:
912 // smallerIter->second < aDeletedRowStoredIndex < greaterIter->first
913 auto greaterIter = mDeletedRowIndexRanges.upper_bound(aDeletedRowStoredIndex);
914 auto smallerIter = greaterIter;
915
916 if (smallerIter != mDeletedRowIndexRanges.begin()) {
917 smallerIter--;
918 // While greaterIter might be out-of-bounds (by being equal to end()),
919 // smallerIter now cannot be, since we returned early above for a 0-size
920 // map.
921 }
922
923 // Note: smallerIter can only be equal to greaterIter when both
924 // of them point to the beginning of the map and in that case smallerIter
925 // does not "exist" but we clip smallerIter to point to beginning of map
926 // so that it doesn't point to something unknown or outside the map boundry.
927 // Note: When greaterIter is not the end (i.e. it "exists") upper_bound()
928 // ensures aDeletedRowStoredIndex < greaterIter->first so no need to
929 // assert that.
930 MOZ_ASSERT(smallerIter == greaterIter ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(smallerIter == greaterIter || aDeletedRowStoredIndex
> smallerIter->second)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(smallerIter == greaterIter ||
aDeletedRowStoredIndex > smallerIter->second))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("smallerIter == greaterIter || aDeletedRowStoredIndex > smallerIter->second"
" (" "aDeletedRowIndexRanges already contains aDeletedRowStoredIndex! "
"Trying to delete an already deleted row?" ")", "/var/lib/jenkins/workspace/firefox-scan-build/layout/tables/nsTableFrame.cpp"
, 933); AnnotateMozCrashReason("MOZ_ASSERT" "(" "smallerIter == greaterIter || aDeletedRowStoredIndex > smallerIter->second"
") (" "aDeletedRowIndexRanges already contains aDeletedRowStoredIndex! "
"Trying to delete an already deleted row?" ")"); do { *((volatile
int*)__null) = 933; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
931 aDeletedRowStoredIndex > smallerIter->second,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(smallerIter == greaterIter || aDeletedRowStoredIndex
> smallerIter->second)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(smallerIter == greaterIter ||
aDeletedRowStoredIndex > smallerIter->second))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("smallerIter == greaterIter || aDeletedRowStoredIndex > smallerIter->second"
" (" "aDeletedRowIndexRanges already contains aDeletedRowStoredIndex! "
"Trying to delete an already deleted row?" ")", "/var/lib/jenkins/workspace/firefox-scan-build/layout/tables/nsTableFrame.cpp"
, 933); AnnotateMozCrashReason("MOZ_ASSERT" "(" "smallerIter == greaterIter || aDeletedRowStoredIndex > smallerIter->second"
") (" "aDeletedRowIndexRanges already contains aDeletedRowStoredIndex! "
"Trying to delete an already deleted row?" ")"); do { *((volatile
int*)__null) = 933; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
932 "aDeletedRowIndexRanges already contains aDeletedRowStoredIndex! "do { static_assert( mozilla::detail::AssertionConditionType<
decltype(smallerIter == greaterIter || aDeletedRowStoredIndex
> smallerIter->second)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(smallerIter == greaterIter ||
aDeletedRowStoredIndex > smallerIter->second))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("smallerIter == greaterIter || aDeletedRowStoredIndex > smallerIter->second"
" (" "aDeletedRowIndexRanges already contains aDeletedRowStoredIndex! "
"Trying to delete an already deleted row?" ")", "/var/lib/jenkins/workspace/firefox-scan-build/layout/tables/nsTableFrame.cpp"
, 933); AnnotateMozCrashReason("MOZ_ASSERT" "(" "smallerIter == greaterIter || aDeletedRowStoredIndex > smallerIter->second"
") (" "aDeletedRowIndexRanges already contains aDeletedRowStoredIndex! "
"Trying to delete an already deleted row?" ")"); do { *((volatile
int*)__null) = 933; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
933 "Trying to delete an already deleted row?")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(smallerIter == greaterIter || aDeletedRowStoredIndex
> smallerIter->second)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(smallerIter == greaterIter ||
aDeletedRowStoredIndex > smallerIter->second))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("smallerIter == greaterIter || aDeletedRowStoredIndex > smallerIter->second"
" (" "aDeletedRowIndexRanges already contains aDeletedRowStoredIndex! "
"Trying to delete an already deleted row?" ")", "/var/lib/jenkins/workspace/firefox-scan-build/layout/tables/nsTableFrame.cpp"
, 933); AnnotateMozCrashReason("MOZ_ASSERT" "(" "smallerIter == greaterIter || aDeletedRowStoredIndex > smallerIter->second"
") (" "aDeletedRowIndexRanges already contains aDeletedRowStoredIndex! "
"Trying to delete an already deleted row?" ")"); do { *((volatile
int*)__null) = 933; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
;
934
935 if (smallerIter->second == aDeletedRowStoredIndex - 1) {
936 if (greaterIter != mDeletedRowIndexRanges.end() &&
937 greaterIter->first == aDeletedRowStoredIndex + 1) {
938 // merge current index with smaller and greater range as they are
939 // consecutive
940 smallerIter->second = greaterIter->second;
941 mDeletedRowIndexRanges.erase(greaterIter);
942 } else {
943 // add aDeletedRowStoredIndex in the smaller range as it is consecutive
944 smallerIter->second = aDeletedRowStoredIndex;
945 }
946 } else if (greaterIter != mDeletedRowIndexRanges.end() &&
947 greaterIter->first == aDeletedRowStoredIndex + 1) {
948 // add aDeletedRowStoredIndex in the greater range as it is consecutive
949 mDeletedRowIndexRanges.insert(std::pair<int32_t, int32_t>(
950 aDeletedRowStoredIndex, greaterIter->second));
951 mDeletedRowIndexRanges.erase(greaterIter);
952 } else {
953 // add new range as aDeletedRowStoredIndex is disjoint from existing ranges
954 mDeletedRowIndexRanges.insert(std::pair<int32_t, int32_t>(
955 aDeletedRowStoredIndex, aDeletedRowStoredIndex));
956 }
957}
958
959int32_t nsTableFrame::GetAdjustmentForStoredIndex(int32_t aStoredIndex) {
960 if (mDeletedRowIndexRanges.empty()) return 0;
961
962 int32_t adjustment = 0;
963
964 // O(log(mDeletedRowIndexRanges.size()))
965 auto endIter = mDeletedRowIndexRanges.upper_bound(aStoredIndex);
966 for (auto iter = mDeletedRowIndexRanges.begin(); iter != endIter; ++iter) {
967 adjustment += iter->second - iter->first + 1;
968 }
969
970 return adjustment;
971}
972
973// this cannot extend beyond a single row group
974void nsTableFrame::RemoveRows(nsTableRowFrame& aFirstRowFrame,
975 int32_t aNumRowsToRemove, bool aConsiderSpans) {
976#ifdef TBD_OPTIMIZATION
977 // decide if we need to rebalance. we have to do this here because the row
978 // group cannot do it when it gets the dirty reflow corresponding to the frame
979 // being destroyed
980 bool stopTelling = false;
981 for (nsIFrame* kidFrame = aFirstFrame.FirstChild(); (kidFrame && !stopAsking);
982 kidFrame = kidFrame->GetNextSibling()) {
983 nsTableCellFrame* cellFrame = do_QueryFrame(kidFrame);
984 if (cellFrame) {
985 stopTelling = tableFrame->CellChangedWidth(
986 *cellFrame, cellFrame->GetPass1MaxElementWidth(),
987 cellFrame->GetMaximumWidth(), true);
988 }
989 }
990 // XXX need to consider what happens if there are cells that have rowspans
991 // into the deleted row. Need to consider moving rows if a rebalance doesn't
992 // happen
993#endif
994
995 int32_t firstRowIndex = aFirstRowFrame.GetRowIndex();
996#ifdef DEBUG_TABLE_CELLMAP
997 printf("=== removeRowsBefore firstRow=%d numRows=%d\n", firstRowIndex,
998 aNumRowsToRemove);
999 Dump(true, false, true);
1000#endif
1001 nsTableCellMap* cellMap = GetCellMap();
1002 if (cellMap) {
1003 TableArea damageArea(0, 0, 0, 0);
1004
1005 // Mark rows starting from aFirstRowFrame to the next 'aNumRowsToRemove-1'
1006 // number of rows as deleted.
1007 nsTableRowGroupFrame* parentFrame = aFirstRowFrame.GetTableRowGroupFrame();
1008 parentFrame->MarkRowsAsDeleted(aFirstRowFrame, aNumRowsToRemove);
1009
1010 cellMap->RemoveRows(firstRowIndex, aNumRowsToRemove, aConsiderSpans,
1011 damageArea);
1012 MatchCellMapToColCache(cellMap);
1013 if (IsBorderCollapse()) {
1014 AddBCDamageArea(damageArea);
1015 }
1016 }
1017
1018#ifdef DEBUG_TABLE_CELLMAP
1019 printf("=== removeRowsAfter\n");
1020 Dump(true, true, true);
1021#endif
1022}
1023
1024// collect the rows ancestors of aFrame
1025int32_t nsTableFrame::CollectRows(nsIFrame* aFrame,
1026 nsTArray<nsTableRowFrame*>& aCollection) {
1027 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"
, 1027); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aFrame" ") ("
"null frame" ")"); do { *((volatile int*)__null) = 1027; __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
;
1028 int32_t numRows = 0;
1029 for (nsIFrame* childFrame : aFrame->PrincipalChildList()) {
1030 aCollection.AppendElement(static_cast<nsTableRowFrame*>(childFrame));
1031 numRows++;
1032 }
1033 return numRows;
1034}
1035
1036void nsTableFrame::InsertRowGroups(const nsFrameList::Slice& aRowGroups) {
1037#ifdef DEBUG_TABLE_CELLMAP
1038 printf("=== insertRowGroupsBefore\n");
1039 Dump(true, false, true);
1040#endif
1041 nsTableCellMap* cellMap = GetCellMap();
1042 if (cellMap) {
1043 RowGroupArray orderedRowGroups = OrderedRowGroups();
1044
1045 AutoTArray<nsTableRowFrame*, 8> rows;
1046 // Loop over the rowgroups and check if some of them are new, if they are
1047 // insert cellmaps in the order that is predefined by OrderedRowGroups.
1048 // XXXbz this code is O(N*M) where N is number of new rowgroups
1049 // and M is number of rowgroups we have!
1050 uint32_t rgIndex;
1051 for (rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) {
1052 for (nsIFrame* rowGroup : aRowGroups) {
1053 if (orderedRowGroups[rgIndex] == rowGroup) {
1054 nsTableRowGroupFrame* priorRG =
1055 (0 == rgIndex) ? nullptr : orderedRowGroups[rgIndex - 1];
1056 // create and add the cell map for the row group
1057 cellMap->InsertGroupCellMap(orderedRowGroups[rgIndex], priorRG);
1058
1059 break;
1060 }
1061 }
1062 }
1063 cellMap->Synchronize(this);
1064 ResetRowIndices(aRowGroups);
1065
1066 // now that the cellmaps are reordered too insert the rows
1067 for (rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) {
1068 for (nsIFrame* rowGroup : aRowGroups) {
1069 if (orderedRowGroups[rgIndex] == rowGroup) {
1070 nsTableRowGroupFrame* priorRG =
1071 (0 == rgIndex) ? nullptr : orderedRowGroups[rgIndex - 1];
1072 // collect the new row frames in an array and add them to the table
1073 int32_t numRows = CollectRows(rowGroup, rows);
1074 if (numRows > 0) {
1075 int32_t rowIndex = 0;
1076 if (priorRG) {
1077 int32_t priorNumRows = priorRG->GetRowCount();
1078 rowIndex = priorRG->GetStartRowIndex() + priorNumRows;
1079 }
1080 InsertRows(orderedRowGroups[rgIndex], rows, rowIndex, true);
1081 rows.Clear();
1082 }
1083 break;
1084 }
1085 }
1086 }
1087 }
1088#ifdef DEBUG_TABLE_CELLMAP
1089 printf("=== insertRowGroupsAfter\n");
1090 Dump(true, true, true);
1091#endif
1092}
1093
1094/////////////////////////////////////////////////////////////////////////////
1095// Child frame enumeration
1096
1097const nsFrameList& nsTableFrame::GetChildList(ChildListID aListID) const {
1098 if (aListID == FrameChildListID::ColGroup) {
1099 return mColGroups;
1100 }
1101 return nsContainerFrame::GetChildList(aListID);
1102}
1103
1104void nsTableFrame::GetChildLists(nsTArray<ChildList>* aLists) const {
1105 nsContainerFrame::GetChildLists(aLists);
1106 mColGroups.AppendIfNonempty(aLists, FrameChildListID::ColGroup);
1107}
1108
1109static inline bool FrameHasBorder(nsIFrame* f) {
1110 if (!f->StyleVisibility()->IsVisible()) {
1111 return false;
1112 }
1113
1114 return f->StyleBorder()->HasBorder();
1115}
1116
1117void nsTableFrame::CalcHasBCBorders() {
1118 if (!IsBorderCollapse()) {
1119 SetHasBCBorders(false);
1120 return;
1121 }
1122
1123 if (FrameHasBorder(this)) {
1124 SetHasBCBorders(true);
1125 return;
1126 }
1127
1128 // Check col and col group has borders.
1129 for (nsIFrame* f : this->GetChildList(FrameChildListID::ColGroup)) {
1130 if (FrameHasBorder(f)) {
1131 SetHasBCBorders(true);
1132 return;
1133 }
1134
1135 nsTableColGroupFrame* colGroup = static_cast<nsTableColGroupFrame*>(f);
1136 for (nsTableColFrame* col = colGroup->GetFirstColumn(); col;
1137 col = col->GetNextCol()) {
1138 if (FrameHasBorder(col)) {
1139 SetHasBCBorders(true);
1140 return;
1141 }
1142 }
1143 }
1144
1145 // check row group, row and cell has borders.
1146 RowGroupArray rowGroups = OrderedRowGroups();
1147 for (nsTableRowGroupFrame* rowGroup : rowGroups) {
1148 if (FrameHasBorder(rowGroup)) {
1149 SetHasBCBorders(true);
1150 return;
1151 }
1152
1153 for (nsTableRowFrame* row = rowGroup->GetFirstRow(); row;
1154 row = row->GetNextRow()) {
1155 if (FrameHasBorder(row)) {
1156 SetHasBCBorders(true);
1157 return;
1158 }
1159
1160 for (nsTableCellFrame* cell = row->GetFirstCell(); cell;
1161 cell = cell->GetNextCell()) {
1162 if (FrameHasBorder(cell)) {
1163 SetHasBCBorders(true);
1164 return;
1165 }
1166 }
1167 }
1168 }
1169
1170 SetHasBCBorders(false);
1171}
1172
1173namespace mozilla {
1174class nsDisplayTableBorderCollapse;
1175}
1176
1177// table paint code is concerned primarily with borders and bg color
1178// SEC: TODO: adjust the rect for captions
1179void nsTableFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
1180 const nsDisplayListSet& aLists) {
1181 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)
;
1182
1183 DisplayBorderBackgroundOutline(aBuilder, aLists);
1184
1185 nsDisplayTableBackgroundSet tableBGs(aBuilder, this);
1186 nsDisplayListCollection lists(aBuilder);
1187
1188 // This is similar to what
1189 // nsContainerFrame::BuildDisplayListForNonBlockChildren does, except that we
1190 // allow the children's background and borders to go in our BorderBackground
1191 // list. This doesn't really affect background painting --- the children won't
1192 // actually draw their own backgrounds because the nsTableFrame already drew
1193 // them, unless a child has its own stacking context, in which case the child
1194 // won't use its passed-in BorderBackground list anyway. It does affect cell
1195 // borders though; this lets us get cell borders into the nsTableFrame's
1196 // BorderBackground list.
1197 for (nsIFrame* colGroup :
1198 FirstContinuation()->GetChildList(FrameChildListID::ColGroup)) {
1199 for (nsIFrame* col : colGroup->PrincipalChildList()) {
1200 tableBGs.AddColumn((nsTableColFrame*)col);
1201 }
1202 }
1203
1204 for (nsIFrame* kid : PrincipalChildList()) {
1205 BuildDisplayListForChild(aBuilder, kid, lists);
1206 }
1207
1208 tableBGs.MoveTo(aLists);
1209 lists.MoveTo(aLists);
1210
1211 if (IsVisibleForPainting()) {
1212 // In the collapsed border model, overlay all collapsed borders.
1213 if (IsBorderCollapse()) {
1214 if (HasBCBorders()) {
1215 aLists.BorderBackground()->AppendNewToTop<nsDisplayTableBorderCollapse>(
1216 aBuilder, this);
1217 }
1218 } else {
1219 const nsStyleBorder* borderStyle = StyleBorder();
1220 if (borderStyle->HasBorder()) {
1221 aLists.BorderBackground()->AppendNewToTop<nsDisplayBorder>(aBuilder,
1222 this);
1223 }
1224 }
1225 }
1226}
1227
1228LogicalSides nsTableFrame::GetLogicalSkipSides() const {
1229 LogicalSides skip(mWritingMode);
1230 if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==(__builtin_expect(!!(StyleBorder()->mBoxDecorationBreak ==
StyleBoxDecorationBreak::Clone), 0))
1231 StyleBoxDecorationBreak::Clone)(__builtin_expect(!!(StyleBorder()->mBoxDecorationBreak ==
StyleBoxDecorationBreak::Clone), 0))
) {
1232 return skip;
1233 }
1234
1235 // frame attribute was accounted for in nsHTMLTableElement::MapTableBorderInto
1236 // account for pagination
1237 if (GetPrevInFlow()) {
1238 skip += LogicalSide::BStart;
1239 }
1240 if (GetNextInFlow()) {
1241 skip += LogicalSide::BEnd;
1242 }
1243 return skip;
1244}
1245
1246void nsTableFrame::SetColumnDimensions(nscoord aBSize, WritingMode aWM,
1247 const LogicalMargin& aBorderPadding,
1248 const nsSize& aContainerSize) {
1249 const nscoord colBSize =
1250 aBSize - (aBorderPadding.BStartEnd(aWM) + GetRowSpacing(-1) +
1251 GetRowSpacing(GetRowCount()));
1252 int32_t colIdx = 0;
1253 LogicalPoint colGroupOrigin(aWM,
1254 aBorderPadding.IStart(aWM) + GetColSpacing(-1),
1255 aBorderPadding.BStart(aWM) + GetRowSpacing(-1));
1256 nsTableFrame* fif = static_cast<nsTableFrame*>(FirstInFlow());
1257 for (nsIFrame* colGroupFrame : mColGroups) {
1258 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"
, 1258); AnnotateMozCrashReason("MOZ_ASSERT" "(" "colGroupFrame->IsTableColGroupFrame()"
")"); do { *((volatile int*)__null) = 1258; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1259 // first we need to figure out the size of the colgroup
1260 int32_t groupFirstCol = colIdx;
1261 nscoord colGroupISize = 0;
1262 nscoord colSpacing = 0;
1263 const nsFrameList& columnList = colGroupFrame->PrincipalChildList();
1264 for (nsIFrame* colFrame : columnList) {
1265 if (mozilla::StyleDisplay::TableColumn ==
1266 colFrame->StyleDisplay()->mDisplay) {
1267 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"
, 1267); MOZ_PretendNoReturn(); } } while (0)
;
1268 colSpacing = GetColSpacing(colIdx);
1269 colGroupISize +=
1270 fif->GetColumnISizeFromFirstInFlow(colIdx) + colSpacing;
1271 ++colIdx;
1272 }
1273 }
1274 if (colGroupISize) {
1275 colGroupISize -= colSpacing;
1276 }
1277
1278 LogicalRect colGroupRect(aWM, colGroupOrigin.I(aWM), colGroupOrigin.B(aWM),
1279 colGroupISize, colBSize);
1280 colGroupFrame->SetRect(aWM, colGroupRect, aContainerSize);
1281 nsSize colGroupSize = colGroupFrame->GetSize();
1282
1283 // then we can place the columns correctly within the group
1284 colIdx = groupFirstCol;
1285 LogicalPoint colOrigin(aWM);
1286 for (nsIFrame* colFrame : columnList) {
1287 if (mozilla::StyleDisplay::TableColumn ==
1288 colFrame->StyleDisplay()->mDisplay) {
1289 nscoord colISize = fif->GetColumnISizeFromFirstInFlow(colIdx);
1290 LogicalRect colRect(aWM, colOrigin.I(aWM), colOrigin.B(aWM), colISize,
1291 colBSize);
1292 colFrame->SetRect(aWM, colRect, colGroupSize);
1293 colSpacing = GetColSpacing(colIdx);
1294 colOrigin.I(aWM) += colISize + colSpacing;
1295 ++colIdx;
1296 }
1297 }
1298
1299 colGroupOrigin.I(aWM) += colGroupISize + colSpacing;
1300 }
1301}
1302
1303// SEC: TODO need to worry about continuing frames prev/next in flow for
1304// splitting across pages.
1305
1306// XXX this could be made more general to handle row modifications that change
1307// the table bsize, but first we need to scrutinize every Invalidate
1308void nsTableFrame::ProcessRowInserted(nscoord aNewBSize) {
1309 SetRowInserted(false); // reset the bit that got us here
1310 RowGroupArray rowGroups = OrderedRowGroups();
1311 // find the row group containing the inserted row
1312 for (uint32_t rgIdx = 0; rgIdx < rowGroups.Length(); rgIdx++) {
1313 nsTableRowGroupFrame* rgFrame = rowGroups[rgIdx];
1314 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"
, 1314); MOZ_PretendNoReturn(); } } while (0)
;
1315 // find the row that was inserted first
1316 for (nsIFrame* childFrame : rgFrame->PrincipalChildList()) {
1317 nsTableRowFrame* rowFrame = do_QueryFrame(childFrame);
1318 if (rowFrame) {
1319 if (rowFrame->IsFirstInserted()) {
1320 rowFrame->SetFirstInserted(false);
1321 // damage the table from the 1st row inserted to the end of the table
1322 nsIFrame::InvalidateFrame();
1323 // XXXbz didn't we do this up front? Why do we need to do it again?
1324 SetRowInserted(false);
1325 return; // found it, so leave
1326 }
1327 }
1328 }
1329 }
1330}
1331
1332/* virtual */
1333void nsTableFrame::MarkIntrinsicISizesDirty() {
1334 nsITableLayoutStrategy* tls = LayoutStrategy();
1335 if (MOZ_UNLIKELY(!tls)(__builtin_expect(!!(!tls), 0))) {
1336 // This is a FrameNeedsReflow() from nsBlockFrame::RemoveFrame()
1337 // walking up the ancestor chain in a table next-in-flow. In this case
1338 // our original first-in-flow (which owns the TableLayoutStrategy) has
1339 // already been destroyed and unhooked from the flow chain and thusly
1340 // LayoutStrategy() returns null. All the frames in the flow will be
1341 // destroyed so no need to mark anything dirty here. See bug 595758.
1342 return;
1343 }
1344 tls->MarkIntrinsicISizesDirty();
1345
1346 // XXXldb Call SetBCDamageArea?
1347
1348 nsContainerFrame::MarkIntrinsicISizesDirty();
1349}
1350
1351/* virtual */
1352nscoord nsTableFrame::GetMinISize(gfxContext* aRenderingContext) {
1353 if (NeedToCalcBCBorders()) CalcBCBorders();
1354
1355 ReflowColGroups(aRenderingContext);
1356
1357 return LayoutStrategy()->GetMinISize(aRenderingContext);
1358}
1359
1360/* virtual */
1361nscoord nsTableFrame::GetPrefISize(gfxContext* aRenderingContext) {
1362 if (NeedToCalcBCBorders()) CalcBCBorders();
1363
1364 ReflowColGroups(aRenderingContext);
1365
1366 return LayoutStrategy()->GetPrefISize(aRenderingContext, false);
1367}
1368
1369/* virtual */ nsIFrame::IntrinsicSizeOffsetData
1370nsTableFrame::IntrinsicISizeOffsets(nscoord aPercentageBasis) {
1371 IntrinsicSizeOffsetData result =
1372 nsContainerFrame::IntrinsicISizeOffsets(aPercentageBasis);
1373
1374 result.margin = 0;
1375
1376 if (IsBorderCollapse()) {
1377 result.padding = 0;
1378
1379 WritingMode wm = GetWritingMode();
1380 LogicalMargin outerBC = GetIncludedOuterBCBorder(wm);
1381 result.border = outerBC.IStartEnd(wm);
1382 }
1383
1384 return result;
1385}
1386
1387/* virtual */
1388nsIFrame::SizeComputationResult nsTableFrame::ComputeSize(
1389 gfxContext* aRenderingContext, WritingMode aWM, const LogicalSize& aCBSize,
1390 nscoord aAvailableISize, const LogicalSize& aMargin,
1391 const LogicalSize& aBorderPadding, const StyleSizeOverrides& aSizeOverrides,
1392 ComputeSizeFlags aFlags) {
1393 // Only table wrapper calls this method, and it should use our writing mode.
1394 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"
, 1395); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aWM == GetWritingMode()"
") (" "aWM should be the same as our writing mode!" ")"); do
{ *((volatile int*)__null) = 1395; __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
1395 "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"
, 1395); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aWM == GetWritingMode()"
") (" "aWM should be the same as our writing mode!" ")"); do
{ *((volatile int*)__null) = 1395; __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
1396
1397 auto result = nsContainerFrame::ComputeSize(
1398 aRenderingContext, aWM, aCBSize, aAvailableISize, aMargin, aBorderPadding,
1399 aSizeOverrides, aFlags);
1400
1401 // If our containing block wants to override inner table frame's inline-size
1402 // (e.g. when resolving flex base size), don't enforce the min inline-size
1403 // later in this method.
1404 if (aSizeOverrides.mApplyOverridesVerbatim && aSizeOverrides.mStyleISize &&
1405 aSizeOverrides.mStyleISize->IsLengthPercentage()) {
1406 return result;
1407 }
1408
1409 // If we're a container for font size inflation, then shrink
1410 // wrapping inside of us should not apply font size inflation.
1411 AutoMaybeDisableFontInflation an(this);
1412
1413 // Tables never shrink below their min inline-size.
1414 nscoord minISize = GetMinISize(aRenderingContext);
1415 if (minISize > result.mLogicalSize.ISize(aWM)) {
1416 result.mLogicalSize.ISize(aWM) = minISize;
1417 }
1418
1419 return result;
1420}
1421
1422nscoord nsTableFrame::TableShrinkISizeToFit(gfxContext* aRenderingContext,
1423 nscoord aISizeInCB) {
1424 // If we're a container for font size inflation, then shrink
1425 // wrapping inside of us should not apply font size inflation.
1426 AutoMaybeDisableFontInflation an(this);
1427
1428 nscoord result;
1429 nscoord minISize = GetMinISize(aRenderingContext);
1430 if (minISize > aISizeInCB) {
1431 result = minISize;
1432 } else {
1433 // Tables shrink inline-size to fit with a slightly different algorithm
1434 // from the one they use for their intrinsic isize (the difference
1435 // relates to handling of percentage isizes on columns). So this
1436 // function differs from nsIFrame::ShrinkISizeToFit by only the
1437 // following line.
1438 // Since we've already called GetMinISize, we don't need to do any
1439 // of the other stuff GetPrefISize does.
1440 nscoord prefISize = LayoutStrategy()->GetPrefISize(aRenderingContext, true);
1441 if (prefISize > aISizeInCB) {
1442 result = aISizeInCB;
1443 } else {
1444 result = prefISize;
1445 }
1446 }
1447 return result;
1448}
1449
1450/* virtual */
1451LogicalSize nsTableFrame::ComputeAutoSize(
1452 gfxContext* aRenderingContext, WritingMode aWM, const LogicalSize& aCBSize,
1453 nscoord aAvailableISize, const LogicalSize& aMargin,
1454 const LogicalSize& aBorderPadding, const StyleSizeOverrides& aSizeOverrides,
1455 ComputeSizeFlags aFlags) {
1456 // Tables always shrink-wrap.
1457 nscoord cbBased =
1458 aAvailableISize - aMargin.ISize(aWM) - aBorderPadding.ISize(aWM);
1459 return LogicalSize(aWM, TableShrinkISizeToFit(aRenderingContext, cbBased),
1460 NS_UNCONSTRAINEDSIZE);
1461}
1462
1463// Return true if aParentReflowInput.frame or any of its ancestors within
1464// the containing table have non-auto bsize. (e.g. pct or fixed bsize)
1465bool nsTableFrame::AncestorsHaveStyleBSize(
1466 const ReflowInput& aParentReflowInput) {
1467 WritingMode wm = aParentReflowInput.GetWritingMode();
1468 for (const ReflowInput* rs = &aParentReflowInput; rs && rs->mFrame;
1469 rs = rs->mParentReflowInput) {
1470 LayoutFrameType frameType = rs->mFrame->Type();
1471 if (LayoutFrameType::TableCell == frameType ||
1472 LayoutFrameType::TableRow == frameType ||
1473 LayoutFrameType::TableRowGroup == frameType) {
1474 const auto& bsize = rs->mStylePosition->BSize(wm);
1475 // calc() with both lengths and percentages treated like 'auto' on
1476 // internal table elements
1477 if (!bsize.IsAuto() && !bsize.HasLengthAndPercentage()) {
1478 return true;
1479 }
1480 } else if (LayoutFrameType::Table == frameType) {
1481 // we reached the containing table, so always return
1482 return !rs->mStylePosition->BSize(wm).IsAuto();
1483 }
1484 }
1485 return false;
1486}
1487
1488// See if a special block-size reflow needs to occur and if so,
1489// call RequestSpecialBSizeReflow
1490void nsTableFrame::CheckRequestSpecialBSizeReflow(
1491 const ReflowInput& aReflowInput) {
1492 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"
, 1496); MOZ_PretendNoReturn(); } } while (0)
1493 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"
, 1496); MOZ_PretendNoReturn(); } } while (0)
1494 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"
, 1496); MOZ_PretendNoReturn(); } } while (0)
1495 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"
, 1496); MOZ_PretendNoReturn(); } } while (0)
1496 "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"
, 1496); MOZ_PretendNoReturn(); } } while (0)
;
1497 WritingMode wm = aReflowInput.GetWritingMode();
1498 if (!aReflowInput.mFrame->GetPrevInFlow() && // 1st in flow
1499 (NS_UNCONSTRAINEDSIZE ==
1500 aReflowInput.ComputedBSize() || // no computed bsize
1501 0 == aReflowInput.ComputedBSize()) &&
1502 aReflowInput.mStylePosition->BSize(wm)
1503 .ConvertsToPercentage() && // pct bsize
1504 nsTableFrame::AncestorsHaveStyleBSize(*aReflowInput.mParentReflowInput)) {
1505 nsTableFrame::RequestSpecialBSizeReflow(aReflowInput);
1506 }
1507}
1508
1509// Notify the frame and its ancestors (up to the containing table) that a
1510// special bsize reflow will occur. During a special bsize reflow, a table, row
1511// group, row, or cell returns the last size it was reflowed at. However, the
1512// table may change the bsize of row groups, rows, cells in
1513// DistributeBSizeToRows after. And the row group can change the bsize of rows,
1514// cells in CalculateRowBSizes.
1515void nsTableFrame::RequestSpecialBSizeReflow(const ReflowInput& aReflowInput) {
1516 // notify the frame and its ancestors of the special reflow, stopping at the
1517 // containing table
1518 for (const ReflowInput* rs = &aReflowInput; rs && rs->mFrame;
1519 rs = rs->mParentReflowInput) {
1520 LayoutFrameType frameType = rs->mFrame->Type();
1521 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"
, 1525); MOZ_PretendNoReturn(); } } while (0)
1522 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"
, 1525); MOZ_PretendNoReturn(); } } while (0)
1523 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"
, 1525); MOZ_PretendNoReturn(); } } while (0)
1524 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"
, 1525); MOZ_PretendNoReturn(); } } while (0)
1525 "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"
, 1525); MOZ_PretendNoReturn(); } } while (0)
;
1526
1527 rs->mFrame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
1528 if (LayoutFrameType::Table == frameType) {
1529 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"
, 1530); MOZ_PretendNoReturn(); } } while (0)
1530 "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"
, 1530); MOZ_PretendNoReturn(); } } while (0)
;
1531 // always stop when we reach a table
1532 break;
1533 }
1534 }
1535}
1536
1537/******************************************************************************************
1538 * Before reflow, intrinsic inline-size calculation is done using GetMinISize
1539 * and GetPrefISize. This used to be known as pass 1 reflow.
1540 *
1541 * After the intrinsic isize calculation, the table determines the
1542 * column widths using BalanceColumnISizes() and
1543 * then reflows each child again with a constrained avail isize. This reflow is
1544 * referred to as the pass 2 reflow.
1545 *
1546 * A special bsize reflow (pass 3 reflow) can occur during an initial or resize
1547 * reflow if (a) a row group, row, cell, or a frame inside a cell has a percent
1548 * bsize but no computed bsize or (b) in paginated mode, a table has a bsize.
1549 * (a) supports percent nested tables contained inside cells whose bsizes aren't
1550 * known until after the pass 2 reflow. (b) is necessary because the table
1551 * cannot split until after the pass 2 reflow. The mechanics of the special
1552 * bsize reflow (variety a) are as follows:
1553 *
1554 * 1) Each table related frame (table, row group, row, cell) implements
1555 * NeedsSpecialReflow() to indicate that it should get the reflow. It does
1556 * this when it has a percent bsize but no computed bsize by calling
1557 * CheckRequestSpecialBSizeReflow(). This method calls
1558 * RequestSpecialBSizeReflow() which calls SetNeedSpecialReflow() on its
1559 * ancestors until it reaches the containing table and calls
1560 * SetNeedToInitiateSpecialReflow() on it. For percent bsize frames inside
1561 * cells, during DidReflow(), the cell's NotifyPercentBSize() is called
1562 * (the cell is the reflow input's mPercentBSizeObserver in this case).
1563 * NotifyPercentBSize() calls RequestSpecialBSizeReflow().
1564 *
1565 * XXX (jfkthame) This comment appears to be out of date; it refers to
1566 * methods/flags that are no longer present in the code.
1567 *
1568 * 2) After the pass 2 reflow, if the table's NeedToInitiateSpecialReflow(true)
1569 * was called, it will do the special bsize reflow, setting the reflow
1570 * input's mFlags.mSpecialBSizeReflow to true and mSpecialHeightInitiator to
1571 * itself. It won't do this if IsPrematureSpecialHeightReflow() returns true
1572 * because in that case another special bsize reflow will be coming along
1573 * with the containing table as the mSpecialHeightInitiator. It is only
1574 * relevant to do the reflow when the mSpecialHeightInitiator is the
1575 * containing table, because if it is a remote ancestor, then appropriate
1576 * bsizes will not be known.
1577 *
1578 * 3) Since the bsizes of the table, row groups, rows, and cells was determined
1579 * during the pass 2 reflow, they return their last desired sizes during the
1580 * special bsize reflow. The reflow only permits percent bsize frames inside
1581 * the cells to resize based on the cells bsize and that bsize was
1582 * determined during the pass 2 reflow.
1583 *
1584 * So, in the case of deeply nested tables, all of the tables that were told to
1585 * initiate a special reflow will do so, but if a table is already in a special
1586 * reflow, it won't inititate the reflow until the current initiator is its
1587 * containing table. Since these reflows are only received by frames that need
1588 * them and they don't cause any rebalancing of tables, the extra overhead is
1589 * minimal.
1590 *
1591 * The type of special reflow that occurs during printing (variety b) follows
1592 * the same mechanism except that all frames will receive the reflow even if
1593 * they don't really need them.
1594 *
1595 * Open issues with the special bsize reflow:
1596 *
1597 * 1) At some point there should be 2 kinds of special bsize reflows because (a)
1598 * and (b) above are really quite different. This would avoid unnecessary
1599 * reflows during printing.
1600 *
1601 * 2) When a cell contains frames whose percent bsizes > 100%, there is data
1602 * loss (see bug 115245). However, this can also occur if a cell has a fixed
1603 * bsize and there is no special bsize reflow.
1604 *
1605 * XXXldb Special bsize reflow should really be its own method, not
1606 * part of nsIFrame::Reflow. It should then call nsIFrame::Reflow on
1607 * the contents of the cells to do the necessary block-axis resizing.
1608 *
1609 ******************************************************************************************/
1610
1611/* Layout the entire inner table. */
1612void nsTableFrame::Reflow(nsPresContext* aPresContext,
1613 ReflowOutput& aDesiredSize,
1614 const ReflowInput& aReflowInput,
1615 nsReflowStatus& aStatus) {
1616 MarkInReflow();
1617 DO_GLOBAL_REFLOW_COUNT("nsTableFrame")aPresContext->CountReflows(("nsTableFrame"), (nsIFrame*)this
);
;
1618 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"
, 1618); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aStatus.IsEmpty()"
") (" "Caller should pass a fresh reflow status!" ")"); do {
*((volatile int*)__null) = 1618; __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
1619 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"
, 1620); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)"
") (" "The nsTableWrapperFrame should be the out-of-flow if needed"
")"); do { *((volatile int*)__null) = 1620; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1620 "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"
, 1620); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)"
") (" "The nsTableWrapperFrame should be the out-of-flow if needed"
")"); do { *((volatile int*)__null) = 1620; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1621
1622 const WritingMode wm = aReflowInput.GetWritingMode();
1623 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"
, 1624); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aReflowInput.ComputedLogicalMargin(wm).IsAllZero()"
") (" "Only nsTableWrapperFrame can have margins!" ")"); do {
*((volatile int*)__null) = 1624; __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
1624 "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"
, 1624); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aReflowInput.ComputedLogicalMargin(wm).IsAllZero()"
") (" "Only nsTableWrapperFrame can have margins!" ")"); do {
*((volatile int*)__null) = 1624; __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
1625
1626 bool isPaginated = aPresContext->IsPaginated();
1627
1628 if (!GetPrevInFlow() && !mTableLayoutStrategy) {
1629 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"
, 1629); MOZ_PretendNoReturn(); } while (0)
;
1630 return;
1631 }
1632
1633 // see if collapsing borders need to be calculated
1634 if (!GetPrevInFlow() && IsBorderCollapse() && NeedToCalcBCBorders()) {
1635 CalcBCBorders();
1636 }
1637
1638 // Check for an overflow list, and append any row group frames being pushed
1639 MoveOverflowToChildList();
1640
1641 bool haveCalledCalcDesiredBSize = false;
1642 SetHaveReflowedColGroups(false);
1643
1644 LogicalMargin borderPadding =
1645 aReflowInput.ComputedLogicalBorderPadding(wm).ApplySkipSides(
1646 PreReflowBlockLevelLogicalSkipSides());
1647 nsIFrame* lastChildReflowed = nullptr;
1648 const nsSize containerSize =
1649 aReflowInput.ComputedSizeAsContainerIfConstrained();
1650
1651 // The tentative width is the width we assumed for the table when the child
1652 // frames were positioned (which only matters in vertical-rl mode, because
1653 // they're positioned relative to the right-hand edge). Then, after reflowing
1654 // the kids, we can check whether the table ends up with a different width
1655 // than this tentative value (either because it was unconstrained, so we used
1656 // zero, or because it was enlarged by the child frames), we make the
1657 // necessary positioning adjustments along the x-axis.
1658 nscoord tentativeContainerWidth = 0;
1659 bool mayAdjustXForAllChildren = false;
1660
1661 // Reflow the entire table (pass 2 and possibly pass 3). This phase is
1662 // necessary during a constrained initial reflow and other reflows which
1663 // require either a strategy init or balance. This isn't done during an
1664 // unconstrained reflow, because it will occur later when the parent reflows
1665 // with a constrained isize.
1666 if (IsSubtreeDirty() || aReflowInput.ShouldReflowAllKids() ||
1667 IsGeometryDirty() || isPaginated || aReflowInput.IsBResize() ||
1668 NeedToCollapse()) {
1669 if (aReflowInput.ComputedBSize() != NS_UNCONSTRAINEDSIZE ||
1670 // Also check IsBResize(), to handle the first Reflow preceding a
1671 // special bsize Reflow, when we've already had a special bsize
1672 // Reflow (where ComputedBSize() would not be
1673 // NS_UNCONSTRAINEDSIZE, but without a style change in between).
1674 aReflowInput.IsBResize()) {
1675 // XXX Eventually, we should modify DistributeBSizeToRows to use
1676 // nsTableRowFrame::GetInitialBSize instead of nsIFrame::BSize().
1677 // That way, it will make its calculations based on internal table
1678 // frame bsizes as they are before they ever had any extra bsize
1679 // distributed to them. In the meantime, this reflows all the
1680 // internal table frames, which restores them to their state before
1681 // DistributeBSizeToRows was called.
1682 SetGeometryDirty();
1683 }
1684
1685 bool needToInitiateSpecialReflow = false;
1686 if (isPaginated) {
1687 // see if an extra reflow will be necessary in pagination mode
1688 // when there is a specified table bsize
1689 if (!GetPrevInFlow() &&
1690 NS_UNCONSTRAINEDSIZE != aReflowInput.AvailableBSize()) {
1691 nscoord tableSpecifiedBSize = CalcBorderBoxBSize(
1692 aReflowInput, borderPadding, NS_UNCONSTRAINEDSIZE);
1693 if (tableSpecifiedBSize != NS_UNCONSTRAINEDSIZE &&
1694 tableSpecifiedBSize > 0) {
1695 needToInitiateSpecialReflow = true;
1696 }
1697 }
1698 } else {
1699 needToInitiateSpecialReflow =
1700 HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
1701 }
1702
1703 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"
, 1704); MOZ_PretendNoReturn(); } } while (0)
1704 "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"
, 1704); MOZ_PretendNoReturn(); } } while (0)
;
1705
1706 const TableReflowMode firstReflowMode = needToInitiateSpecialReflow
1707 ? TableReflowMode::Measuring
1708 : TableReflowMode::Final;
1709 ReflowTable(aDesiredSize, aReflowInput, borderPadding, firstReflowMode,
1710 lastChildReflowed, aStatus);
1711
1712 // When in vertical-rl mode, there may be two kinds of scenarios in which
1713 // the positioning of all the children need to be adjusted along the x-axis
1714 // because the width we assumed for the table when the child frames were
1715 // being positioned(i.e. tentative width) may be different from the final
1716 // width for the table:
1717 // 1. If the computed width for the table is unconstrained, a dummy zero
1718 // width was assumed as the tentative width to begin with.
1719 // 2. If the child frames enlarge the width for the table, the final width
1720 // becomes larger than the tentative one.
1721 // Let's record the tentative width here, if later the final width turns out
1722 // to be different from this tentative one, it means one of the above
1723 // scenarios happens, then we adjust positioning of all the children.
1724 // Note that vertical-lr, unlike vertical-rl, doesn't need to take special
1725 // care of this situation, because they're positioned relative to the
1726 // left-hand edge.
1727 if (wm.IsVerticalRL()) {
1728 tentativeContainerWidth = containerSize.width;
1729 mayAdjustXForAllChildren = true;
1730 }
1731
1732 // reevaluate special bsize reflow conditions
1733 if (HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE)) {
1734 needToInitiateSpecialReflow = true;
1735 }
1736
1737 // XXXldb Are all these conditions correct?
1738 if (needToInitiateSpecialReflow && aStatus.IsComplete()) {
1739 // XXXldb Do we need to set the IsBResize flag on any reflow inputs?
1740
1741 ReflowInput& mutable_rs = const_cast<ReflowInput&>(aReflowInput);
1742
1743 // distribute extra block-direction space to rows
1744 aDesiredSize.BSize(wm) =
1745 CalcDesiredBSize(aReflowInput, borderPadding, aStatus);
1746 haveCalledCalcDesiredBSize = true;
1747
1748 mutable_rs.mFlags.mSpecialBSizeReflow = true;
1749
1750 ReflowTable(aDesiredSize, aReflowInput, borderPadding,
1751 TableReflowMode::Final, lastChildReflowed, aStatus);
1752
1753 mutable_rs.mFlags.mSpecialBSizeReflow = false;
1754 }
1755 }
1756
1757 if (aStatus.IsIncomplete() &&
1758 aReflowInput.mStyleBorder->mBoxDecorationBreak ==
1759 StyleBoxDecorationBreak::Slice) {
1760 borderPadding.BEnd(wm) = 0;
1761 }
1762
1763 aDesiredSize.ISize(wm) =
1764 aReflowInput.ComputedISize() + borderPadding.IStartEnd(wm);
1765 if (!haveCalledCalcDesiredBSize) {
1766 aDesiredSize.BSize(wm) =
1767 CalcDesiredBSize(aReflowInput, borderPadding, aStatus);
1768 } else if (lastChildReflowed && aStatus.IsIncomplete()) {
1769 // If there is an incomplete child, then set the desired block-size to
1770 // include it but not the next one.
1771 aDesiredSize.BSize(wm) =
1772 borderPadding.BEnd(wm) +
1773 lastChildReflowed->GetLogicalNormalRect(wm, containerSize).BEnd(wm);
1774 }
1775 if (IsRowInserted()) {
1776 ProcessRowInserted(aDesiredSize.BSize(wm));
1777 }
1778
1779 // For more information on the reason for what we should do this, refer to the
1780 // code which defines and evaluates the variables xAdjustmentForAllKids and
1781 // tentativeContainerWidth in the previous part in this function.
1782 if (mayAdjustXForAllChildren) {
1783 nscoord xAdjustmentForAllKids =
1784 aDesiredSize.Width() - tentativeContainerWidth;
1785 if (0 != xAdjustmentForAllKids) {
1786 for (nsIFrame* kid : mFrames) {
1787 kid->MovePositionBy(nsPoint(xAdjustmentForAllKids, 0));
1788 RePositionViews(kid);
1789 }
1790 }
1791 }
1792
1793 // Calculate the overflow area contribution from our children. We couldn't
1794 // do this on the fly during ReflowChildren(), because in vertical-rl mode
1795 // with unconstrained width, we weren't placing them in their final positions
1796 // until the fixupKidPositions loop just above.
1797 for (nsIFrame* kid : mFrames) {
1798 ConsiderChildOverflow(aDesiredSize.mOverflowAreas, kid);
1799 }
1800
1801 SetColumnDimensions(aDesiredSize.BSize(wm), wm, borderPadding,
1802 aDesiredSize.PhysicalSize());
1803 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"
, 1804); } } while (false)
1804 "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"
, 1804); } } while (false)
;
1805 if (NeedToCollapse()) {
1806 // This code and the code it depends on assumes that all row groups
1807 // and rows have just been reflowed (i.e., it makes adjustments to
1808 // their rects that are not idempotent). Thus the reflow code
1809 // checks NeedToCollapse() to ensure this is true.
1810 AdjustForCollapsingRowsCols(aDesiredSize, wm, borderPadding);
1811 }
1812
1813 // If there are any relatively-positioned table parts, we need to reflow their
1814 // absolutely-positioned descendants now that their dimensions are final.
1815 FixupPositionedTableParts(aPresContext, aDesiredSize, aReflowInput);
1816
1817 // make sure the table overflow area does include the table rect.
1818 nsRect tableRect(0, 0, aDesiredSize.Width(), aDesiredSize.Height());
1819
1820 if (ShouldApplyOverflowClipping(aReflowInput.mStyleDisplay) !=
1821 PhysicalAxes::Both) {
1822 // collapsed border may leak out
1823 LogicalMargin bcMargin = GetExcludedOuterBCBorder(wm);
1824 tableRect.Inflate(bcMargin.GetPhysicalMargin(wm));
1825 }
1826 aDesiredSize.mOverflowAreas.UnionAllWith(tableRect);
1827
1828 FinishAndStoreOverflow(&aDesiredSize);
1829}
1830
1831void nsTableFrame::FixupPositionedTableParts(nsPresContext* aPresContext,
1832 ReflowOutput& aDesiredSize,
1833 const ReflowInput& aReflowInput) {
1834 FrameTArray* positionedParts = GetProperty(PositionedTablePartArray());
1835 if (!positionedParts) {
1836 return;
1837 }
1838
1839 OverflowChangedTracker overflowTracker;
1840 overflowTracker.SetSubtreeRoot(this);
1841
1842 for (size_t i = 0; i < positionedParts->Length(); ++i) {
1843 nsIFrame* positionedPart = positionedParts->ElementAt(i);
1844
1845 // As we've already finished reflow, positionedParts's size and overflow
1846 // areas have already been assigned, so we just pull them back out.
1847 const WritingMode wm = positionedPart->GetWritingMode();
1848 const LogicalSize size = positionedPart->GetLogicalSize(wm);
1849 ReflowOutput desiredSize(aReflowInput.GetWritingMode());
1850 desiredSize.SetSize(wm, size);
1851 desiredSize.mOverflowAreas =
1852 positionedPart->GetOverflowAreasRelativeToSelf();
1853
1854 // Construct a dummy reflow input and reflow status.
1855 // XXX(seth): Note that the dummy reflow input doesn't have a correct
1856 // chain of parent reflow inputs. It also doesn't necessarily have a
1857 // correct containing block.
1858 LogicalSize availSize = size;
1859 availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
1860 ReflowInput reflowInput(aPresContext, positionedPart,
1861 aReflowInput.mRenderingContext, availSize,
1862 ReflowInput::InitFlag::DummyParentReflowInput);
1863 nsReflowStatus reflowStatus;
1864
1865 // Reflow absolutely-positioned descendants of the positioned part.
1866 // FIXME: Unconditionally using NS_UNCONSTRAINEDSIZE for the bsize and
1867 // ignoring any change to the reflow status aren't correct. We'll never
1868 // paginate absolutely positioned frames.
1869 positionedPart->FinishReflowWithAbsoluteFrames(
1870 PresContext(), desiredSize, reflowInput, reflowStatus, true);
1871
1872 // FinishReflowWithAbsoluteFrames has updated overflow on
1873 // |positionedPart|. We need to make sure that update propagates
1874 // through the intermediate frames between it and this frame.
1875 nsIFrame* positionedFrameParent = positionedPart->GetParent();
1876 if (positionedFrameParent != this) {
1877 overflowTracker.AddFrame(positionedFrameParent,
1878 OverflowChangedTracker::CHILDREN_CHANGED);
1879 }
1880 }
1881
1882 // Propagate updated overflow areas up the tree.
1883 overflowTracker.Flush();
1884
1885 // Update our own overflow areas. (OverflowChangedTracker doesn't update the
1886 // subtree root itself.)
1887 aDesiredSize.SetOverflowAreasToDesiredBounds();
1888 nsLayoutUtils::UnionChildOverflow(this, aDesiredSize.mOverflowAreas);
1889}
1890
1891bool nsTableFrame::ComputeCustomOverflow(OverflowAreas& aOverflowAreas) {
1892 // As above in Reflow, make sure the table overflow area includes the table
1893 // rect, and check for collapsed borders leaking out.
1894 if (ShouldApplyOverflowClipping(StyleDisplay()) != PhysicalAxes::Both) {
1895 nsRect bounds(nsPoint(0, 0), GetSize());
1896 WritingMode wm = GetWritingMode();
1897 LogicalMargin bcMargin = GetExcludedOuterBCBorder(wm);
1898 bounds.Inflate(bcMargin.GetPhysicalMargin(wm));
1899
1900 aOverflowAreas.UnionAllWith(bounds);
1901 }
1902 return nsContainerFrame::ComputeCustomOverflow(aOverflowAreas);
1903}
1904
1905void nsTableFrame::ReflowTable(ReflowOutput& aDesiredSize,
1906 const ReflowInput& aReflowInput,
1907 const LogicalMargin& aBorderPadding,
1908 TableReflowMode aReflowMode,
1909 nsIFrame*& aLastChildReflowed,
1910 nsReflowStatus& aStatus) {
1911 aLastChildReflowed = nullptr;
1912
1913 if (!GetPrevInFlow()) {
1914 mTableLayoutStrategy->ComputeColumnISizes(aReflowInput);
1915 }
1916
1917 TableReflowInput reflowInput(aReflowInput, aBorderPadding, aReflowMode);
1918 ReflowChildren(reflowInput, aStatus, aLastChildReflowed,
1919 aDesiredSize.mOverflowAreas);
1920
1921 ReflowColGroups(aReflowInput.mRenderingContext);
1922}
1923
1924void nsTableFrame::PushChildrenToOverflow(const RowGroupArray& aRowGroups,
1925 size_t aPushFrom) {
1926 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"
, 1926); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aPushFrom > 0"
") (" "pushing first child" ")"); do { *((volatile int*)__null
) = 1926; __attribute__((nomerge)) ::abort(); } while (false)
; } } while (false)
;
1927
1928 // Extract the frames from the array into a frame list.
1929 nsFrameList frames;
1930 for (size_t childX = aPushFrom; childX < aRowGroups.Length(); ++childX) {
1931 nsTableRowGroupFrame* rgFrame = aRowGroups[childX];
1932 if (!rgFrame->IsRepeatable()) {
1933 mFrames.RemoveFrame(rgFrame);
1934 frames.AppendFrame(nullptr, rgFrame);
1935 }
1936 }
1937
1938 if (frames.IsEmpty()) {
1939 return;
1940 }
1941
1942 // Add the frames to our overflow list.
1943 SetOverflowFrames(std::move(frames));
1944}
1945
1946// collapsing row groups, rows, col groups and cols are accounted for after both
1947// passes of reflow so that it has no effect on the calculations of reflow.
1948void nsTableFrame::AdjustForCollapsingRowsCols(
1949 ReflowOutput& aDesiredSize, const WritingMode aWM,
1950 const LogicalMargin& aBorderPadding) {
1951 nscoord bTotalOffset = 0; // total offset among all rows in all row groups
1952
1953 // reset the bit, it will be set again if row/rowgroup or col/colgroup are
1954 // collapsed
1955 SetNeedToCollapse(false);
1956
1957 // collapse the rows and/or row groups as necessary
1958 // Get the ordered children
1959 RowGroupArray rowGroups = OrderedRowGroups();
1960
1961 nsTableFrame* firstInFlow = static_cast<nsTableFrame*>(FirstInFlow());
1962 nscoord iSize = firstInFlow->GetCollapsedISize(aWM, aBorderPadding);
1963 nscoord rgISize = iSize - GetColSpacing(-1) - GetColSpacing(GetColCount());
1964 OverflowAreas overflow;
1965 // Walk the list of children
1966 for (uint32_t childX = 0; childX < rowGroups.Length(); childX++) {
1967 nsTableRowGroupFrame* rgFrame = rowGroups[childX];
1968 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"
, 1968); MOZ_PretendNoReturn(); } } while (0)
;
1969 bTotalOffset +=
1970 rgFrame->CollapseRowGroupIfNecessary(bTotalOffset, rgISize, aWM);
1971 ConsiderChildOverflow(overflow, rgFrame);
1972 }
1973
1974 aDesiredSize.BSize(aWM) -= bTotalOffset;
1975 aDesiredSize.ISize(aWM) = iSize;
1976 overflow.UnionAllWith(
1977 nsRect(0, 0, aDesiredSize.Width(), aDesiredSize.Height()));
1978 FinishAndStoreOverflow(overflow,
1979 nsSize(aDesiredSize.Width(), aDesiredSize.Height()));
1980}
1981
1982nscoord nsTableFrame::GetCollapsedISize(const WritingMode aWM,
1983 const LogicalMargin& aBorderPadding) {
1984 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"
, 1984); MOZ_PretendNoReturn(); } } while (0)
;
1985 nscoord iSize = GetColSpacing(GetColCount());
1986 iSize += aBorderPadding.IStartEnd(aWM);
1987 nsTableFrame* fif = static_cast<nsTableFrame*>(FirstInFlow());
1988 for (nsIFrame* groupFrame : mColGroups) {
1989 const nsStyleVisibility* groupVis = groupFrame->StyleVisibility();
1990 bool collapseGroup = StyleVisibility::Collapse == groupVis->mVisible;
1991 nsTableColGroupFrame* cgFrame = (nsTableColGroupFrame*)groupFrame;
1992 for (nsTableColFrame* colFrame = cgFrame->GetFirstColumn(); colFrame;
1993 colFrame = colFrame->GetNextCol()) {
1994 const nsStyleDisplay* colDisplay = colFrame->StyleDisplay();
1995 nscoord colIdx = colFrame->GetColIndex();
1996 if (mozilla::StyleDisplay::TableColumn == colDisplay->mDisplay) {
1997 const nsStyleVisibility* colVis = colFrame->StyleVisibility();
1998 bool collapseCol = StyleVisibility::Collapse == colVis->mVisible;
1999 nscoord colISize = fif->GetColumnISizeFromFirstInFlow(colIdx);
2000 if (!collapseGroup && !collapseCol) {
2001 iSize += colISize;
2002 if (ColumnHasCellSpacingBefore(colIdx)) {
2003 iSize += GetColSpacing(colIdx - 1);
2004 }
2005 } else {
2006 SetNeedToCollapse(true);
2007 }
2008 }
2009 }
2010 }
2011 return iSize;
2012}
2013
2014/* virtual */
2015void nsTableFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) {
2016 nsContainerFrame::DidSetComputedStyle(aOldComputedStyle);
2017
2018 if (!aOldComputedStyle) // avoid this on init
2019 return;
2020
2021 if (IsBorderCollapse() && BCRecalcNeeded(aOldComputedStyle, Style())) {
2022 SetFullBCDamageArea();
2023 }
2024
2025 // avoid this on init or nextinflow
2026 if (!mTableLayoutStrategy || GetPrevInFlow()) return;
2027
2028 bool isAuto = IsAutoLayout();
2029 if (isAuto != (LayoutStrategy()->GetType() == nsITableLayoutStrategy::Auto)) {
2030 if (isAuto)
2031 mTableLayoutStrategy = MakeUnique<BasicTableLayoutStrategy>(this);
2032 else
2033 mTableLayoutStrategy = MakeUnique<FixedTableLayoutStrategy>(this);
2034 }
2035}
2036
2037void nsTableFrame::AppendFrames(ChildListID aListID, nsFrameList&& aFrameList) {
2038 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"
, 2040); MOZ_PretendNoReturn(); } } while (0)
2039 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"
, 2040); MOZ_PretendNoReturn(); } } while (0)
2040 "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"
, 2040); MOZ_PretendNoReturn(); } } while (0)
;
2041
2042 // Because we actually have two child lists, one for col group frames and one
2043 // for everything else, we need to look at each frame individually
2044 // XXX The frame construction code should be separating out child frames
2045 // based on the type, bug 343048.
2046 while (!aFrameList.IsEmpty()) {
2047 nsIFrame* f = aFrameList.FirstChild();
2048 aFrameList.RemoveFrame(f);
2049
2050 // See what kind of frame we have
2051 const nsStyleDisplay* display = f->StyleDisplay();
2052
2053 if (mozilla::StyleDisplay::TableColumnGroup == display->mDisplay) {
2054 if (MOZ_UNLIKELY(GetPrevInFlow())(__builtin_expect(!!(GetPrevInFlow()), 0))) {
2055 nsFrameList colgroupFrame(f, f);
2056 auto firstInFlow = static_cast<nsTableFrame*>(FirstInFlow());
2057 firstInFlow->AppendFrames(aListID, std::move(colgroupFrame));
2058 continue;
2059 }
2060 nsTableColGroupFrame* lastColGroup =
2061 nsTableColGroupFrame::GetLastRealColGroup(this);
2062 int32_t startColIndex = (lastColGroup)
2063 ? lastColGroup->GetStartColumnIndex() +
2064 lastColGroup->GetColCount()
2065 : 0;
2066 mColGroups.InsertFrame(this, lastColGroup, f);
2067 // Insert the colgroup and its cols into the table
2068 InsertColGroups(startColIndex,
2069 nsFrameList::Slice(f, f->GetNextSibling()));
2070 } else if (IsRowGroup(display->mDisplay)) {
2071 DrainSelfOverflowList(); // ensure the last frame is in mFrames
2072 // Append the new row group frame to the sibling chain
2073 mFrames.AppendFrame(nullptr, f);
2074
2075 // insert the row group and its rows into the table
2076 InsertRowGroups(nsFrameList::Slice(f, nullptr));
2077 } else {
2078 // Nothing special to do, just add the frame to our child list
2079 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"
, 2080); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "How did we get here? Frame construction screwed up"
")"); do { *((volatile int*)__null) = 2080; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2080 "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"
, 2080); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "How did we get here? Frame construction screwed up"
")"); do { *((volatile int*)__null) = 2080; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2081 mFrames.AppendFrame(nullptr, f);
2082 }
2083 }
2084
2085#ifdef DEBUG_TABLE_CELLMAP
2086 printf("=== TableFrame::AppendFrames\n");
2087 Dump(true, true, true);
2088#endif
2089 PresShell()->FrameNeedsReflow(this, IntrinsicDirty::FrameAndAncestors,
2090 NS_FRAME_HAS_DIRTY_CHILDREN);
2091 SetGeometryDirty();
2092}
2093
2094void nsTableFrame::InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame,
2095 const nsLineList::iterator* aPrevFrameLine,
2096 nsFrameList&& aFrameList) {
2097 // The frames in aFrameList can be a mix of row group frames and col group
2098 // frames. The problem is that they should go in separate child lists so
2099 // we need to deal with that here...
2100 // XXX The frame construction code should be separating out child frames
2101 // based on the type, bug 343048.
2102
2103 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"
, 2104); MOZ_PretendNoReturn(); } } while (0)
2104 "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"
, 2104); MOZ_PretendNoReturn(); } } while (0)
;
2105
2106 if ((aPrevFrame && !aPrevFrame->GetNextSibling()) ||
2107 (!aPrevFrame && GetChildList(aListID).IsEmpty())) {
2108 // Treat this like an append; still a workaround for bug 343048.
2109 AppendFrames(aListID, std::move(aFrameList));
2110 return;
2111 }
2112
2113 // Collect ColGroupFrames into a separate list and insert those separately
2114 // from the other frames (bug 759249).
2115 nsFrameList colGroupList;
2116 nsFrameList principalList;
2117 do {
2118 const auto display = aFrameList.FirstChild()->StyleDisplay()->mDisplay;
2119 nsFrameList head = aFrameList.Split([display](nsIFrame* aFrame) {
2120 return aFrame->StyleDisplay()->mDisplay != display;
2121 });
2122 if (display == mozilla::StyleDisplay::TableColumnGroup) {
2123 colGroupList.AppendFrames(nullptr, std::move(head));
2124 } else {
2125 principalList.AppendFrames(nullptr, std::move(head));
2126 }
2127 } while (aFrameList.NotEmpty());
2128
2129 // We pass aPrevFrame for both ColGroup and other frames since
2130 // HomogenousInsertFrames will only use it if it's a suitable
2131 // prev-sibling for the frames in the frame list.
2132 if (colGroupList.NotEmpty()) {
2133 HomogenousInsertFrames(FrameChildListID::ColGroup, aPrevFrame,
2134 colGroupList);
2135 }
2136 if (principalList.NotEmpty()) {
2137 HomogenousInsertFrames(FrameChildListID::Principal, aPrevFrame,
2138 principalList);
2139 }
2140}
2141
2142void nsTableFrame::HomogenousInsertFrames(ChildListID aListID,
2143 nsIFrame* aPrevFrame,
2144 nsFrameList& aFrameList) {
2145 // See what kind of frame we have
2146 const nsStyleDisplay* display = aFrameList.FirstChild()->StyleDisplay();
2147 bool isColGroup =
2148 mozilla::StyleDisplay::TableColumnGroup == display->mDisplay;
2149#ifdef DEBUG1
2150 // Verify that either all siblings have display:table-column-group, or they
2151 // all have display values different from table-column-group.
2152 for (nsIFrame* frame : aFrameList) {
2153 auto nextDisplay = frame->StyleDisplay()->mDisplay;
2154 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"
, 2156); AnnotateMozCrashReason("MOZ_ASSERT" "(" "isColGroup == (nextDisplay == mozilla::StyleDisplay::TableColumnGroup)"
") (" "heterogenous childlist" ")"); do { *((volatile int*)__null
) = 2156; __attribute__((nomerge)) ::abort(); } while (false)
; } } while (false)
2155 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"
, 2156); AnnotateMozCrashReason("MOZ_ASSERT" "(" "isColGroup == (nextDisplay == mozilla::StyleDisplay::TableColumnGroup)"
") (" "heterogenous childlist" ")"); do { *((volatile int*)__null
) = 2156; __attribute__((nomerge)) ::abort(); } while (false)
; } } while (false)
2156 "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"
, 2156); AnnotateMozCrashReason("MOZ_ASSERT" "(" "isColGroup == (nextDisplay == mozilla::StyleDisplay::TableColumnGroup)"
") (" "heterogenous childlist" ")"); do { *((volatile int*)__null
) = 2156; __attribute__((nomerge)) ::abort(); } while (false)
; } } while (false)
;
2157 }
2158#endif
2159 if (MOZ_UNLIKELY(isColGroup && GetPrevInFlow())(__builtin_expect(!!(isColGroup && GetPrevInFlow()), 0
))
) {
2160 auto firstInFlow = static_cast<nsTableFrame*>(FirstInFlow());
2161 firstInFlow->AppendFrames(aListID, std::move(aFrameList));
2162 return;
2163 }
2164 if (aPrevFrame) {
2165 const nsStyleDisplay* prevDisplay = aPrevFrame->StyleDisplay();
2166 // Make sure they belong on the same frame list
2167 if ((display->mDisplay == mozilla::StyleDisplay::TableColumnGroup) !=
2168 (prevDisplay->mDisplay == mozilla::StyleDisplay::TableColumnGroup)) {
2169 // the previous frame is not valid, see comment at ::AppendFrames
2170 // XXXbz Using content indices here means XBL will get screwed
2171 // over... Oh, well.
2172 nsIFrame* pseudoFrame = aFrameList.FirstChild();
2173 nsIContent* parentContent = GetContent();
2174 nsIContent* content = nullptr;
2175 aPrevFrame = nullptr;
2176 while (pseudoFrame &&
2177 (parentContent == (content = pseudoFrame->GetContent()))) {
2178 pseudoFrame = pseudoFrame->PrincipalChildList().FirstChild();
2179 }
2180 nsCOMPtr<nsIContent> container = content->GetParent();
2181 if (MOZ_LIKELY(container)(__builtin_expect(!!(container), 1))) { // XXX need this null-check, see bug 411823.
2182 const Maybe<uint32_t> newIndex = container->ComputeIndexOf(content);
2183 nsIFrame* kidFrame;
2184 nsTableColGroupFrame* lastColGroup = nullptr;
2185 if (isColGroup) {
2186 kidFrame = mColGroups.FirstChild();
2187 lastColGroup = nsTableColGroupFrame::GetLastRealColGroup(this);
2188 } else {
2189 kidFrame = mFrames.FirstChild();
2190 }
2191 // Important: need to start at a value smaller than all valid indices
2192 Maybe<uint32_t> lastIndex;
2193 while (kidFrame) {
2194 if (isColGroup) {
2195 if (kidFrame == lastColGroup) {
2196 aPrevFrame =
2197 kidFrame; // there is no real colgroup after this one
2198 break;
2199 }
2200 }
2201 pseudoFrame = kidFrame;
2202 while (pseudoFrame &&
2203 (parentContent == (content = pseudoFrame->GetContent()))) {
2204 pseudoFrame = pseudoFrame->PrincipalChildList().FirstChild();
2205 }
2206 const Maybe<uint32_t> index = container->ComputeIndexOf(content);
2207 // XXX Keep the odd traditional behavior in some indices are nothing
2208 // cases for now.
2209 if ((index.isSome() &&
2210 (lastIndex.isNothing() || *index > *lastIndex)) &&
2211 (newIndex.isSome() &&
2212 (index.isNothing() || *index < *newIndex))) {
2213 lastIndex = index;
2214 aPrevFrame = kidFrame;
2215 }
2216 kidFrame = kidFrame->GetNextSibling();
2217 }
2218 }
2219 }
2220 }
2221 if (mozilla::StyleDisplay::TableColumnGroup == display->mDisplay) {
2222 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"
, 2223); MOZ_PretendNoReturn(); } } while (0)
2223 "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"
, 2223); MOZ_PretendNoReturn(); } } while (0)
;
2224 // Insert the column group frames
2225 const nsFrameList::Slice& newColgroups =
2226 mColGroups.InsertFrames(this, aPrevFrame, std::move(aFrameList));
2227 // find the starting col index for the first new col group
2228 int32_t startColIndex = 0;
2229 if (aPrevFrame) {
2230 nsTableColGroupFrame* prevColGroup =
2231 (nsTableColGroupFrame*)GetFrameAtOrBefore(
2232 this, aPrevFrame, LayoutFrameType::TableColGroup);
2233 if (prevColGroup) {
2234 startColIndex =
2235 prevColGroup->GetStartColumnIndex() + prevColGroup->GetColCount();
2236 }
2237 }
2238 InsertColGroups(startColIndex, newColgroups);
2239 } else if (IsRowGroup(display->mDisplay)) {
2240 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"
, 2241); MOZ_PretendNoReturn(); } } while (0)
2241 "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"
, 2241); MOZ_PretendNoReturn(); } } while (0)
;
2242 DrainSelfOverflowList(); // ensure aPrevFrame is in mFrames
2243 // Insert the frames in the sibling chain
2244 const nsFrameList::Slice& newRowGroups =
2245 mFrames.InsertFrames(nullptr, aPrevFrame, std::move(aFrameList));
2246
2247 InsertRowGroups(newRowGroups);
2248 } else {
2249 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"
, 2250); MOZ_PretendNoReturn(); } } while (0)
2250 "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"
, 2250); MOZ_PretendNoReturn(); } } while (0)
;
2251 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"
, 2251); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "How did we even get here?" ")");
do { *((volatile int*)__null) = 2251; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2252 // Just insert the frame and don't worry about reflowing it
2253 mFrames.InsertFrames(nullptr, aPrevFrame, std::move(aFrameList));
2254 return;
2255 }
2256
2257 PresShell()->FrameNeedsReflow(this, IntrinsicDirty::FrameAndAncestors,
2258 NS_FRAME_HAS_DIRTY_CHILDREN);
2259 SetGeometryDirty();
2260#ifdef DEBUG_TABLE_CELLMAP
2261 printf("=== TableFrame::InsertFrames\n");
2262 Dump(true, true, true);
2263#endif
2264}
2265
2266void nsTableFrame::DoRemoveFrame(DestroyContext& aContext, ChildListID aListID,
2267 nsIFrame* aOldFrame) {
2268 if (aListID == FrameChildListID::ColGroup) {
2269 nsIFrame* nextColGroupFrame = aOldFrame->GetNextSibling();
2270 nsTableColGroupFrame* colGroup = (nsTableColGroupFrame*)aOldFrame;
2271 int32_t firstColIndex = colGroup->GetStartColumnIndex();
2272 int32_t lastColIndex = firstColIndex + colGroup->GetColCount() - 1;
2273 mColGroups.DestroyFrame(aContext, aOldFrame);
2274 nsTableColGroupFrame::ResetColIndices(nextColGroupFrame, firstColIndex);
2275 // remove the cols from the table
2276 int32_t colIdx;
2277 for (colIdx = lastColIndex; colIdx >= firstColIndex; colIdx--) {
2278 nsTableColFrame* colFrame = mColFrames.SafeElementAt(colIdx);
2279 if (colFrame) {
2280 RemoveCol(colGroup, colIdx, true, false);
2281 }
2282 }
2283
2284 // If we have some anonymous cols at the end already, we just
2285 // add more of them.
2286 if (!mColFrames.IsEmpty() &&
2287 mColFrames.LastElement() && // XXXbz is this ever null?
2288 mColFrames.LastElement()->GetColType() == eColAnonymousCell) {
2289 int32_t numAnonymousColsToAdd = GetColCount() - mColFrames.Length();
2290 if (numAnonymousColsToAdd > 0) {
2291 // this sets the child list, updates the col cache and cell map
2292 AppendAnonymousColFrames(numAnonymousColsToAdd);
2293 }
2294 } else {
2295 // All of our colframes correspond to actual <col> tags. It's possible
2296 // that we still have at least as many <col> tags as we have logical
2297 // columns from cells, but we might have one less. Handle the latter case
2298 // as follows: First ask the cellmap to drop its last col if it doesn't
2299 // have any actual cells in it. Then call MatchCellMapToColCache to
2300 // append an anonymous column if it's needed; this needs to be after
2301 // RemoveColsAtEnd, since it will determine the need for a new column
2302 // frame based on the width of the cell map.
2303 nsTableCellMap* cellMap = GetCellMap();
2304 if (cellMap) { // XXXbz is this ever null?
2305 cellMap->RemoveColsAtEnd();
2306 MatchCellMapToColCache(cellMap);
2307 }
2308 }
2309
2310 } else {
2311 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"
, 2312); MOZ_PretendNoReturn(); } } while (0)
2312 "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"
, 2312); MOZ_PretendNoReturn(); } } while (0)
;
2313 nsTableRowGroupFrame* rgFrame =
2314 static_cast<nsTableRowGroupFrame*>(aOldFrame);
2315 // remove the row group from the cell map
2316 nsTableCellMap* cellMap = GetCellMap();
2317 if (cellMap) {
2318 cellMap->RemoveGroupCellMap(rgFrame);
2319 }
2320
2321 // remove the row group frame from the sibling chain
2322 mFrames.DestroyFrame(aContext, aOldFrame);
2323
2324 // the removal of a row group changes the cellmap, the columns might change
2325 if (cellMap) {
2326 cellMap->Synchronize(this);
2327 // Create an empty slice
2328 ResetRowIndices(nsFrameList::Slice(nullptr, nullptr));
2329 TableArea damageArea;
2330 cellMap->RebuildConsideringCells(nullptr, nullptr, 0, 0, false,
2331 damageArea);
2332
2333 static_cast<nsTableFrame*>(FirstInFlow())
2334 ->MatchCellMapToColCache(cellMap);
2335 }
2336 }
2337}
2338
2339void nsTableFrame::RemoveFrame(DestroyContext& aContext, ChildListID aListID,
2340 nsIFrame* aOldFrame) {
2341 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"
, 2344); MOZ_PretendNoReturn(); } } while (0)
2342 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"
, 2344); MOZ_PretendNoReturn(); } } while (0)
2343 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"
, 2344); MOZ_PretendNoReturn(); } } while (0)
2344 "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"
, 2344); MOZ_PretendNoReturn(); } } while (0)
;
2345 mozilla::PresShell* presShell = PresShell();
2346 nsTableFrame* lastParent = nullptr;
2347 while (aOldFrame) {
2348 nsIFrame* oldFrameNextContinuation = aOldFrame->GetNextContinuation();
2349 nsTableFrame* parent = static_cast<nsTableFrame*>(aOldFrame->GetParent());
2350 if (parent != lastParent) {
2351 parent->DrainSelfOverflowList();
2352 }
2353 parent->DoRemoveFrame(aContext, aListID, aOldFrame);
2354 aOldFrame = oldFrameNextContinuation;
2355 if (parent != lastParent) {
2356 // for now, just bail and recalc all of the collapsing borders
2357 // as the cellmap changes we need to recalc
2358 if (parent->IsBorderCollapse()) {
2359 parent->SetFullBCDamageArea();
2360 }
2361 parent->SetGeometryDirty();
2362 presShell->FrameNeedsReflow(parent, IntrinsicDirty::FrameAndAncestors,
2363 NS_FRAME_HAS_DIRTY_CHILDREN);
2364 lastParent = parent;
2365 }
2366 }
2367#ifdef DEBUG_TABLE_CELLMAP
2368 printf("=== TableFrame::RemoveFrame\n");
2369 Dump(true, true, true);
2370#endif
2371}
2372
2373/* virtual */
2374nsMargin nsTableFrame::GetUsedBorder() const {
2375 if (!IsBorderCollapse()) return nsContainerFrame::GetUsedBorder();
2376
2377 WritingMode wm = GetWritingMode();
2378 return GetIncludedOuterBCBorder(wm).GetPhysicalMargin(wm);
2379}
2380
2381/* virtual */
2382nsMargin nsTableFrame::GetUsedPadding() const {
2383 if (!IsBorderCollapse()) return nsContainerFrame::GetUsedPadding();
2384
2385 return nsMargin(0, 0, 0, 0);
2386}
2387
2388/* virtual */
2389nsMargin nsTableFrame::GetUsedMargin() const {
2390 // The margin is inherited to the table wrapper frame via
2391 // the ::-moz-table-wrapper rule in ua.css.
2392 return nsMargin(0, 0, 0, 0);
2393}
2394
2395// TODO(TYLin, dshin): This ideally should be set only in first-in-flow.
2396// However, the current implementation of border-collapsed table does not
2397// handle continuation gracefully. One concrete issue is shown in bug 1881157
2398// comment 3. It is also unclear if the damage area, current included in this
2399// property, should be stored separately per-continuation.
2400NS_DECLARE_FRAME_PROPERTY_DELETABLE(TableBCDataProperty, TableBCData)static const mozilla::FramePropertyDescriptor<TableBCData>
* TableBCDataProperty() { static const auto descriptor = mozilla
::FramePropertyDescriptor<TableBCData>::NewWithDestructor
<DeleteValue>(); return &descriptor; }
2401
2402TableBCData* nsTableFrame::GetTableBCData() const {
2403 return GetProperty(TableBCDataProperty());
2404}
2405
2406TableBCData* nsTableFrame::GetOrCreateTableBCData() {
2407 TableBCData* value = GetProperty(TableBCDataProperty());
2408 if (!value) {
2409 value = new TableBCData();
2410 SetProperty(TableBCDataProperty(), value);
2411 }
2412
2413 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"
, 2413); AnnotateMozCrashReason("MOZ_ASSERT" "(" "value" ") ("
"TableBCData must exist!" ")"); do { *((volatile int*)__null
) = 2413; __attribute__((nomerge)) ::abort(); } while (false)
; } } while (false)
;
2414 return value;
2415}
2416
2417static void DivideBCBorderSize(nscoord aPixelSize, nscoord& aSmallHalf,
2418 nscoord& aLargeHalf) {
2419 aSmallHalf = aPixelSize / 2;
2420 aLargeHalf = aPixelSize - aSmallHalf;
2421}
2422
2423LogicalMargin nsTableFrame::GetOuterBCBorder(const WritingMode aWM) const {
2424 if (NeedToCalcBCBorders()) {
2425 const_cast<nsTableFrame*>(this)->CalcBCBorders();
2426 }
2427 TableBCData* propData = GetTableBCData();
2428 if (propData) {
2429 return LogicalMargin(aWM,
2430 BC_BORDER_START_HALF(propData->mBStartBorderWidth),
2431 BC_BORDER_END_HALF(propData->mIEndBorderWidth),
2432 BC_BORDER_END_HALF(propData->mBEndBorderWidth),
2433 BC_BORDER_START_HALF(propData->mIStartBorderWidth));
2434 }
2435 return LogicalMargin(aWM);
2436}
2437
2438LogicalMargin nsTableFrame::GetIncludedOuterBCBorder(
2439 const WritingMode aWM) const {
2440 if (NeedToCalcBCBorders()) {
2441 const_cast<nsTableFrame*>(this)->CalcBCBorders();
2442 }
2443
2444 TableBCData* propData = GetTableBCData();
2445 if (propData) {
2446 return LogicalMargin(
2447 aWM, BC_BORDER_START_HALF(propData->mBStartBorderWidth),
2448 BC_BORDER_END_HALF(propData->mIEndCellBorderWidth),
2449 BC_BORDER_END_HALF(propData->mBEndBorderWidth),
2450 BC_BORDER_START_HALF(propData->mIStartCellBorderWidth));
2451 }
2452 return LogicalMargin(aWM);
2453}
2454
2455LogicalMargin nsTableFrame::GetExcludedOuterBCBorder(
2456 const WritingMode aWM) const {
2457 return GetOuterBCBorder(aWM) - GetIncludedOuterBCBorder(aWM);
2458}
2459
2460void nsTableFrame::GetCollapsedBorderPadding(
2461 Maybe<LogicalMargin>& aBorder, Maybe<LogicalMargin>& aPadding) const {
2462 if (IsBorderCollapse()) {
2463 // Border-collapsed tables don't use any of their padding, and only part of
2464 // their border.
2465 const auto wm = GetWritingMode();
2466 aBorder.emplace(GetIncludedOuterBCBorder(wm));
2467 aPadding.emplace(wm);
2468 }
2469}
2470
2471void nsTableFrame::InitChildReflowInput(ReflowInput& aReflowInput) {
2472 const auto childWM = aReflowInput.GetWritingMode();
2473 LogicalMargin border(childWM);
2474 if (IsBorderCollapse()) {
2475 nsTableRowGroupFrame* rgFrame =
2476 static_cast<nsTableRowGroupFrame*>(aReflowInput.mFrame);
2477 border = rgFrame->GetBCBorderWidth(childWM);
2478 }
2479 const LogicalMargin zeroPadding(childWM);
2480 aReflowInput.Init(PresContext(), Nothing(), Some(border), Some(zeroPadding));
2481
2482 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"
, 2484); MOZ_PretendNoReturn(); } } while (0)
2483 !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"
, 2484); MOZ_PretendNoReturn(); } } while (0)
2484 "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"
, 2484); MOZ_PretendNoReturn(); } } while (0)
;
2485 if (mBits.mResizedColumns) {
2486 aReflowInput.SetIResize(true);
2487 }
2488}
2489
2490// Position and size aKidFrame and update our reflow input. The origin of
2491// aKidRect is relative to the upper-left origin of our frame
2492void nsTableFrame::PlaceChild(TableReflowInput& aReflowInput,
2493 nsIFrame* aKidFrame,
2494 const ReflowInput& aKidReflowInput,
2495 const mozilla::LogicalPoint& aKidPosition,
2496 const nsSize& aContainerSize,
2497 ReflowOutput& aKidDesiredSize,
2498 const nsRect& aOriginalKidRect,
2499 const nsRect& aOriginalKidInkOverflow) {
2500 WritingMode wm = aReflowInput.mReflowInput.GetWritingMode();
2501 bool isFirstReflow = aKidFrame->HasAnyStateBits(NS_FRAME_FIRST_REFLOW);
2502
2503 // Place and size the child
2504 FinishReflowChild(aKidFrame, PresContext(), aKidDesiredSize, &aKidReflowInput,
2505 wm, aKidPosition, aContainerSize,
2506 ReflowChildFlags::ApplyRelativePositioning);
2507
2508 InvalidateTableFrame(aKidFrame, aOriginalKidRect, aOriginalKidInkOverflow,
2509 isFirstReflow);
2510
2511 aReflowInput.AdvanceBCoord(aKidDesiredSize.BSize(wm));
2512}
2513
2514nsTableFrame::RowGroupArray nsTableFrame::OrderedRowGroups(
2515 nsTableRowGroupFrame** aHead, nsTableRowGroupFrame** aFoot) const {
2516 RowGroupArray children;
2517 nsTableRowGroupFrame* head = nullptr;
2518 nsTableRowGroupFrame* foot = nullptr;
2519
2520 nsIFrame* kidFrame = mFrames.FirstChild();
2521 while (kidFrame) {
2522 const nsStyleDisplay* kidDisplay = kidFrame->StyleDisplay();
2523 auto* rowGroup = static_cast<nsTableRowGroupFrame*>(kidFrame);
2524
2525 switch (kidDisplay->DisplayInside()) {
2526 case StyleDisplayInside::TableHeaderGroup:
2527 if (head) { // treat additional thead like tbody
2528 children.AppendElement(rowGroup);
2529 } else {
2530 head = rowGroup;
2531 }
2532 break;
2533 case StyleDisplayInside::TableFooterGroup:
2534 if (foot) { // treat additional tfoot like tbody
2535 children.AppendElement(rowGroup);
2536 } else {
2537 foot = rowGroup;
2538 }
2539 break;
2540 case StyleDisplayInside::TableRowGroup:
2541 children.AppendElement(rowGroup);
2542 break;
2543 default:
2544 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"
, 2544); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "How did this produce an nsTableRowGroupFrame?"
")"); do { *((volatile int*)__null) = 2544; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2545 // Just ignore it
2546 break;
2547 }
2548 // Get the next sibling but skip it if it's also the next-in-flow, since
2549 // a next-in-flow will not be part of the current table.
2550 while (kidFrame) {
2551 nsIFrame* nif = kidFrame->GetNextInFlow();
2552 kidFrame = kidFrame->GetNextSibling();
2553 if (kidFrame != nif) {
2554 break;
2555 }
2556 }
2557 }
2558
2559 // put the thead first
2560 if (head) {
2561 children.InsertElementAt(0, head);
2562 }
2563 if (aHead) {
2564 *aHead = head;
2565 }
2566 // put the tfoot after the last tbody
2567 if (foot) {
2568 children.AppendElement(foot);
2569 }
2570 if (aFoot) {
2571 *aFoot = foot;
2572 }
2573
2574 return children;
2575}
2576
2577static bool IsRepeatable(nscoord aFrameBSize, nscoord aPageBSize) {
2578 return aFrameBSize < (aPageBSize / 4);
2579}
2580
2581nscoord nsTableFrame::SetupHeaderFooterChild(
2582 const TableReflowInput& aReflowInput, nsTableRowGroupFrame* aFrame) {
2583 nsPresContext* presContext = PresContext();
2584 const WritingMode wm = GetWritingMode();
2585 const nscoord pageBSize =
2586 LogicalSize(wm, presContext->GetPageSize()).BSize(wm);
2587
2588 // Reflow the child with unconstrained block-size.
2589 LogicalSize availSize = aReflowInput.AvailableSize();
2590 availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
2591
2592 const nsSize containerSize =
2593 aReflowInput.mReflowInput.ComputedSizeAsContainerIfConstrained();
2594 ReflowInput kidReflowInput(presContext, aReflowInput.mReflowInput, aFrame,
2595 availSize, Nothing(),
2596 ReflowInput::InitFlag::CallerWillInit);
2597 InitChildReflowInput(kidReflowInput);
2598 kidReflowInput.mFlags.mIsTopOfPage = true;
2599 ReflowOutput desiredSize(aReflowInput.mReflowInput);
2600 nsReflowStatus status;
2601 ReflowChild(aFrame, presContext, desiredSize, kidReflowInput, wm,
2602 LogicalPoint(wm, aReflowInput.mICoord, aReflowInput.mBCoord),
2603 containerSize, ReflowChildFlags::Default, status);
2604 // The child will be reflowed again "for real" so no need to place it now
2605
2606 aFrame->SetRepeatable(IsRepeatable(desiredSize.BSize(wm), pageBSize));
2607 return desiredSize.BSize(wm);
2608}
2609
2610void nsTableFrame::PlaceRepeatedFooter(TableReflowInput& aReflowInput,
2611 nsTableRowGroupFrame* aTfoot,
2612 nscoord aFooterBSize) {
2613 nsPresContext* presContext = PresContext();
2614 const WritingMode wm = GetWritingMode();
2615 LogicalSize kidAvailSize = aReflowInput.AvailableSize();
2616 kidAvailSize.BSize(wm) = aFooterBSize;
2617
2618 const nsSize containerSize =
2619 aReflowInput.mReflowInput.ComputedSizeAsContainerIfConstrained();
2620 ReflowInput footerReflowInput(presContext, aReflowInput.mReflowInput, aTfoot,
2621 kidAvailSize, Nothing(),
2622 ReflowInput::InitFlag::CallerWillInit);
2623 InitChildReflowInput(footerReflowInput);
2624
2625 nsRect origTfootRect = aTfoot->GetRect();
2626 nsRect origTfootInkOverflow = aTfoot->InkOverflowRect();
2627
2628 nsReflowStatus footerStatus;
2629 ReflowOutput desiredSize(aReflowInput.mReflowInput);
2630 LogicalPoint kidPosition(wm, aReflowInput.mICoord, aReflowInput.mBCoord);
2631 ReflowChild(aTfoot, presContext, desiredSize, footerReflowInput, wm,
2632 kidPosition, containerSize, ReflowChildFlags::Default,
2633 footerStatus);
2634
2635 PlaceChild(aReflowInput, aTfoot, footerReflowInput, kidPosition,
2636 containerSize, desiredSize, origTfootRect, origTfootInkOverflow);
2637}
2638
2639// Reflow the children based on the avail size and reason in aReflowInput
2640void nsTableFrame::ReflowChildren(TableReflowInput& aReflowInput,
2641 nsReflowStatus& aStatus,
2642 nsIFrame*& aLastChildReflowed,
2643 OverflowAreas& aOverflowAreas) {
2644 aStatus.Reset();
2645 aLastChildReflowed = nullptr;
2646
2647 nsIFrame* prevKidFrame = nullptr;
2648 WritingMode wm = aReflowInput.mReflowInput.GetWritingMode();
2649 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"
, 2652); } } while (false)
2650 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"
, 2652); } } while (false)
2651 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"
, 2652); } } while (false)
2652 "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"
, 2652); } } while (false)
;
2653 nsSize containerSize =
2654 aReflowInput.mReflowInput.ComputedSizeAsContainerIfConstrained();
2655
2656 nsPresContext* presContext = PresContext();
2657 // nsTableFrame is not able to pull back children from its next-in-flow, per
2658 // bug 1772383. So even under paginated contexts, tables should not fragment
2659 // if they are inside of (i.e. potentially being fragmented by) a column-set
2660 // frame. (This is indicated by the "mTableIsSplittable" flag.)
2661 bool isPaginated =
2662 presContext->IsPaginated() &&
2663 aReflowInput.mReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE &&
2664 aReflowInput.mReflowInput.mFlags.mTableIsSplittable;
2665
2666 // Tables currently (though we ought to fix this) only fragment in
2667 // paginated contexts, not in multicolumn contexts. (See bug 888257.)
2668 // This is partly because they don't correctly handle incremental
2669 // layout when paginated.
2670 //
2671 // Since we propagate NS_FRAME_IS_DIRTY from parent to child at the
2672 // start of the parent's reflow (behavior that's new as of bug
2673 // 1308876), we can do things that are effectively incremental reflow
2674 // during paginated layout. Since the table code doesn't handle this
2675 // correctly, we need to set the flag that says to reflow everything
2676 // within the table structure.
2677 if (presContext->IsPaginated()) {
2678 SetGeometryDirty();
2679 }
2680
2681 aOverflowAreas.Clear();
2682
2683 bool reflowAllKids = aReflowInput.mReflowInput.ShouldReflowAllKids() ||
2684 mBits.mResizedColumns || IsGeometryDirty() ||
2685 NeedToCollapse();
2686
2687 nsTableRowGroupFrame* thead = nullptr;
2688 nsTableRowGroupFrame* tfoot = nullptr;
2689 RowGroupArray rowGroups = OrderedRowGroups(&thead, &tfoot);
2690 bool pageBreak = false;
2691 nscoord footerBSize = 0;
2692
2693 // Determine the repeatablility of headers and footers, and also the desired
2694 // height of any repeatable footer.
2695 // The repeatability of headers on continued tables is handled
2696 // when they are created in nsCSSFrameConstructor::CreateContinuingTableFrame.
2697 // We handle the repeatability of footers again here because we need to
2698 // determine the footer's height anyway. We could perhaps optimize by
2699 // using the footer's prev-in-flow's height instead of reflowing it again,
2700 // but there's no real need.
2701 if (isPaginated) {
2702 bool reorder = false;
2703 if (thead && !GetPrevInFlow()) {
2704 reorder = thead->GetNextInFlow();
2705 SetupHeaderFooterChild(aReflowInput, thead);
2706 }
2707 if (tfoot) {
2708 reorder = reorder || tfoot->GetNextInFlow();
2709 footerBSize = SetupHeaderFooterChild(aReflowInput, tfoot);
2710 }
2711 if (reorder) {
2712 // Reorder row groups - the reflow may have changed the nextinflows.
2713 rowGroups = OrderedRowGroups(&thead, &tfoot);
2714 }
2715 }
2716 bool allowRepeatedFooter = false;
2717 for (size_t childX = 0; childX < rowGroups.Length(); childX++) {
2718 nsTableRowGroupFrame* kidFrame = rowGroups[childX];
2719 const nscoord rowSpacing =
2720 GetRowSpacing(kidFrame->GetStartRowIndex() + kidFrame->GetRowCount());
2721 // See if we should only reflow the dirty child frames
2722 if (reflowAllKids || kidFrame->IsSubtreeDirty() ||
2723 (aReflowInput.mReflowInput.mFlags.mSpecialBSizeReflow &&
2724 (isPaginated ||
2725 kidFrame->HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE)))) {
2726 // A helper to place a repeated footer if allowed, or set it as
2727 // non-repeatable.
2728 auto MaybePlaceRepeatedFooter = [&]() {
2729 if (allowRepeatedFooter) {
2730 PlaceRepeatedFooter(aReflowInput, tfoot, footerBSize);
2731 } else if (tfoot && tfoot->IsRepeatable()) {
2732 tfoot->SetRepeatable(false);
2733 }
2734 };
2735
2736 if (pageBreak) {
2737 MaybePlaceRepeatedFooter();
2738 PushChildrenToOverflow(rowGroups, childX);
2739 aStatus.Reset();
2740 aStatus.SetIncomplete();
2741 aLastChildReflowed = allowRepeatedFooter ? tfoot : prevKidFrame;
2742 break;
2743 }
2744
2745 LogicalSize kidAvailSize = aReflowInput.AvailableSize();
2746 allowRepeatedFooter = false;
2747
2748 // If the child is a tbody in paginated mode, reduce the available
2749 // block-size by a repeated footer.
2750 if (isPaginated && (NS_UNCONSTRAINEDSIZE != kidAvailSize.BSize(wm))) {
2751 if (kidFrame != thead && kidFrame != tfoot && tfoot &&
2752 tfoot->IsRepeatable()) {
2753 // the child is a tbody and there is a repeatable footer
2754 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"
, 2755); MOZ_PretendNoReturn(); } } while (0)
2755 "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"
, 2755); MOZ_PretendNoReturn(); } } while (0)
;
2756 if (footerBSize + rowSpacing < kidAvailSize.BSize(wm)) {
2757 allowRepeatedFooter = true;
2758 kidAvailSize.BSize(wm) -= footerBSize + rowSpacing;
2759 }
2760 }
2761 }
2762
2763 nsRect oldKidRect = kidFrame->GetRect();
2764 nsRect oldKidInkOverflow = kidFrame->InkOverflowRect();
2765
2766 ReflowOutput desiredSize(aReflowInput.mReflowInput);
2767
2768 // Reflow the child into the available space
2769 ReflowInput kidReflowInput(presContext, aReflowInput.mReflowInput,
2770 kidFrame, kidAvailSize, Nothing(),
2771 ReflowInput::InitFlag::CallerWillInit);
2772 InitChildReflowInput(kidReflowInput);
2773
2774 // If this isn't the first row group, and the previous row group has a
2775 // nonzero BEnd, then we can't be at the top of the page.
2776 // We ignore a repeated head row group in this check to avoid causing
2777 // infinite loops in some circumstances - see bug 344883.
2778 if (childX > ((thead && IsRepeatedFrame(thead)) ? 1u : 0u) &&
2779 (rowGroups[childX - 1]
2780 ->GetLogicalNormalRect(wm, containerSize)
2781 .BEnd(wm) > 0)) {
2782 kidReflowInput.mFlags.mIsTopOfPage = false;
2783 }
2784
2785 // record the presence of a next in flow, it might get destroyed so we
2786 // need to reorder the row group array
2787 const bool reorder = kidFrame->GetNextInFlow();
2788
2789 LogicalPoint kidPosition(wm, aReflowInput.mICoord, aReflowInput.mBCoord);
2790 aStatus.Reset();
2791 ReflowChild(kidFrame, presContext, desiredSize, kidReflowInput, wm,
2792 kidPosition, containerSize, ReflowChildFlags::Default,
2793 aStatus);
2794
2795 if (reorder) {
2796 // Reorder row groups - the reflow may have changed the nextinflows.
2797 rowGroups = OrderedRowGroups(&thead, &tfoot);
2798 childX = rowGroups.IndexOf(kidFrame);
2799 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"
, 2800); AnnotateMozCrashReason("MOZ_ASSERT" "(" "childX != RowGroupArray::NoIndex"
") (" "kidFrame should still be in rowGroups!" ")"); do { *(
(volatile int*)__null) = 2800; __attribute__((nomerge)) ::abort
(); } while (false); } } while (false)
2800 "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"
, 2800); AnnotateMozCrashReason("MOZ_ASSERT" "(" "childX != RowGroupArray::NoIndex"
") (" "kidFrame should still be in rowGroups!" ")"); do { *(
(volatile int*)__null) = 2800; __attribute__((nomerge)) ::abort
(); } while (false); } } while (false)
;
2801 }
2802 if (isPaginated && !aStatus.IsFullyComplete() &&
2803 ShouldAvoidBreakInside(aReflowInput.mReflowInput)) {
2804 aStatus.SetInlineLineBreakBeforeAndReset();
2805 break;
2806 }
2807 // see if the rowgroup did not fit on this page might be pushed on
2808 // the next page
2809 if (isPaginated &&
2810 (aStatus.IsInlineBreakBefore() ||
2811 (aStatus.IsComplete() &&
2812 (kidReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE) &&
2813 kidReflowInput.AvailableBSize() < desiredSize.BSize(wm)))) {
2814 if (ShouldAvoidBreakInside(aReflowInput.mReflowInput)) {
2815 aStatus.SetInlineLineBreakBeforeAndReset();
2816 break;
2817 }
2818 // if we are on top of the page place with dataloss
2819 if (kidReflowInput.mFlags.mIsTopOfPage) {
2820 if (childX + 1 < rowGroups.Length()) {
2821 PlaceChild(aReflowInput, kidFrame, kidReflowInput, kidPosition,
2822 containerSize, desiredSize, oldKidRect,
2823 oldKidInkOverflow);
2824 MaybePlaceRepeatedFooter();
2825 aStatus.Reset();
2826 aStatus.SetIncomplete();
2827 PushChildrenToOverflow(rowGroups, childX + 1);
2828 aLastChildReflowed = allowRepeatedFooter ? tfoot : kidFrame;
2829 break;
2830 }
2831 } else { // we are not on top, push this rowgroup onto the next page
2832 if (prevKidFrame) { // we had a rowgroup before so push this
2833 MaybePlaceRepeatedFooter();
2834 aStatus.Reset();
2835 aStatus.SetIncomplete();
2836 PushChildrenToOverflow(rowGroups, childX);
2837 aLastChildReflowed = allowRepeatedFooter ? tfoot : prevKidFrame;
2838 break;
2839 } else { // we can't push so lets make clear how much space we need
2840 PlaceChild(aReflowInput, kidFrame, kidReflowInput, kidPosition,
2841 containerSize, desiredSize, oldKidRect,
2842 oldKidInkOverflow);
2843 MaybePlaceRepeatedFooter();
2844 aLastChildReflowed = allowRepeatedFooter ? tfoot : kidFrame;
2845 break;
2846 }
2847 }
2848 }
2849
2850 aLastChildReflowed = kidFrame;
2851
2852 pageBreak = false;
2853 // see if there is a page break after this row group or before the next
2854 // one
2855 if (aStatus.IsComplete() && isPaginated &&
2856 (kidReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE)) {
2857 nsIFrame* nextKid =
2858 (childX + 1 < rowGroups.Length()) ? rowGroups[childX + 1] : nullptr;
2859 pageBreak = PageBreakAfter(kidFrame, nextKid);
2860 }
2861
2862 // Place the child
2863 PlaceChild(aReflowInput, kidFrame, kidReflowInput, kidPosition,
2864 containerSize, desiredSize, oldKidRect, oldKidInkOverflow);
2865 aReflowInput.AdvanceBCoord(rowSpacing);
2866
2867 // Remember where we just were in case we end up pushing children
2868 prevKidFrame = kidFrame;
2869
2870 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"
, 2871); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!aStatus.IsIncomplete() || isPaginated"
") (" "Table contents should only fragment in paginated contexts"
")"); do { *((volatile int*)__null) = 2871; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2871 "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"
, 2871); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!aStatus.IsIncomplete() || isPaginated"
") (" "Table contents should only fragment in paginated contexts"
")"); do { *((volatile int*)__null) = 2871; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2872
2873 // Special handling for incomplete children
2874 if (isPaginated && aStatus.IsIncomplete()) {
2875 nsIFrame* kidNextInFlow = kidFrame->GetNextInFlow();
2876 if (!kidNextInFlow) {
2877 // The child doesn't have a next-in-flow so create a continuing
2878 // frame. This hooks the child into the flow
2879 kidNextInFlow =
2880 PresShell()->FrameConstructor()->CreateContinuingFrame(kidFrame,
2881 this);
2882
2883 // Insert the kid's new next-in-flow into our sibling list...
2884 mFrames.InsertFrame(nullptr, kidFrame, kidNextInFlow);
2885 // and in rowGroups after childX so that it will get pushed below.
2886 rowGroups.InsertElementAt(
2887 childX + 1, static_cast<nsTableRowGroupFrame*>(kidNextInFlow));
2888 } else if (kidNextInFlow == kidFrame->GetNextSibling()) {
2889 // OrderedRowGroups excludes NIFs in the child list from 'rowGroups'
2890 // so we deal with that here to make sure they get pushed.
2891 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"
, 2892); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!rowGroups.Contains(kidNextInFlow)"
") (" "OrderedRowGroups must not put our NIF in 'rowGroups'"
")"); do { *((volatile int*)__null) = 2892; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2892 "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"
, 2892); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!rowGroups.Contains(kidNextInFlow)"
") (" "OrderedRowGroups must not put our NIF in 'rowGroups'"
")"); do { *((volatile int*)__null) = 2892; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2893 rowGroups.InsertElementAt(
2894 childX + 1, static_cast<nsTableRowGroupFrame*>(kidNextInFlow));
2895 }
2896
2897 // We've used up all of our available space so push the remaining
2898 // children.
2899 MaybePlaceRepeatedFooter();
2900 if (kidFrame->GetNextSibling()) {
2901 PushChildrenToOverflow(rowGroups, childX + 1);
2902 }
2903 aLastChildReflowed = allowRepeatedFooter ? tfoot : kidFrame;
2904 break;
2905 }
2906 } else { // it isn't being reflowed
2907 aReflowInput.AdvanceBCoord(rowSpacing);
2908 const LogicalRect kidRect =
2909 kidFrame->GetLogicalNormalRect(wm, containerSize);
2910 if (kidRect.BStart(wm) != aReflowInput.mBCoord) {
2911 // invalidate the old position
2912 kidFrame->InvalidateFrameSubtree();
2913 // move to the new position
2914 kidFrame->MovePositionBy(
2915 wm, LogicalPoint(wm, 0, aReflowInput.mBCoord - kidRect.BStart(wm)));
2916 RePositionViews(kidFrame);
2917 // invalidate the new position
2918 kidFrame->InvalidateFrameSubtree();
2919 }
2920
2921 aReflowInput.AdvanceBCoord(kidRect.BSize(wm));
2922 }
2923 }
2924
2925 // We've now propagated the column resizes and geometry changes to all
2926 // the children.
2927 mBits.mResizedColumns = false;
2928 ClearGeometryDirty();
2929
2930 // nsTableFrame does not pull children from its next-in-flow (bug 1772383).
2931 // This is generally fine, since tables only fragment for printing
2932 // (bug 888257) where incremental-reflow is impossible, and so children don't
2933 // usually dynamically move back and forth between continuations. However,
2934 // there are edge cases even with printing where nsTableFrame:
2935 // (1) Generates a continuation and passes children to it,
2936 // (2) Receives another call to Reflow, during which it
2937 // (3) Successfully lays out its remaining children.
2938 // If the completed status flows up as-is, the continuation will be destroyed.
2939 // To avoid that, we return an incomplete status if the continuation contains
2940 // any child that is not a repeated frame.
2941 auto hasNextInFlowThatMustBePreserved = [this, isPaginated]() -> bool {
2942 if (!isPaginated) {
2943 return false;
2944 }
2945 auto* nextInFlow = static_cast<nsTableFrame*>(GetNextInFlow());
2946 if (!nextInFlow) {
2947 return false;
2948 }
2949 for (nsIFrame* kidFrame : nextInFlow->mFrames) {
2950 if (!IsRepeatedFrame(kidFrame)) {
2951 return true;
2952 }
2953 }
2954 return false;
2955 };
2956 if (aStatus.IsComplete() && hasNextInFlowThatMustBePreserved()) {
2957 aStatus.SetIncomplete();
2958 }
2959}
2960
2961void nsTableFrame::ReflowColGroups(gfxContext* aRenderingContext) {
2962 if (!GetPrevInFlow() && !HaveReflowedColGroups()) {
2963 const WritingMode wm = GetWritingMode();
2964 nsPresContext* presContext = PresContext();
2965 for (nsIFrame* kidFrame : mColGroups) {
2966 if (kidFrame->IsSubtreeDirty()) {
2967 // The column groups don't care about dimensions or reflow inputs.
2968 ReflowOutput kidSize(wm);
2969 ReflowInput kidReflowInput(presContext, kidFrame, aRenderingContext,
2970 LogicalSize(kidFrame->GetWritingMode()));
2971 nsReflowStatus cgStatus;
2972 const LogicalPoint dummyPos(wm);
2973 const nsSize dummyContainerSize;
2974 ReflowChild(kidFrame, presContext, kidSize, kidReflowInput, wm,
2975 dummyPos, dummyContainerSize, ReflowChildFlags::Default,
2976 cgStatus);
2977 FinishReflowChild(kidFrame, presContext, kidSize, &kidReflowInput, wm,
2978 dummyPos, dummyContainerSize,
2979 ReflowChildFlags::Default);
2980 }
2981 }
2982 SetHaveReflowedColGroups(true);
2983 }
2984}
2985
2986nscoord nsTableFrame::CalcDesiredBSize(const ReflowInput& aReflowInput,
2987 const LogicalMargin& aBorderPadding,
2988 const nsReflowStatus& aStatus) {
2989 WritingMode wm = aReflowInput.GetWritingMode();
2990
2991 RowGroupArray rowGroups = OrderedRowGroups();
2992 if (rowGroups.IsEmpty()) {
2993 if (eCompatibility_NavQuirks == PresContext()->CompatibilityMode()) {
2994 // empty tables should not have a size in quirks mode
2995 return 0;
2996 }
2997 return CalcBorderBoxBSize(aReflowInput, aBorderPadding,
2998 aBorderPadding.BStartEnd(wm));
2999 }
3000
3001 nsTableCellMap* cellMap = GetCellMap();
3002 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"
, 3002); AnnotateMozCrashReason("MOZ_ASSERT" "(" "cellMap" ")"
); do { *((volatile int*)__null) = 3002; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3003 int32_t rowCount = cellMap->GetRowCount();
3004 int32_t colCount = cellMap->GetColCount();
3005 nscoord desiredBSize = aBorderPadding.BStartEnd(wm);
3006 if (rowCount > 0 && colCount > 0) {
3007 if (!GetPrevInFlow()) {
3008 desiredBSize += GetRowSpacing(-1);
3009 }
3010 const nsTableRowGroupFrame* lastRG = rowGroups.LastElement();
3011 for (nsTableRowGroupFrame* rg : rowGroups) {
3012 desiredBSize += rg->BSize(wm);
3013 if (rg != lastRG || aStatus.IsFullyComplete()) {
3014 desiredBSize +=
3015 GetRowSpacing(rg->GetStartRowIndex() + rg->GetRowCount());
3016 }
3017 }
3018 if (aReflowInput.ComputedBSize() == NS_UNCONSTRAINEDSIZE &&
3019 aStatus.IsIncomplete()) {
3020 desiredBSize = std::max(desiredBSize, aReflowInput.AvailableBSize());
3021 }
3022 }
3023
3024 // see if a specified table bsize requires dividing additional space to rows
3025 if (!GetPrevInFlow()) {
3026 nscoord bSize =
3027 CalcBorderBoxBSize(aReflowInput, aBorderPadding, desiredBSize);
3028 if (bSize > desiredBSize) {
3029 // proportionately distribute the excess bsize to unconstrained rows in
3030 // each unconstrained row group.
3031 DistributeBSizeToRows(aReflowInput, bSize - desiredBSize);
3032 return bSize;
3033 }
3034 // Tables don't shrink below their intrinsic size, apparently, even when
3035 // constrained by stuff like flex / grid or what not.
3036 return desiredBSize;
3037 }
3038
3039 // FIXME(emilio): Is this right? This only affects fragmented tables...
3040 return desiredBSize;
3041}
3042
3043static void ResizeCells(nsTableFrame& aTableFrame) {
3044 nsTableFrame::RowGroupArray rowGroups = aTableFrame.OrderedRowGroups();
3045 WritingMode wm = aTableFrame.GetWritingMode();
3046 ReflowOutput tableDesiredSize(wm);
3047 tableDesiredSize.SetSize(wm, aTableFrame.GetLogicalSize(wm));
3048 tableDesiredSize.SetOverflowAreasToDesiredBounds();
3049
3050 for (uint32_t rgIdx = 0; rgIdx < rowGroups.Length(); rgIdx++) {
3051 nsTableRowGroupFrame* rgFrame = rowGroups[rgIdx];
3052
3053 ReflowOutput groupDesiredSize(wm);
3054 groupDesiredSize.SetSize(wm, rgFrame->GetLogicalSize(wm));
3055 groupDesiredSize.SetOverflowAreasToDesiredBounds();
3056
3057 nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
3058 while (rowFrame) {
3059 rowFrame->DidResize();
3060 rgFrame->ConsiderChildOverflow(groupDesiredSize.mOverflowAreas, rowFrame);
3061 rowFrame = rowFrame->GetNextRow();
3062 }
3063 rgFrame->FinishAndStoreOverflow(&groupDesiredSize);
3064 tableDesiredSize.mOverflowAreas.UnionWith(groupDesiredSize.mOverflowAreas +
3065 rgFrame->GetPosition());
3066 }
3067 aTableFrame.FinishAndStoreOverflow(&tableDesiredSize);
3068}
3069
3070void nsTableFrame::DistributeBSizeToRows(const ReflowInput& aReflowInput,
3071 nscoord aAmount) {
3072 WritingMode wm = aReflowInput.GetWritingMode();
3073 LogicalMargin borderPadding = aReflowInput.ComputedLogicalBorderPadding(wm);
3074
3075 nsSize containerSize = aReflowInput.ComputedSizeAsContainerIfConstrained();
3076
3077 RowGroupArray rowGroups = OrderedRowGroups();
3078
3079 nscoord amountUsed = 0;
3080 // distribute space to each pct bsize row whose row group doesn't have a
3081 // computed bsize, and base the pct on the table bsize. If the row group had a
3082 // computed bsize, then this was already done in
3083 // nsTableRowGroupFrame::CalculateRowBSizes
3084 nscoord pctBasis =
3085 aReflowInput.ComputedBSize() - GetRowSpacing(-1, GetRowCount());
3086 nscoord bOriginRG = borderPadding.BStart(wm) + GetRowSpacing(0);
3087 nscoord bEndRG = bOriginRG;
3088 uint32_t rgIdx;
3089 for (rgIdx = 0; rgIdx < rowGroups.Length(); rgIdx++) {
3090 nsTableRowGroupFrame* rgFrame = rowGroups[rgIdx];
3091 nscoord amountUsedByRG = 0;
3092 nscoord bOriginRow = 0;
3093 const LogicalRect rgNormalRect =
3094 rgFrame->GetLogicalNormalRect(wm, containerSize);
3095 if (!rgFrame->HasStyleBSize()) {
3096 nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
3097 while (rowFrame) {
3098 // We don't know the final width of the rowGroupFrame yet, so use 0,0
3099 // as a dummy containerSize here; we'll adjust the row positions at
3100 // the end, after the rowGroup size is finalized.
3101 const nsSize dummyContainerSize;
3102 const LogicalRect rowNormalRect =
3103 rowFrame->GetLogicalNormalRect(wm, dummyContainerSize);
3104 const nscoord rowSpacing = GetRowSpacing(rowFrame->GetRowIndex());
3105 if ((amountUsed < aAmount) && rowFrame->HasPctBSize()) {
3106 nscoord pctBSize = rowFrame->GetInitialBSize(pctBasis);
3107 nscoord amountForRow = std::min(aAmount - amountUsed,
3108 pctBSize - rowNormalRect.BSize(wm));
3109 if (amountForRow > 0) {
3110 // XXXbz we don't need to move the row's b-position to bOriginRow?
3111 nsRect origRowRect = rowFrame->GetRect();
3112 nscoord newRowBSize = rowNormalRect.BSize(wm) + amountForRow;
3113 rowFrame->SetSize(
3114 wm, LogicalSize(wm, rowNormalRect.ISize(wm), newRowBSize));
3115 bOriginRow += newRowBSize + rowSpacing;
3116 bEndRG += newRowBSize + rowSpacing;
3117 amountUsed += amountForRow;
3118 amountUsedByRG += amountForRow;
3119 // rowFrame->DidResize();
3120 nsTableFrame::RePositionViews(rowFrame);
3121
3122 rgFrame->InvalidateFrameWithRect(origRowRect);
3123 rgFrame->InvalidateFrame();
3124 }
3125 } else {
3126 if (amountUsed > 0 && bOriginRow != rowNormalRect.BStart(wm) &&
3127 !HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
3128 rowFrame->InvalidateFrameSubtree();
3129 rowFrame->MovePositionBy(
3130 wm, LogicalPoint(wm, 0, bOriginRow - rowNormalRect.BStart(wm)));
3131 nsTableFrame::RePositionViews(rowFrame);
3132 rowFrame->InvalidateFrameSubtree();
3133 }
3134 bOriginRow += rowNormalRect.BSize(wm) + rowSpacing;
3135 bEndRG += rowNormalRect.BSize(wm) + rowSpacing;
3136 }
3137 rowFrame = rowFrame->GetNextRow();
3138 }
3139 if (amountUsed > 0) {
3140 if (rgNormalRect.BStart(wm) != bOriginRG) {
3141 rgFrame->InvalidateFrameSubtree();
3142 }
3143
3144 nsRect origRgNormalRect = rgFrame->GetRect();
3145 nsRect origRgInkOverflow = rgFrame->InkOverflowRect();
3146
3147 rgFrame->MovePositionBy(
3148 wm, LogicalPoint(wm, 0, bOriginRG - rgNormalRect.BStart(wm)));
3149 rgFrame->SetSize(wm,
3150 LogicalSize(wm, rgNormalRect.ISize(wm),
3151 rgNormalRect.BSize(wm) + amountUsedByRG));
3152
3153 nsTableFrame::InvalidateTableFrame(rgFrame, origRgNormalRect,
3154 origRgInkOverflow, false);
3155 }
3156 } else if (amountUsed > 0 && bOriginRG != rgNormalRect.BStart(wm)) {
3157 rgFrame->InvalidateFrameSubtree();
3158 rgFrame->MovePositionBy(
3159 wm, LogicalPoint(wm, 0, bOriginRG - rgNormalRect.BStart(wm)));
3160 // Make sure child views are properly positioned
3161 nsTableFrame::RePositionViews(rgFrame);
3162 rgFrame->InvalidateFrameSubtree();
3163 }
3164 bOriginRG = bEndRG;
3165 }
3166
3167 if (amountUsed >= aAmount) {
3168 ResizeCells(*this);
3169 return;
3170 }
3171
3172 // get the first row without a style bsize where its row group has an
3173 // unconstrained bsize
3174 nsTableRowGroupFrame* firstUnStyledRG = nullptr;
3175 nsTableRowFrame* firstUnStyledRow = nullptr;
3176 for (rgIdx = 0; rgIdx < rowGroups.Length() && !firstUnStyledRG; rgIdx++) {
3177 nsTableRowGroupFrame* rgFrame = rowGroups[rgIdx];
3178 if (!rgFrame->HasStyleBSize()) {
3179 nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
3180 while (rowFrame) {
3181 if (!rowFrame->HasStyleBSize()) {
3182 firstUnStyledRG = rgFrame;
3183 firstUnStyledRow = rowFrame;
3184 break;
3185 }
3186 rowFrame = rowFrame->GetNextRow();
3187 }
3188 }
3189 }
3190
3191 nsTableRowFrame* lastEligibleRow = nullptr;
3192 // Accumulate the correct divisor. This will be the total bsize of all
3193 // unstyled rows inside unstyled row groups, unless there are none, in which
3194 // case, it will be number of all rows. If the unstyled rows don't have a
3195 // bsize, divide the space equally among them.
3196 nscoord divisor = 0;
3197 int32_t eligibleRows = 0;
3198 bool expandEmptyRows = false;
3199
3200 if (!firstUnStyledRow) {
3201 // there is no unstyled row
3202 divisor = GetRowCount();
3203 } else {
3204 for (rgIdx = 0; rgIdx < rowGroups.Length(); rgIdx++) {
3205 nsTableRowGroupFrame* rgFrame = rowGroups[rgIdx];
3206 if (!firstUnStyledRG || !rgFrame->HasStyleBSize()) {
3207 nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
3208 while (rowFrame) {
3209 if (!firstUnStyledRG || !rowFrame->HasStyleBSize()) {
3210 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"
, 3211); MOZ_PretendNoReturn(); } } while (0)
3211 "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"
, 3211); MOZ_PretendNoReturn(); } } while (0)
;
3212 divisor += rowFrame->BSize(wm);
3213 eligibleRows++;
3214 lastEligibleRow = rowFrame;
3215 }
3216 rowFrame = rowFrame->GetNextRow();
3217 }
3218 }
3219 }
3220 if (divisor <= 0) {
3221 if (eligibleRows > 0) {
3222 expandEmptyRows = true;
3223 } else {
3224 NS_ERROR("invalid divisor")do { NS_DebugBreak(NS_DEBUG_ASSERTION, "invalid divisor", "Error"
, "/var/lib/jenkins/workspace/firefox-scan-build/layout/tables/nsTableFrame.cpp"
, 3224); MOZ_PretendNoReturn(); } while (0)
;
3225 return;
3226 }
3227 }
3228 }
3229 // allocate the extra bsize to the unstyled row groups and rows
3230 nscoord bSizeToDistribute = aAmount - amountUsed;
3231 bOriginRG = borderPadding.BStart(wm) + GetRowSpacing(-1);
3232 bEndRG = bOriginRG;
3233 for (rgIdx = 0; rgIdx < rowGroups.Length(); rgIdx++) {
3234 nsTableRowGroupFrame* rgFrame = rowGroups[rgIdx];
3235 nscoord amountUsedByRG = 0;
3236 nscoord bOriginRow = 0;
3237 const LogicalRect rgNormalRect =
3238 rgFrame->GetLogicalNormalRect(wm, containerSize);
3239 nsRect rgInkOverflow = rgFrame->InkOverflowRect();
3240 // see if there is an eligible row group or we distribute to all rows
3241 if (!firstUnStyledRG || !rgFrame->HasStyleBSize() || !eligibleRows) {
3242 for (nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); rowFrame;
3243 rowFrame = rowFrame->GetNextRow()) {
3244 const nscoord rowSpacing = GetRowSpacing(rowFrame->GetRowIndex());
3245 // We don't know the final width of the rowGroupFrame yet, so use 0,0
3246 // as a dummy containerSize here; we'll adjust the row positions at
3247 // the end, after the rowGroup size is finalized.
3248 const nsSize dummyContainerSize;
3249 const LogicalRect rowNormalRect =
3250 rowFrame->GetLogicalNormalRect(wm, dummyContainerSize);
3251 nsRect rowInkOverflow = rowFrame->InkOverflowRect();
3252 // see if there is an eligible row or we distribute to all rows
3253 if (!firstUnStyledRow || !rowFrame->HasStyleBSize() || !eligibleRows) {
3254 float ratio;
3255 if (eligibleRows) {
3256 if (!expandEmptyRows) {
3257 // The amount of additional space each row gets is proportional
3258 // to its bsize
3259 ratio = float(rowNormalRect.BSize(wm)) / float(divisor);
3260 } else {
3261 // empty rows get all the same additional space
3262 ratio = 1.0f / float(eligibleRows);
3263 }
3264 } else {
3265 // all rows get the same additional space
3266 ratio = 1.0f / float(divisor);
3267 }
3268 // give rows their additional space, except for the last row which
3269 // gets the remainder
3270 nscoord amountForRow =
3271 (rowFrame == lastEligibleRow)
3272 ? aAmount - amountUsed
3273 : NSToCoordRound(((float)(bSizeToDistribute)) * ratio);
3274 amountForRow = std::min(amountForRow, aAmount - amountUsed);
3275
3276 if (bOriginRow != rowNormalRect.BStart(wm)) {
3277 rowFrame->InvalidateFrameSubtree();
3278 }
3279
3280 // update the row bsize
3281 nsRect origRowRect = rowFrame->GetRect();
3282 nscoord newRowBSize = rowNormalRect.BSize(wm) + amountForRow;
3283 rowFrame->MovePositionBy(
3284 wm, LogicalPoint(wm, 0, bOriginRow - rowNormalRect.BStart(wm)));
3285 rowFrame->SetSize(
3286 wm, LogicalSize(wm, rowNormalRect.ISize(wm), newRowBSize));
3287
3288 bOriginRow += newRowBSize + rowSpacing;
3289 bEndRG += newRowBSize + rowSpacing;
3290
3291 amountUsed += amountForRow;
3292 amountUsedByRG += amountForRow;
3293 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"
, 3293); MOZ_PretendNoReturn(); } } while (0)
;
3294 // rowFrame->DidResize();
3295 nsTableFrame::RePositionViews(rowFrame);
3296
3297 nsTableFrame::InvalidateTableFrame(rowFrame, origRowRect,
3298 rowInkOverflow, false);
3299 } else {
3300 if (amountUsed > 0 && bOriginRow != rowNormalRect.BStart(wm)) {
3301 rowFrame->InvalidateFrameSubtree();
3302 rowFrame->MovePositionBy(
3303 wm, LogicalPoint(wm, 0, bOriginRow - rowNormalRect.BStart(wm)));
3304 nsTableFrame::RePositionViews(rowFrame);
3305 rowFrame->InvalidateFrameSubtree();
3306 }
3307 bOriginRow += rowNormalRect.BSize(wm) + rowSpacing;
3308 bEndRG += rowNormalRect.BSize(wm) + rowSpacing;
3309 }
3310 }
3311
3312 if (amountUsed > 0) {
3313 if (rgNormalRect.BStart(wm) != bOriginRG) {
3314 rgFrame->InvalidateFrameSubtree();
3315 }
3316
3317 nsRect origRgNormalRect = rgFrame->GetRect();
3318 rgFrame->MovePositionBy(
3319 wm, LogicalPoint(wm, 0, bOriginRG - rgNormalRect.BStart(wm)));
3320 rgFrame->SetSize(wm,
3321 LogicalSize(wm, rgNormalRect.ISize(wm),
3322 rgNormalRect.BSize(wm) + amountUsedByRG));
3323
3324 nsTableFrame::InvalidateTableFrame(rgFrame, origRgNormalRect,
3325 rgInkOverflow, false);
3326 }
3327
3328 // For vertical-rl mode, we needed to position the rows relative to the
3329 // right-hand (block-start) side of the group; but we couldn't do that
3330 // above, as we didn't know the rowGroupFrame's final block size yet.
3331 // So we used a dummyContainerSize of 0,0 earlier, placing the rows to
3332 // the left of the rowGroupFrame's (physical) origin. Now we move them
3333 // all rightwards by its final width.
3334 if (wm.IsVerticalRL()) {
3335 nscoord rgWidth = rgFrame->GetSize().width;
3336 for (nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); rowFrame;
3337 rowFrame = rowFrame->GetNextRow()) {
3338 rowFrame->InvalidateFrameSubtree();
3339 rowFrame->MovePositionBy(nsPoint(rgWidth, 0));
3340 nsTableFrame::RePositionViews(rowFrame);
3341 rowFrame->InvalidateFrameSubtree();
3342 }
3343 }
3344 } else if (amountUsed > 0 && bOriginRG != rgNormalRect.BStart(wm)) {
3345 rgFrame->InvalidateFrameSubtree();
3346 rgFrame->MovePositionBy(
3347 wm, LogicalPoint(wm, 0, bOriginRG - rgNormalRect.BStart(wm)));
3348 // Make sure child views are properly positioned
3349 nsTableFrame::RePositionViews(rgFrame);
3350 rgFrame->InvalidateFrameSubtree();
3351 }
3352 bOriginRG = bEndRG;
3353 }
3354
3355 ResizeCells(*this);
3356}
3357
3358nscoord nsTableFrame::GetColumnISizeFromFirstInFlow(int32_t aColIndex) {
3359 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"
, 3359); AnnotateMozCrashReason("MOZ_ASSERT" "(" "this == FirstInFlow()"
")"); do { *((volatile int*)__null) = 3359; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3360 nsTableColFrame* colFrame = GetColFrame(aColIndex);
3361 return colFrame ? colFrame->GetFinalISize() : 0;
3362}
3363
3364nscoord nsTableFrame::GetColSpacing() {
3365 if (IsBorderCollapse()) return 0;
3366
3367 return StyleTableBorder()->mBorderSpacingCol;
3368}
3369
3370// XXX: could cache this. But be sure to check style changes if you do!
3371nscoord nsTableFrame::GetColSpacing(int32_t aColIndex) {
3372 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"
, 3373); MOZ_PretendNoReturn(); } } while (0)
3373 "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"
, 3373); MOZ_PretendNoReturn(); } } while (0)
;
3374 // Index is irrelevant for ordinary tables. We check that it falls within
3375 // appropriate bounds to increase confidence of correctness in situations
3376 // where it does matter.
3377 return GetColSpacing();
3378}
3379
3380nscoord nsTableFrame::GetColSpacing(int32_t aStartColIndex,
3381 int32_t aEndColIndex) {
3382 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"
, 3383); MOZ_PretendNoReturn(); } } while (0)
3383 "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"
, 3383); MOZ_PretendNoReturn(); } } while (0)
;
3384 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"
, 3385); MOZ_PretendNoReturn(); } } while (0)
3385 "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"
, 3385); MOZ_PretendNoReturn(); } } while (0)
;
3386 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"
, 3387); MOZ_PretendNoReturn(); } } while (0)
3387 "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"
, 3387); MOZ_PretendNoReturn(); } } while (0)
;
3388 // Only one possible value so just multiply it out. Tables where index
3389 // matters will override this function
3390 return GetColSpacing() * (aEndColIndex - aStartColIndex);
3391}
3392
3393nscoord nsTableFrame::GetRowSpacing() {
3394 if (IsBorderCollapse()) return 0;
3395
3396 return StyleTableBorder()->mBorderSpacingRow;
3397}
3398
3399// XXX: could cache this. But be sure to check style changes if you do!
3400nscoord nsTableFrame::GetRowSpacing(int32_t aRowIndex) {
3401 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"
, 3402); MOZ_PretendNoReturn(); } } while (0)
3402 "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"
, 3402); MOZ_PretendNoReturn(); } } while (0)
;
3403 // Index is irrelevant for ordinary tables. We check that it falls within
3404 // appropriate bounds to increase confidence of correctness in situations
3405 // where it does matter.
3406 return GetRowSpacing();
3407}
3408
3409nscoord nsTableFrame::GetRowSpacing(int32_t aStartRowIndex,
3410 int32_t aEndRowIndex) {
3411 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"
, 3412); MOZ_PretendNoReturn(); } } while (0)
3412 "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"
, 3412); MOZ_PretendNoReturn(); } } while (0)
;
3413 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"
, 3414); MOZ_PretendNoReturn(); } } while (0)
3414 "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"
, 3414); MOZ_PretendNoReturn(); } } while (0)
;
3415 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"
, 3416); MOZ_PretendNoReturn(); } } while (0)
3416 "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"
, 3416); MOZ_PretendNoReturn(); } } while (0)
;
3417 // Only one possible value so just multiply it out. Tables where index
3418 // matters will override this function
3419 return GetRowSpacing() * (aEndRowIndex - aStartRowIndex);
3420}
3421
3422nscoord nsTableFrame::SynthesizeFallbackBaseline(
3423 mozilla::WritingMode aWM, BaselineSharingGroup aBaselineGroup) const {
3424 if (aBaselineGroup == BaselineSharingGroup::Last) {
3425 return 0;
3426 }
3427 return BSize(aWM);
3428}
3429
3430/* virtual */
3431Maybe<nscoord> nsTableFrame::GetNaturalBaselineBOffset(
3432 WritingMode aWM, BaselineSharingGroup aBaselineGroup,
3433 BaselineExportContext) const {
3434 if (StyleDisplay()->IsContainLayout()) {
3435 return Nothing{};
3436 }
3437
3438 RowGroupArray orderedRowGroups = OrderedRowGroups();
3439 // XXX not sure if this should be the size of the containing block instead.
3440 nsSize containerSize = mRect.Size();
3441 auto TableBaseline = [aWM, containerSize](
3442 nsTableRowGroupFrame* aRowGroup,
3443 nsTableRowFrame* aRow) -> Maybe<nscoord> {
3444 const nscoord rgBStart =
3445 aRowGroup->GetLogicalNormalRect(aWM, containerSize).BStart(aWM);
3446 const nscoord rowBStart =
3447 aRow->GetLogicalNormalRect(aWM, aRowGroup->GetSize()).BStart(aWM);
3448 return aRow->GetRowBaseline(aWM).map(
3449 [rgBStart, rowBStart](nscoord aBaseline) {
3450 return rgBStart + rowBStart + aBaseline;
3451 });
3452 };
3453 if (aBaselineGroup == BaselineSharingGroup::First) {
3454 for (uint32_t rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) {
3455 nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgIndex];
3456 nsTableRowFrame* row = rgFrame->GetFirstRow();
3457 if (row) {
3458 return TableBaseline(rgFrame, row);
3459 }
3460 }
3461 } else {
3462 for (uint32_t rgIndex = orderedRowGroups.Length(); rgIndex-- > 0;) {
3463 nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgIndex];
3464 nsTableRowFrame* row = rgFrame->GetLastRow();
3465 if (row) {
3466 return TableBaseline(rgFrame, row).map([this, aWM](nscoord aBaseline) {
3467 return BSize(aWM) - aBaseline;
3468 });
3469 }
3470 }
3471 }
3472 return Nothing{};
3473}
3474
3475/* ----- global methods ----- */
3476
3477nsTableFrame* NS_NewTableFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
3478 return new (aPresShell) nsTableFrame(aStyle, aPresShell->GetPresContext());
3479}
3480
3481NS_IMPL_FRAMEARENA_HELPERS(nsTableFrame)void* nsTableFrame ::operator new(size_t sz, mozilla::PresShell
* aShell) { return aShell->AllocateFrame(nsQueryFrame::nsTableFrame_id
, sz); }
3482
3483nsTableFrame* nsTableFrame::GetTableFrame(nsIFrame* aFrame) {
3484 for (nsIFrame* ancestor = aFrame->GetParent(); ancestor;
3485 ancestor = ancestor->GetParent()) {
3486 if (ancestor->IsTableFrame()) {
3487 return static_cast<nsTableFrame*>(ancestor);
3488 }
3489 }
3490 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"
, 3490); AnnotateMozCrashReason("MOZ_CRASH(" "unable to find table parent"
")"); do { *((volatile int*)__null) = 3490; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
3491 return nullptr;
3492}
3493
3494bool nsTableFrame::IsAutoBSize(WritingMode aWM) {
3495 const auto& bsize = StylePosition()->BSize(aWM);
3496 if (bsize.IsAuto()) {
3497 return true;
3498 }
3499 return bsize.ConvertsToPercentage() && bsize.ToPercentage() <= 0.0f;
3500}
3501
3502nscoord nsTableFrame::CalcBorderBoxBSize(const ReflowInput& aReflowInput,
3503 const LogicalMargin& aBorderPadding,
3504 nscoord aIntrinsicBorderBoxBSize) {
3505 WritingMode wm = aReflowInput.GetWritingMode();
3506 nscoord bSize = aReflowInput.ComputedBSize();
3507 nscoord bp = aBorderPadding.BStartEnd(wm);
3508 if (bSize == NS_UNCONSTRAINEDSIZE) {
3509 if (aIntrinsicBorderBoxBSize == NS_UNCONSTRAINEDSIZE) {
3510 return NS_UNCONSTRAINEDSIZE;
3511 }
3512 bSize = std::max(0, aIntrinsicBorderBoxBSize - bp);
3513 }
3514 return aReflowInput.ApplyMinMaxBSize(bSize) + bp;
3515}
3516
3517bool nsTableFrame::IsAutoLayout() {
3518 if (StyleTable()->mLayoutStrategy == StyleTableLayout::Auto) return true;
3519 // a fixed-layout inline-table must have a inline size
3520 // and tables with inline size set to 'max-content' must be
3521 // auto-layout (at least as long as
3522 // FixedTableLayoutStrategy::GetPrefISize returns nscoord_MAX)
3523 const auto& iSize = StylePosition()->ISize(GetWritingMode());
3524 return iSize.IsAuto() || iSize.IsMaxContent();
3525}
3526
3527#ifdef DEBUG_FRAME_DUMP1
3528nsresult nsTableFrame::GetFrameName(nsAString& aResult) const {
3529 return MakeFrameName(u"Table"_ns, aResult);
3530}
3531#endif
3532
3533// Find the closet sibling before aPriorChildFrame (including aPriorChildFrame)
3534// that is of type aChildType
3535nsIFrame* nsTableFrame::GetFrameAtOrBefore(nsIFrame* aParentFrame,
3536 nsIFrame* aPriorChildFrame,
3537 LayoutFrameType aChildType) {
3538 nsIFrame* result = nullptr;
3539 if (!aPriorChildFrame) {
3540 return result;
3541 }
3542 if (aChildType == aPriorChildFrame->Type()) {
3543 return aPriorChildFrame;
3544 }
3545
3546 // aPriorChildFrame is not of type aChildType, so we need start from
3547 // the beginnng and find the closest one
3548 nsIFrame* lastMatchingFrame = nullptr;
3549 nsIFrame* childFrame = aParentFrame->PrincipalChildList().FirstChild();
3550 while (childFrame && (childFrame != aPriorChildFrame)) {
3551 if (aChildType == childFrame->Type()) {
3552 lastMatchingFrame = childFrame;
3553 }
3554 childFrame = childFrame->GetNextSibling();
3555 }
3556 return lastMatchingFrame;
3557}
3558
3559#ifdef DEBUG1
3560void nsTableFrame::DumpRowGroup(nsIFrame* aKidFrame) {
3561 if (!aKidFrame) return;
3562
3563 for (nsIFrame* cFrame : aKidFrame->PrincipalChildList()) {
3564 nsTableRowFrame* rowFrame = do_QueryFrame(cFrame);
3565 if (rowFrame) {
3566 printf("row(%d)=%p ", rowFrame->GetRowIndex(),
3567 static_cast<void*>(rowFrame));
3568 for (nsIFrame* childFrame : cFrame->PrincipalChildList()) {
3569 nsTableCellFrame* cellFrame = do_QueryFrame(childFrame);
3570 if (cellFrame) {
3571 uint32_t colIndex = cellFrame->ColIndex();
3572 printf("cell(%u)=%p ", colIndex, static_cast<void*>(childFrame));
3573 }
3574 }
3575 printf("\n");
3576 } else {
3577 DumpRowGroup(rowFrame);
3578 }
3579 }
3580}
3581
3582void nsTableFrame::Dump(bool aDumpRows, bool aDumpCols, bool aDumpCellMap) {
3583 printf("***START TABLE DUMP*** \n");
3584 // dump the columns widths array
3585 printf("mColWidths=");
3586 int32_t numCols = GetColCount();
3587 int32_t colIdx;
3588 nsTableFrame* fif = static_cast<nsTableFrame*>(FirstInFlow());
3589 for (colIdx = 0; colIdx < numCols; colIdx++) {
3590 printf("%d ", fif->GetColumnISizeFromFirstInFlow(colIdx));
3591 }
3592 printf("\n");
3593
3594 if (aDumpRows) {
3595 nsIFrame* kidFrame = mFrames.FirstChild();
3596 while (kidFrame) {
3597 DumpRowGroup(kidFrame);
3598 kidFrame = kidFrame->GetNextSibling();
3599 }
3600 }
3601
3602 if (aDumpCols) {
3603 // output col frame cache
3604 printf("\n col frame cache ->");
3605 for (colIdx = 0; colIdx < numCols; colIdx++) {
3606 nsTableColFrame* colFrame = mColFrames.ElementAt(colIdx);
3607 if (0 == (colIdx % 8)) {
3608 printf("\n");
3609 }
3610 printf("%d=%p ", colIdx, static_cast<void*>(colFrame));
3611 nsTableColType colType = colFrame->GetColType();
3612 switch (colType) {
3613 case eColContent:
3614 printf(" content ");
3615 break;
3616 case eColAnonymousCol:
3617 printf(" anonymous-column ");
3618 break;
3619 case eColAnonymousColGroup:
3620 printf(" anonymous-colgroup ");
3621 break;
3622 case eColAnonymousCell:
3623 printf(" anonymous-cell ");
3624 break;
3625 }
3626 }
3627 printf("\n colgroups->");
3628 for (nsIFrame* childFrame : mColGroups) {
3629 if (LayoutFrameType::TableColGroup == childFrame->Type()) {
3630 nsTableColGroupFrame* colGroupFrame = (nsTableColGroupFrame*)childFrame;
3631 colGroupFrame->Dump(1);
3632 }
3633 }
3634 for (colIdx = 0; colIdx < numCols; colIdx++) {
3635 printf("\n");
3636 nsTableColFrame* colFrame = GetColFrame(colIdx);
3637 colFrame->Dump(1);
3638 }
3639 }
3640 if (aDumpCellMap) {
3641 nsTableCellMap* cellMap = GetCellMap();
3642 cellMap->Dump();
3643 }
3644 printf(" ***END TABLE DUMP*** \n");
3645}
3646#endif
3647
3648bool nsTableFrame::ColumnHasCellSpacingBefore(int32_t aColIndex) const {
3649 if (aColIndex == 0) {
3650 return true;
3651 }
3652 // Since fixed-layout tables should not have their column sizes change
3653 // as they load, we assume that all columns are significant.
3654 auto* fif = static_cast<nsTableFrame*>(FirstInFlow());
3655 if (fif->LayoutStrategy()->GetType() == nsITableLayoutStrategy::Fixed) {
3656 return true;
3657 }
3658 nsTableCellMap* cellMap = fif->GetCellMap();
3659 if (!cellMap) {
3660 return false;
3661 }
3662 if (cellMap->GetNumCellsOriginatingInCol(aColIndex) > 0) {
3663 return true;
3664 }
3665 // Check if we have a <col> element with a non-zero definite inline size.
3666 // Note: percentages and calc(%) are intentionally not considered.
3667 if (const auto* col = fif->GetColFrame(aColIndex)) {
3668 const auto& iSize = col->StylePosition()->ISize(GetWritingMode());
3669 if (iSize.ConvertsToLength() && iSize.ToLength() > 0) {
3670 const auto& maxISize = col->StylePosition()->MaxISize(GetWritingMode());
3671 if (!maxISize.ConvertsToLength() || maxISize.ToLength() > 0) {
3672 return true;
3673 }
3674 }
3675 const auto& minISize = col->StylePosition()->MinISize(GetWritingMode());
3676 if (minISize.ConvertsToLength() && minISize.ToLength() > 0) {
3677 return true;
3678 }
3679 }
3680 return false;
3681}
3682
3683/********************************************************************************
3684 * Collapsing Borders
3685 *
3686 * The CSS spec says to resolve border conflicts in this order:
3687 * 1) any border with the style HIDDEN wins
3688 * 2) the widest border with a style that is not NONE wins
3689 * 3) the border styles are ranked in this order, highest to lowest precedence:
3690 * double, solid, dashed, dotted, ridge, outset, groove, inset
3691 * 4) borders that are of equal width and style (differ only in color) have
3692 * this precedence: cell, row, rowgroup, col, colgroup, table
3693 * 5) if all border styles are NONE, then that's the computed border style.
3694 *******************************************************************************/
3695
3696#ifdef DEBUG1
3697# 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"
, 3697); 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"
, 3697); 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"
, 3697); 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"
, 3697); MOZ_PretendNoReturn(); } } while (0);
\
3698 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"
, 3698); MOZ_PretendNoReturn(); } } while (0)
; \
3699 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"
, 3699); MOZ_PretendNoReturn(); } } while (0)
; \
3700 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"
, 3700); MOZ_PretendNoReturn(); } } while (0)
; \
3701 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"
, 3701); MOZ_PretendNoReturn(); } } while (0)
;
3702# 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"
, 3702); 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"
, 3702); 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"
, 3702); 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"
, 3702); 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"
, 3702); 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"
, 3702); MOZ_PretendNoReturn(); } } while (0);
\
3703 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"
, 3703); 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"
, 3703); 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"
, 3703); 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"
, 3703); MOZ_PretendNoReturn(); } } while (0);
; \
3704 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"
, 3705); MOZ_PretendNoReturn(); } } while (0)
3705 "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"
, 3705); MOZ_PretendNoReturn(); } } while (0)
; \
3706 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"
, 3707); MOZ_PretendNoReturn(); } } while (0)
3707 "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"
, 3707); MOZ_PretendNoReturn(); } } while (0)
;
3708#endif
3709
3710void nsTableFrame::AddBCDamageArea(const TableArea& aValue) {
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#ifdef DEBUG1
3714 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"
, 3714); 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"
, 3714); 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"
, 3714); 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"
, 3714); 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"
, 3714); 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"
, 3714); MOZ_PretendNoReturn(); } } while (0);
;
3715#endif
3716
3717 SetNeedToCalcBCBorders(true);
3718 SetNeedToCalcHasBCBorders(true);
3719 // Get the property
3720 TableBCData* value = GetOrCreateTableBCData();
3721
3722#ifdef DEBUG1
3723 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"
, 3723); 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"
, 3723); 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"
, 3723); 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"
, 3723); MOZ_PretendNoReturn(); } } while (0);
;
3724#endif
3725 // Clamp the old damage area to the current table area in case it shrunk.
3726 int32_t cols = GetColCount();
3727 if (value->mDamageArea.EndCol() > cols) {
3728 if (value->mDamageArea.StartCol() > cols) {
3729 value->mDamageArea.StartCol() = cols;
3730 value->mDamageArea.ColCount() = 0;
3731 } else {
3732 value->mDamageArea.ColCount() = cols - value->mDamageArea.StartCol();
3733 }
3734 }
3735 int32_t rows = GetRowCount();
3736 if (value->mDamageArea.EndRow() > rows) {
3737 if (value->mDamageArea.StartRow() > rows) {
3738 value->mDamageArea.StartRow() = rows;
3739 value->mDamageArea.RowCount() = 0;
3740 } else {
3741 value->mDamageArea.RowCount() = rows - value->mDamageArea.StartRow();
3742 }
3743 }
3744
3745 // Construct a union of the new and old damage areas.
3746 value->mDamageArea.UnionArea(value->mDamageArea, aValue);
3747}
3748
3749void nsTableFrame::SetFullBCDamageArea() {
3750 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"
, 3751); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsBorderCollapse()"
") (" "Why call this if we are not border-collapsed?" ")"); do
{ *((volatile int*)__null) = 3751; __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
3751 "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"
, 3751); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsBorderCollapse()"
") (" "Why call this if we are not border-collapsed?" ")"); do
{ *((volatile int*)__null) = 3751; __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
3752
3753 SetNeedToCalcBCBorders(true);
3754 SetNeedToCalcHasBCBorders(true);
3755
3756 TableBCData* value = GetOrCreateTableBCData();
3757 value->mDamageArea = TableArea(0, 0, GetColCount(), GetRowCount());
3758}
3759
3760/* BCCellBorder represents a border segment which can be either an inline-dir
3761 * or a block-dir segment. For each segment we need to know the color, width,
3762 * style, who owns it and how long it is in cellmap coordinates.
3763 * Ownership of these segments is important to calculate which corners should
3764 * be bevelled. This structure has dual use, its used first to compute the
3765 * dominant border for inline-dir and block-dir segments and to store the
3766 * preliminary computed border results in the BCCellBorders structure.
3767 * This temporary storage is not symmetric with respect to inline-dir and
3768 * block-dir border segments, its always column oriented. For each column in
3769 * the cellmap there is a temporary stored block-dir and inline-dir segment.
3770 * XXX_Bernd this asymmetry is the root of those rowspan bc border errors
3771 */
3772struct BCCellBorder {
3773 BCCellBorder() { Reset(0, 1); }
3774 void Reset(uint32_t aRowIndex, uint32_t aRowSpan);
3775 nscolor color; // border segment color
3776 nscoord width; // border segment width
3777 StyleBorderStyle style; // border segment style, possible values are defined
3778 // in nsStyleConsts.h as StyleBorderStyle::*
3779 BCBorderOwner owner; // border segment owner, possible values are defined
3780 // in celldata.h. In the cellmap for each border
3781 // segment we store the owner and later when
3782 // painting we know the owner and can retrieve the
3783 // style info from the corresponding frame
3784 int32_t rowIndex; // rowIndex of temporary stored inline-dir border
3785 // segments relative to the table
3786 int32_t rowSpan; // row span of temporary stored inline-dir border
3787 // segments
3788};
3789
3790void BCCellBorder::Reset(uint32_t aRowIndex, uint32_t aRowSpan) {
3791 style = StyleBorderStyle::None;
3792 color = 0;
3793 width = 0;
3794 owner = eTableOwner;
3795 rowIndex = aRowIndex;
3796 rowSpan = aRowSpan;
3797}
3798
3799class BCMapCellIterator;
3800
3801/*****************************************************************
3802 * BCMapCellInfo
3803 * This structure stores information during the computation of winning borders
3804 * in CalcBCBorders, so that they don't need to be looked up repeatedly.
3805 ****************************************************************/
3806struct BCMapCellInfo final {
3807 explicit BCMapCellInfo(nsTableFrame* aTableFrame);
3808 void ResetCellInfo();
3809 void SetInfo(nsTableRowFrame* aNewRow, int32_t aColIndex,
3810 BCCellData* aCellData, BCMapCellIterator* aIter,
3811 nsCellMap* aCellMap = nullptr);
3812
3813 // Functions to (re)set the border widths on the table related cell frames,
3814 // where the knowledge about the current position in the table is used.
3815 // For most "normal" cells that have row/colspan of 1, these functions
3816 // are called once at most during the reflow, setting the value as given
3817 // (Discarding the value from the previous reflow, which is now irrelevant).
3818 // However, for cells spanning multiple rows/coluns, the maximum border
3819 // width seen is stored. This is controlled by calling the reset functions
3820 // before the cell's border is computed the first time.
3821 void ResetIStartBorderWidths();
3822 void ResetIEndBorderWidths();
3823 void ResetBStartBorderWidths();
3824 void ResetBEndBorderWidths();
3825
3826 void SetIStartBorderWidths(nscoord aWidth);
3827 void SetIEndBorderWidths(nscoord aWidth);
3828 void SetBStartBorderWidths(nscoord aWidth);
3829 void SetBEndBorderWidths(nscoord aWidth);
3830
3831 // functions to compute the borders; they depend on the
3832 // knowledge about the current position in the table. The edge functions
3833 // should be called if a table edge is involved, otherwise the internal
3834 // functions should be called.
3835 BCCellBorder GetBStartEdgeBorder();
3836 BCCellBorder GetBEndEdgeBorder();
3837 BCCellBorder GetIStartEdgeBorder();
3838 BCCellBorder GetIEndEdgeBorder();
3839 BCCellBorder GetIEndInternalBorder();
3840 BCCellBorder GetIStartInternalBorder();
3841 BCCellBorder GetBStartInternalBorder();
3842 BCCellBorder GetBEndInternalBorder();
3843
3844 // functions to set the internal position information
3845 void SetColumn(int32_t aColX);
3846 // Increment the row as we loop over the rows of a rowspan
3847 void IncrementRow(bool aResetToBStartRowOfCell = false);
3848
3849 // Helper functions to get extent of the cell
3850 int32_t GetCellEndRowIndex() const;
3851 int32_t GetCellEndColIndex() const;
3852
3853 // Storage of table information required to compute individual cell
3854 // information.
3855 nsTableFrame* mTableFrame;
3856 nsTableFrame* mTableFirstInFlow;
3857 int32_t mNumTableRows;
3858 int32_t mNumTableCols;
3859 WritingMode mTableWM;
3860
3861 // a cell can only belong to one rowgroup
3862 nsTableRowGroupFrame* mRowGroup;
3863
3864 // a cell with a rowspan has a bstart and a bend row, and rows in between
3865 nsTableRowFrame* mStartRow;
3866 nsTableRowFrame* mEndRow;
3867 nsTableRowFrame* mCurrentRowFrame;
3868
3869 // a cell with a colspan has an istart and iend column and columns in between
3870 // they can belong to different colgroups
3871 nsTableColGroupFrame* mColGroup;
3872 nsTableColGroupFrame* mCurrentColGroupFrame;
3873
3874 nsTableColFrame* mStartCol;
3875 nsTableColFrame* mEndCol;
3876 nsTableColFrame* mCurrentColFrame;
3877
3878 // cell information
3879 BCCellData* mCellData;
3880 nsBCTableCellFrame* mCell;
3881
3882 int32_t mRowIndex;
3883 int32_t mRowSpan;
3884 int32_t mColIndex;
3885 int32_t mColSpan;
3886
3887 // flags to describe the position of the cell with respect to the row- and
3888 // colgroups, for instance mRgAtStart documents that the bStart cell border
3889 // hits a rowgroup border
3890 bool mRgAtStart;
3891 bool mRgAtEnd;
3892 bool mCgAtStart;
3893 bool mCgAtEnd;
3894};
3895
3896BCMapCellInfo::BCMapCellInfo(nsTableFrame* aTableFrame)
3897 : mTableFrame(aTableFrame),
3898 mTableFirstInFlow(static_cast<nsTableFrame*>(aTableFrame->FirstInFlow())),
3899 mNumTableRows(aTableFrame->GetRowCount()),
3900 mNumTableCols(aTableFrame->GetColCount()),
3901 mTableWM(aTableFrame->Style()),
3902 mCurrentRowFrame(nullptr),
3903 mCurrentColGroupFrame(nullptr),
3904 mCurrentColFrame(nullptr) {
3905 ResetCellInfo();
3906}
3907
3908void BCMapCellInfo::ResetCellInfo() {
3909 mCellData = nullptr;
3910 mRowGroup = nullptr;
3911 mStartRow = nullptr;
3912 mEndRow = nullptr;
3913 mColGroup = nullptr;
3914 mStartCol = nullptr;
3915 mEndCol = nullptr;
3916 mCell = nullptr;
3917 mRowIndex = mRowSpan = mColIndex = mColSpan = 0;
3918 mRgAtStart = mRgAtEnd = mCgAtStart = mCgAtEnd = false;
3919}
3920
3921inline int32_t BCMapCellInfo::GetCellEndRowIndex() const {
3922 return mRowIndex + mRowSpan - 1;
3923}
3924
3925inline int32_t BCMapCellInfo::GetCellEndColIndex() const {
3926 return mColIndex + mColSpan - 1;
3927}
3928
3929static TableBCData* GetTableBCData(nsTableFrame* aTableFrame) {
3930 auto* firstInFlow = static_cast<nsTableFrame*>(aTableFrame->FirstInFlow());
3931 return firstInFlow->GetTableBCData();
3932}
3933
3934/*****************************************************************
3935 * BCMapTableInfo
3936 * This structure stores controls border information global to the
3937 * table computed during the border-collapsed border calcuation.
3938 ****************************************************************/
3939struct BCMapTableInfo final {
3940 explicit BCMapTableInfo(nsTableFrame* aTableFrame)
3941 : mTableBCData{GetTableBCData(aTableFrame)} {}
3942
3943 void ResetTableIStartBorderWidth() { mTableBCData->mIStartBorderWidth = 0; }
3944
3945 void ResetTableIEndBorderWidth() { mTableBCData->mIEndBorderWidth = 0; }
3946
3947 void ResetTableBStartBorderWidth() { mTableBCData->mBStartBorderWidth = 0; }
3948
3949 void ResetTableBEndBorderWidth() { mTableBCData->mBEndBorderWidth = 0; }
3950
3951 void SetTableIStartBorderWidth(int32_t aRowB, nscoord aWidth);
3952 void SetTableIEndBorderWidth(int32_t aRowB, nscoord aWidth);
3953 void SetTableBStartBorderWidth(nscoord aWidth);
3954 void SetTableBEndBorderWidth(nscoord aWidth);
3955
3956 TableBCData* mTableBCData;
3957};
3958
3959class BCMapCellIterator {
3960 public:
3961 BCMapCellIterator(nsTableFrame* aTableFrame, const TableArea& aDamageArea);
3962
3963 void First(BCMapCellInfo& aMapInfo);
3964
3965 void Next(BCMapCellInfo& aMapInfo);
3966
3967 void PeekIEnd(const BCMapCellInfo& aRefInfo, int32_t aRowIndex,
3968 BCMapCellInfo& aAjaInfo);
3969
3970 void PeekBEnd(const BCMapCellInfo& aRefInfo, int32_t aColIndex,
3971 BCMapCellInfo& aAjaInfo);
3972
3973 void PeekIStart(const BCMapCellInfo& aRefInfo, int32_t aRowIndex,
3974 BCMapCellInfo& aAjaInfo);
3975
3976 bool IsNewRow() { return mIsNewRow; }
3977
3978 nsTableRowFrame* GetPrevRow() const { return mPrevRow; }
3979 nsTableRowFrame* GetCurrentRow() const { return mRow; }
3980 nsTableRowGroupFrame* GetCurrentRowGroup() const { return mRowGroup; }
3981
3982 int32_t mRowGroupStart;
3983 int32_t mRowGroupEnd;
3984 bool mAtEnd;
3985 nsCellMap* mCellMap;
3986
3987 private:
3988 bool SetNewRow(nsTableRowFrame* row = nullptr);
3989 bool SetNewRowGroup(bool aFindFirstDamagedRow);
3990 void PeekIAt(const BCMapCellInfo& aRefInfo, int32_t aRowIndex,
3991 int32_t aColIndex, BCMapCellInfo& aAjaInfo);
3992
3993 nsTableFrame* mTableFrame;
3994 nsTableCellMap* mTableCellMap;
3995 nsTableFrame::RowGroupArray mRowGroups;
3996 nsTableRowGroupFrame* mRowGroup;
3997 int32_t mRowGroupIndex;
3998 uint32_t mNumTableRows;
3999 nsTableRowFrame* mRow;
4000 nsTableRowFrame* mPrevRow;
4001 bool mIsNewRow;
4002 int32_t mRowIndex;
4003 uint32_t mNumTableCols;
4004 int32_t mColIndex;
4005 // We don't necessarily want to traverse all areas
4006 // of the table - mArea(Start|End) specify the area to traverse.
4007 // TODO(dshin): Should be not abuse `nsPoint` for this - See bug 1879847.
4008 nsPoint mAreaStart;
4009 nsPoint mAreaEnd;
4010};
4011
4012BCMapCellIterator::BCMapCellIterator(nsTableFrame* aTableFrame,
4013 const TableArea& aDamageArea)
4014 : mRowGroupStart(0),
4015 mRowGroupEnd(0),
4016 mCellMap(nullptr),
4017 mTableFrame(aTableFrame),
4018 mRowGroups(aTableFrame->OrderedRowGroups()),
4019 mRowGroup(nullptr),
4020 mPrevRow(nullptr),
4021 mIsNewRow(false) {
4022 mTableCellMap = aTableFrame->GetCellMap();
4023
4024 mAreaStart.x = aDamageArea.StartCol();
4025 mAreaStart.y = aDamageArea.StartRow();
4026 mAreaEnd.x = aDamageArea.EndCol() - 1;
4027 mAreaEnd.y = aDamageArea.EndRow() - 1;
4028
4029 mNumTableRows = mTableFrame->GetRowCount();
4030 mRow = nullptr;
4031 mRowIndex = 0;
4032 mNumTableCols = mTableFrame->GetColCount();
4033 mColIndex = 0;
4034 mRowGroupIndex = -1;
4035
4036 mAtEnd = true; // gets reset when First() is called
4037}
4038
4039// fill fields that we need for border collapse computation on a given cell
4040void BCMapCellInfo::SetInfo(nsTableRowFrame* aNewRow, int32_t aColIndex,
4041 BCCellData* aCellData, BCMapCellIterator* aIter,
4042 nsCellMap* aCellMap) {
4043 // fill the cell information
4044 mCellData = aCellData;
4045 mColIndex = aColIndex;
4046
4047 // initialize the row information if it was not previously set for cells in
4048 // this row
4049 mRowIndex = 0;
4050 if (aNewRow) {
4051 mStartRow = aNewRow;
4052 mRowIndex = aNewRow->GetRowIndex();
4053 }
4054
4055 // fill cell frame info and row information
4056 mCell = nullptr;
4057 mRowSpan = 1;
4058 mColSpan = 1;
4059 if (aCellData) {
4060 mCell = static_cast<nsBCTableCellFrame*>(aCellData->GetCellFrame());
4061 if (mCell) {
4062 if (!mStartRow) {
4063 mStartRow = mCell->GetTableRowFrame();
4064 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"
, 4064); MOZ_PretendNoReturn(); } } while (0); return; }
;
4065 mRowIndex = mStartRow->GetRowIndex();
4066 }
4067 mColSpan = mTableFrame->GetEffectiveColSpan(*mCell, aCellMap);
4068 mRowSpan = mTableFrame->GetEffectiveRowSpan(*mCell, aCellMap);
4069 }
4070 }
4071
4072 if (!mStartRow) {
4073 mStartRow = aIter->GetCurrentRow();
4074 }
4075 if (1 == mRowSpan) {
4076 mEndRow = mStartRow;
4077 } else {
4078 mEndRow = mStartRow->GetNextRow();
4079 if (mEndRow) {
4080 for (int32_t span = 2; mEndRow && span < mRowSpan; span++) {
4081 mEndRow = mEndRow->GetNextRow();
4082 }
4083 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"
, 4083); MOZ_PretendNoReturn(); } } while (0)
;
4084 } else {
4085 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"
, 4085); MOZ_PretendNoReturn(); } while (0)
;
4086 mRowSpan = 1;
4087 mEndRow = mStartRow;
4088 }
4089 }
4090 // row group frame info
4091 // try to reuse the rgStart and rgEnd from the iterator as calls to
4092 // GetRowCount() are computationally expensive and should be avoided if
4093 // possible
4094 uint32_t rgStart = aIter->mRowGroupStart;
4095 uint32_t rgEnd = aIter->mRowGroupEnd;
4096 mRowGroup = mStartRow->GetTableRowGroupFrame();
4097 if (mRowGroup != aIter->GetCurrentRowGroup()) {
4098 rgStart = mRowGroup->GetStartRowIndex();
4099 rgEnd = rgStart + mRowGroup->GetRowCount() - 1;
4100 }
4101 uint32_t rowIndex = mStartRow->GetRowIndex();
4102 mRgAtStart = rgStart == rowIndex;
4103 mRgAtEnd = rgEnd == rowIndex + mRowSpan - 1;
4104
4105 // col frame info
4106 mStartCol = mTableFirstInFlow->GetColFrame(aColIndex);
4107 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"
, 4107); MOZ_PretendNoReturn(); } } while (0); return; }
;
4108
4109 mEndCol = mStartCol;
4110 if (mColSpan > 1) {
4111 nsTableColFrame* colFrame =
4112 mTableFirstInFlow->GetColFrame(aColIndex + mColSpan - 1);
4113 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"
, 4113); MOZ_PretendNoReturn(); } } while (0); return; }
;
4114 mEndCol = colFrame;
4115 }
4116
4117 // col group frame info
4118 mColGroup = mStartCol->GetTableColGroupFrame();
4119 int32_t cgStart = mColGroup->GetStartColumnIndex();
4120 int32_t cgEnd = std::max(0, cgStart + mColGroup->GetColCount() - 1);
4121 mCgAtStart = cgStart == aColIndex;
4122 mCgAtEnd = cgEnd == aColIndex + mColSpan - 1;
4123}
4124
4125bool BCMapCellIterator::SetNewRow(nsTableRowFrame* aRow) {
4126 mAtEnd = true;
4127 mPrevRow = mRow;
4128 if (aRow) {
4129 mRow = aRow;
4130 } else if (mRow) {
4131 mRow = mRow->GetNextRow();
4132 }
4133 if (mRow) {
4134 mRowIndex = mRow->GetRowIndex();
4135 // get to the first entry with an originating cell
4136 int32_t rgRowIndex = mRowIndex - mRowGroupStart;
4137 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"
, 4137); MOZ_PretendNoReturn(); } } while (0); return false; }
;
4138 const nsCellMap::CellDataArray& row = mCellMap->mRows[rgRowIndex];
4139
4140 for (mColIndex = mAreaStart.x; mColIndex <= mAreaEnd.x; mColIndex++) {
4141 CellData* cellData = row.SafeElementAt(mColIndex);
4142 if (!cellData) { // add a dead cell data
4143 TableArea damageArea;
4144 cellData = mCellMap->AppendCell(*mTableCellMap, nullptr, rgRowIndex,
4145 false, 0, damageArea);
4146 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"
, 4146); MOZ_PretendNoReturn(); } } while (0); return false; }
;
4147 }
4148 if (cellData && (cellData->IsOrig() || cellData->IsDead())) {
4149 break;
4150 }
4151 }
4152 mIsNewRow = true;
4153 mAtEnd = false;
4154 } else
4155 ABORT1(false){ do { if (!(false)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "CellIterator program error"
, "false", "/var/lib/jenkins/workspace/firefox-scan-build/layout/tables/nsTableFrame.cpp"
, 4155); MOZ_PretendNoReturn(); } } while (0); return false; }
;
4156
4157 return !mAtEnd;
4158}
4159
4160bool BCMapCellIterator::SetNewRowGroup(bool aFindFirstDamagedRow) {
4161 mAtEnd = true;
4162 int32_t numRowGroups = mRowGroups.Length();
4163 mCellMap = nullptr;
4164 for (mRowGroupIndex++; mRowGroupIndex < numRowGroups; mRowGroupIndex++) {
4165 mRowGroup = mRowGroups[mRowGroupIndex];
4166 int32_t rowCount = mRowGroup->GetRowCount();
4167 mRowGroupStart = mRowGroup->GetStartRowIndex();
4168 mRowGroupEnd = mRowGroupStart + rowCount - 1;
4169 if (rowCount > 0) {
4170 mCellMap = mTableCellMap->GetMapFor(mRowGroup, mCellMap);
4171 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"
, 4171); MOZ_PretendNoReturn(); } } while (0); return false; }
;
4172 nsTableRowFrame* firstRow = mRowGroup->GetFirstRow();
4173 if (aFindFirstDamagedRow) {
4174 if ((mAreaStart.y >= mRowGroupStart) &&
4175 (mAreaStart.y <= mRowGroupEnd)) {
4176 // the damage area starts in the row group
4177
4178 // find the correct first damaged row
4179 int32_t numRows = mAreaStart.y - mRowGroupStart;
4180 for (int32_t i = 0; i < numRows; i++) {
4181 firstRow = firstRow->GetNextRow();
4182 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"
, 4182); MOZ_PretendNoReturn(); } } while (0); return false; }
;
4183 }
4184
4185 } else {
4186 continue;
4187 }
4188 }
4189 if (SetNewRow(firstRow)) { // sets mAtEnd
4190 break;
4191 }
4192 }
4193 }
4194
4195 return !mAtEnd;
4196}
4197
4198void BCMapCellIterator::First(BCMapCellInfo& aMapInfo) {
4199 aMapInfo.ResetCellInfo();
4200
4201 SetNewRowGroup(true); // sets mAtEnd
4202 while (!mAtEnd) {
4203 if ((mAreaStart.y >= mRowGroupStart) && (mAreaStart.y <= mRowGroupEnd)) {
4204 BCCellData* cellData = static_cast<BCCellData*>(
4205 mCellMap->GetDataAt(mAreaStart.y - mRowGroupStart, mAreaStart.x));
4206 if (cellData && (cellData->IsOrig() || cellData->IsDead())) {
4207 aMapInfo.SetInfo(mRow, mAreaStart.x, cellData, this);
4208 return;
4209 } else {
4210 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"
, 4211); MOZ_PretendNoReturn(); } } while (0)
4211 "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"
, 4211); MOZ_PretendNoReturn(); } } while (0)
;
4212 }
4213 }
4214 SetNewRowGroup(true); // sets mAtEnd
4215 }
4216}
4217
4218void BCMapCellIterator::Next(BCMapCellInfo& aMapInfo) {
4219 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"
, 4219); MOZ_PretendNoReturn(); } } while (0); return; }
;
4220 aMapInfo.ResetCellInfo();
4221
4222 mIsNewRow = false;
4223 mColIndex++;
4224 while ((mRowIndex <= mAreaEnd.y) && !mAtEnd) {
4225 for (; mColIndex <= mAreaEnd.x; mColIndex++) {
4226 int32_t rgRowIndex = mRowIndex - mRowGroupStart;
4227 BCCellData* cellData =
4228 static_cast<BCCellData*>(mCellMap->GetDataAt(rgRowIndex, mColIndex));
4229 if (!cellData) { // add a dead cell data
4230 TableArea damageArea;
4231 cellData = static_cast<BCCellData*>(mCellMap->AppendCell(
4232 *mTableCellMap, nullptr, rgRowIndex, false, 0, damageArea));
4233 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"
, 4233); MOZ_PretendNoReturn(); } } while (0); return; }
;
4234 }
4235 if (cellData && (cellData->IsOrig() || cellData->IsDead())) {
4236 aMapInfo.SetInfo(mRow, mColIndex, cellData, this);
4237 return;
4238 }
4239 }
4240 if (mRowIndex >= mRowGroupEnd) {
4241 SetNewRowGroup(false); // could set mAtEnd
4242 } else {
4243 SetNewRow(); // could set mAtEnd
4244 }
4245 }
4246 mAtEnd = true;
4247}
4248
4249void BCMapCellIterator::PeekIEnd(const BCMapCellInfo& aRefInfo,
4250 int32_t aRowIndex, BCMapCellInfo& aAjaInfo) {
4251 PeekIAt(aRefInfo, aRowIndex, aRefInfo.mColIndex + aRefInfo.mColSpan,
4252 aAjaInfo);
4253}
4254
4255void BCMapCellIterator::PeekBEnd(const BCMapCellInfo& aRefInfo,
4256 int32_t aColIndex, BCMapCellInfo& aAjaInfo) {
4257 aAjaInfo.ResetCellInfo();
4258 int32_t rowIndex = aRefInfo.mRowIndex + aRefInfo.mRowSpan;
4259 int32_t rgRowIndex = rowIndex - mRowGroupStart;
4260 nsTableRowGroupFrame* rg = mRowGroup;
Value stored to 'rg' during its initialization is never read
4261 nsCellMap* cellMap = mCellMap;
4262 nsTableRowFrame* nextRow = nullptr;
4263 if (rowIndex > mRowGroupEnd) {
4264 int32_t nextRgIndex = mRowGroupIndex;
4265 do {
4266 nextRgIndex++;
4267 rg = mRowGroups.SafeElementAt(nextRgIndex);
4268 if (rg) {
4269 cellMap = mTableCellMap->GetMapFor(rg, cellMap);
4270 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"
, 4270); MOZ_PretendNoReturn(); } } while (0); return; }
;
4271 // First row of the next row group
4272 rgRowIndex = 0;
4273 nextRow = rg->GetFirstRow();
4274 }
4275 } while (rg && !nextRow);
4276 if (!rg) return;
4277 } else {
4278 // get the row within the same row group
4279 nextRow = mRow;
4280 for (int32_t i = 0; i < aRefInfo.mRowSpan; i++) {
4281 nextRow = nextRow->GetNextRow();
4282 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"
, 4282); MOZ_PretendNoReturn(); } } while (0); return; }
;
4283 }
4284 }
4285
4286 BCCellData* cellData =
4287 static_cast<BCCellData*>(cellMap->GetDataAt(rgRowIndex, aColIndex));
4288 if (!cellData) { // add a dead cell data
4289 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"
, 4289); MOZ_PretendNoReturn(); } } while (0)
;
4290 TableArea damageArea;
4291 cellData = static_cast<BCCellData*>(cellMap->AppendCell(
4292 *mTableCellMap, nullptr, rgRowIndex, false, 0, damageArea));
4293 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"
, 4293); MOZ_PretendNoReturn(); } } while (0); return; }
;
4294 }
4295 if (cellData->IsColSpan()) {
4296 aColIndex -= static_cast<int32_t>(cellData->GetColSpanOffset());
4297 cellData =
4298 static_cast<BCCellData*>(cellMap->GetDataAt(rgRowIndex, aColIndex));
4299 }
4300 aAjaInfo.SetInfo(nextRow, aColIndex, cellData, this, cellMap);
4301}
4302
4303void BCMapCellIterator::PeekIStart(const BCMapCellInfo& aRefInfo,
4304 int32_t aRowIndex, BCMapCellInfo& aAjaInfo) {
4305 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"
, 4305); MOZ_PretendNoReturn(); } } while (0)
;
4306 PeekIAt(aRefInfo, aRowIndex, aRefInfo.mColIndex - 1, aAjaInfo);
4307}
4308
4309void BCMapCellIterator::PeekIAt(const BCMapCellInfo& aRefInfo,
4310 int32_t aRowIndex, int32_t aColIndex,
4311 BCMapCellInfo& aAjaInfo) {
4312 aAjaInfo.ResetCellInfo();
4313 int32_t rgRowIndex = aRowIndex - mRowGroupStart;
4314
4315 auto* cellData =
4316 static_cast<BCCellData*>(mCellMap->GetDataAt(rgRowIndex, aColIndex));
4317 if (!cellData) { // add a dead cell data
4318 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"
, 4318); MOZ_PretendNoReturn(); } } while (0)
;
4319 TableArea damageArea;
4320 cellData = static_cast<BCCellData*>(mCellMap->AppendCell(
4321 *mTableCellMap, nullptr, rgRowIndex, false, 0, damageArea));
4322 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"
, 4322); MOZ_PretendNoReturn(); } } while (0); return; }
;
4323 }
4324 nsTableRowFrame* row = nullptr;
4325 if (cellData->IsRowSpan()) {
4326 rgRowIndex -= static_cast<int32_t>(cellData->GetRowSpanOffset());
4327 cellData =
4328 static_cast<BCCellData*>(mCellMap->GetDataAt(rgRowIndex, aColIndex));
4329 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"
, 4329); MOZ_PretendNoReturn(); } } while (0); return; }
;
4330 } else {
4331 row = mRow;
4332 }
4333 aAjaInfo.SetInfo(row, aColIndex, cellData, this);
4334}
4335
4336#define CELL_CORNERtrue true
4337
4338/** return the border style, border color and optionally the width for a given
4339 * frame and side
4340 * @param aFrame - query the info for this frame
4341 * @param aTableWM - the writing-mode of the frame
4342 * @param aSide - the side of the frame
4343 * @param aStyle - the border style
4344 * @param aColor - the border color
4345 * @param aWidth - the border width
4346 */
4347static void GetColorAndStyle(const nsIFrame* aFrame, WritingMode aTableWM,
4348 LogicalSide aSide, StyleBorderStyle* aStyle,
4349 nscolor* aColor, nscoord* aWidth = nullptr) {
4350 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"
, 4350); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aFrame" ") ("
"null frame" ")"); do { *((volatile int*)__null) = 4350; __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
;
4351 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"
, 4351); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aStyle && aColor"
") (" "null argument" ")"); do { *((volatile int*)__null) = 4351
; __attribute__((nomerge)) ::abort(); } while (false); } } while
(false)
;
4352
4353 // initialize out arg
4354 *aColor = 0;
4355 if (aWidth) {
4356 *aWidth = 0;
4357 }
4358
4359 const nsStyleBorder* styleData = aFrame->StyleBorder();
4360 mozilla::Side physicalSide = aTableWM.PhysicalSide(aSide);
4361 *aStyle = styleData->GetBorderStyle(physicalSide);
4362
4363 if ((StyleBorderStyle::None == *aStyle) ||
4364 (StyleBorderStyle::Hidden == *aStyle)) {
4365 return;
4366 }
4367 *aColor = aFrame->Style()->GetVisitedDependentColor(
4368 nsStyleBorder::BorderColorFieldFor(physicalSide));
4369
4370 if (aWidth) {
4371 *aWidth = styleData->GetComputedBorderWidth(physicalSide);
4372 }
4373}
4374
4375/** coerce the paint style as required by CSS2.1
4376 * @param aFrame - query the info for this frame
4377 * @param aTableWM - the writing mode of the frame
4378 * @param aSide - the side of the frame
4379 * @param aStyle - the border style
4380 * @param aColor - the border color
4381 */
4382static void GetPaintStyleInfo(const nsIFrame* aFrame, WritingMode aTableWM,
4383 LogicalSide aSide, StyleBorderStyle* aStyle,
4384 nscolor* aColor) {
4385 GetColorAndStyle(aFrame, aTableWM, aSide, aStyle, aColor);
4386 if (StyleBorderStyle::Inset == *aStyle) {
4387 *aStyle = StyleBorderStyle::Ridge;
4388 } else if (StyleBorderStyle::Outset == *aStyle) {
4389 *aStyle = StyleBorderStyle::Groove;
4390 }
4391}
4392
4393class nsDelayedCalcBCBorders : public Runnable {
4394 public:
4395 explicit nsDelayedCalcBCBorders(nsIFrame* aFrame)
4396 : mozilla::Runnable("nsDelayedCalcBCBorders"), mFrame(aFrame) {}
4397
4398 NS_IMETHODvirtual nsresult Run() override {
4399 if (mFrame) {
4400 nsTableFrame* tableFrame = static_cast<nsTableFrame*>(mFrame.GetFrame());
4401 if (tableFrame->NeedToCalcBCBorders()) {
4402 tableFrame->CalcBCBorders();
4403 }
4404 }
4405 return NS_OK;
4406 }
4407
4408 private:
4409 WeakFrame mFrame;
4410};
4411
4412bool nsTableFrame::BCRecalcNeeded(ComputedStyle* aOldComputedStyle,
4413 ComputedStyle* aNewComputedStyle) {
4414 // Attention: the old ComputedStyle is the one we're forgetting,
4415 // and hence possibly completely bogus for GetStyle* purposes.
4416 // We use PeekStyleData instead.
4417
4418 const nsStyleBorder* oldStyleData = aOldComputedStyle->StyleBorder();
4419 const nsStyleBorder* newStyleData = aNewComputedStyle->StyleBorder();
4420 nsChangeHint change = newStyleData->CalcDifference(*oldStyleData);
4421 if (!change) return false;
4422 if (change & nsChangeHint_NeedReflow)
4423 return true; // the caller only needs to mark the bc damage area
4424 if (change & nsChangeHint_RepaintFrame) {
4425 // we need to recompute the borders and the caller needs to mark
4426 // the bc damage area
4427 // XXX In principle this should only be necessary for border style changes
4428 // However the bc painting code tries to maximize the drawn border segments
4429 // so it stores in the cellmap where a new border segment starts and this
4430 // introduces a unwanted cellmap data dependence on color
4431 nsCOMPtr<nsIRunnable> evt = new nsDelayedCalcBCBorders(this);
4432 nsresult rv = GetContent()->OwnerDoc()->Dispatch(evt.forget());
4433 return NS_SUCCEEDED(rv)((bool)(__builtin_expect(!!(!NS_FAILED_impl(rv)), 1)));
4434 }
4435 return false;
4436}
4437
4438// Compare two border segments, this comparison depends whether the two
4439// segments meet at a corner and whether the second segment is inline-dir.
4440// The return value is whichever of aBorder1 or aBorder2 dominates.
4441static const BCCellBorder& CompareBorders(
4442 bool aIsCorner, // Pass true for corner calculations
4443 const BCCellBorder& aBorder1, const BCCellBorder& aBorder2,
4444 bool aSecondIsInlineDir, bool* aFirstDominates = nullptr) {
4445 bool firstDominates = true;
4446
4447 if (StyleBorderStyle::Hidden == aBorder1.style) {
4448 firstDominates = !aIsCorner;
4449 } else if (StyleBorderStyle::Hidden == aBorder2.style) {
4450 firstDominates = aIsCorner;
4451 } else if (aBorder1.width < aBorder2.width) {
4452 firstDominates = false;
4453 } else if (aBorder1.width == aBorder2.width) {
4454 if (static_cast<uint8_t>(aBorder1.style) <
4455 static_cast<uint8_t>(aBorder2.style)) {
4456 firstDominates = false;
4457 } else if (aBorder1.style == aBorder2.style) {
4458 if (aBorder1.owner == aBorder2.owner) {
4459 firstDominates = !aSecondIsInlineDir;
4460 } else if (aBorder1.owner < aBorder2.owner) {
4461 firstDominates = false;
4462 }
4463 }
4464 }
4465
4466 if (aFirstDominates) *aFirstDominates = firstDominates;
4467
4468 if (firstDominates) return aBorder1;
4469 return aBorder2;
4470}
4471
4472/** calc the dominant border by considering the table, row/col group, row/col,
4473 * cell.
4474 * Depending on whether the side is block-dir or inline-dir and whether
4475 * adjacent frames are taken into account the ownership of a single border
4476 * segment is defined. The return value is the dominating border
4477 * The cellmap stores only bstart and istart borders for each cellmap position.
4478 * If the cell border is owned by the cell that is istart-wards of the border
4479 * it will be an adjacent owner aka eAjaCellOwner. See celldata.h for the other
4480 * scenarios with a adjacent owner.
4481 * @param xxxFrame - the frame for style information, might be zero if
4482 * it should not be considered
4483 * @param aTableWM - the writing mode of the frame
4484 * @param aSide - side of the frames that should be considered
4485 * @param aAja - the border comparison takes place from the point of
4486 * a frame that is adjacent to the cellmap entry, for
4487 * when a cell owns its lower border it will be the
4488 * adjacent owner as in the cellmap only bstart and
4489 * istart borders are stored.
4490 */
4491static BCCellBorder CompareBorders(
4492 const nsIFrame* aTableFrame, const nsIFrame* aColGroupFrame,
4493 const nsIFrame* aColFrame, const nsIFrame* aRowGroupFrame,
4494 const nsIFrame* aRowFrame, const nsIFrame* aCellFrame, WritingMode aTableWM,
4495 LogicalSide aSide, bool aAja) {
4496 BCCellBorder border, tempBorder;
4497 bool inlineAxis = IsBlock(aSide);
4498
4499 // start with the table as dominant if present
4500 if (aTableFrame) {
4501 GetColorAndStyle(aTableFrame, aTableWM, aSide, &border.style, &border.color,
4502 &border.width);
4503 border.owner = eTableOwner;
4504 if (StyleBorderStyle::Hidden == border.style) {
4505 return border;
4506 }
4507 }
4508 // see if the colgroup is dominant
4509 if (aColGroupFrame) {
4510 GetColorAndStyle(aColGroupFrame, aTableWM, aSide, &tempBorder.style,
4511 &tempBorder.color, &tempBorder.width);
4512 tempBorder.owner = aAja && !inlineAxis ? eAjaColGroupOwner : eColGroupOwner;
4513 // pass here and below false for aSecondIsInlineDir as it is only used for
4514 // corner calculations.
4515 border = CompareBorders(!CELL_CORNERtrue, border, tempBorder, false);
4516 if (StyleBorderStyle::Hidden == border.style) {
4517 return border;
4518 }
4519 }
4520 // see if the col is dominant
4521 if (aColFrame) {
4522 GetColorAndStyle(aColFrame, aTableWM, aSide, &tempBorder.style,
4523 &tempBorder.color, &tempBorder.width);
4524 tempBorder.owner = aAja && !inlineAxis ? eAjaColOwner : eColOwner;
4525 border = CompareBorders(!CELL_CORNERtrue, border, tempBorder, false);
4526 if (StyleBorderStyle::Hidden == border.style) {
4527 return border;
4528 }
4529 }
4530 // see if the rowgroup is dominant
4531 if (aRowGroupFrame) {
4532 GetColorAndStyle(aRowGroupFrame, aTableWM, aSide, &tempBorder.style,
4533 &tempBorder.color, &tempBorder.width);
4534 tempBorder.owner = aAja && inlineAxis ? eAjaRowGroupOwner : eRowGroupOwner;
4535 border = CompareBorders(!CELL_CORNERtrue, border, tempBorder, false);
4536 if (StyleBorderStyle::Hidden == border.style) {
4537 return border;
4538 }
4539 }
4540 // see if the row is dominant
4541 if (aRowFrame) {
4542 GetColorAndStyle(aRowFrame, aTableWM, aSide, &tempBorder.style,
4543 &tempBorder.color, &tempBorder.width);
4544 tempBorder.owner = aAja && inlineAxis ? eAjaRowOwner : eRowOwner;
4545 border = CompareBorders(!CELL_CORNERtrue, border, tempBorder, false);
4546 if (StyleBorderStyle::Hidden == border.style) {
4547 return border;
4548 }
4549 }
4550 // see if the cell is dominant
4551 if (aCellFrame) {
4552 GetColorAndStyle(aCellFrame, aTableWM, aSide, &tempBorder.style,
4553 &tempBorder.color, &tempBorder.width);
4554 tempBorder.owner = aAja ? eAjaCellOwner : eCellOwner;
4555 border = CompareBorders(!CELL_CORNERtrue, border, tempBorder, false);
4556 }
4557 return border;
4558}
4559
4560static bool Perpendicular(mozilla::LogicalSide aSide1,
4561 mozilla::LogicalSide aSide2) {
4562 return IsInline(aSide1) != IsInline(aSide2);
4563}
4564
4565// Initial value indicating that BCCornerInfo's ownerStyle hasn't been set yet.
4566#define BORDER_STYLE_UNSETstatic_cast<StyleBorderStyle>(255) static_cast<StyleBorderStyle>(255)
4567
4568struct BCCornerInfo {
4569 BCCornerInfo() {
4570 ownerColor = 0;
4571 ownerWidth = subWidth = ownerElem = subSide = subElem = hasDashDot =
4572 numSegs = bevel = 0;
4573 ownerSide = static_cast<uint16_t>(LogicalSide::BStart);
4574 ownerStyle = BORDER_STYLE_UNSETstatic_cast<StyleBorderStyle>(255);
4575 subStyle = StyleBorderStyle::Solid;
4576 }
4577
4578 void Set(mozilla::LogicalSide aSide, BCCellBorder border);
4579
4580 void Update(mozilla::LogicalSide aSide, BCCellBorder border);
4581
4582 nscolor ownerColor; // color of borderOwner
4583 uint16_t ownerWidth; // width of borderOwner
4584 uint16_t subWidth; // width of the largest border intersecting the
4585 // border perpendicular to ownerSide
4586 StyleBorderStyle subStyle; // border style of subElem
4587 StyleBorderStyle ownerStyle; // border style of ownerElem
4588 uint16_t ownerSide : 2; // LogicalSide (e.g LogicalSide::BStart, etc) of the
4589 // border owning the corner relative to the corner
4590 uint16_t
4591 ownerElem : 4; // elem type (e.g. eTable, eGroup, etc) owning the corner
4592 uint16_t subSide : 2; // side of border with subWidth relative to the corner
4593 uint16_t subElem : 4; // elem type (e.g. eTable, eGroup, etc) of sub owner
4594 uint16_t hasDashDot : 1; // does a dashed, dotted segment enter the corner,
4595 // they cannot be beveled
4596 uint16_t numSegs : 3; // number of segments entering corner
4597 uint16_t bevel : 1; // is the corner beveled (uses the above two fields
4598 // together with subWidth)
4599 // 7 bits are unused
4600};
4601
4602// Start a new border at this corner, going in the direction of a given side.
4603void BCCornerInfo::Set(mozilla::LogicalSide aSide, BCCellBorder aBorder) {
4604 // FIXME bug 1508921: We mask 4-bit BCBorderOwner enum to 3 bits to preserve
4605 // buggy behavior found by the frame_above_rules_all.html mochitest.
4606 ownerElem = aBorder.owner & 0x7;
4607
4608 ownerStyle = aBorder.style;
4609 ownerWidth = aBorder.width;
4610 ownerColor = aBorder.color;
4611 ownerSide = static_cast<uint16_t>(aSide);
4612 hasDashDot = 0;
4613 numSegs = 0;
4614 if (aBorder.width > 0) {
4615 numSegs++;
4616 hasDashDot = (StyleBorderStyle::Dashed == aBorder.style) ||
4617 (StyleBorderStyle::Dotted == aBorder.style);
4618 }
4619 bevel = 0;
4620 subWidth = 0;
4621 // the following will get set later
4622 subSide = static_cast<uint16_t>(IsInline(aSide) ? LogicalSide::BStart
4623 : LogicalSide::IStart);
4624 subElem = eTableOwner;
4625 subStyle = StyleBorderStyle::Solid;
4626}
4627
4628// Add a new border going in the direction of a given side, and update the
4629// dominant border.
4630void BCCornerInfo::Update(mozilla::LogicalSide aSide, BCCellBorder aBorder) {
4631 if (ownerStyle == BORDER_STYLE_UNSETstatic_cast<StyleBorderStyle>(255)) {
4632 Set(aSide, aBorder);
4633 } else {
4634 bool isInline = IsInline(aSide); // relative to the corner
4635 BCCellBorder oldBorder, tempBorder;
4636 oldBorder.owner = (BCBorderOwner)ownerElem;
4637 oldBorder.style = ownerStyle;
4638 oldBorder.width = ownerWidth;
4639 oldBorder.color = ownerColor;
4640
4641 LogicalSide oldSide = LogicalSide(ownerSide);
4642
4643 bool existingWins = false;
4644 tempBorder = CompareBorders(CELL_CORNERtrue, oldBorder, aBorder, isInline,
4645 &existingWins);
4646
4647 ownerElem = tempBorder.owner;
4648 ownerStyle = tempBorder.style;
4649 ownerWidth = tempBorder.width;
4650 ownerColor = tempBorder.color;
4651 if (existingWins) { // existing corner is dominant
4652 if (::Perpendicular(LogicalSide(ownerSide), aSide)) {
4653 // see if the new sub info replaces the old
4654 BCCellBorder subBorder;
4655 subBorder.owner = (BCBorderOwner)subElem;
4656 subBorder.style = subStyle;
4657 subBorder.width = subWidth;
4658 subBorder.color = 0; // we are not interested in subBorder color
4659 bool firstWins;
4660
4661 tempBorder = CompareBorders(CELL_CORNERtrue, subBorder, aBorder, isInline,
4662 &firstWins);
4663
4664 subElem = tempBorder.owner;
4665 subStyle = tempBorder.style;
4666 subWidth = tempBorder.width;
4667 if (!firstWins) {
4668 subSide = static_cast<uint16_t>(aSide);
4669 }
4670 }
4671 } else { // input args are dominant
4672 ownerSide = static_cast<uint16_t>(aSide);
4673 if (::Perpendicular(oldSide, LogicalSide(ownerSide))) {
4674 subElem = oldBorder.owner;
4675 subStyle = oldBorder.style;
4676 subWidth = oldBorder.width;
4677 subSide = static_cast<uint16_t>(oldSide);
4678 }
4679 }
4680 if (aBorder.width > 0) {
4681 numSegs++;
4682 if (!hasDashDot && ((StyleBorderStyle::Dashed == aBorder.style) ||
4683 (StyleBorderStyle::Dotted == aBorder.style))) {
4684 hasDashDot = 1;
4685 }
4686 }
4687
4688 // bevel the corner if only two perpendicular non dashed/dotted segments
4689 // enter the corner
4690 bevel = (2 == numSegs) && (subWidth > 1) && (0 == hasDashDot);
4691 }
4692}
4693
4694struct BCCorners {
4695 BCCorners(int32_t aNumCorners, int32_t aStartIndex);
4696
4697 BCCornerInfo& operator[](int32_t i) const {
4698 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"
, 4698); MOZ_PretendNoReturn(); } } while (0)
;
4699 return corners[clamped(i, startIndex, endIndex) - startIndex];
4700 }
4701
4702 int32_t startIndex;
4703 int32_t endIndex;
4704 UniquePtr<BCCornerInfo[]> corners;
4705};
4706
4707BCCorners::BCCorners(int32_t aNumCorners, int32_t aStartIndex) {
4708 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"
, 4708); MOZ_PretendNoReturn(); } } while (0)
;
4709 startIndex = aStartIndex;
4710 endIndex = aStartIndex + aNumCorners - 1;
4711 corners = MakeUnique<BCCornerInfo[]>(aNumCorners);
4712}
4713
4714struct BCCellBorders {
4715 BCCellBorders(int32_t aNumBorders, int32_t aStartIndex);
4716
4717 BCCellBorder& operator[](int32_t i) const {
4718 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"
, 4718); MOZ_PretendNoReturn(); } } while (0)
;
4719 return borders[clamped(i, startIndex, endIndex) - startIndex];
4720 }
4721
4722 int32_t startIndex;
4723 int32_t endIndex;
4724 UniquePtr<BCCellBorder[]> borders;
4725};
4726
4727BCCellBorders::BCCellBorders(int32_t aNumBorders, int32_t aStartIndex) {
4728 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"
, 4728); MOZ_PretendNoReturn(); } } while (0)
;
4729 startIndex = aStartIndex;
4730 endIndex = aStartIndex + aNumBorders - 1;
4731 borders = MakeUnique<BCCellBorder[]>(aNumBorders);
4732}
4733
4734// this function sets the new border properties and returns true if the border
4735// segment will start a new segment and not be accumulated into the previous
4736// segment.
4737static bool SetBorder(const BCCellBorder& aNewBorder, BCCellBorder& aBorder) {
4738 bool changed = (aNewBorder.style != aBorder.style) ||
4739 (aNewBorder.width != aBorder.width) ||
4740 (aNewBorder.color != aBorder.color);
4741 aBorder.color = aNewBorder.color;
4742 aBorder.width = aNewBorder.width;
4743 aBorder.style = aNewBorder.style;
4744 aBorder.owner = aNewBorder.owner;
4745
4746 return changed;
4747}
4748
4749// this function will set the inline-dir border. It will return true if the
4750// existing segment will not be continued. Having a block-dir owner of a corner
4751// should also start a new segment.
4752static bool SetInlineDirBorder(const BCCellBorder& aNewBorder,
4753 const BCCornerInfo& aCorner,
4754 BCCellBorder& aBorder) {
4755 bool startSeg = ::SetBorder(aNewBorder, aBorder);
4756 if (!startSeg) {
4757 startSeg = !IsInline(LogicalSide(aCorner.ownerSide));
4758 }
4759 return startSeg;
4760}
4761
4762// Make the damage area larger on the top and bottom by at least one row and on
4763// the left and right at least one column. This is done so that adjacent
4764// elements are part of the border calculations. The extra segments and borders
4765// outside the actual damage area will not be updated in the cell map, because
4766// they in turn would need info from adjacent segments outside the damage area
4767// to be accurate.
4768void nsTableFrame::ExpandBCDamageArea(TableArea& aArea) const {
4769 int32_t numRows = GetRowCount();
4770 int32_t numCols = GetColCount();
4771
4772 int32_t dStartX = aArea.StartCol();
4773 int32_t dEndX = aArea.EndCol() - 1;
4774 int32_t dStartY = aArea.StartRow();
4775 int32_t dEndY = aArea.EndRow() - 1;
4776
4777 // expand the damage area in each direction
4778 if (dStartX > 0) {
4779 dStartX--;
4780 }
4781 if (dEndX < (numCols - 1)) {
4782 dEndX++;
4783 }
4784 if (dStartY > 0) {
4785 dStartY--;
4786 }
4787 if (dEndY < (numRows - 1)) {
4788 dEndY++;
4789 }
4790 // Check the damage area so that there are no cells spanning in or out. If
4791 // there are any then make the damage area as big as the table, similarly to
4792 // the way the cell map decides whether to rebuild versus expand. This could
4793 // be optimized to expand to the smallest area that contains no spanners, but
4794 // it may not be worth the effort in general, and it would need to be done in
4795 // the cell map as well.
4796 bool haveSpanner = false;
4797 if ((dStartX > 0) || (dEndX < (numCols - 1)) || (dStartY > 0) ||
4798 (dEndY < (numRows - 1))) {
4799 nsTableCellMap* tableCellMap = GetCellMap();
4800 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"
, 4800); MOZ_PretendNoReturn(); } } while (0); return; }
;
4801 // Get the ordered row groups
4802 RowGroupArray rowGroups = OrderedRowGroups();
4803
4804 // Scope outside loop to be used as hint.
4805 nsCellMap* cellMap = nullptr;
4806 for (uint32_t rgIdx = 0; rgIdx < rowGroups.Length(); rgIdx++) {
4807 nsTableRowGroupFrame* rgFrame = rowGroups[rgIdx];
4808 int32_t rgStartY = rgFrame->GetStartRowIndex();
4809 int32_t rgEndY = rgStartY + rgFrame->GetRowCount() - 1;
4810 if (dEndY < rgStartY) break;
4811 cellMap = tableCellMap->GetMapFor(rgFrame, cellMap);
4812 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"
, 4812); MOZ_PretendNoReturn(); } } while (0); return; }
;
4813 // check for spanners from above and below
4814 if ((dStartY > 0) && (dStartY >= rgStartY) && (dStartY <= rgEndY)) {
4815 if (uint32_t(dStartY - 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"
, 4815); MOZ_PretendNoReturn(); } } while (0); return; }
;
4816 const nsCellMap::CellDataArray& row =
4817 cellMap->mRows[dStartY - rgStartY];
4818 for (int32_t x = dStartX; x <= dEndX; x++) {
4819 CellData* cellData = row.SafeElementAt(x);
4820 if (cellData && (cellData->IsRowSpan())) {
4821 haveSpanner = true;
4822 break;
4823 }
4824 }
4825 if (dEndY < rgEndY) {
4826 if (uint32_t(dEndY + 1 - rgStartY) >= cellMap->mRows.Length())
4827 ABORT0(){ do { if (!(false)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "CellIterator program error"
, "false", "/var/lib/jenkins/workspace/firefox-scan-build/layout/tables/nsTableFrame.cpp"
, 4827); MOZ_PretendNoReturn(); } } while (0); return; }
;
4828 const nsCellMap::CellDataArray& row2 =
4829 cellMap->mRows[dEndY + 1 - rgStartY];
4830 for (int32_t x = dStartX; x <= dEndX; x++) {
4831 CellData* cellData = row2.SafeElementAt(x);
4832 if (cellData && (cellData->IsRowSpan())) {
4833 haveSpanner = true;
4834 break;
4835 }
4836 }
4837 }
4838 }
4839 // check for spanners on the left and right
4840 int32_t iterStartY;
4841 int32_t iterEndY;
4842 if ((dStartY >= rgStartY) && (dStartY <= rgEndY)) {
4843 // the damage area starts in the row group
4844 iterStartY = dStartY;
4845 iterEndY = std::min(dEndY, rgEndY);
4846 } else if ((dEndY >= rgStartY) && (dEndY <= rgEndY)) {
4847 // the damage area ends in the row group
4848 iterStartY = rgStartY;
4849 iterEndY = dEndY;
4850 } else if ((rgStartY >= dStartY) && (rgEndY <= dEndY)) {
4851 // the damage area contains the row group
4852 iterStartY = rgStartY;
4853 iterEndY = rgEndY;
4854 } else {
4855 // the damage area does not overlap the row group
4856 continue;
4857 }
4858 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"
, 4859); MOZ_PretendNoReturn(); } } while (0)
4859 "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"
, 4859); MOZ_PretendNoReturn(); } } while (0)
;
4860 for (int32_t y = iterStartY; y <= iterEndY; y++) {
4861 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"
, 4861); MOZ_PretendNoReturn(); } } while (0); return; }
;
4862 const nsCellMap::CellDataArray& row = cellMap->mRows[y - rgStartY];
4863 CellData* cellData = row.SafeElementAt(dStartX);
4864 if (cellData && (cellData->IsColSpan())) {
4865 haveSpanner = true;
4866 break;
4867 }
4868 if (dEndX < (numCols - 1)) {
4869 cellData = row.SafeElementAt(dEndX + 1);
4870 if (cellData && (cellData->IsColSpan())) {
4871 haveSpanner = true;
4872 break;
4873 }
4874 }
4875 }
4876 }
4877 }
4878 if (haveSpanner) {
4879 // make the damage area the whole table
4880 aArea.StartCol() = 0;
4881 aArea.StartRow() = 0;
4882 aArea.ColCount() = numCols;
4883 aArea.RowCount() = numRows;
4884 } else {
4885 aArea.StartCol() = dStartX;
4886 aArea.StartRow() = dStartY;
4887 aArea.ColCount() = 1 + dEndX - dStartX;
4888 aArea.RowCount() = 1 + dEndY - dStartY;
4889 }
4890}
4891
4892#define ADJACENTtrue true
4893#define INLINE_DIRtrue true
4894
4895void BCMapTableInfo::SetTableIStartBorderWidth(int32_t aRowB, nscoord aWidth) {
4896 // update the iStart first cell border
4897 if (aRowB == 0) {
4898 mTableBCData->mIStartCellBorderWidth = aWidth;
4899 }
4900 mTableBCData->mIStartBorderWidth =
4901 std::max(mTableBCData->mIStartBorderWidth, aWidth);
4902}
4903
4904void BCMapTableInfo::SetTableIEndBorderWidth(int32_t aRowB, nscoord aWidth) {
4905 // update the iEnd first cell border
4906 if (aRowB == 0) {
4907 mTableBCData->mIEndCellBorderWidth = aWidth;
4908 }
4909 mTableBCData->mIEndBorderWidth =
4910 std::max(mTableBCData->mIEndBorderWidth, aWidth);
4911}
4912
4913void BCMapTableInfo::SetTableBStartBorderWidth(nscoord aWidth) {
4914 mTableBCData->mBStartBorderWidth =
4915 std::max(mTableBCData->mBStartBorderWidth, aWidth);
4916}
4917
4918void BCMapTableInfo::SetTableBEndBorderWidth(nscoord aWidth) {
4919 mTableBCData->mBEndBorderWidth =
4920 std::max(mTableBCData->mBEndBorderWidth, aWidth);
4921}
4922
4923void BCMapCellInfo::ResetIStartBorderWidths() {
4924 if (mCell) {
4925 mCell->SetBorderWidth(LogicalSide::IStart, 0);
4926 }
4927 if (mStartCol) {
4928 mStartCol->SetIStartBorderWidth(0);
4929 }
4930}
4931
4932void BCMapCellInfo::ResetIEndBorderWidths() {
4933 if (mCell) {
4934 mCell->SetBorderWidth(LogicalSide::IEnd, 0);
4935 }
4936 if (mEndCol) {
4937 mEndCol->SetIEndBorderWidth(0);
4938 }
4939}
4940
4941void BCMapCellInfo::ResetBStartBorderWidths() {
4942 if (mCell) {
4943 mCell->SetBorderWidth(LogicalSide::BStart, 0);
4944 }
4945 if (mStartRow) {
4946 mStartRow->SetBStartBCBorderWidth(0);
4947 }
4948}
4949
4950void BCMapCellInfo::ResetBEndBorderWidths() {
4951 if (mCell) {
4952 mCell->SetBorderWidth(LogicalSide::BEnd, 0);
4953 }
4954 if (mEndRow) {
4955 mEndRow->SetBEndBCBorderWidth(0);
4956 }
4957}
4958
4959void BCMapCellInfo::SetIStartBorderWidths(nscoord aWidth) {
4960 if (mCell) {
4961 mCell->SetBorderWidth(
4962 LogicalSide::IStart,
4963 std::max(aWidth, mCell->GetBorderWidth(LogicalSide::IStart)));
4964 }
4965 if (mStartCol) {
4966 nscoord half = BC_BORDER_END_HALF(aWidth);
4967 mStartCol->SetIStartBorderWidth(
4968 std::max(half, mStartCol->GetIStartBorderWidth()));
4969 }
4970}
4971
4972void BCMapCellInfo::SetIEndBorderWidths(nscoord aWidth) {
4973 // update the borders of the cells and cols affected
4974 if (mCell) {
4975 mCell->SetBorderWidth(
4976 LogicalSide::IEnd,
4977 std::max(aWidth, mCell->GetBorderWidth(LogicalSide::IEnd)));
4978 }
4979 if (mEndCol) {
4980 nscoord half = BC_BORDER_START_HALF(aWidth);
4981 mEndCol->SetIEndBorderWidth(std::max(half, mEndCol->GetIEndBorderWidth()));
4982 }
4983}
4984
4985void BCMapCellInfo::SetBStartBorderWidths(nscoord aWidth) {
4986 if (mCell) {
4987 mCell->SetBorderWidth(
4988 LogicalSide::BStart,
4989 std::max(aWidth, mCell->GetBorderWidth(LogicalSide::BStart)));
4990 }
4991 if (mStartRow) {
4992 nscoord half = BC_BORDER_END_HALF(aWidth);
4993 mStartRow->SetBStartBCBorderWidth(
4994 std::max(half, mStartRow->GetBStartBCBorderWidth()));
4995 }
4996}
4997
4998void BCMapCellInfo::SetBEndBorderWidths(nscoord aWidth) {
4999 // update the borders of the affected cells and rows
5000 if (mCell) {
5001 mCell->SetBorderWidth(
5002 LogicalSide::BEnd,
5003 std::max(aWidth, mCell->GetBorderWidth(LogicalSide::BEnd)));
5004 }
5005 if (mEndRow) {
5006 nscoord half = BC_BORDER_START_HALF(aWidth);
5007 mEndRow->SetBEndBCBorderWidth(
5008 std::max(half, mEndRow->GetBEndBCBorderWidth()));
5009 }
5010}
5011
5012void BCMapCellInfo::SetColumn(int32_t aColX) {
5013 mCurrentColFrame = mTableFirstInFlow->GetColFrame(aColX);
5014 mCurrentColGroupFrame =
5015 static_cast<nsTableColGroupFrame*>(mCurrentColFrame->GetParent());
5016 if (!mCurrentColGroupFrame) {
5017 NS_ERROR("null mCurrentColGroupFrame")do { NS_DebugBreak(NS_DEBUG_ASSERTION, "null mCurrentColGroupFrame"
, "Error", "/var/lib/jenkins/workspace/firefox-scan-build/layout/tables/nsTableFrame.cpp"
, 5017); MOZ_PretendNoReturn(); } while (0)
;
5018 }
5019}
5020
5021void BCMapCellInfo::IncrementRow(bool aResetToBStartRowOfCell) {
5022 mCurrentRowFrame =
5023 aResetToBStartRowOfCell ? mStartRow : mCurrentRowFrame->GetNextRow();
5024}
5025
5026BCCellBorder BCMapCellInfo::GetBStartEdgeBorder() {
5027 return CompareBorders(mTableFrame, mCurrentColGroupFrame, mCurrentColFrame,
5028 mRowGroup, mStartRow, mCell, mTableWM,
5029 LogicalSide::BStart, !ADJACENTtrue);
5030}
5031
5032BCCellBorder BCMapCellInfo::GetBEndEdgeBorder() {
5033 return CompareBorders(mTableFrame, mCurrentColGroupFrame, mCurrentColFrame,
5034 mRowGroup, mEndRow, mCell, mTableWM, LogicalSide::BEnd,
5035 ADJACENTtrue);
5036}
5037BCCellBorder BCMapCellInfo::GetIStartEdgeBorder() {
5038 return CompareBorders(mTableFrame, mColGroup, mStartCol, mRowGroup,
5039 mCurrentRowFrame, mCell, mTableWM, LogicalSide::IStart,
5040 !ADJACENTtrue);
5041}
5042BCCellBorder BCMapCellInfo::GetIEndEdgeBorder() {
5043 return CompareBorders(mTableFrame, mColGroup, mEndCol, mRowGroup,
5044 mCurrentRowFrame, mCell, mTableWM, LogicalSide::IEnd,
5045 ADJACENTtrue);
5046}
5047BCCellBorder BCMapCellInfo::GetIEndInternalBorder() {
5048 const nsIFrame* cg = mCgAtEnd ? mColGroup : nullptr;
5049 return CompareBorders(nullptr, cg, mEndCol, nullptr, nullptr, mCell, mTableWM,
5050 LogicalSide::IEnd, ADJACENTtrue);
5051}
5052
5053BCCellBorder BCMapCellInfo::GetIStartInternalBorder() {
5054 const nsIFrame* cg = mCgAtStart ? mColGroup : nullptr;
5055 return CompareBorders(nullptr, cg, mStartCol, nullptr, nullptr, mCell,
5056 mTableWM, LogicalSide::IStart, !ADJACENTtrue);
5057}
5058
5059BCCellBorder BCMapCellInfo::GetBEndInternalBorder() {
5060 const nsIFrame* rg = mRgAtEnd ? mRowGroup : nullptr;
5061 return CompareBorders(nullptr, nullptr, nullptr, rg, mEndRow, mCell, mTableWM,
5062 LogicalSide::BEnd, ADJACENTtrue);
5063}
5064
5065BCCellBorder BCMapCellInfo::GetBStartInternalBorder() {
5066 const nsIFrame* rg = mRgAtStart ? mRowGroup : nullptr;
5067 return CompareBorders(nullptr, nullptr, nullptr, rg, mStartRow, mCell,
5068 mTableWM, LogicalSide::BStart, !ADJACENTtrue);
5069}
5070
5071// Calculate border information for border-collapsed tables.
5072// Because borders of table/row/cell, etc merge into one, we need to
5073// determine which border dominates at each cell. In addition, corner-specific
5074// information, e.g. bevelling, is computed as well.
5075//
5076// Here is the order for storing border edges in the cell map as a cell is
5077// processed.
5078//
5079// For each cell, at least 4 edges are processed:
5080// * There are colspan * N block-start and block-end edges.
5081// * There are rowspan * N inline-start and inline-end edges.
5082//
5083// 1) If the cell being processed is at the block-start of the table, store the
5084// block-start edge.
5085// 2) If the cell being processed is at the inline-start of the table, store
5086// the
5087// inline-start edge.
5088// 3) Store the inline-end edge.
5089// 4) Store the block-end edge.
5090//
5091// These steps are traced by calls to `SetBCBorderEdge`.
5092//
5093// Corners are indexed by columns only, to avoid allocating a full row * col
5094// array of `BCCornerInfo`. This trades off memory allocation versus moving
5095// previous corner information around.
5096//
5097// For each cell:
5098// 1) If the cell is at the block-start of the table, but not at the
5099// inline-start of the table, store its block-start inline-start corner.
5100//
5101// 2) If the cell is at the inline-start of the table, store the block-start
5102// inline-start corner.
5103//
5104// 3) If the cell is at the block-start inline-end of the table, or not at the
5105// block-start of the table, store the block-start inline-end corner.
5106//
5107// 4) If the cell is at the block-end inline-end of the table, store the
5108// block-end inline-end corner.
5109//
5110// 5) If the cell is at the block-end of the table, store the block-end
5111// inline-start.
5112//
5113// Visually, it looks like this:
5114//
5115// 2--1--1--1--1--1--3
5116// | | | | | | |
5117// 2--3--3--3--3--3--3
5118// | | | | | | |
5119// 2--3--3--3--3--3--3
5120// | | | | | | |
5121// 5--5--5--5--5--5--4
5122//
5123// For rowspan/colspan cells, the latest border information is propagated
5124// along its "corners".
5125//
5126// These steps are traced by calls to `SetBCBorderCorner`.
5127void nsTableFrame::CalcBCBorders() {
5128 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"
, 5129); MOZ_PretendNoReturn(); } } while (0)
5129 "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"
, 5129); MOZ_PretendNoReturn(); } } while (0)
;
5130 nsTableCellMap* tableCellMap = GetCellMap();
5131 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"
, 5131); MOZ_PretendNoReturn(); } } while (0); return; }
;
5132 int32_t numRows = GetRowCount();
5133 int32_t numCols = GetColCount();
5134 if (!numRows || !numCols) return; // nothing to do
5135
5136 // Get the property holding the table damage area and border widths
5137 TableBCData* propData = GetTableBCData();
5138 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"
, 5138); MOZ_PretendNoReturn(); } } while (0); return; }
;
5139
5140 TableArea damageArea(propData->mDamageArea);
5141 // See documentation for why we do this.
5142 ExpandBCDamageArea(damageArea);
5143
5144 // We accumulate border widths as we process the cells, so we need
5145 // to reset it once in the beginning.
5146 bool tableBorderReset[4];
5147 for (uint32_t sideX = 0; sideX < ArrayLength(tableBorderReset); sideX++) {
5148 tableBorderReset[sideX] = false;
5149 }
5150
5151 // Storage for block-direction borders from the previous row, indexed by
5152 // columns.
5153 BCCellBorders lastBlockDirBorders(damageArea.ColCount() + 1,
5154 damageArea.StartCol());
5155 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"
, 5155); MOZ_PretendNoReturn(); } } while (0); return; }
;
5156 if (damageArea.StartRow() != 0) {
5157 // Ok, we've filled with information about the previous row's borders with
5158 // the default state, which is "no borders." This is incorrect, and leaving
5159 // it will result in an erroneous behaviour if the previous row did have
5160 // borders, and the dirty rows don't, as we will not mark the beginning of
5161 // the no border segment.
5162 TableArea prevRowArea(damageArea.StartCol(), damageArea.StartRow() - 1,
5163 damageArea.ColCount(), 1);
5164 BCMapCellIterator iter(this, prevRowArea);
5165 BCMapCellInfo info(this);
5166 for (iter.First(info); !iter.mAtEnd; iter.Next(info)) {
5167 if (info.mColIndex == prevRowArea.StartCol()) {
5168 lastBlockDirBorders.borders[0] = info.GetIStartEdgeBorder();
5169 }
5170 lastBlockDirBorders.borders[info.mColIndex - prevRowArea.StartCol() + 1] =
5171 info.GetIEndEdgeBorder();
5172 }
5173 }
5174 // Inline direction border at block start of the table, computed by the
5175 // previous cell. Unused afterwards.
5176 Maybe<BCCellBorder> firstRowBStartEdgeBorder;
5177 BCCellBorder lastBEndBorder;
5178 // Storage for inline-direction borders from previous cells, indexed by
5179 // columns.
5180 // TODO(dshin): Why ColCount + 1? Number of inline segments should match
5181 // column count exactly, unlike block direction segments...
5182 BCCellBorders lastBEndBorders(damageArea.ColCount() + 1,
5183 damageArea.StartCol());
5184 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"
, 5184); MOZ_PretendNoReturn(); } } while (0); return; }
;
5185
5186 BCMapCellInfo info(this);
5187 // TODO(dshin): This is basically propData, except it uses first-in-flow's
5188 // data. Consult the definition of `TableBCDataProperty` regarding
5189 // using the first-in-flow only.
5190 BCMapTableInfo tableInfo(this);
5191
5192 // Block-start corners of the cell being traversed, indexed by columns.
5193 BCCorners bStartCorners(damageArea.ColCount() + 1, damageArea.StartCol());
5194 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"
, 5194); MOZ_PretendNoReturn(); } } while (0); return; }
;
5195 // Block-end corners of the cell being traversed, indexed by columns.
5196 // Note that when a new row starts, they become block-start corners and used
5197 // as such, until cleared with `Set`.
5198 BCCorners bEndCorners(damageArea.ColCount() + 1, damageArea.StartCol());
5199 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"
, 5199); MOZ_PretendNoReturn(); } } while (0); return; }
;
5200
5201 BCMapCellIterator iter(this, damageArea);
5202 for (iter.First(info); !iter.mAtEnd; iter.Next(info)) {
5203 // see if firstRowBStartEdgeBorder, lastBEndBorder need to be reset
5204 if (iter.IsNewRow()) {
5205 if (info.mRowIndex == 0) {
5206 BCCellBorder border;
5207 if (info.mColIndex == 0) {
5208 border.Reset(info.mRowIndex, info.mRowSpan);
5209 } else {
5210 // Similar to lastBlockDirBorders, the previous block-start border
5211 // is filled by actually quering the adjacent cell.
5212 BCMapCellInfo ajaInfo(this);
5213 iter.PeekIStart(info, info.mRowIndex, ajaInfo);
5214 border = ajaInfo.GetBStartEdgeBorder();
5215 }
5216 firstRowBStartEdgeBorder = Some(border);
5217 } else {
5218 firstRowBStartEdgeBorder = Nothing{};
5219 }
5220 if (info.mColIndex == 0) {
5221 lastBEndBorder.Reset(info.GetCellEndRowIndex() + 1, info.mRowSpan);
5222 } else {
5223 // Same as above, but for block-end border.
5224 BCMapCellInfo ajaInfo(this);
5225 iter.PeekIStart(info, info.mRowIndex, ajaInfo);
5226 lastBEndBorder = ajaInfo.GetBEndEdgeBorder();
5227 }
5228 } else if (info.mColIndex > damageArea.StartCol()) {
5229 lastBEndBorder = lastBEndBorders[info.mColIndex - 1];
5230 if (lastBEndBorder.rowIndex > (info.GetCellEndRowIndex() + 1)) {
5231 // the bEnd border's iStart edge butts against the middle of a rowspan
5232 lastBEndBorder.Reset(info.GetCellEndRowIndex() + 1, info.mRowSpan);
5233 }
5234 }
5235
5236 // find the dominant border considering the cell's bStart border and the
5237 // table, row group, row if the border is at the bStart of the table,
5238 // otherwise it was processed in a previous row
5239 if (0 == info.mRowIndex) {
5240 uint8_t idxBStart = static_cast<uint8_t>(LogicalSide::BStart);
5241 if (!tableBorderReset[idxBStart]) {
5242 tableInfo.ResetTableBStartBorderWidth();
5243 tableBorderReset[idxBStart] = true;
5244 }
5245 bool reset = false;
5246 for (int32_t colIdx = info.mColIndex; colIdx <= info.GetCellEndColIndex();
5247 colIdx++) {
5248 info.SetColumn(colIdx);
5249 BCCellBorder currentBorder = info.GetBStartEdgeBorder();
5250 BCCornerInfo& bStartIStartCorner = bStartCorners[colIdx];
5251 // Mark inline-end direction border from this corner.
5252 if (0 == colIdx) {
5253 bStartIStartCorner.Set(LogicalSide::IEnd, currentBorder);
5254 } else {
5255 bStartIStartCorner.Update(LogicalSide::IEnd, currentBorder);
5256 tableCellMap->SetBCBorderCorner(
5257 LogicalCorner::BStartIStart, *iter.mCellMap, 0, 0, colIdx,
5258 LogicalSide(bStartIStartCorner.ownerSide),
5259 bStartIStartCorner.subWidth, bStartIStartCorner.bevel);
5260 }
5261 // Above, we set the corner `colIndex` column as having a border towards
5262 // inline-end, heading towards the next column. Vice versa is also true,
5263 // where the next column has a border heading towards this column.
5264 bStartCorners[colIdx + 1].Set(LogicalSide::IStart, currentBorder);
5265 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"
, 5266); AnnotateMozCrashReason("MOZ_ASSERT" "(" "firstRowBStartEdgeBorder"
") (" "Inline start border tracking not set?" ")"); do { *((
volatile int*)__null) = 5266; __attribute__((nomerge)) ::abort
(); } while (false); } } while (false)
5266 "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"
, 5266); AnnotateMozCrashReason("MOZ_ASSERT" "(" "firstRowBStartEdgeBorder"
") (" "Inline start border tracking not set?" ")"); do { *((
volatile int*)__null) = 5266; __attribute__((nomerge)) ::abort
(); } while (false); } } while (false)
;
5267 // update firstRowBStartEdgeBorder and see if a new segment starts
5268 bool startSeg =
5269 firstRowBStartEdgeBorder
5270 ? SetInlineDirBorder(currentBorder, bStartIStartCorner,
5271 firstRowBStartEdgeBorder.ref())
5272 : true;
5273 // store the border segment in the cell map
5274 tableCellMap->SetBCBorderEdge(LogicalSide::BStart, *iter.mCellMap, 0, 0,
5275 colIdx, 1, currentBorder.owner,
5276 currentBorder.width, startSeg);
5277
5278 // Set border width at block-start (table-wide and for the cell), but
5279 // only if it's the largest we've encountered.
5280 tableInfo.SetTableBStartBorderWidth(currentBorder.width);
5281 if (!reset) {
5282 info.ResetBStartBorderWidths();
5283 reset = true;
5284 }
5285 info.SetBStartBorderWidths(currentBorder.width);
5286 }
5287 } else {
5288 // see if the bStart border needs to be the start of a segment due to a
5289 // block-dir border owning the corner
5290 if (info.mColIndex > 0) {
5291 BCData& data = info.mCellData->mData;
5292 if (!data.IsBStartStart()) {
5293 LogicalSide cornerSide;
5294 bool bevel;
5295 data.GetCorner(cornerSide, bevel);
5296 if (IsBlock(cornerSide)) {
5297 data.SetBStartStart(true);
5298 }
5299 }
5300 }
5301 }
5302
5303 // find the dominant border considering the cell's iStart border and the
5304 // table, col group, col if the border is at the iStart of the table,
5305 // otherwise it was processed in a previous col
5306 if (0 == info.mColIndex) {
5307 uint8_t idxIStart = static_cast<uint8_t>(LogicalSide::IStart);
5308 if (!tableBorderReset[idxIStart]) {
5309 tableInfo.ResetTableIStartBorderWidth();
5310 tableBorderReset[idxIStart] = true;
5311 }
5312 info.mCurrentRowFrame = nullptr;
5313 bool reset = false;
5314 for (int32_t rowB = info.mRowIndex; rowB <= info.GetCellEndRowIndex();
5315 rowB++) {
5316 info.IncrementRow(rowB == info.mRowIndex);
5317 BCCellBorder currentBorder = info.GetIStartEdgeBorder();
5318 BCCornerInfo& bStartIStartCorner =
5319 (0 == rowB) ? bStartCorners[0] : bEndCorners[0];
5320 bStartIStartCorner.Update(LogicalSide::BEnd, currentBorder);
5321 tableCellMap->SetBCBorderCorner(
5322 LogicalCorner::BStartIStart, *iter.mCellMap, iter.mRowGroupStart,
5323 rowB, 0, LogicalSide(bStartIStartCorner.ownerSide),
5324 bStartIStartCorner.subWidth, bStartIStartCorner.bevel);
5325 bEndCorners[0].Set(LogicalSide::BStart, currentBorder);
5326
5327 // update lastBlockDirBorders and see if a new segment starts
5328 bool startSeg = SetBorder(currentBorder, lastBlockDirBorders[0]);
5329 // store the border segment in the cell map
5330 tableCellMap->SetBCBorderEdge(LogicalSide::IStart, *iter.mCellMap,
5331 iter.mRowGroupStart, rowB, info.mColIndex,
5332 1, currentBorder.owner,
5333 currentBorder.width, startSeg);
5334 // Set border width at inline-start (table-wide and for the cell), but
5335 // only if it's the largest we've encountered.
5336 tableInfo.SetTableIStartBorderWidth(rowB, currentBorder.width);
5337 if (!reset) {
5338 info.ResetIStartBorderWidths();
5339 reset = true;
5340 }
5341 info.SetIStartBorderWidths(currentBorder.width);
5342 }
5343 }
5344
5345 // find the dominant border considering the cell's iEnd border, adjacent
5346 // cells and the table, row group, row
5347 if (info.mNumTableCols == info.GetCellEndColIndex() + 1) {
5348 // touches iEnd edge of table
5349 uint8_t idxIEnd = static_cast<uint8_t>(LogicalSide::IEnd);
5350 if (!tableBorderReset[idxIEnd]) {
5351 tableInfo.ResetTableIEndBorderWidth();
5352 tableBorderReset[idxIEnd] = true;
5353 }
5354 info.mCurrentRowFrame = nullptr;
5355 bool reset = false;
5356 for (int32_t rowB = info.mRowIndex; rowB <= info.GetCellEndRowIndex();
5357 rowB++) {
5358 info.IncrementRow(rowB == info.mRowIndex);
5359 BCCellBorder currentBorder = info.GetIEndEdgeBorder();
5360 // Update/store the bStart-iEnd & bEnd-iEnd corners. Note that we
5361 // overwrite all corner information to the end of the column span.
5362 BCCornerInfo& bStartIEndCorner =
5363 (0 == rowB) ? bStartCorners[info.GetCellEndColIndex() + 1]
5364 : bEndCorners[info.GetCellEndColIndex() + 1];
5365 bStartIEndCorner.Update(LogicalSide::BEnd, currentBorder);
5366 tableCellMap->SetBCBorderCorner(
5367 LogicalCorner::BStartIEnd, *iter.mCellMap, iter.mRowGroupStart,
5368 rowB, info.GetCellEndColIndex(),
5369 LogicalSide(bStartIEndCorner.ownerSide), bStartIEndCorner.subWidth,
5370 bStartIEndCorner.bevel);
5371 BCCornerInfo& bEndIEndCorner =
5372 bEndCorners[info.GetCellEndColIndex() + 1];
5373 bEndIEndCorner.Set(LogicalSide::BStart, currentBorder);
5374 tableCellMap->SetBCBorderCorner(
5375 LogicalCorner::BEndIEnd, *iter.mCellMap, iter.mRowGroupStart, rowB,
5376 info.GetCellEndColIndex(), LogicalSide(bEndIEndCorner.ownerSide),
5377 bEndIEndCorner.subWidth, bEndIEndCorner.bevel);
5378 // update lastBlockDirBorders and see if a new segment starts
5379 bool startSeg = SetBorder(
5380 currentBorder, lastBlockDirBorders[info.GetCellEndColIndex() + 1]);
5381 // store the border segment in the cell map and update cellBorders
5382 tableCellMap->SetBCBorderEdge(
5383 LogicalSide::IEnd, *iter.mCellMap, iter.mRowGroupStart, rowB,
5384 info.GetCellEndColIndex(), 1, currentBorder.owner,
5385 currentBorder.width, startSeg);
5386 // Set border width at inline-end (table-wide and for the cell), but
5387 // only if it's the largest we've encountered.
5388 tableInfo.SetTableIEndBorderWidth(rowB, currentBorder.width);
5389 if (!reset) {
5390 info.ResetIEndBorderWidths();
5391 reset = true;
5392 }
5393 info.SetIEndBorderWidths(currentBorder.width);
5394 }
5395 } else {
5396 // Cell entries, but not on the block-end side of the entire table.
5397 int32_t segLength = 0;
5398 BCMapCellInfo ajaInfo(this);
5399 BCMapCellInfo priorAjaInfo(this);
5400 bool reset = false;
5401 for (int32_t rowB = info.mRowIndex; rowB <= info.GetCellEndRowIndex();
5402 rowB += segLength) {
5403 // Grab the cell adjacent to our inline-end.
5404 iter.PeekIEnd(info, rowB, ajaInfo);
5405 BCCellBorder currentBorder = info.GetIEndInternalBorder();
5406 BCCellBorder adjacentBorder = ajaInfo.GetIStartInternalBorder();
5407 currentBorder = CompareBorders(!CELL_CORNERtrue, currentBorder,
5408 adjacentBorder, !INLINE_DIRtrue);
5409
5410 segLength = std::max(1, ajaInfo.mRowIndex + ajaInfo.mRowSpan - rowB);
5411 segLength = std::min(segLength, info.mRowIndex + info.mRowSpan - rowB);
5412
5413 // update lastBlockDirBorders and see if a new segment starts
5414 bool startSeg = SetBorder(
5415 currentBorder, lastBlockDirBorders[info.GetCellEndColIndex() + 1]);
5416 // store the border segment in the cell map and update cellBorders
5417 if (info.GetCellEndColIndex() < damageArea.EndCol() &&
5418 rowB >= damageArea.StartRow() && rowB < damageArea.EndRow()) {
5419 tableCellMap->SetBCBorderEdge(
5420 LogicalSide::IEnd, *iter.mCellMap, iter.mRowGroupStart, rowB,
5421 info.GetCellEndColIndex(), segLength, currentBorder.owner,
5422 currentBorder.width, startSeg);
5423 if (!reset) {
5424 info.ResetIEndBorderWidths();
5425 ajaInfo.ResetIStartBorderWidths();
5426 reset = true;
5427 }
5428 info.SetIEndBorderWidths(currentBorder.width);
5429 ajaInfo.SetIStartBorderWidths(currentBorder.width);
5430 }
5431 // Does the block-start inline-end corner hit the inline-end adjacent
5432 // cell that wouldn't have an inline border? e.g.
5433 //
5434 // o-----------o---------------o
5435 // | | |
5436 // o-----------x Adjacent cell o
5437 // | This Cell | (rowspan) |
5438 // o-----------o---------------o
5439 bool hitsSpanOnIEnd = (rowB > ajaInfo.mRowIndex) &&
5440 (rowB < ajaInfo.mRowIndex + ajaInfo.mRowSpan);
5441 BCCornerInfo* bStartIEndCorner =
5442 ((0 == rowB) || hitsSpanOnIEnd)
5443 ? &bStartCorners[info.GetCellEndColIndex() + 1]
5444 : &bEndCorners[info.GetCellEndColIndex() +
5445 1]; // From previous row.
5446 bStartIEndCorner->Update(LogicalSide::BEnd, currentBorder);
5447 // If this is a rowspan, need to consider if this "corner" is generating
5448 // an inline segment for the adjacent cell. e.g.
5449 //
5450 // o--------------o----o
5451 // | | |
5452 // o x----o
5453 // | (This "row") | |
5454 // o--------------o----o
5455 if (rowB != info.mRowIndex) {
5456 currentBorder = priorAjaInfo.GetBEndInternalBorder();
5457 BCCellBorder adjacentBorder = ajaInfo.GetBStartInternalBorder();
5458 currentBorder = CompareBorders(!CELL_CORNERtrue, currentBorder,
5459 adjacentBorder, INLINE_DIRtrue);
5460 bStartIEndCorner->Update(LogicalSide::IEnd, currentBorder);
5461 }
5462 // Check that the spanned area is inside of the invalidation area
5463 if (info.GetCellEndColIndex() < damageArea.EndCol() &&
5464 rowB >= damageArea.StartRow()) {
5465 if (0 != rowB) {
5466 // Ok, actually store the information
5467 tableCellMap->SetBCBorderCorner(
5468 LogicalCorner::BStartIEnd, *iter.mCellMap, iter.mRowGroupStart,
5469 rowB, info.GetCellEndColIndex(),
5470 LogicalSide(bStartIEndCorner->ownerSide),
5471 bStartIEndCorner->subWidth, bStartIEndCorner->bevel);
5472 }
5473 // Propagate this segment down the rowspan
5474 for (int32_t rX = rowB + 1; rX < rowB + segLength; rX++) {
5475 tableCellMap->SetBCBorderCorner(
5476 LogicalCorner::BEndIEnd, *iter.mCellMap, iter.mRowGroupStart,
5477 rX, info.GetCellEndColIndex(),
5478 LogicalSide(bStartIEndCorner->ownerSide),
5479 bStartIEndCorner->subWidth, false);
5480 }
5481 }
5482 hitsSpanOnIEnd =
5483 (rowB + segLength < ajaInfo.mRowIndex + ajaInfo.mRowSpan);
5484 BCCornerInfo& bEndIEndCorner =
5485 (hitsSpanOnIEnd) ? bStartCorners[info.GetCellEndColIndex() + 1]
5486 : bEndCorners[info.GetCellEndColIndex() + 1];
5487 bEndIEndCorner.Set(LogicalSide::BStart, currentBorder);
5488 priorAjaInfo = ajaInfo;
5489 }
5490 }
5491 for (int32_t colIdx = info.mColIndex + 1;
5492 colIdx <= info.GetCellEndColIndex(); colIdx++) {
5493 lastBlockDirBorders[colIdx].Reset(0, 1);
5494 }
5495
5496 // find the dominant border considering the cell's bEnd border, adjacent
5497 // cells and the table, row group, row
5498 if (info.mNumTableRows == info.GetCellEndRowIndex() + 1) {
5499 // touches bEnd edge of table
5500 uint8_t idxBEnd = static_cast<uint8_t>(LogicalSide::BEnd);
5501 if (!tableBorderReset[idxBEnd]) {
5502 tableInfo.ResetTableBEndBorderWidth();
5503 tableBorderReset[idxBEnd] = true;
5504 }
5505 bool reset = false;
5506 for (int32_t colIdx = info.mColIndex; colIdx <= info.GetCellEndColIndex();
5507 colIdx++) {
5508 info.SetColumn(colIdx);
5509 BCCellBorder currentBorder = info.GetBEndEdgeBorder();
5510 BCCornerInfo& bEndIStartCorner = bEndCorners[colIdx];
5511 bEndIStartCorner.Update(LogicalSide::IEnd, currentBorder);
5512 tableCellMap->SetBCBorderCorner(
5513 LogicalCorner::BEndIStart, *iter.mCellMap, iter.mRowGroupStart,
5514 info.GetCellEndRowIndex(), colIdx,
5515 LogicalSide(bEndIStartCorner.ownerSide), bEndIStartCorner.subWidth,
5516 bEndIStartCorner.bevel);
5517 BCCornerInfo& bEndIEndCorner = bEndCorners[colIdx + 1];
5518 bEndIEndCorner.Update(LogicalSide::IStart, currentBorder);
5519 // Store the block-end inline-end corner if it also is the block-end
5520 // inline-end of the overall table.
5521 if (info.mNumTableCols == colIdx + 1) {
5522 tableCellMap->SetBCBorderCorner(
5523 LogicalCorner::BEndIEnd, *iter.mCellMap, iter.mRowGroupStart,
5524 info.GetCellEndRowIndex(), colIdx,
5525 LogicalSide(bEndIEndCorner.ownerSide), bEndIEndCorner.subWidth,
5526 bEndIEndCorner.bevel, true);
5527 }
5528 // update lastBEndBorder and see if a new segment starts
5529 bool startSeg =
5530 SetInlineDirBorder(currentBorder, bEndIStartCorner, lastBEndBorder);
5531 if (!startSeg) {
5532 // make sure that we did not compare apples to oranges i.e. the
5533 // current border should be a continuation of the lastBEndBorder,
5534 // as it is a bEnd border
5535 // add 1 to the info.GetCellEndRowIndex()
5536 startSeg =
5537 (lastBEndBorder.rowIndex != (info.GetCellEndRowIndex() + 1));
5538 }
5539 // store the border segment in the cell map and update cellBorders
5540 tableCellMap->SetBCBorderEdge(
5541 LogicalSide::BEnd, *iter.mCellMap, iter.mRowGroupStart,
5542 info.GetCellEndRowIndex(), colIdx, 1, currentBorder.owner,
5543 currentBorder.width, startSeg);
5544 // update lastBEndBorders
5545 lastBEndBorder.rowIndex = info.GetCellEndRowIndex() + 1;
5546 lastBEndBorder.rowSpan = info.mRowSpan;
5547 lastBEndBorders[colIdx] = lastBEndBorder;
5548
5549 // Set border width at block-end (table-wide and for the cell), but
5550 // only if it's the largest we've encountered.
5551 if (!reset) {
5552 info.ResetBEndBorderWidths();
5553 reset = true;
5554 }
5555 info.SetBEndBorderWidths(currentBorder.width);
5556 tableInfo.SetTableBEndBorderWidth(currentBorder.width);
5557 }
5558 } else {
5559 int32_t segLength = 0;
5560 BCMapCellInfo ajaInfo(this);
5561 bool reset = false;
5562 for (int32_t colIdx = info.mColIndex; colIdx <= info.GetCellEndColIndex();
5563 colIdx += segLength) {
5564 // Grab the cell adjacent to our block-end.
5565 iter.PeekBEnd(info, colIdx, ajaInfo);
5566 BCCellBorder currentBorder = info.GetBEndInternalBorder();
5567 BCCellBorder adjacentBorder = ajaInfo.GetBStartInternalBorder();
5568 currentBorder = CompareBorders(!CELL_CORNERtrue, currentBorder,
5569 adjacentBorder, INLINE_DIRtrue);
5570 segLength = std::max(1, ajaInfo.mColIndex + ajaInfo.mColSpan - colIdx);
5571 segLength =
5572 std::min(segLength, info.mColIndex + info.mColSpan - colIdx);
5573
5574 BCCornerInfo& bEndIStartCorner = bEndCorners[colIdx];
5575 // e.g.
5576 // o--o----------o
5577 // | | This col |
5578 // o--x----------o
5579 // | Adjacent |
5580 // o--o----------o
5581 bool hitsSpanBelow = (colIdx > ajaInfo.mColIndex) &&
5582 (colIdx < ajaInfo.mColIndex + ajaInfo.mColSpan);
5583 bool update = true;
5584 if (colIdx == info.mColIndex && colIdx > damageArea.StartCol()) {
5585 int32_t prevRowIndex = lastBEndBorders[colIdx - 1].rowIndex;
5586 if (prevRowIndex > info.GetCellEndRowIndex() + 1) {
5587 // hits a rowspan on the iEnd side
5588 update = false;
5589 // the corner was taken care of during the cell on the iStart side
5590 } else if (prevRowIndex < info.GetCellEndRowIndex() + 1) {
5591 // spans below the cell to the iStart side