/src/mozilla-central/layout/generic/nsAbsoluteContainingBlock.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | /* |
8 | | * code for managing absolutely positioned children of a rendering |
9 | | * object that is a containing block for them |
10 | | */ |
11 | | |
12 | | #include "nsAbsoluteContainingBlock.h" |
13 | | |
14 | | #include "nsContainerFrame.h" |
15 | | #include "nsGkAtoms.h" |
16 | | #include "nsIPresShell.h" |
17 | | #include "mozilla/CSSAlignUtils.h" |
18 | | #include "mozilla/ReflowInput.h" |
19 | | #include "nsPlaceholderFrame.h" |
20 | | #include "nsPresContext.h" |
21 | | #include "nsCSSFrameConstructor.h" |
22 | | #include "nsGridContainerFrame.h" |
23 | | |
24 | | #include "mozilla/Sprintf.h" |
25 | | |
26 | | #ifdef DEBUG |
27 | | #include "nsBlockFrame.h" |
28 | | |
29 | | static void PrettyUC(nscoord aSize, char* aBuf, int aBufSize) |
30 | | { |
31 | | if (NS_UNCONSTRAINEDSIZE == aSize) { |
32 | | strcpy(aBuf, "UC"); |
33 | | } else { |
34 | | if((int32_t)0xdeadbeef == aSize) { |
35 | | strcpy(aBuf, "deadbeef"); |
36 | | } else { |
37 | | snprintf(aBuf, aBufSize, "%d", aSize); |
38 | | } |
39 | | } |
40 | | } |
41 | | #endif |
42 | | |
43 | | using namespace mozilla; |
44 | | |
45 | | typedef mozilla::CSSAlignUtils::AlignJustifyFlags AlignJustifyFlags; |
46 | | |
47 | | void |
48 | | nsAbsoluteContainingBlock::SetInitialChildList(nsIFrame* aDelegatingFrame, |
49 | | ChildListID aListID, |
50 | | nsFrameList& aChildList) |
51 | 0 | { |
52 | 0 | MOZ_ASSERT(mChildListID == aListID, "unexpected child list name"); |
53 | | #ifdef DEBUG |
54 | | nsFrame::VerifyDirtyBitSet(aChildList); |
55 | | #endif |
56 | | mAbsoluteFrames.SetFrames(aChildList); |
57 | 0 | } |
58 | | |
59 | | void |
60 | | nsAbsoluteContainingBlock::AppendFrames(nsIFrame* aDelegatingFrame, |
61 | | ChildListID aListID, |
62 | | nsFrameList& aFrameList) |
63 | 0 | { |
64 | 0 | NS_ASSERTION(mChildListID == aListID, "unexpected child list"); |
65 | 0 |
|
66 | 0 | // Append the frames to our list of absolutely positioned frames |
67 | | #ifdef DEBUG |
68 | | nsFrame::VerifyDirtyBitSet(aFrameList); |
69 | | #endif |
70 | | mAbsoluteFrames.AppendFrames(nullptr, aFrameList); |
71 | 0 |
|
72 | 0 | // no damage to intrinsic widths, since absolutely positioned frames can't |
73 | 0 | // change them |
74 | 0 | aDelegatingFrame->PresShell()-> |
75 | 0 | FrameNeedsReflow(aDelegatingFrame, nsIPresShell::eResize, |
76 | 0 | NS_FRAME_HAS_DIRTY_CHILDREN); |
77 | 0 | } |
78 | | |
79 | | void |
80 | | nsAbsoluteContainingBlock::InsertFrames(nsIFrame* aDelegatingFrame, |
81 | | ChildListID aListID, |
82 | | nsIFrame* aPrevFrame, |
83 | | nsFrameList& aFrameList) |
84 | 0 | { |
85 | 0 | NS_ASSERTION(mChildListID == aListID, "unexpected child list"); |
86 | 0 | NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == aDelegatingFrame, |
87 | 0 | "inserting after sibling frame with different parent"); |
88 | 0 |
|
89 | | #ifdef DEBUG |
90 | | nsFrame::VerifyDirtyBitSet(aFrameList); |
91 | | #endif |
92 | | mAbsoluteFrames.InsertFrames(nullptr, aPrevFrame, aFrameList); |
93 | 0 |
|
94 | 0 | // no damage to intrinsic widths, since absolutely positioned frames can't |
95 | 0 | // change them |
96 | 0 | aDelegatingFrame->PresShell()-> |
97 | 0 | FrameNeedsReflow(aDelegatingFrame, nsIPresShell::eResize, |
98 | 0 | NS_FRAME_HAS_DIRTY_CHILDREN); |
99 | 0 | } |
100 | | |
101 | | void |
102 | | nsAbsoluteContainingBlock::RemoveFrame(nsIFrame* aDelegatingFrame, |
103 | | ChildListID aListID, |
104 | | nsIFrame* aOldFrame) |
105 | 0 | { |
106 | 0 | NS_ASSERTION(mChildListID == aListID, "unexpected child list"); |
107 | 0 | nsIFrame* nif = aOldFrame->GetNextInFlow(); |
108 | 0 | if (nif) { |
109 | 0 | nif->GetParent()->DeleteNextInFlowChild(nif, false); |
110 | 0 | } |
111 | 0 |
|
112 | 0 | mAbsoluteFrames.DestroyFrame(aOldFrame); |
113 | 0 | } |
114 | | |
115 | | void |
116 | | nsAbsoluteContainingBlock::Reflow(nsContainerFrame* aDelegatingFrame, |
117 | | nsPresContext* aPresContext, |
118 | | const ReflowInput& aReflowInput, |
119 | | nsReflowStatus& aReflowStatus, |
120 | | const nsRect& aContainingBlock, |
121 | | AbsPosReflowFlags aFlags, |
122 | | nsOverflowAreas* aOverflowAreas) |
123 | 0 | { |
124 | 0 | nsReflowStatus reflowStatus; |
125 | 0 |
|
126 | 0 | const bool reflowAll = aReflowInput.ShouldReflowAllKids(); |
127 | 0 | const bool isGrid = !!(aFlags & AbsPosReflowFlags::eIsGridContainerCB); |
128 | 0 | nsIFrame* kidFrame; |
129 | 0 | nsOverflowContinuationTracker tracker(aDelegatingFrame, true); |
130 | 0 | for (kidFrame = mAbsoluteFrames.FirstChild(); kidFrame; kidFrame = kidFrame->GetNextSibling()) { |
131 | 0 | bool kidNeedsReflow = reflowAll || NS_SUBTREE_DIRTY(kidFrame) || |
132 | 0 | FrameDependsOnContainer(kidFrame, |
133 | 0 | !!(aFlags & AbsPosReflowFlags::eCBWidthChanged), |
134 | 0 | !!(aFlags & AbsPosReflowFlags::eCBHeightChanged)); |
135 | 0 | nscoord availBSize = aReflowInput.AvailableBSize(); |
136 | 0 | const nsRect& cb = isGrid ? nsGridContainerFrame::GridItemCB(kidFrame) |
137 | 0 | : aContainingBlock; |
138 | 0 | WritingMode containerWM = aReflowInput.GetWritingMode(); |
139 | 0 | if (!kidNeedsReflow && availBSize != NS_UNCONSTRAINEDSIZE) { |
140 | 0 | // If we need to redo pagination on the kid, we need to reflow it. |
141 | 0 | // This can happen either if the available height shrunk and the |
142 | 0 | // kid (or its overflow that creates overflow containers) is now |
143 | 0 | // too large to fit in the available height, or if the available |
144 | 0 | // height has increased and the kid has a next-in-flow that we |
145 | 0 | // might need to pull from. |
146 | 0 | WritingMode kidWM = kidFrame->GetWritingMode(); |
147 | 0 | if (containerWM.GetBlockDir() != kidWM.GetBlockDir()) { |
148 | 0 | // Not sure what the right test would be here. |
149 | 0 | kidNeedsReflow = true; |
150 | 0 | } else { |
151 | 0 | nscoord kidBEnd = kidFrame->GetLogicalRect(cb.Size()).BEnd(kidWM); |
152 | 0 | nscoord kidOverflowBEnd = |
153 | 0 | LogicalRect(containerWM, |
154 | 0 | // Use ...RelativeToSelf to ignore transforms |
155 | 0 | kidFrame->GetScrollableOverflowRectRelativeToSelf() + |
156 | 0 | kidFrame->GetPosition(), |
157 | 0 | aContainingBlock.Size()).BEnd(containerWM); |
158 | 0 | MOZ_ASSERT(kidOverflowBEnd >= kidBEnd); |
159 | 0 | if (kidOverflowBEnd > availBSize || |
160 | 0 | (kidBEnd < availBSize && kidFrame->GetNextInFlow())) { |
161 | 0 | kidNeedsReflow = true; |
162 | 0 | } |
163 | 0 | } |
164 | 0 | } |
165 | 0 | if (kidNeedsReflow && !aPresContext->HasPendingInterrupt()) { |
166 | 0 | // Reflow the frame |
167 | 0 | nsReflowStatus kidStatus; |
168 | 0 | ReflowAbsoluteFrame(aDelegatingFrame, aPresContext, aReflowInput, cb, |
169 | 0 | aFlags, kidFrame, kidStatus, aOverflowAreas); |
170 | 0 | MOZ_ASSERT(!kidStatus.IsInlineBreakBefore(), |
171 | 0 | "ShouldAvoidBreakInside should prevent this from happening"); |
172 | 0 | nsIFrame* nextFrame = kidFrame->GetNextInFlow(); |
173 | 0 | if (!kidStatus.IsFullyComplete() && |
174 | 0 | aDelegatingFrame->IsFrameOfType(nsIFrame::eCanContainOverflowContainers)) { |
175 | 0 | // Need a continuation |
176 | 0 | if (!nextFrame) { |
177 | 0 | nextFrame = |
178 | 0 | aPresContext->PresShell()->FrameConstructor()-> |
179 | 0 | CreateContinuingFrame(aPresContext, kidFrame, aDelegatingFrame); |
180 | 0 | } |
181 | 0 | // Add it as an overflow container. |
182 | 0 | //XXXfr This is a hack to fix some of our printing dataloss. |
183 | 0 | // See bug 154892. Not sure how to do it "right" yet; probably want |
184 | 0 | // to keep continuations within an nsAbsoluteContainingBlock eventually. |
185 | 0 | tracker.Insert(nextFrame, kidStatus); |
186 | 0 | reflowStatus.MergeCompletionStatusFrom(kidStatus); |
187 | 0 | } |
188 | 0 | else { |
189 | 0 | // Delete any continuations |
190 | 0 | if (nextFrame) { |
191 | 0 | nsOverflowContinuationTracker::AutoFinish fini(&tracker, kidFrame); |
192 | 0 | nextFrame->GetParent()->DeleteNextInFlowChild(nextFrame, true); |
193 | 0 | } |
194 | 0 | } |
195 | 0 | } |
196 | 0 | else { |
197 | 0 | tracker.Skip(kidFrame, reflowStatus); |
198 | 0 | if (aOverflowAreas) { |
199 | 0 | aDelegatingFrame->ConsiderChildOverflow(*aOverflowAreas, kidFrame); |
200 | 0 | } |
201 | 0 | } |
202 | 0 |
|
203 | 0 | // Make a CheckForInterrupt call, here, not just HasPendingInterrupt. That |
204 | 0 | // will make sure that we end up reflowing aDelegatingFrame in cases when |
205 | 0 | // one of our kids interrupted. Otherwise we'd set the dirty or |
206 | 0 | // dirty-children bit on the kid in the condition below, and then when |
207 | 0 | // reflow completes and we go to mark dirty bits on all ancestors of that |
208 | 0 | // kid we'll immediately bail out, because the kid already has a dirty bit. |
209 | 0 | // In particular, we won't set any dirty bits on aDelegatingFrame, so when |
210 | 0 | // the following reflow happens we won't reflow the kid in question. This |
211 | 0 | // might be slightly suboptimal in cases where |kidFrame| itself did not |
212 | 0 | // interrupt, since we'll trigger a reflow of it too when it's not strictly |
213 | 0 | // needed. But the logic to not do that is enough more complicated, and |
214 | 0 | // the case enough of an edge case, that this is probably better. |
215 | 0 | if (kidNeedsReflow && aPresContext->CheckForInterrupt(aDelegatingFrame)) { |
216 | 0 | if (aDelegatingFrame->GetStateBits() & NS_FRAME_IS_DIRTY) { |
217 | 0 | kidFrame->AddStateBits(NS_FRAME_IS_DIRTY); |
218 | 0 | } else { |
219 | 0 | kidFrame->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN); |
220 | 0 | } |
221 | 0 | } |
222 | 0 | } |
223 | 0 |
|
224 | 0 | // Abspos frames can't cause their parent to be incomplete, |
225 | 0 | // only overflow incomplete. |
226 | 0 | if (reflowStatus.IsIncomplete()) |
227 | 0 | reflowStatus.SetOverflowIncomplete(); |
228 | 0 |
|
229 | 0 | aReflowStatus.MergeCompletionStatusFrom(reflowStatus); |
230 | 0 | } |
231 | | |
232 | | static inline bool IsFixedPaddingSize(const nsStyleCoord& aCoord) |
233 | 0 | { return aCoord.ConvertsToLength(); } |
234 | | static inline bool IsFixedMarginSize(const nsStyleCoord& aCoord) |
235 | 0 | { return aCoord.ConvertsToLength(); } |
236 | | static inline bool IsFixedOffset(const nsStyleCoord& aCoord) |
237 | 0 | { return aCoord.ConvertsToLength(); } |
238 | | |
239 | | bool |
240 | | nsAbsoluteContainingBlock::FrameDependsOnContainer(nsIFrame* f, |
241 | | bool aCBWidthChanged, |
242 | | bool aCBHeightChanged) |
243 | 0 | { |
244 | 0 | const nsStylePosition* pos = f->StylePosition(); |
245 | 0 | // See if f's position might have changed because it depends on a |
246 | 0 | // placeholder's position |
247 | 0 | // This can happen in the following cases: |
248 | 0 | // 1) Vertical positioning. "top" must be auto and "bottom" must be auto |
249 | 0 | // (otherwise the vertical position is completely determined by |
250 | 0 | // whichever of them is not auto and the height). |
251 | 0 | // 2) Horizontal positioning. "left" must be auto and "right" must be auto |
252 | 0 | // (otherwise the horizontal position is completely determined by |
253 | 0 | // whichever of them is not auto and the width). |
254 | 0 | // See ReflowInput::InitAbsoluteConstraints -- these are the |
255 | 0 | // only cases when we call CalculateHypotheticalBox(). |
256 | 0 | if ((pos->mOffset.GetTopUnit() == eStyleUnit_Auto && |
257 | 0 | pos->mOffset.GetBottomUnit() == eStyleUnit_Auto) || |
258 | 0 | (pos->mOffset.GetLeftUnit() == eStyleUnit_Auto && |
259 | 0 | pos->mOffset.GetRightUnit() == eStyleUnit_Auto)) { |
260 | 0 | return true; |
261 | 0 | } |
262 | 0 | if (!aCBWidthChanged && !aCBHeightChanged) { |
263 | 0 | // skip getting style data |
264 | 0 | return false; |
265 | 0 | } |
266 | 0 | const nsStylePadding* padding = f->StylePadding(); |
267 | 0 | const nsStyleMargin* margin = f->StyleMargin(); |
268 | 0 | WritingMode wm = f->GetWritingMode(); |
269 | 0 | if (wm.IsVertical() ? aCBHeightChanged : aCBWidthChanged) { |
270 | 0 | // See if f's inline-size might have changed. |
271 | 0 | // If margin-inline-start/end, padding-inline-start/end, |
272 | 0 | // inline-size, min/max-inline-size are all lengths, 'none', or enumerated, |
273 | 0 | // then our frame isize does not depend on the parent isize. |
274 | 0 | // Note that borders never depend on the parent isize. |
275 | 0 | // XXX All of the enumerated values except -moz-available are ok too. |
276 | 0 | if (pos->ISizeDependsOnContainer(wm) || |
277 | 0 | pos->MinISizeDependsOnContainer(wm) || |
278 | 0 | pos->MaxISizeDependsOnContainer(wm) || |
279 | 0 | !IsFixedPaddingSize(padding->mPadding.GetIStart(wm)) || |
280 | 0 | !IsFixedPaddingSize(padding->mPadding.GetIEnd(wm))) { |
281 | 0 | return true; |
282 | 0 | } |
283 | 0 | |
284 | 0 | // See if f's position might have changed. If we're RTL then the |
285 | 0 | // rules are slightly different. We'll assume percentage or auto |
286 | 0 | // margins will always induce a dependency on the size |
287 | 0 | if (!IsFixedMarginSize(margin->mMargin.GetIStart(wm)) || |
288 | 0 | !IsFixedMarginSize(margin->mMargin.GetIEnd(wm))) { |
289 | 0 | return true; |
290 | 0 | } |
291 | 0 | } |
292 | 0 | if (wm.IsVertical() ? aCBWidthChanged : aCBHeightChanged) { |
293 | 0 | // See if f's block-size might have changed. |
294 | 0 | // If margin-block-start/end, padding-block-start/end, |
295 | 0 | // min-block-size, and max-block-size are all lengths or 'none', |
296 | 0 | // and bsize is a length or bsize and bend are auto and bstart is not auto, |
297 | 0 | // then our frame bsize does not depend on the parent bsize. |
298 | 0 | // Note that borders never depend on the parent bsize. |
299 | 0 | if ((pos->BSizeDependsOnContainer(wm) && |
300 | 0 | !(pos->BSize(wm).GetUnit() == eStyleUnit_Auto && |
301 | 0 | pos->mOffset.GetBEndUnit(wm) == eStyleUnit_Auto && |
302 | 0 | pos->mOffset.GetBStartUnit(wm) != eStyleUnit_Auto)) || |
303 | 0 | pos->MinBSizeDependsOnContainer(wm) || |
304 | 0 | pos->MaxBSizeDependsOnContainer(wm) || |
305 | 0 | !IsFixedPaddingSize(padding->mPadding.GetBStart(wm)) || |
306 | 0 | !IsFixedPaddingSize(padding->mPadding.GetBEnd(wm))) { |
307 | 0 | return true; |
308 | 0 | } |
309 | 0 | |
310 | 0 | // See if f's position might have changed. |
311 | 0 | if (!IsFixedMarginSize(margin->mMargin.GetBStart(wm)) || |
312 | 0 | !IsFixedMarginSize(margin->mMargin.GetBEnd(wm))) { |
313 | 0 | return true; |
314 | 0 | } |
315 | 0 | } |
316 | 0 | |
317 | 0 | // Since we store coordinates relative to top and left, the position |
318 | 0 | // of a frame depends on that of its container if it is fixed relative |
319 | 0 | // to the right or bottom, or if it is positioned using percentages |
320 | 0 | // relative to the left or top. Because of the dependency on the |
321 | 0 | // sides (left and top) that we use to store coordinates, these tests |
322 | 0 | // are easier to do using physical coordinates rather than logical. |
323 | 0 | if (aCBWidthChanged) { |
324 | 0 | if (!IsFixedOffset(pos->mOffset.GetLeft())) { |
325 | 0 | return true; |
326 | 0 | } |
327 | 0 | // Note that even if 'left' is a length, our position can still |
328 | 0 | // depend on the containing block width, because if our direction or |
329 | 0 | // writing-mode moves from right to left (in either block or inline |
330 | 0 | // progression) and 'right' is not 'auto', we will discard 'left' |
331 | 0 | // and be positioned relative to the containing block right edge. |
332 | 0 | // 'left' length and 'right' auto is the only combination we can be |
333 | 0 | // sure of. |
334 | 0 | if ((wm.GetInlineDir() == WritingMode::eInlineRTL || |
335 | 0 | wm.GetBlockDir() == WritingMode::eBlockRL) && |
336 | 0 | pos->mOffset.GetRightUnit() != eStyleUnit_Auto) { |
337 | 0 | return true; |
338 | 0 | } |
339 | 0 | } |
340 | 0 | if (aCBHeightChanged) { |
341 | 0 | if (!IsFixedOffset(pos->mOffset.GetTop())) { |
342 | 0 | return true; |
343 | 0 | } |
344 | 0 | // See comment above for width changes. |
345 | 0 | if (wm.GetInlineDir() == WritingMode::eInlineBTT && |
346 | 0 | pos->mOffset.GetBottomUnit() != eStyleUnit_Auto) { |
347 | 0 | return true; |
348 | 0 | } |
349 | 0 | } |
350 | 0 | |
351 | 0 | return false; |
352 | 0 | } |
353 | | |
354 | | void |
355 | | nsAbsoluteContainingBlock::DestroyFrames(nsIFrame* aDelegatingFrame, |
356 | | nsIFrame* aDestructRoot, |
357 | | PostDestroyData& aPostDestroyData) |
358 | 0 | { |
359 | 0 | mAbsoluteFrames.DestroyFramesFrom(aDestructRoot, aPostDestroyData); |
360 | 0 | } |
361 | | |
362 | | void |
363 | | nsAbsoluteContainingBlock::MarkSizeDependentFramesDirty() |
364 | 0 | { |
365 | 0 | DoMarkFramesDirty(false); |
366 | 0 | } |
367 | | |
368 | | void |
369 | | nsAbsoluteContainingBlock::MarkAllFramesDirty() |
370 | 0 | { |
371 | 0 | DoMarkFramesDirty(true); |
372 | 0 | } |
373 | | |
374 | | void |
375 | | nsAbsoluteContainingBlock::DoMarkFramesDirty(bool aMarkAllDirty) |
376 | 0 | { |
377 | 0 | for (nsIFrame* kidFrame : mAbsoluteFrames) { |
378 | 0 | if (aMarkAllDirty) { |
379 | 0 | kidFrame->AddStateBits(NS_FRAME_IS_DIRTY); |
380 | 0 | } else if (FrameDependsOnContainer(kidFrame, true, true)) { |
381 | 0 | // Add the weakest flags that will make sure we reflow this frame later |
382 | 0 | kidFrame->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN); |
383 | 0 | } |
384 | 0 | } |
385 | 0 | } |
386 | | |
387 | | // Given an out-of-flow frame, this method returns the parent frame of its |
388 | | // placeholder frame or null if it doesn't have a placeholder for some reason. |
389 | | static nsContainerFrame* |
390 | | GetPlaceholderContainer(nsIFrame* aPositionedFrame) |
391 | 0 | { |
392 | 0 | nsIFrame* placeholder = aPositionedFrame->GetPlaceholderFrame(); |
393 | 0 | return placeholder ? placeholder->GetParent() : nullptr; |
394 | 0 | } |
395 | | |
396 | | /** |
397 | | * This function returns the offset of an abs/fixed-pos child's static |
398 | | * position, with respect to the "start" corner of its alignment container, |
399 | | * according to CSS Box Alignment. This function only operates in a single |
400 | | * axis at a time -- callers can choose which axis via the |aAbsPosCBAxis| |
401 | | * parameter. |
402 | | * |
403 | | * @param aKidReflowInput The ReflowInput for the to-be-aligned abspos child. |
404 | | * @param aKidSizeInAbsPosCBWM The child frame's size (after it's been given |
405 | | * the opportunity to reflow), in terms of |
406 | | * aAbsPosCBWM. |
407 | | * @param aAbsPosCBSize The abspos CB size, in terms of aAbsPosCBWM. |
408 | | * @param aPlaceholderContainer The parent of the child frame's corresponding |
409 | | * placeholder frame, cast to a nsContainerFrame. |
410 | | * (This will help us choose which alignment enum |
411 | | * we should use for the child.) |
412 | | * @param aAbsPosCBWM The child frame's containing block's WritingMode. |
413 | | * @param aAbsPosCBAxis The axis (of the containing block) that we should |
414 | | * be doing this computation for. |
415 | | */ |
416 | | static nscoord |
417 | | OffsetToAlignedStaticPos(const ReflowInput& aKidReflowInput, |
418 | | const LogicalSize& aKidSizeInAbsPosCBWM, |
419 | | const LogicalSize& aAbsPosCBSize, |
420 | | nsContainerFrame* aPlaceholderContainer, |
421 | | WritingMode aAbsPosCBWM, |
422 | | LogicalAxis aAbsPosCBAxis) |
423 | 0 | { |
424 | 0 | if (!aPlaceholderContainer) { |
425 | 0 | // (The placeholder container should be the thing that kicks this whole |
426 | 0 | // process off, by setting PLACEHOLDER_STATICPOS_NEEDS_CSSALIGN. So it |
427 | 0 | // should exist... but bail gracefully if it doesn't.) |
428 | 0 | NS_ERROR("Missing placeholder-container when computing a " |
429 | 0 | "CSS Box Alignment static position"); |
430 | 0 | return 0; |
431 | 0 | } |
432 | 0 |
|
433 | 0 | // (Most of this function is simply preparing args that we'll pass to |
434 | 0 | // AlignJustifySelf at the end.) |
435 | 0 |
|
436 | 0 | // NOTE: Our alignment container is aPlaceholderContainer's content-box |
437 | 0 | // (or an area within it, if aPlaceholderContainer is a grid). So, we'll |
438 | 0 | // perform most of our arithmetic/alignment in aPlaceholderContainer's |
439 | 0 | // WritingMode. For brevity, we use the abbreviation "pc" for "placeholder |
440 | 0 | // container" in variables below. |
441 | 0 | WritingMode pcWM = aPlaceholderContainer->GetWritingMode(); |
442 | 0 |
|
443 | 0 | // Find what axis aAbsPosCBAxis corresponds to, in placeholder's parent's |
444 | 0 | // writing-mode. |
445 | 0 | LogicalAxis pcAxis = (pcWM.IsOrthogonalTo(aAbsPosCBWM) |
446 | 0 | ? GetOrthogonalAxis(aAbsPosCBAxis) |
447 | 0 | : aAbsPosCBAxis); |
448 | 0 |
|
449 | 0 | const bool placeholderContainerIsContainingBlock = |
450 | 0 | aPlaceholderContainer == aKidReflowInput.mCBReflowInput->mFrame; |
451 | 0 |
|
452 | 0 | LayoutFrameType parentType = aPlaceholderContainer->Type(); |
453 | 0 | LogicalSize alignAreaSize(pcWM); |
454 | 0 | if (parentType == LayoutFrameType::FlexContainer) { |
455 | 0 | // We store the frame rect in FinishAndStoreOverflow, which runs _after_ |
456 | 0 | // reflowing the absolute frames, so handle the special case of the frame |
457 | 0 | // being the actual containing block here, by getting the size from |
458 | 0 | // aAbsPosCBSize. |
459 | 0 | // |
460 | 0 | // The alignment container is the flex container's content box. |
461 | 0 | if (placeholderContainerIsContainingBlock) { |
462 | 0 | alignAreaSize = aAbsPosCBSize.ConvertTo(pcWM, aAbsPosCBWM); |
463 | 0 | // aAbsPosCBSize is the padding-box, so substract the padding to get the |
464 | 0 | // content box. |
465 | 0 | alignAreaSize -= |
466 | 0 | aPlaceholderContainer->GetLogicalUsedPadding(pcWM).Size(pcWM); |
467 | 0 | } else { |
468 | 0 | alignAreaSize = aPlaceholderContainer->GetLogicalSize(pcWM); |
469 | 0 | LogicalMargin pcBorderPadding = |
470 | 0 | aPlaceholderContainer->GetLogicalUsedBorderAndPadding(pcWM); |
471 | 0 | alignAreaSize -= pcBorderPadding.Size(pcWM); |
472 | 0 | } |
473 | 0 | } else if (parentType == LayoutFrameType::GridContainer) { |
474 | 0 | // This abspos elem's parent is a grid container. Per CSS Grid 10.1 & 10.2: |
475 | 0 | // - If the grid container *also* generates the abspos containing block (a |
476 | 0 | // grid area) for this abspos child, we use that abspos containing block as |
477 | 0 | // the alignment container, too. (And its size is aAbsPosCBSize.) |
478 | 0 | // - Otherwise, we use the grid's padding box as the alignment container. |
479 | 0 | // https://drafts.csswg.org/css-grid/#static-position |
480 | 0 | if (placeholderContainerIsContainingBlock) { |
481 | 0 | // The alignment container is the grid area that we're using as the |
482 | 0 | // absolute containing block. |
483 | 0 | alignAreaSize = aAbsPosCBSize.ConvertTo(pcWM, aAbsPosCBWM); |
484 | 0 | } else { |
485 | 0 | // The alignment container is a the grid container's padding box (which |
486 | 0 | // we can get by subtracting away its border from frame's size): |
487 | 0 | alignAreaSize = aPlaceholderContainer->GetLogicalSize(pcWM); |
488 | 0 | LogicalMargin pcBorder = |
489 | 0 | aPlaceholderContainer->GetLogicalUsedBorder(pcWM); |
490 | 0 | alignAreaSize -= pcBorder.Size(pcWM); |
491 | 0 | } |
492 | 0 | } else { |
493 | 0 | NS_ERROR("Unsupported container for abpsos CSS Box Alignment"); |
494 | 0 | return 0; // (leave the child at the start of its alignment container) |
495 | 0 | } |
496 | 0 |
|
497 | 0 | nscoord alignAreaSizeInAxis = (pcAxis == eLogicalAxisInline) |
498 | 0 | ? alignAreaSize.ISize(pcWM) |
499 | 0 | : alignAreaSize.BSize(pcWM); |
500 | 0 |
|
501 | 0 | AlignJustifyFlags flags = AlignJustifyFlags::eIgnoreAutoMargins; |
502 | 0 | uint16_t alignConst = |
503 | 0 | aPlaceholderContainer->CSSAlignmentForAbsPosChild(aKidReflowInput, pcAxis); |
504 | 0 | // If the safe bit in alignConst is set, set the safe flag in |flags|. |
505 | 0 | // Note: If no <overflow-position> is specified, we behave as 'unsafe'. |
506 | 0 | // This doesn't quite match the css-align spec, which has an [at-risk] |
507 | 0 | // "smart default" behavior with some extra nuance about scroll containers. |
508 | 0 | if (alignConst & NS_STYLE_ALIGN_SAFE) { |
509 | 0 | flags |= AlignJustifyFlags::eOverflowSafe; |
510 | 0 | } |
511 | 0 | alignConst &= ~NS_STYLE_ALIGN_FLAG_BITS; |
512 | 0 |
|
513 | 0 | // Find out if placeholder-container & the OOF child have the same start-sides |
514 | 0 | // in the placeholder-container's pcAxis. |
515 | 0 | WritingMode kidWM = aKidReflowInput.GetWritingMode(); |
516 | 0 | if (pcWM.ParallelAxisStartsOnSameSide(pcAxis, kidWM)) { |
517 | 0 | flags |= AlignJustifyFlags::eSameSide; |
518 | 0 | } |
519 | 0 |
|
520 | 0 | // (baselineAdjust is unused. CSSAlignmentForAbsPosChild() should've |
521 | 0 | // converted 'baseline'/'last baseline' enums to their fallback values.) |
522 | 0 | const nscoord baselineAdjust = nscoord(0); |
523 | 0 |
|
524 | 0 | // AlignJustifySelf operates in the kid's writing mode, so we need to |
525 | 0 | // represent the child's size and the desired axis in that writing mode: |
526 | 0 | LogicalSize kidSizeInOwnWM = aKidSizeInAbsPosCBWM.ConvertTo(kidWM, |
527 | 0 | aAbsPosCBWM); |
528 | 0 | LogicalAxis kidAxis = (kidWM.IsOrthogonalTo(aAbsPosCBWM) |
529 | 0 | ? GetOrthogonalAxis(aAbsPosCBAxis) |
530 | 0 | : aAbsPosCBAxis); |
531 | 0 |
|
532 | 0 | nscoord offset = |
533 | 0 | CSSAlignUtils::AlignJustifySelf(alignConst, kidAxis, flags, |
534 | 0 | baselineAdjust, alignAreaSizeInAxis, |
535 | 0 | aKidReflowInput, kidSizeInOwnWM); |
536 | 0 |
|
537 | 0 | // "offset" is in terms of the CSS Box Alignment container (i.e. it's in |
538 | 0 | // terms of pcWM). But our return value needs to in terms of the containing |
539 | 0 | // block's writing mode, which might have the opposite directionality in the |
540 | 0 | // given axis. In that case, we just need to negate "offset" when returning, |
541 | 0 | // to make it have the right effect as an offset for coordinates in the |
542 | 0 | // containing block's writing mode. |
543 | 0 | if (!pcWM.ParallelAxisStartsOnSameSide(pcAxis, aAbsPosCBWM)) { |
544 | 0 | return -offset; |
545 | 0 | } |
546 | 0 | return offset; |
547 | 0 | } |
548 | | |
549 | | void |
550 | | nsAbsoluteContainingBlock::ResolveSizeDependentOffsets( |
551 | | nsPresContext* aPresContext, |
552 | | ReflowInput& aKidReflowInput, |
553 | | const LogicalSize& aKidSize, |
554 | | const LogicalMargin& aMargin, |
555 | | LogicalMargin* aOffsets, |
556 | | LogicalSize* aLogicalCBSize) |
557 | 0 | { |
558 | 0 | WritingMode wm = aKidReflowInput.GetWritingMode(); |
559 | 0 | WritingMode outerWM = aKidReflowInput.mParentReflowInput->GetWritingMode(); |
560 | 0 |
|
561 | 0 | // Now that we know the child's size, we resolve any sentinel values in its |
562 | 0 | // IStart/BStart offset coordinates that depend on that size. |
563 | 0 | // * NS_AUTOOFFSET indicates that the child's position in the given axis |
564 | 0 | // is determined by its end-wards offset property, combined with its size and |
565 | 0 | // available space. e.g.: "top: auto; height: auto; bottom: 50px" |
566 | 0 | // * m{I,B}OffsetsResolvedAfterSize indicate that the child is using its |
567 | 0 | // static position in that axis, *and* its static position is determined by |
568 | 0 | // the axis-appropriate css-align property (which may require the child's |
569 | 0 | // size, e.g. to center it within the parent). |
570 | 0 | if ((NS_AUTOOFFSET == aOffsets->IStart(outerWM)) || |
571 | 0 | (NS_AUTOOFFSET == aOffsets->BStart(outerWM)) || |
572 | 0 | aKidReflowInput.mFlags.mIOffsetsNeedCSSAlign || |
573 | 0 | aKidReflowInput.mFlags.mBOffsetsNeedCSSAlign) { |
574 | 0 | if (-1 == aLogicalCBSize->ISize(wm)) { |
575 | 0 | // Get the containing block width/height |
576 | 0 | const ReflowInput* parentRI = aKidReflowInput.mParentReflowInput; |
577 | 0 | *aLogicalCBSize = |
578 | 0 | aKidReflowInput.ComputeContainingBlockRectangle(aPresContext, |
579 | 0 | parentRI); |
580 | 0 | } |
581 | 0 |
|
582 | 0 | const LogicalSize logicalCBSizeOuterWM = aLogicalCBSize->ConvertTo(outerWM, |
583 | 0 | wm); |
584 | 0 |
|
585 | 0 | // placeholderContainer is used in each of the m{I,B}OffsetsNeedCSSAlign |
586 | 0 | // clauses. We declare it at this scope so we can avoid having to look |
587 | 0 | // it up twice (and only look it up if it's needed). |
588 | 0 | nsContainerFrame* placeholderContainer = nullptr; |
589 | 0 |
|
590 | 0 | if (NS_AUTOOFFSET == aOffsets->IStart(outerWM)) { |
591 | 0 | NS_ASSERTION(NS_AUTOOFFSET != aOffsets->IEnd(outerWM), |
592 | 0 | "Can't solve for both start and end"); |
593 | 0 | aOffsets->IStart(outerWM) = |
594 | 0 | logicalCBSizeOuterWM.ISize(outerWM) - |
595 | 0 | aOffsets->IEnd(outerWM) - aMargin.IStartEnd(outerWM) - |
596 | 0 | aKidSize.ISize(outerWM); |
597 | 0 | } else if (aKidReflowInput.mFlags.mIOffsetsNeedCSSAlign) { |
598 | 0 | placeholderContainer = GetPlaceholderContainer(aKidReflowInput.mFrame); |
599 | 0 | nscoord offset = OffsetToAlignedStaticPos(aKidReflowInput, aKidSize, |
600 | 0 | logicalCBSizeOuterWM, |
601 | 0 | placeholderContainer, |
602 | 0 | outerWM, eLogicalAxisInline); |
603 | 0 | // Shift IStart from its current position (at start corner of the |
604 | 0 | // alignment container) by the returned offset. And set IEnd to the |
605 | 0 | // distance between the kid's end edge to containing block's end edge. |
606 | 0 | aOffsets->IStart(outerWM) += offset; |
607 | 0 | aOffsets->IEnd(outerWM) = |
608 | 0 | logicalCBSizeOuterWM.ISize(outerWM) - |
609 | 0 | (aOffsets->IStart(outerWM) + aKidSize.ISize(outerWM)); |
610 | 0 | } |
611 | 0 |
|
612 | 0 | if (NS_AUTOOFFSET == aOffsets->BStart(outerWM)) { |
613 | 0 | aOffsets->BStart(outerWM) = |
614 | 0 | logicalCBSizeOuterWM.BSize(outerWM) - |
615 | 0 | aOffsets->BEnd(outerWM) - aMargin.BStartEnd(outerWM) - |
616 | 0 | aKidSize.BSize(outerWM); |
617 | 0 | } else if (aKidReflowInput.mFlags.mBOffsetsNeedCSSAlign) { |
618 | 0 | if (!placeholderContainer) { |
619 | 0 | placeholderContainer = GetPlaceholderContainer(aKidReflowInput.mFrame); |
620 | 0 | } |
621 | 0 | nscoord offset = OffsetToAlignedStaticPos(aKidReflowInput, aKidSize, |
622 | 0 | logicalCBSizeOuterWM, |
623 | 0 | placeholderContainer, |
624 | 0 | outerWM, eLogicalAxisBlock); |
625 | 0 | // Shift BStart from its current position (at start corner of the |
626 | 0 | // alignment container) by the returned offset. And set BEnd to the |
627 | 0 | // distance between the kid's end edge to containing block's end edge. |
628 | 0 | aOffsets->BStart(outerWM) += offset; |
629 | 0 | aOffsets->BEnd(outerWM) = |
630 | 0 | logicalCBSizeOuterWM.BSize(outerWM) - |
631 | 0 | (aOffsets->BStart(outerWM) + aKidSize.BSize(outerWM)); |
632 | 0 | } |
633 | 0 | aKidReflowInput.SetComputedLogicalOffsets(aOffsets->ConvertTo(wm, outerWM)); |
634 | 0 | } |
635 | 0 | } |
636 | | |
637 | | // XXX Optimize the case where it's a resize reflow and the absolutely |
638 | | // positioned child has the exact same size and position and skip the |
639 | | // reflow... |
640 | | |
641 | | // When bug 154892 is checked in, make sure that when |
642 | | // mChildListID == kFixedList, the height is unconstrained. |
643 | | // since we don't allow replicated frames to split. |
644 | | |
645 | | void |
646 | | nsAbsoluteContainingBlock::ReflowAbsoluteFrame(nsIFrame* aDelegatingFrame, |
647 | | nsPresContext* aPresContext, |
648 | | const ReflowInput& aReflowInput, |
649 | | const nsRect& aContainingBlock, |
650 | | AbsPosReflowFlags aFlags, |
651 | | nsIFrame* aKidFrame, |
652 | | nsReflowStatus& aStatus, |
653 | | nsOverflowAreas* aOverflowAreas) |
654 | 0 | { |
655 | 0 | MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); |
656 | 0 |
|
657 | | #ifdef DEBUG |
658 | | if (nsBlockFrame::gNoisyReflow) { |
659 | | nsFrame::IndentBy(stdout,nsBlockFrame::gNoiseIndent); |
660 | | printf("abs pos "); |
661 | | nsAutoString name; |
662 | | aKidFrame->GetFrameName(name); |
663 | | printf("%s ", NS_LossyConvertUTF16toASCII(name).get()); |
664 | | |
665 | | char width[16]; |
666 | | char height[16]; |
667 | | PrettyUC(aReflowInput.AvailableWidth(), width, 16); |
668 | | PrettyUC(aReflowInput.AvailableHeight(), height, 16); |
669 | | printf(" a=%s,%s ", width, height); |
670 | | PrettyUC(aReflowInput.ComputedWidth(), width, 16); |
671 | | PrettyUC(aReflowInput.ComputedHeight(), height, 16); |
672 | | printf("c=%s,%s \n", width, height); |
673 | | } |
674 | | AutoNoisyIndenter indent(nsBlockFrame::gNoisy); |
675 | | #endif // DEBUG |
676 | |
|
677 | 0 | WritingMode wm = aKidFrame->GetWritingMode(); |
678 | 0 | LogicalSize logicalCBSize(wm, aContainingBlock.Size()); |
679 | 0 | nscoord availISize = logicalCBSize.ISize(wm); |
680 | 0 | if (availISize == -1) { |
681 | 0 | NS_ASSERTION(aReflowInput.ComputedSize(wm).ISize(wm) != |
682 | 0 | NS_UNCONSTRAINEDSIZE, |
683 | 0 | "Must have a useful inline-size _somewhere_"); |
684 | 0 | availISize = |
685 | 0 | aReflowInput.ComputedSizeWithPadding(wm).ISize(wm); |
686 | 0 | } |
687 | 0 |
|
688 | 0 | uint32_t rsFlags = 0; |
689 | 0 | if (aFlags & AbsPosReflowFlags::eIsGridContainerCB) { |
690 | 0 | // When a grid container generates the abs.pos. CB for a *child* then |
691 | 0 | // the static position is determined via CSS Box Alignment within the |
692 | 0 | // abs.pos. CB (a grid area, i.e. a piece of the grid). In this scenario, |
693 | 0 | // due to the multiple coordinate spaces in play, we use a convenience flag |
694 | 0 | // to simply have the child's ReflowInput give it a static position at its |
695 | 0 | // abs.pos. CB origin, and then we'll align & offset it from there. |
696 | 0 | nsIFrame* placeholder = aKidFrame->GetPlaceholderFrame(); |
697 | 0 | if (placeholder && placeholder->GetParent() == aDelegatingFrame) { |
698 | 0 | rsFlags |= ReflowInput::STATIC_POS_IS_CB_ORIGIN; |
699 | 0 | } |
700 | 0 | } |
701 | 0 | ReflowInput kidReflowInput(aPresContext, aReflowInput, aKidFrame, |
702 | 0 | LogicalSize(wm, availISize, |
703 | 0 | NS_UNCONSTRAINEDSIZE), |
704 | 0 | &logicalCBSize, rsFlags); |
705 | 0 |
|
706 | 0 | // Get the border values |
707 | 0 | WritingMode outerWM = aReflowInput.GetWritingMode(); |
708 | 0 | const LogicalMargin border(outerWM, aDelegatingFrame->GetUsedBorder()); |
709 | 0 |
|
710 | 0 | LogicalMargin margin = |
711 | 0 | kidReflowInput.ComputedLogicalMargin().ConvertTo(outerWM, wm); |
712 | 0 |
|
713 | 0 | // If we're doing CSS Box Alignment in either axis, that will apply the |
714 | 0 | // margin for us in that axis (since the thing that's aligned is the margin |
715 | 0 | // box). So, we clear out the margin here to avoid applying it twice. |
716 | 0 | if (kidReflowInput.mFlags.mIOffsetsNeedCSSAlign) { |
717 | 0 | margin.IStart(outerWM) = margin.IEnd(outerWM) = 0; |
718 | 0 | } |
719 | 0 | if (kidReflowInput.mFlags.mBOffsetsNeedCSSAlign) { |
720 | 0 | margin.BStart(outerWM) = margin.BEnd(outerWM) = 0; |
721 | 0 | } |
722 | 0 |
|
723 | 0 | bool constrainBSize = (aReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE) |
724 | 0 | && (aFlags & AbsPosReflowFlags::eConstrainHeight) |
725 | 0 | // Don't split if told not to (e.g. for fixed frames) |
726 | 0 | && !aDelegatingFrame->IsInlineFrame() |
727 | 0 | //XXX we don't handle splitting frames for inline absolute containing blocks yet |
728 | 0 | && (aKidFrame->GetLogicalRect(aContainingBlock.Size()).BStart(wm) <= |
729 | 0 | aReflowInput.AvailableBSize()); |
730 | 0 | // Don't split things below the fold. (Ideally we shouldn't *have* |
731 | 0 | // anything totally below the fold, but we can't position frames |
732 | 0 | // across next-in-flow breaks yet. |
733 | 0 | if (constrainBSize) { |
734 | 0 | kidReflowInput.AvailableBSize() = |
735 | 0 | aReflowInput.AvailableBSize() - border.ConvertTo(wm, outerWM).BStart(wm) - |
736 | 0 | kidReflowInput.ComputedLogicalMargin().BStart(wm); |
737 | 0 | if (NS_AUTOOFFSET != kidReflowInput.ComputedLogicalOffsets().BStart(wm)) { |
738 | 0 | kidReflowInput.AvailableBSize() -= |
739 | 0 | kidReflowInput.ComputedLogicalOffsets().BStart(wm); |
740 | 0 | } |
741 | 0 | } |
742 | 0 |
|
743 | 0 | // Do the reflow |
744 | 0 | ReflowOutput kidDesiredSize(kidReflowInput); |
745 | 0 | aKidFrame->Reflow(aPresContext, kidDesiredSize, kidReflowInput, aStatus); |
746 | 0 |
|
747 | 0 | const LogicalSize kidSize = kidDesiredSize.Size(wm).ConvertTo(outerWM, wm); |
748 | 0 |
|
749 | 0 | LogicalMargin offsets = |
750 | 0 | kidReflowInput.ComputedLogicalOffsets().ConvertTo(outerWM, wm); |
751 | 0 |
|
752 | 0 | // If we're solving for start in either inline or block direction, |
753 | 0 | // then compute it now that we know the dimensions. |
754 | 0 | ResolveSizeDependentOffsets(aPresContext, kidReflowInput, kidSize, margin, |
755 | 0 | &offsets, &logicalCBSize); |
756 | 0 |
|
757 | 0 | // Position the child relative to our padding edge |
758 | 0 | LogicalRect rect(outerWM, |
759 | 0 | border.IStart(outerWM) + offsets.IStart(outerWM) + |
760 | 0 | margin.IStart(outerWM), |
761 | 0 | border.BStart(outerWM) + offsets.BStart(outerWM) + |
762 | 0 | margin.BStart(outerWM), |
763 | 0 | kidSize.ISize(outerWM), kidSize.BSize(outerWM)); |
764 | 0 | nsRect r = |
765 | 0 | rect.GetPhysicalRect(outerWM, logicalCBSize.GetPhysicalSize(wm) + |
766 | 0 | border.Size(outerWM).GetPhysicalSize(outerWM)); |
767 | 0 |
|
768 | 0 | // Offset the frame rect by the given origin of the absolute containing block. |
769 | 0 | // If the frame is auto-positioned on both sides of an axis, it will be |
770 | 0 | // positioned based on its containing block and we don't need to offset |
771 | 0 | // (unless the caller demands it (the STATIC_POS_IS_CB_ORIGIN case)). |
772 | 0 | if (aContainingBlock.TopLeft() != nsPoint(0, 0)) { |
773 | 0 | const nsStyleSides& offsets = kidReflowInput.mStylePosition->mOffset; |
774 | 0 | if (!(offsets.GetLeftUnit() == eStyleUnit_Auto && |
775 | 0 | offsets.GetRightUnit() == eStyleUnit_Auto) || |
776 | 0 | (rsFlags & ReflowInput::STATIC_POS_IS_CB_ORIGIN)) { |
777 | 0 | r.x += aContainingBlock.x; |
778 | 0 | } |
779 | 0 | if (!(offsets.GetTopUnit() == eStyleUnit_Auto && |
780 | 0 | offsets.GetBottomUnit() == eStyleUnit_Auto) || |
781 | 0 | (rsFlags & ReflowInput::STATIC_POS_IS_CB_ORIGIN)) { |
782 | 0 | r.y += aContainingBlock.y; |
783 | 0 | } |
784 | 0 | } |
785 | 0 |
|
786 | 0 | aKidFrame->SetRect(r); |
787 | 0 |
|
788 | 0 | nsView* view = aKidFrame->GetView(); |
789 | 0 | if (view) { |
790 | 0 | // Size and position the view and set its opacity, visibility, content |
791 | 0 | // transparency, and clip |
792 | 0 | nsContainerFrame::SyncFrameViewAfterReflow(aPresContext, aKidFrame, view, |
793 | 0 | kidDesiredSize.VisualOverflow()); |
794 | 0 | } else { |
795 | 0 | nsContainerFrame::PositionChildViews(aKidFrame); |
796 | 0 | } |
797 | 0 |
|
798 | 0 | aKidFrame->DidReflow(aPresContext, &kidReflowInput); |
799 | 0 |
|
800 | | #ifdef DEBUG |
801 | | if (nsBlockFrame::gNoisyReflow) { |
802 | | nsFrame::IndentBy(stdout,nsBlockFrame::gNoiseIndent - 1); |
803 | | printf("abs pos "); |
804 | | nsAutoString name; |
805 | | aKidFrame->GetFrameName(name); |
806 | | printf("%s ", NS_LossyConvertUTF16toASCII(name).get()); |
807 | | printf("%p rect=%d,%d,%d,%d\n", static_cast<void*>(aKidFrame), |
808 | | r.x, r.y, r.width, r.height); |
809 | | } |
810 | | #endif |
811 | |
|
812 | 0 | if (aOverflowAreas) { |
813 | 0 | aOverflowAreas->UnionWith(kidDesiredSize.mOverflowAreas + r.TopLeft()); |
814 | 0 | } |
815 | 0 | } |