Bug Summary

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