Bug Summary

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