/src/mozilla-central/layout/generic/ReflowInput.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 | | /* struct containing the input to nsIFrame::Reflow */ |
8 | | |
9 | | #include "mozilla/ReflowInput.h" |
10 | | |
11 | | #include "LayoutLogging.h" |
12 | | #include "nsStyleConsts.h" |
13 | | #include "nsCSSAnonBoxes.h" |
14 | | #include "nsFrame.h" |
15 | | #include "nsIContent.h" |
16 | | #include "nsGkAtoms.h" |
17 | | #include "nsPresContext.h" |
18 | | #include "nsIPresShell.h" |
19 | | #include "nsFontMetrics.h" |
20 | | #include "nsBlockFrame.h" |
21 | | #include "nsLineBox.h" |
22 | | #include "nsImageFrame.h" |
23 | | #include "nsTableFrame.h" |
24 | | #include "nsTableCellFrame.h" |
25 | | #include "nsIPercentBSizeObserver.h" |
26 | | #include "nsLayoutUtils.h" |
27 | | #include "mozilla/Preferences.h" |
28 | | #include "nsFontInflationData.h" |
29 | | #include "StickyScrollContainer.h" |
30 | | #include "nsIFrameInlines.h" |
31 | | #include "CounterStyleManager.h" |
32 | | #include <algorithm> |
33 | | #include "mozilla/dom/HTMLInputElement.h" |
34 | | |
35 | | #ifdef DEBUG |
36 | | #undef NOISY_VERTICAL_ALIGN |
37 | | #else |
38 | | #undef NOISY_VERTICAL_ALIGN |
39 | | #endif |
40 | | |
41 | | using namespace mozilla; |
42 | | using namespace mozilla::css; |
43 | | using namespace mozilla::dom; |
44 | | using namespace mozilla::layout; |
45 | | |
46 | | enum eNormalLineHeightControl { |
47 | | eUninitialized = -1, |
48 | | eNoExternalLeading = 0, // does not include external leading |
49 | | eIncludeExternalLeading, // use whatever value font vendor provides |
50 | | eCompensateLeading // compensate leading if leading provided by font vendor is not enough |
51 | | }; |
52 | | |
53 | | static eNormalLineHeightControl sNormalLineHeightControl = eUninitialized; |
54 | | |
55 | | // Initialize a <b>root</b> reflow state with a rendering context to |
56 | | // use for measuring things. |
57 | | ReflowInput::ReflowInput(nsPresContext* aPresContext, |
58 | | nsIFrame* aFrame, |
59 | | gfxContext* aRenderingContext, |
60 | | const LogicalSize& aAvailableSpace, |
61 | | uint32_t aFlags) |
62 | | : SizeComputationInput(aFrame, aRenderingContext) |
63 | | , mCBReflowInput(nullptr) // will be setup properly later in InitCBReflowInput |
64 | | , mBlockDelta(0) |
65 | | , mOrthogonalLimit(NS_UNCONSTRAINEDSIZE) |
66 | | , mAvailableWidth(0) |
67 | | , mAvailableHeight(0) |
68 | | , mContainingBlockSize(mWritingMode) |
69 | | , mReflowDepth(0) |
70 | 0 | { |
71 | 0 | MOZ_ASSERT(aRenderingContext, "no rendering context"); |
72 | 0 | MOZ_ASSERT(aPresContext, "no pres context"); |
73 | 0 | MOZ_ASSERT(aFrame, "no frame"); |
74 | 0 | MOZ_ASSERT(aPresContext == aFrame->PresContext(), "wrong pres context"); |
75 | 0 | mParentReflowInput = nullptr; |
76 | 0 | AvailableISize() = aAvailableSpace.ISize(mWritingMode); |
77 | 0 | AvailableBSize() = aAvailableSpace.BSize(mWritingMode); |
78 | 0 | mFloatManager = nullptr; |
79 | 0 | mLineLayout = nullptr; |
80 | 0 | mDiscoveredClearance = nullptr; |
81 | 0 | mPercentBSizeObserver = nullptr; |
82 | 0 |
|
83 | 0 | if (aFlags & DUMMY_PARENT_REFLOW_STATE) { |
84 | 0 | mFlags.mDummyParentReflowInput = true; |
85 | 0 | } |
86 | 0 | if (aFlags & COMPUTE_SIZE_SHRINK_WRAP) { |
87 | 0 | mFlags.mShrinkWrap = true; |
88 | 0 | } |
89 | 0 | if (aFlags & COMPUTE_SIZE_USE_AUTO_BSIZE) { |
90 | 0 | mFlags.mUseAutoBSize = true; |
91 | 0 | } |
92 | 0 | if (aFlags & STATIC_POS_IS_CB_ORIGIN) { |
93 | 0 | mFlags.mStaticPosIsCBOrigin = true; |
94 | 0 | } |
95 | 0 | if (aFlags & I_CLAMP_MARGIN_BOX_MIN_SIZE) { |
96 | 0 | mFlags.mIClampMarginBoxMinSize = true; |
97 | 0 | } |
98 | 0 | if (aFlags & B_CLAMP_MARGIN_BOX_MIN_SIZE) { |
99 | 0 | mFlags.mBClampMarginBoxMinSize = true; |
100 | 0 | } |
101 | 0 | if (aFlags & I_APPLY_AUTO_MIN_SIZE) { |
102 | 0 | mFlags.mApplyAutoMinSize = true; |
103 | 0 | } |
104 | 0 |
|
105 | 0 | if (!(aFlags & CALLER_WILL_INIT)) { |
106 | 0 | Init(aPresContext); |
107 | 0 | } |
108 | 0 | } |
109 | | |
110 | | static bool CheckNextInFlowParenthood(nsIFrame* aFrame, nsIFrame* aParent) |
111 | 0 | { |
112 | 0 | nsIFrame* frameNext = aFrame->GetNextInFlow(); |
113 | 0 | nsIFrame* parentNext = aParent->GetNextInFlow(); |
114 | 0 | return frameNext && parentNext && frameNext->GetParent() == parentNext; |
115 | 0 | } |
116 | | |
117 | | /** |
118 | | * Adjusts the margin for a list (ol, ul), if necessary, depending on |
119 | | * font inflation settings. Unfortunately, because bullets from a list are |
120 | | * placed in the margin area, we only have ~40px in which to place the |
121 | | * bullets. When they are inflated, however, this causes problems, since |
122 | | * the text takes up more space than is available in the margin. |
123 | | * |
124 | | * This method will return a small amount (in app units) by which the |
125 | | * margin can be adjusted, so that the space is available for list |
126 | | * bullets to be rendered with font inflation enabled. |
127 | | */ |
128 | | static nscoord |
129 | | FontSizeInflationListMarginAdjustment(const nsIFrame* aFrame) |
130 | 0 | { |
131 | 0 | float inflation = nsLayoutUtils::FontSizeInflationFor(aFrame); |
132 | 0 | if (aFrame->IsFrameOfType(nsIFrame::eBlockFrame)) { |
133 | 0 | const nsBlockFrame* blockFrame = static_cast<const nsBlockFrame*>(aFrame); |
134 | 0 |
|
135 | 0 | // We only want to adjust the margins if we're dealing with an ordered |
136 | 0 | // list. |
137 | 0 | if (inflation > 1.0f && |
138 | 0 | blockFrame->HasBullet() && |
139 | 0 | inflation > 1.0f) { |
140 | 0 |
|
141 | 0 | auto listStyleType = aFrame->StyleList()->mCounterStyle->GetStyle(); |
142 | 0 | if (listStyleType != NS_STYLE_LIST_STYLE_NONE && |
143 | 0 | listStyleType != NS_STYLE_LIST_STYLE_DISC && |
144 | 0 | listStyleType != NS_STYLE_LIST_STYLE_CIRCLE && |
145 | 0 | listStyleType != NS_STYLE_LIST_STYLE_SQUARE && |
146 | 0 | listStyleType != NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED && |
147 | 0 | listStyleType != NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN) { |
148 | 0 | // The HTML spec states that the default padding for ordered lists |
149 | 0 | // begins at 40px, indicating that we have 40px of space to place a |
150 | 0 | // bullet. When performing font inflation calculations, we add space |
151 | 0 | // equivalent to this, but simply inflated at the same amount as the |
152 | 0 | // text, in app units. |
153 | 0 | return nsPresContext::CSSPixelsToAppUnits(40) * (inflation - 1); |
154 | 0 | } |
155 | 0 | |
156 | 0 | } |
157 | 0 | } |
158 | 0 | |
159 | 0 | return 0; |
160 | 0 | } |
161 | | |
162 | | SizeComputationInput::SizeComputationInput(nsIFrame *aFrame, |
163 | | gfxContext *aRenderingContext, |
164 | | WritingMode aContainingBlockWritingMode, |
165 | | nscoord aContainingBlockISize) |
166 | | : mFrame(aFrame) |
167 | | , mRenderingContext(aRenderingContext) |
168 | | , mWritingMode(aFrame->GetWritingMode()) |
169 | 0 | { |
170 | 0 | ReflowInputFlags flags; |
171 | 0 | InitOffsets(aContainingBlockWritingMode, aContainingBlockISize, |
172 | 0 | mFrame->Type(), flags); |
173 | 0 | } |
174 | | |
175 | | // Initialize a reflow state for a child frame's reflow. Some state |
176 | | // is copied from the parent reflow state; the remaining state is |
177 | | // computed. |
178 | | ReflowInput::ReflowInput( |
179 | | nsPresContext* aPresContext, |
180 | | const ReflowInput& aParentReflowInput, |
181 | | nsIFrame* aFrame, |
182 | | const LogicalSize& aAvailableSpace, |
183 | | const LogicalSize* aContainingBlockSize, |
184 | | uint32_t aFlags) |
185 | | : SizeComputationInput(aFrame, aParentReflowInput.mRenderingContext) |
186 | | , mCBReflowInput(nullptr) // will be setup properly later in InitCBReflowInput |
187 | | , mBlockDelta(0) |
188 | | , mOrthogonalLimit(NS_UNCONSTRAINEDSIZE) |
189 | | , mAvailableWidth(0) |
190 | | , mAvailableHeight(0) |
191 | | , mContainingBlockSize(mWritingMode) |
192 | | , mFlags(aParentReflowInput.mFlags) |
193 | | , mReflowDepth(aParentReflowInput.mReflowDepth + 1) |
194 | 0 | { |
195 | 0 | MOZ_ASSERT(aPresContext, "no pres context"); |
196 | 0 | MOZ_ASSERT(aFrame, "no frame"); |
197 | 0 | MOZ_ASSERT(aPresContext == aFrame->PresContext(), "wrong pres context"); |
198 | 0 | MOZ_ASSERT(!mFlags.mSpecialBSizeReflow || |
199 | 0 | !NS_SUBTREE_DIRTY(aFrame), |
200 | 0 | "frame should be clean when getting special bsize reflow"); |
201 | 0 |
|
202 | 0 | mParentReflowInput = &aParentReflowInput; |
203 | 0 |
|
204 | 0 | AvailableISize() = aAvailableSpace.ISize(mWritingMode); |
205 | 0 | AvailableBSize() = aAvailableSpace.BSize(mWritingMode); |
206 | 0 |
|
207 | 0 | if (mWritingMode.IsOrthogonalTo(aParentReflowInput.GetWritingMode())) { |
208 | 0 | // If we're setting up for an orthogonal flow, and the parent reflow state |
209 | 0 | // had a constrained ComputedBSize, we can use that as our AvailableISize |
210 | 0 | // in preference to leaving it unconstrained. |
211 | 0 | if (AvailableISize() == NS_UNCONSTRAINEDSIZE && |
212 | 0 | aParentReflowInput.ComputedBSize() != NS_UNCONSTRAINEDSIZE) { |
213 | 0 | AvailableISize() = aParentReflowInput.ComputedBSize(); |
214 | 0 | } |
215 | 0 | } |
216 | 0 |
|
217 | 0 | mFloatManager = aParentReflowInput.mFloatManager; |
218 | 0 | if (mFrame->IsFrameOfType(nsIFrame::eLineParticipant)) |
219 | 0 | mLineLayout = aParentReflowInput.mLineLayout; |
220 | 0 | else |
221 | 0 | mLineLayout = nullptr; |
222 | 0 |
|
223 | 0 | // Note: mFlags was initialized as a copy of aParentReflowInput.mFlags up in |
224 | 0 | // this constructor's init list, so the only flags that we need to explicitly |
225 | 0 | // initialize here are those that may need a value other than our parent's. |
226 | 0 | mFlags.mNextInFlowUntouched = aParentReflowInput.mFlags.mNextInFlowUntouched && |
227 | 0 | CheckNextInFlowParenthood(aFrame, aParentReflowInput.mFrame); |
228 | 0 | mFlags.mAssumingHScrollbar = mFlags.mAssumingVScrollbar = false; |
229 | 0 | mFlags.mIsColumnBalancing = false; |
230 | 0 | mFlags.mIsFlexContainerMeasuringBSize = false; |
231 | 0 | mFlags.mDummyParentReflowInput = false; |
232 | 0 | mFlags.mShrinkWrap = !!(aFlags & COMPUTE_SIZE_SHRINK_WRAP); |
233 | 0 | mFlags.mUseAutoBSize = !!(aFlags & COMPUTE_SIZE_USE_AUTO_BSIZE); |
234 | 0 | mFlags.mStaticPosIsCBOrigin = !!(aFlags & STATIC_POS_IS_CB_ORIGIN); |
235 | 0 | mFlags.mIOffsetsNeedCSSAlign = mFlags.mBOffsetsNeedCSSAlign = false; |
236 | 0 | mFlags.mIClampMarginBoxMinSize = !!(aFlags & I_CLAMP_MARGIN_BOX_MIN_SIZE); |
237 | 0 | mFlags.mBClampMarginBoxMinSize = !!(aFlags & B_CLAMP_MARGIN_BOX_MIN_SIZE); |
238 | 0 | mFlags.mApplyAutoMinSize = !!(aFlags & I_APPLY_AUTO_MIN_SIZE); |
239 | 0 |
|
240 | 0 | mDiscoveredClearance = nullptr; |
241 | 0 | mPercentBSizeObserver = (aParentReflowInput.mPercentBSizeObserver && |
242 | 0 | aParentReflowInput.mPercentBSizeObserver->NeedsToObserve(*this)) |
243 | 0 | ? aParentReflowInput.mPercentBSizeObserver : nullptr; |
244 | 0 |
|
245 | 0 | if ((aFlags & DUMMY_PARENT_REFLOW_STATE) || |
246 | 0 | (mParentReflowInput->mFlags.mDummyParentReflowInput && |
247 | 0 | mFrame->IsTableFrame())) { |
248 | 0 | mFlags.mDummyParentReflowInput = true; |
249 | 0 | } |
250 | 0 |
|
251 | 0 | if (!(aFlags & CALLER_WILL_INIT)) { |
252 | 0 | Init(aPresContext, aContainingBlockSize); |
253 | 0 | } |
254 | 0 | } |
255 | | |
256 | | inline nscoord |
257 | | SizeComputationInput::ComputeISizeValue(nscoord aContainingBlockISize, |
258 | | nscoord aContentEdgeToBoxSizing, |
259 | | nscoord aBoxSizingToMarginEdge, |
260 | | const nsStyleCoord& aCoord) const |
261 | 0 | { |
262 | 0 | return mFrame->ComputeISizeValue(mRenderingContext, |
263 | 0 | aContainingBlockISize, |
264 | 0 | aContentEdgeToBoxSizing, |
265 | 0 | aBoxSizingToMarginEdge, |
266 | 0 | aCoord); |
267 | 0 | } |
268 | | |
269 | | nscoord |
270 | | SizeComputationInput::ComputeISizeValue(nscoord aContainingBlockISize, |
271 | | StyleBoxSizing aBoxSizing, |
272 | | const nsStyleCoord& aCoord) const |
273 | 0 | { |
274 | 0 | WritingMode wm = GetWritingMode(); |
275 | 0 | nscoord inside = 0, outside = ComputedLogicalBorderPadding().IStartEnd(wm) + |
276 | 0 | ComputedLogicalMargin().IStartEnd(wm); |
277 | 0 | if (aBoxSizing == StyleBoxSizing::Border) { |
278 | 0 | inside = ComputedLogicalBorderPadding().IStartEnd(wm); |
279 | 0 | } |
280 | 0 | outside -= inside; |
281 | 0 |
|
282 | 0 | return ComputeISizeValue(aContainingBlockISize, inside, |
283 | 0 | outside, aCoord); |
284 | 0 | } |
285 | | |
286 | | nscoord |
287 | | SizeComputationInput::ComputeBSizeValue(nscoord aContainingBlockBSize, |
288 | | StyleBoxSizing aBoxSizing, |
289 | | const nsStyleCoord& aCoord) const |
290 | 0 | { |
291 | 0 | WritingMode wm = GetWritingMode(); |
292 | 0 | nscoord inside = 0; |
293 | 0 | if (aBoxSizing == StyleBoxSizing::Border) { |
294 | 0 | inside = ComputedLogicalBorderPadding().BStartEnd(wm); |
295 | 0 | } |
296 | 0 | return nsLayoutUtils::ComputeBSizeValue(aContainingBlockBSize, |
297 | 0 | inside, aCoord); |
298 | 0 | } |
299 | | |
300 | | void |
301 | | ReflowInput::SetComputedWidth(nscoord aComputedWidth) |
302 | 0 | { |
303 | 0 | NS_ASSERTION(mFrame, "Must have a frame!"); |
304 | 0 | // It'd be nice to assert that |frame| is not in reflow, but this fails for |
305 | 0 | // two reasons: |
306 | 0 | // |
307 | 0 | // 1) Viewport frames reset the computed width on a copy of their reflow |
308 | 0 | // state when reflowing fixed-pos kids. In that case we actually don't |
309 | 0 | // want to mess with the resize flags, because comparing the frame's rect |
310 | 0 | // to the munged computed width is pointless. |
311 | 0 | // 2) nsFrame::BoxReflow creates a reflow state for its parent. This reflow |
312 | 0 | // state is not used to reflow the parent, but just as a parent for the |
313 | 0 | // frame's own reflow state. So given a nsBoxFrame inside some non-XUL |
314 | 0 | // (like a text control, for example), we'll end up creating a reflow |
315 | 0 | // state for the parent while the parent is reflowing. |
316 | 0 |
|
317 | 0 | MOZ_ASSERT(aComputedWidth >= 0, "Invalid computed width"); |
318 | 0 | if (ComputedWidth() != aComputedWidth) { |
319 | 0 | ComputedWidth() = aComputedWidth; |
320 | 0 | LayoutFrameType frameType = mFrame->Type(); |
321 | 0 | if (frameType != LayoutFrameType::Viewport || // Or check GetParent()? |
322 | 0 | mWritingMode.IsVertical()) { |
323 | 0 | InitResizeFlags(mFrame->PresContext(), frameType); |
324 | 0 | } |
325 | 0 | } |
326 | 0 | } |
327 | | |
328 | | void |
329 | | ReflowInput::SetComputedHeight(nscoord aComputedHeight) |
330 | 0 | { |
331 | 0 | NS_ASSERTION(mFrame, "Must have a frame!"); |
332 | 0 | // It'd be nice to assert that |frame| is not in reflow, but this fails |
333 | 0 | // because: |
334 | 0 | // |
335 | 0 | // nsFrame::BoxReflow creates a reflow state for its parent. This reflow |
336 | 0 | // state is not used to reflow the parent, but just as a parent for the |
337 | 0 | // frame's own reflow state. So given a nsBoxFrame inside some non-XUL |
338 | 0 | // (like a text control, for example), we'll end up creating a reflow |
339 | 0 | // state for the parent while the parent is reflowing. |
340 | 0 |
|
341 | 0 | MOZ_ASSERT(aComputedHeight >= 0, "Invalid computed height"); |
342 | 0 | if (ComputedHeight() != aComputedHeight) { |
343 | 0 | ComputedHeight() = aComputedHeight; |
344 | 0 | LayoutFrameType frameType = mFrame->Type(); |
345 | 0 | if (frameType != LayoutFrameType::Viewport || !mWritingMode.IsVertical()) { |
346 | 0 | InitResizeFlags(mFrame->PresContext(), frameType); |
347 | 0 | } |
348 | 0 | } |
349 | 0 | } |
350 | | |
351 | | void |
352 | | ReflowInput::Init(nsPresContext* aPresContext, |
353 | | const LogicalSize* aContainingBlockSize, |
354 | | const nsMargin* aBorder, |
355 | | const nsMargin* aPadding) |
356 | 0 | { |
357 | 0 | if ((mFrame->GetStateBits() & NS_FRAME_IS_DIRTY) && |
358 | 0 | !mFrame->IsXULBoxFrame()) { |
359 | 0 | // Mark all child frames as dirty. |
360 | 0 | // |
361 | 0 | // We don't do this for XUL boxes because they handle their child |
362 | 0 | // reflow separately. |
363 | 0 | // |
364 | 0 | // FIXME (bug 1376530): It would be better for memory locality if we |
365 | 0 | // did this as we went. However, we need to be careful not to do |
366 | 0 | // this twice for any particular child if we reflow it twice. The |
367 | 0 | // easiest way to accomplish that is to do it at the start. |
368 | 0 | for (nsIFrame::ChildListIterator childLists(mFrame); |
369 | 0 | !childLists.IsDone(); childLists.Next()) { |
370 | 0 | for (nsIFrame* childFrame : childLists.CurrentList()) { |
371 | 0 | if (!childFrame->IsTableColGroupFrame()) { |
372 | 0 | childFrame->AddStateBits(NS_FRAME_IS_DIRTY); |
373 | 0 | } |
374 | 0 | } |
375 | 0 | } |
376 | 0 | } |
377 | 0 |
|
378 | 0 | if (AvailableISize() == NS_UNCONSTRAINEDSIZE) { |
379 | 0 | // Look up the parent chain for an orthogonal inline limit, |
380 | 0 | // and reset AvailableISize() if found. |
381 | 0 | for (const ReflowInput *parent = mParentReflowInput; |
382 | 0 | parent != nullptr; parent = parent->mParentReflowInput) { |
383 | 0 | if (parent->GetWritingMode().IsOrthogonalTo(mWritingMode) && |
384 | 0 | parent->mOrthogonalLimit != NS_UNCONSTRAINEDSIZE) { |
385 | 0 | AvailableISize() = parent->mOrthogonalLimit; |
386 | 0 | break; |
387 | 0 | } |
388 | 0 | } |
389 | 0 | } |
390 | 0 |
|
391 | 0 | LAYOUT_WARN_IF_FALSE(AvailableISize() != NS_UNCONSTRAINEDSIZE, |
392 | 0 | "have unconstrained inline-size; this should only " |
393 | 0 | "result from very large sizes, not attempts at " |
394 | 0 | "intrinsic inline-size calculation"); |
395 | 0 |
|
396 | 0 | mStylePosition = mFrame->StylePosition(); |
397 | 0 | mStyleDisplay = mFrame->StyleDisplay(); |
398 | 0 | mStyleVisibility = mFrame->StyleVisibility(); |
399 | 0 | mStyleBorder = mFrame->StyleBorder(); |
400 | 0 | mStyleMargin = mFrame->StyleMargin(); |
401 | 0 | mStylePadding = mFrame->StylePadding(); |
402 | 0 | mStyleText = mFrame->StyleText(); |
403 | 0 |
|
404 | 0 | InitCBReflowInput(); |
405 | 0 |
|
406 | 0 | LayoutFrameType type = mFrame->Type(); |
407 | 0 | if (type == mozilla::LayoutFrameType::Placeholder) { |
408 | 0 | // Placeholders have a no-op Reflow method that doesn't need the rest of |
409 | 0 | // this initialization, so we bail out early. |
410 | 0 | ComputedBSize() = ComputedISize() = 0; |
411 | 0 | return; |
412 | 0 | } |
413 | 0 | |
414 | 0 | InitFrameType(type); |
415 | 0 |
|
416 | 0 | LogicalSize cbSize(mWritingMode, -1, -1); |
417 | 0 | if (aContainingBlockSize) { |
418 | 0 | cbSize = *aContainingBlockSize; |
419 | 0 | } |
420 | 0 |
|
421 | 0 | InitConstraints(aPresContext, cbSize, aBorder, aPadding, type); |
422 | 0 |
|
423 | 0 | InitResizeFlags(aPresContext, type); |
424 | 0 |
|
425 | 0 | nsIFrame *parent = mFrame->GetParent(); |
426 | 0 | if (parent && |
427 | 0 | (parent->GetStateBits() & NS_FRAME_IN_CONSTRAINED_BSIZE) && |
428 | 0 | !(parent->IsScrollFrame() && |
429 | 0 | parent->StyleDisplay()->mOverflowY != NS_STYLE_OVERFLOW_HIDDEN)) { |
430 | 0 | mFrame->AddStateBits(NS_FRAME_IN_CONSTRAINED_BSIZE); |
431 | 0 | } else if (type == LayoutFrameType::SVGForeignObject) { |
432 | 0 | // An SVG foreignObject frame is inherently constrained block-size. |
433 | 0 | mFrame->AddStateBits(NS_FRAME_IN_CONSTRAINED_BSIZE); |
434 | 0 | } else { |
435 | 0 | const nsStyleCoord& bSizeCoord = mStylePosition->BSize(mWritingMode); |
436 | 0 | const nsStyleCoord& maxBSizeCoord = mStylePosition->MaxBSize(mWritingMode); |
437 | 0 | if ((bSizeCoord.GetUnit() != eStyleUnit_Auto || |
438 | 0 | maxBSizeCoord.GetUnit() != eStyleUnit_None) && |
439 | 0 | // Don't set NS_FRAME_IN_CONSTRAINED_BSIZE on body or html elements. |
440 | 0 | (mFrame->GetContent() && |
441 | 0 | !(mFrame->GetContent()->IsAnyOfHTMLElements(nsGkAtoms::body, |
442 | 0 | nsGkAtoms::html)))) { |
443 | 0 |
|
444 | 0 | // If our block-size was specified as a percentage, then this could |
445 | 0 | // actually resolve to 'auto', based on: |
446 | 0 | // http://www.w3.org/TR/CSS21/visudet.html#the-height-property |
447 | 0 | nsIFrame* containingBlk = mFrame; |
448 | 0 | while (containingBlk) { |
449 | 0 | const nsStylePosition* stylePos = containingBlk->StylePosition(); |
450 | 0 | const nsStyleCoord& bSizeCoord = stylePos->BSize(mWritingMode); |
451 | 0 | const nsStyleCoord& maxBSizeCoord = stylePos->MaxBSize(mWritingMode); |
452 | 0 | if ((bSizeCoord.IsCoordPercentCalcUnit() && |
453 | 0 | !bSizeCoord.HasPercent()) || |
454 | 0 | (maxBSizeCoord.IsCoordPercentCalcUnit() && |
455 | 0 | !maxBSizeCoord.HasPercent())) { |
456 | 0 | mFrame->AddStateBits(NS_FRAME_IN_CONSTRAINED_BSIZE); |
457 | 0 | break; |
458 | 0 | } else if ((bSizeCoord.IsCoordPercentCalcUnit() && |
459 | 0 | bSizeCoord.HasPercent()) || |
460 | 0 | (maxBSizeCoord.IsCoordPercentCalcUnit() && |
461 | 0 | maxBSizeCoord.HasPercent())) { |
462 | 0 | if (!(containingBlk = containingBlk->GetContainingBlock())) { |
463 | 0 | // If we've reached the top of the tree, then we don't have |
464 | 0 | // a constrained block-size. |
465 | 0 | mFrame->RemoveStateBits(NS_FRAME_IN_CONSTRAINED_BSIZE); |
466 | 0 | break; |
467 | 0 | } |
468 | 0 | |
469 | 0 | continue; |
470 | 0 | } else { |
471 | 0 | mFrame->RemoveStateBits(NS_FRAME_IN_CONSTRAINED_BSIZE); |
472 | 0 | break; |
473 | 0 | } |
474 | 0 | } |
475 | 0 | } else { |
476 | 0 | mFrame->RemoveStateBits(NS_FRAME_IN_CONSTRAINED_BSIZE); |
477 | 0 | } |
478 | 0 | } |
479 | 0 |
|
480 | 0 | if (mParentReflowInput && |
481 | 0 | mParentReflowInput->GetWritingMode().IsOrthogonalTo(mWritingMode)) { |
482 | 0 | // Orthogonal frames are always reflowed with an unconstrained |
483 | 0 | // dimension to avoid incomplete reflow across an orthogonal |
484 | 0 | // boundary. Normally this is the block-size, but for column sets |
485 | 0 | // with auto-height it's the inline-size, so that they can add |
486 | 0 | // columns in the container's block direction |
487 | 0 | if (type == LayoutFrameType::ColumnSet && |
488 | 0 | eStyleUnit_Auto == mStylePosition->ISize(mWritingMode).GetUnit()) { |
489 | 0 | ComputedISize() = NS_UNCONSTRAINEDSIZE; |
490 | 0 | } else { |
491 | 0 | AvailableBSize() = NS_UNCONSTRAINEDSIZE; |
492 | 0 | } |
493 | 0 | } |
494 | 0 |
|
495 | 0 | if (mStyleDisplay->IsContainSize()) { |
496 | 0 | // In the case that a box is size contained, we want to ensure |
497 | 0 | // that it is also monolithic. We do this by unsetting |
498 | 0 | // AvailableBSize() to avoid fragmentaiton. |
499 | 0 | AvailableBSize() = NS_UNCONSTRAINEDSIZE; |
500 | 0 | } |
501 | 0 |
|
502 | 0 | LAYOUT_WARN_IF_FALSE((mFrameType == NS_CSS_FRAME_TYPE_INLINE && |
503 | 0 | !mFrame->IsFrameOfType(nsIFrame::eReplaced)) || |
504 | 0 | type == LayoutFrameType::Text || |
505 | 0 | ComputedISize() != NS_UNCONSTRAINEDSIZE, |
506 | 0 | "have unconstrained inline-size; this should only " |
507 | 0 | "result from very large sizes, not attempts at " |
508 | 0 | "intrinsic inline-size calculation"); |
509 | 0 | } |
510 | | |
511 | | void ReflowInput::InitCBReflowInput() |
512 | 0 | { |
513 | 0 | if (!mParentReflowInput) { |
514 | 0 | mCBReflowInput = nullptr; |
515 | 0 | return; |
516 | 0 | } |
517 | 0 | if (mParentReflowInput->mFlags.mDummyParentReflowInput) { |
518 | 0 | mCBReflowInput = mParentReflowInput; |
519 | 0 | return; |
520 | 0 | } |
521 | 0 | |
522 | 0 | if (mParentReflowInput->mFrame == mFrame->GetContainingBlock(0, mStyleDisplay)) { |
523 | 0 | // Inner table frames need to use the containing block of the outer |
524 | 0 | // table frame. |
525 | 0 | if (mFrame->IsTableFrame()) { |
526 | 0 | mCBReflowInput = mParentReflowInput->mCBReflowInput; |
527 | 0 | } else { |
528 | 0 | mCBReflowInput = mParentReflowInput; |
529 | 0 | } |
530 | 0 | } else { |
531 | 0 | mCBReflowInput = mParentReflowInput->mCBReflowInput; |
532 | 0 | } |
533 | 0 | } |
534 | | |
535 | | /* Check whether CalcQuirkContainingBlockHeight would stop on the |
536 | | * given reflow state, using its block as a height. (essentially |
537 | | * returns false for any case in which CalcQuirkContainingBlockHeight |
538 | | * has a "continue" in its main loop.) |
539 | | * |
540 | | * XXX Maybe refactor CalcQuirkContainingBlockHeight so it uses |
541 | | * this function as well |
542 | | */ |
543 | | static bool |
544 | | IsQuirkContainingBlockHeight(const ReflowInput* rs, LayoutFrameType aFrameType) |
545 | 0 | { |
546 | 0 | if (LayoutFrameType::Block == aFrameType || |
547 | 0 | #ifdef MOZ_XUL |
548 | 0 | LayoutFrameType::XULLabel == aFrameType || |
549 | 0 | #endif |
550 | 0 | LayoutFrameType::Scroll == aFrameType) { |
551 | 0 | // Note: This next condition could change due to a style change, |
552 | 0 | // but that would cause a style reflow anyway, which means we're ok. |
553 | 0 | if (NS_AUTOHEIGHT == rs->ComputedHeight()) { |
554 | 0 | if (!rs->mFrame->IsAbsolutelyPositioned(rs->mStyleDisplay)) { |
555 | 0 | return false; |
556 | 0 | } |
557 | 0 | } |
558 | 0 | } |
559 | 0 | return true; |
560 | 0 | } |
561 | | |
562 | | void |
563 | | ReflowInput::InitResizeFlags(nsPresContext* aPresContext, |
564 | | LayoutFrameType aFrameType) |
565 | 0 | { |
566 | 0 | SetBResize(false); |
567 | 0 | SetIResize(false); |
568 | 0 |
|
569 | 0 | const WritingMode wm = mWritingMode; // just a shorthand |
570 | 0 | // We should report that we have a resize in the inline dimension if |
571 | 0 | // *either* the border-box size or the content-box size in that |
572 | 0 | // dimension has changed. It might not actually be necessary to do |
573 | 0 | // this if the border-box size has changed and the content-box size |
574 | 0 | // has not changed, but since we've historically used the flag to mean |
575 | 0 | // border-box size change, continue to do that. (It's possible for |
576 | 0 | // the content-box size to change without a border-box size change or |
577 | 0 | // a style change given (1) a fixed width (possibly fixed by max-width |
578 | 0 | // or min-width), (2) box-sizing:border-box or padding-box, and |
579 | 0 | // (3) percentage padding.) |
580 | 0 | // |
581 | 0 | // However, we don't actually have the information at this point to |
582 | 0 | // tell whether the content-box size has changed, since both style |
583 | 0 | // data and the UsedPaddingProperty() have already been updated. So, |
584 | 0 | // instead, we explicitly check for the case where it's possible for |
585 | 0 | // the content-box size to have changed without either (a) a change in |
586 | 0 | // the border-box size or (b) an nsChangeHint_NeedDirtyReflow change |
587 | 0 | // hint due to change in border or padding. Thus we test using the |
588 | 0 | // conditions from the previous paragraph, except without testing (1) |
589 | 0 | // since it's complicated to test properly and less likely to help |
590 | 0 | // with optimizing cases away. |
591 | 0 | bool isIResize = |
592 | 0 | // is the border-box resizing? |
593 | 0 | mFrame->ISize(wm) != |
594 | 0 | ComputedISize() + ComputedLogicalBorderPadding().IStartEnd(wm) || |
595 | 0 | // or is the content-box resizing? (see comment above) |
596 | 0 | (mStylePosition->mBoxSizing != StyleBoxSizing::Content && |
597 | 0 | mStylePadding->IsWidthDependent()); |
598 | 0 |
|
599 | 0 | if ((mFrame->GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT) && |
600 | 0 | nsLayoutUtils::FontSizeInflationEnabled(aPresContext)) { |
601 | 0 | // Create our font inflation data if we don't have it already, and |
602 | 0 | // give it our current width information. |
603 | 0 | bool dirty = nsFontInflationData::UpdateFontInflationDataISizeFor(*this) && |
604 | 0 | // Avoid running this at the box-to-block interface |
605 | 0 | // (where we shouldn't be inflating anyway, and where |
606 | 0 | // reflow state construction is probably to construct a |
607 | 0 | // dummy parent reflow state anyway). |
608 | 0 | !mFlags.mDummyParentReflowInput; |
609 | 0 |
|
610 | 0 | if (dirty || (!mFrame->GetParent() && isIResize)) { |
611 | 0 | // When font size inflation is enabled, a change in either: |
612 | 0 | // * the effective width of a font inflation flow root |
613 | 0 | // * the width of the frame |
614 | 0 | // needs to cause a dirty reflow since they change the font size |
615 | 0 | // inflation calculations, which in turn change the size of text, |
616 | 0 | // line-heights, etc. This is relatively similar to a classic |
617 | 0 | // case of style change reflow, except that because inflation |
618 | 0 | // doesn't affect the intrinsic sizing codepath, there's no need |
619 | 0 | // to invalidate intrinsic sizes. |
620 | 0 | // |
621 | 0 | // Note that this makes horizontal resizing a good bit more |
622 | 0 | // expensive. However, font size inflation is targeted at a set of |
623 | 0 | // devices (zoom-and-pan devices) where the main use case for |
624 | 0 | // horizontal resizing needing to be efficient (window resizing) is |
625 | 0 | // not present. It does still increase the cost of dynamic changes |
626 | 0 | // caused by script where a style or content change in one place |
627 | 0 | // causes a resize in another (e.g., rebalancing a table). |
628 | 0 |
|
629 | 0 | // FIXME: This isn't so great for the cases where |
630 | 0 | // ReflowInput::SetComputedWidth is called, if the first time |
631 | 0 | // we go through InitResizeFlags we set IsHResize() to true, and then |
632 | 0 | // the second time we'd set it to false even without the |
633 | 0 | // NS_FRAME_IS_DIRTY bit already set. |
634 | 0 | if (mFrame->IsSVGForeignObjectFrame()) { |
635 | 0 | // Foreign object frames use dirty bits in a special way. |
636 | 0 | mFrame->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN); |
637 | 0 | nsIFrame *kid = mFrame->PrincipalChildList().FirstChild(); |
638 | 0 | if (kid) { |
639 | 0 | kid->AddStateBits(NS_FRAME_IS_DIRTY); |
640 | 0 | } |
641 | 0 | } else { |
642 | 0 | mFrame->AddStateBits(NS_FRAME_IS_DIRTY); |
643 | 0 | } |
644 | 0 |
|
645 | 0 | // Mark intrinsic widths on all descendants dirty. We need to do |
646 | 0 | // this (1) since we're changing the size of text and need to |
647 | 0 | // clear text runs on text frames and (2) since we actually are |
648 | 0 | // changing some intrinsic widths, but only those that live inside |
649 | 0 | // of containers. |
650 | 0 |
|
651 | 0 | // It makes sense to do this for descendants but not ancestors |
652 | 0 | // (which is unusual) because we're only changing the unusual |
653 | 0 | // inflation-dependent intrinsic widths (i.e., ones computed with |
654 | 0 | // nsPresContext::mInflationDisabledForShrinkWrap set to false), |
655 | 0 | // which should never affect anything outside of their inflation |
656 | 0 | // flow root (or, for that matter, even their inflation |
657 | 0 | // container). |
658 | 0 |
|
659 | 0 | // This is also different from what PresShell::FrameNeedsReflow |
660 | 0 | // does because it doesn't go through placeholders. It doesn't |
661 | 0 | // need to because we're actually doing something that cares about |
662 | 0 | // frame tree geometry (the width on an ancestor) rather than |
663 | 0 | // style. |
664 | 0 |
|
665 | 0 | AutoTArray<nsIFrame*, 32> stack; |
666 | 0 | stack.AppendElement(mFrame); |
667 | 0 |
|
668 | 0 | do { |
669 | 0 | nsIFrame *f = stack.PopLastElement(); |
670 | 0 |
|
671 | 0 | nsIFrame::ChildListIterator lists(f); |
672 | 0 | for (; !lists.IsDone(); lists.Next()) { |
673 | 0 | nsFrameList::Enumerator childFrames(lists.CurrentList()); |
674 | 0 | for (; !childFrames.AtEnd(); childFrames.Next()) { |
675 | 0 | nsIFrame* kid = childFrames.get(); |
676 | 0 | kid->MarkIntrinsicISizesDirty(); |
677 | 0 | stack.AppendElement(kid); |
678 | 0 | } |
679 | 0 | } |
680 | 0 | } while (stack.Length() != 0); |
681 | 0 | } |
682 | 0 | } |
683 | 0 |
|
684 | 0 | SetIResize(!(mFrame->GetStateBits() & NS_FRAME_IS_DIRTY) && |
685 | 0 | isIResize); |
686 | 0 |
|
687 | 0 | // XXX Should we really need to null check mCBReflowInput? (We do for |
688 | 0 | // at least nsBoxFrame). |
689 | 0 | if (IsTableCell(aFrameType) && |
690 | 0 | (mFlags.mSpecialBSizeReflow || |
691 | 0 | (mFrame->FirstInFlow()->GetStateBits() & |
692 | 0 | NS_TABLE_CELL_HAD_SPECIAL_REFLOW)) && |
693 | 0 | (mFrame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_BSIZE)) { |
694 | 0 | // Need to set the bit on the cell so that |
695 | 0 | // mCBReflowInput->IsBResize() is set correctly below when |
696 | 0 | // reflowing descendant. |
697 | 0 | SetBResize(true); |
698 | 0 | } else if (mCBReflowInput && mFrame->IsBlockWrapper()) { |
699 | 0 | // XXX Is this problematic for relatively positioned inlines acting |
700 | 0 | // as containing block for absolutely positioned elements? |
701 | 0 | // Possibly; in that case we should at least be checking |
702 | 0 | // NS_SUBTREE_DIRTY, I'd think. |
703 | 0 | SetBResize(mCBReflowInput->IsBResizeForWM(wm)); |
704 | 0 | } else if (mCBReflowInput && !nsLayoutUtils::GetAsBlock(mFrame)) { |
705 | 0 | // Some non-block frames (e.g. table frames) aggressively optimize out their |
706 | 0 | // BSize recomputation when they don't have the BResize flag set. This |
707 | 0 | // means that if they go from having a computed non-auto height to having an |
708 | 0 | // auto height and don't have that flag set, they will not actually compute |
709 | 0 | // their auto height and will just remain at whatever size they already |
710 | 0 | // were. We can end up in that situation if the child has a percentage |
711 | 0 | // specified height and the parent changes from non-auto height to auto |
712 | 0 | // height. When that happens, the parent will typically have the BResize |
713 | 0 | // flag set, and we want to propagate that flag to the kid. |
714 | 0 | // |
715 | 0 | // Ideally it seems like we'd do this for blocks too, of course... but we'd |
716 | 0 | // really want to restrict it to the percentage height case or something, to |
717 | 0 | // avoid extra reflows in common cases. Maybe we should be examining |
718 | 0 | // mStylePosition->BSize(wm).GetUnit() for that purpose? |
719 | 0 | // |
720 | 0 | // Note that we _also_ need to set the BResize flag if we have auto |
721 | 0 | // ComputedBSize() and a dirty subtree, since that might require us to |
722 | 0 | // change BSize due to kids having been added or removed. |
723 | 0 | SetBResize(mCBReflowInput->IsBResizeForWM(wm)); |
724 | 0 | if (ComputedBSize() == NS_AUTOHEIGHT) { |
725 | 0 | SetBResize(IsBResize() || NS_SUBTREE_DIRTY(mFrame)); |
726 | 0 | } |
727 | 0 | } else if (ComputedBSize() == NS_AUTOHEIGHT) { |
728 | 0 | if (eCompatibility_NavQuirks == aPresContext->CompatibilityMode() && |
729 | 0 | mCBReflowInput) { |
730 | 0 | SetBResize(mCBReflowInput->IsBResizeForWM(wm)); |
731 | 0 | } else { |
732 | 0 | SetBResize(IsIResize()); |
733 | 0 | } |
734 | 0 | SetBResize(IsBResize() || NS_SUBTREE_DIRTY(mFrame)); |
735 | 0 | } else { |
736 | 0 | // not 'auto' block-size |
737 | 0 | SetBResize(mFrame->BSize(wm) != |
738 | 0 | ComputedBSize() + ComputedLogicalBorderPadding().BStartEnd(wm)); |
739 | 0 | } |
740 | 0 |
|
741 | 0 | bool dependsOnCBBSize = |
742 | 0 | (mStylePosition->BSizeDependsOnContainer(wm) && |
743 | 0 | // FIXME: condition this on not-abspos? |
744 | 0 | mStylePosition->BSize(wm).GetUnit() != eStyleUnit_Auto) || |
745 | 0 | mStylePosition->MinBSizeDependsOnContainer(wm) || |
746 | 0 | mStylePosition->MaxBSizeDependsOnContainer(wm) || |
747 | 0 | mStylePosition->OffsetHasPercent(wm.PhysicalSide(eLogicalSideBStart)) || |
748 | 0 | mStylePosition->mOffset.GetBEndUnit(wm) != eStyleUnit_Auto || |
749 | 0 | mFrame->IsXULBoxFrame(); |
750 | 0 |
|
751 | 0 | if (mStyleText->mLineHeight.GetUnit() == eStyleUnit_Enumerated) { |
752 | 0 | NS_ASSERTION(mStyleText->mLineHeight.GetIntValue() == |
753 | 0 | NS_STYLE_LINE_HEIGHT_BLOCK_HEIGHT, |
754 | 0 | "bad line-height value"); |
755 | 0 |
|
756 | 0 | // line-height depends on block bsize |
757 | 0 | mFrame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE); |
758 | 0 | // but only on containing blocks if this frame is not a suitable block |
759 | 0 | dependsOnCBBSize |= !nsLayoutUtils::IsNonWrapperBlock(mFrame); |
760 | 0 | } |
761 | 0 |
|
762 | 0 | // If we're the descendant of a table cell that performs special bsize |
763 | 0 | // reflows and we could be the child that requires them, always set |
764 | 0 | // the block-axis resize in case this is the first pass before the |
765 | 0 | // special bsize reflow. However, don't do this if it actually is |
766 | 0 | // the special bsize reflow, since in that case it will already be |
767 | 0 | // set correctly above if we need it set. |
768 | 0 | if (!IsBResize() && mCBReflowInput && |
769 | 0 | (IsTableCell(mCBReflowInput->mFrame->Type()) || |
770 | 0 | mCBReflowInput->mFlags.mHeightDependsOnAncestorCell) && |
771 | 0 | !mCBReflowInput->mFlags.mSpecialBSizeReflow && |
772 | 0 | dependsOnCBBSize) { |
773 | 0 | SetBResize(true); |
774 | 0 | mFlags.mHeightDependsOnAncestorCell = true; |
775 | 0 | } |
776 | 0 |
|
777 | 0 | // Set NS_FRAME_CONTAINS_RELATIVE_BSIZE if it's needed. |
778 | 0 |
|
779 | 0 | // It would be nice to check that |ComputedBSize != NS_AUTOHEIGHT| |
780 | 0 | // &&ed with the percentage bsize check. However, this doesn't get |
781 | 0 | // along with table special bsize reflows, since a special bsize |
782 | 0 | // reflow (a quirk that makes such percentage height work on children |
783 | 0 | // of table cells) can cause not just a single percentage height to |
784 | 0 | // become fixed, but an entire descendant chain of percentage height |
785 | 0 | // to become fixed. |
786 | 0 | if (dependsOnCBBSize && mCBReflowInput) { |
787 | 0 | const ReflowInput *rs = this; |
788 | 0 | bool hitCBReflowInput = false; |
789 | 0 | do { |
790 | 0 | rs = rs->mParentReflowInput; |
791 | 0 | if (!rs) { |
792 | 0 | break; |
793 | 0 | } |
794 | 0 | |
795 | 0 | if (rs->mFrame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_BSIZE) { |
796 | 0 | break; // no need to go further |
797 | 0 | } |
798 | 0 | rs->mFrame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE); |
799 | 0 |
|
800 | 0 | // Keep track of whether we've hit the containing block, because |
801 | 0 | // we need to go at least that far. |
802 | 0 | if (rs == mCBReflowInput) { |
803 | 0 | hitCBReflowInput = true; |
804 | 0 | } |
805 | 0 |
|
806 | 0 | // XXX What about orthogonal flows? It doesn't make sense to |
807 | 0 | // keep propagating this bit across an orthogonal boundary, |
808 | 0 | // where the meaning of BSize changes. Bug 1175517. |
809 | 0 | } while (!hitCBReflowInput || |
810 | 0 | (eCompatibility_NavQuirks == aPresContext->CompatibilityMode() && |
811 | 0 | !IsQuirkContainingBlockHeight(rs, rs->mFrame->Type()))); |
812 | 0 | // Note: We actually don't need to set the |
813 | 0 | // NS_FRAME_CONTAINS_RELATIVE_BSIZE bit for the cases |
814 | 0 | // where we hit the early break statements in |
815 | 0 | // CalcQuirkContainingBlockHeight. But it doesn't hurt |
816 | 0 | // us to set the bit in these cases. |
817 | 0 |
|
818 | 0 | } |
819 | 0 | if (mFrame->GetStateBits() & NS_FRAME_IS_DIRTY) { |
820 | 0 | // If we're reflowing everything, then we'll find out if we need |
821 | 0 | // to re-set this. |
822 | 0 | mFrame->RemoveStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE); |
823 | 0 | } |
824 | 0 | } |
825 | | |
826 | | nscoord |
827 | | ReflowInput::GetContainingBlockContentISize(WritingMode aWritingMode) const |
828 | 0 | { |
829 | 0 | if (!mCBReflowInput) { |
830 | 0 | return 0; |
831 | 0 | } |
832 | 0 | return mCBReflowInput->GetWritingMode().IsOrthogonalTo(aWritingMode) |
833 | 0 | ? mCBReflowInput->ComputedBSize() |
834 | 0 | : mCBReflowInput->ComputedISize(); |
835 | 0 | } |
836 | | |
837 | | void |
838 | | ReflowInput::InitFrameType(LayoutFrameType aFrameType) |
839 | 0 | { |
840 | 0 | const nsStyleDisplay *disp = mStyleDisplay; |
841 | 0 | nsCSSFrameType frameType; |
842 | 0 |
|
843 | 0 | DISPLAY_INIT_TYPE(mFrame, this); |
844 | 0 |
|
845 | 0 | if (aFrameType == LayoutFrameType::Table) { |
846 | 0 | mFrameType = NS_CSS_FRAME_TYPE_BLOCK; |
847 | 0 | return; |
848 | 0 | } |
849 | 0 |
|
850 | 0 | NS_ASSERTION(mFrame->StyleDisplay()->IsAbsolutelyPositionedStyle() == |
851 | 0 | disp->IsAbsolutelyPositionedStyle(), |
852 | 0 | "Unexpected position style"); |
853 | 0 | NS_ASSERTION(mFrame->StyleDisplay()->IsFloatingStyle() == |
854 | 0 | disp->IsFloatingStyle(), "Unexpected float style"); |
855 | 0 | if (mFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) { |
856 | 0 | if (disp->IsAbsolutelyPositioned(mFrame)) { |
857 | 0 | frameType = NS_CSS_FRAME_TYPE_ABSOLUTE; |
858 | 0 | //XXXfr hack for making frames behave properly when in overflow container lists |
859 | 0 | // see bug 154892; need to revisit later |
860 | 0 | if (mFrame->GetPrevInFlow()) |
861 | 0 | frameType = NS_CSS_FRAME_TYPE_BLOCK; |
862 | 0 | } |
863 | 0 | else if (disp->IsFloating(mFrame)) { |
864 | 0 | frameType = NS_CSS_FRAME_TYPE_FLOATING; |
865 | 0 | } else { |
866 | 0 | NS_ASSERTION(disp->mDisplay == StyleDisplay::MozPopup, |
867 | 0 | "unknown out of flow frame type"); |
868 | 0 | frameType = NS_CSS_FRAME_TYPE_UNKNOWN; |
869 | 0 | } |
870 | 0 | } |
871 | 0 | else { |
872 | 0 | switch (GetDisplay()) { |
873 | 0 | case StyleDisplay::Block: |
874 | 0 | case StyleDisplay::ListItem: |
875 | 0 | case StyleDisplay::Table: |
876 | 0 | case StyleDisplay::TableCaption: |
877 | 0 | case StyleDisplay::Flex: |
878 | 0 | case StyleDisplay::WebkitBox: |
879 | 0 | case StyleDisplay::Grid: |
880 | 0 | case StyleDisplay::FlowRoot: |
881 | 0 | case StyleDisplay::RubyTextContainer: |
882 | 0 | frameType = NS_CSS_FRAME_TYPE_BLOCK; |
883 | 0 | break; |
884 | 0 |
|
885 | 0 | case StyleDisplay::Inline: |
886 | 0 | case StyleDisplay::InlineBlock: |
887 | 0 | case StyleDisplay::InlineTable: |
888 | 0 | case StyleDisplay::MozInlineBox: |
889 | 0 | case StyleDisplay::MozInlineGrid: |
890 | 0 | case StyleDisplay::MozInlineStack: |
891 | 0 | case StyleDisplay::InlineFlex: |
892 | 0 | case StyleDisplay::WebkitInlineBox: |
893 | 0 | case StyleDisplay::InlineGrid: |
894 | 0 | case StyleDisplay::Ruby: |
895 | 0 | case StyleDisplay::RubyBase: |
896 | 0 | case StyleDisplay::RubyText: |
897 | 0 | case StyleDisplay::RubyBaseContainer: |
898 | 0 | frameType = NS_CSS_FRAME_TYPE_INLINE; |
899 | 0 | break; |
900 | 0 |
|
901 | 0 | case StyleDisplay::TableCell: |
902 | 0 | case StyleDisplay::TableRowGroup: |
903 | 0 | case StyleDisplay::TableColumn: |
904 | 0 | case StyleDisplay::TableColumnGroup: |
905 | 0 | case StyleDisplay::TableHeaderGroup: |
906 | 0 | case StyleDisplay::TableFooterGroup: |
907 | 0 | case StyleDisplay::TableRow: |
908 | 0 | frameType = NS_CSS_FRAME_TYPE_INTERNAL_TABLE; |
909 | 0 | break; |
910 | 0 |
|
911 | 0 | case StyleDisplay::None: |
912 | 0 | default: |
913 | 0 | frameType = NS_CSS_FRAME_TYPE_UNKNOWN; |
914 | 0 | break; |
915 | 0 | } |
916 | 0 | } |
917 | 0 |
|
918 | 0 | // See if the frame is replaced |
919 | 0 | if (mFrame->IsFrameOfType(nsIFrame::eReplacedContainsBlock)) { |
920 | 0 | frameType = NS_FRAME_REPLACED_CONTAINS_BLOCK(frameType); |
921 | 0 | } else if (mFrame->IsFrameOfType(nsIFrame::eReplaced)) { |
922 | 0 | frameType = NS_FRAME_REPLACED(frameType); |
923 | 0 | } |
924 | 0 |
|
925 | 0 | mFrameType = frameType; |
926 | 0 | } |
927 | | |
928 | | /* static */ void |
929 | | ReflowInput::ComputeRelativeOffsets(WritingMode aWM, |
930 | | nsIFrame* aFrame, |
931 | | const LogicalSize& aCBSize, |
932 | | nsMargin& aComputedOffsets) |
933 | 0 | { |
934 | 0 | LogicalMargin offsets(aWM); |
935 | 0 | mozilla::Side inlineStart = aWM.PhysicalSide(eLogicalSideIStart); |
936 | 0 | mozilla::Side inlineEnd = aWM.PhysicalSide(eLogicalSideIEnd); |
937 | 0 | mozilla::Side blockStart = aWM.PhysicalSide(eLogicalSideBStart); |
938 | 0 | mozilla::Side blockEnd = aWM.PhysicalSide(eLogicalSideBEnd); |
939 | 0 |
|
940 | 0 | const nsStylePosition* position = aFrame->StylePosition(); |
941 | 0 |
|
942 | 0 | // Compute the 'inlineStart' and 'inlineEnd' values. 'inlineStart' |
943 | 0 | // moves the boxes to the end of the line, and 'inlineEnd' moves the |
944 | 0 | // boxes to the start of the line. The computed values are always: |
945 | 0 | // inlineStart=-inlineEnd |
946 | 0 | bool inlineStartIsAuto = |
947 | 0 | eStyleUnit_Auto == position->mOffset.GetUnit(inlineStart); |
948 | 0 | bool inlineEndIsAuto = |
949 | 0 | eStyleUnit_Auto == position->mOffset.GetUnit(inlineEnd); |
950 | 0 |
|
951 | 0 | // If neither 'inlineStart' nor 'inlineEnd' is auto, then we're |
952 | 0 | // over-constrained and we ignore one of them |
953 | 0 | if (!inlineStartIsAuto && !inlineEndIsAuto) { |
954 | 0 | inlineEndIsAuto = true; |
955 | 0 | } |
956 | 0 |
|
957 | 0 | if (inlineStartIsAuto) { |
958 | 0 | if (inlineEndIsAuto) { |
959 | 0 | // If both are 'auto' (their initial values), the computed values are 0 |
960 | 0 | offsets.IStart(aWM) = offsets.IEnd(aWM) = 0; |
961 | 0 | } else { |
962 | 0 | // 'inlineEnd' isn't 'auto' so compute its value |
963 | 0 | offsets.IEnd(aWM) = nsLayoutUtils:: |
964 | 0 | ComputeCBDependentValue(aCBSize.ISize(aWM), |
965 | 0 | position->mOffset.Get(inlineEnd)); |
966 | 0 |
|
967 | 0 | // Computed value for 'inlineStart' is minus the value of 'inlineEnd' |
968 | 0 | offsets.IStart(aWM) = -offsets.IEnd(aWM); |
969 | 0 | } |
970 | 0 |
|
971 | 0 | } else { |
972 | 0 | NS_ASSERTION(inlineEndIsAuto, "unexpected specified constraint"); |
973 | 0 |
|
974 | 0 | // 'InlineStart' isn't 'auto' so compute its value |
975 | 0 | offsets.IStart(aWM) = nsLayoutUtils:: |
976 | 0 | ComputeCBDependentValue(aCBSize.ISize(aWM), |
977 | 0 | position->mOffset.Get(inlineStart)); |
978 | 0 |
|
979 | 0 | // Computed value for 'inlineEnd' is minus the value of 'inlineStart' |
980 | 0 | offsets.IEnd(aWM) = -offsets.IStart(aWM); |
981 | 0 | } |
982 | 0 |
|
983 | 0 | // Compute the 'blockStart' and 'blockEnd' values. The 'blockStart' |
984 | 0 | // and 'blockEnd' properties move relatively positioned elements in |
985 | 0 | // the block progression direction. They also must be each other's |
986 | 0 | // negative |
987 | 0 | bool blockStartIsAuto = |
988 | 0 | eStyleUnit_Auto == position->mOffset.GetUnit(blockStart); |
989 | 0 | bool blockEndIsAuto = |
990 | 0 | eStyleUnit_Auto == position->mOffset.GetUnit(blockEnd); |
991 | 0 |
|
992 | 0 | // Check for percentage based values and a containing block block-size |
993 | 0 | // that depends on the content block-size. Treat them like 'auto' |
994 | 0 | if (NS_AUTOHEIGHT == aCBSize.BSize(aWM)) { |
995 | 0 | if (position->OffsetHasPercent(blockStart)) { |
996 | 0 | blockStartIsAuto = true; |
997 | 0 | } |
998 | 0 | if (position->OffsetHasPercent(blockEnd)) { |
999 | 0 | blockEndIsAuto = true; |
1000 | 0 | } |
1001 | 0 | } |
1002 | 0 |
|
1003 | 0 | // If neither is 'auto', 'block-end' is ignored |
1004 | 0 | if (!blockStartIsAuto && !blockEndIsAuto) { |
1005 | 0 | blockEndIsAuto = true; |
1006 | 0 | } |
1007 | 0 |
|
1008 | 0 | if (blockStartIsAuto) { |
1009 | 0 | if (blockEndIsAuto) { |
1010 | 0 | // If both are 'auto' (their initial values), the computed values are 0 |
1011 | 0 | offsets.BStart(aWM) = offsets.BEnd(aWM) = 0; |
1012 | 0 | } else { |
1013 | 0 | // 'blockEnd' isn't 'auto' so compute its value |
1014 | 0 | offsets.BEnd(aWM) = nsLayoutUtils:: |
1015 | 0 | ComputeBSizeDependentValue(aCBSize.BSize(aWM), |
1016 | 0 | position->mOffset.Get(blockEnd)); |
1017 | 0 |
|
1018 | 0 | // Computed value for 'blockStart' is minus the value of 'blockEnd' |
1019 | 0 | offsets.BStart(aWM) = -offsets.BEnd(aWM); |
1020 | 0 | } |
1021 | 0 |
|
1022 | 0 | } else { |
1023 | 0 | NS_ASSERTION(blockEndIsAuto, "unexpected specified constraint"); |
1024 | 0 |
|
1025 | 0 | // 'blockStart' isn't 'auto' so compute its value |
1026 | 0 | offsets.BStart(aWM) = nsLayoutUtils:: |
1027 | 0 | ComputeBSizeDependentValue(aCBSize.BSize(aWM), |
1028 | 0 | position->mOffset.Get(blockStart)); |
1029 | 0 |
|
1030 | 0 | // Computed value for 'blockEnd' is minus the value of 'blockStart' |
1031 | 0 | offsets.BEnd(aWM) = -offsets.BStart(aWM); |
1032 | 0 | } |
1033 | 0 |
|
1034 | 0 | // Convert the offsets to physical coordinates and store them on the frame |
1035 | 0 | aComputedOffsets = offsets.GetPhysicalMargin(aWM); |
1036 | 0 | nsMargin* physicalOffsets = |
1037 | 0 | aFrame->GetProperty(nsIFrame::ComputedOffsetProperty()); |
1038 | 0 | if (physicalOffsets) { |
1039 | 0 | *physicalOffsets = aComputedOffsets; |
1040 | 0 | } else { |
1041 | 0 | aFrame->AddProperty(nsIFrame::ComputedOffsetProperty(), |
1042 | 0 | new nsMargin(aComputedOffsets)); |
1043 | 0 | } |
1044 | 0 | } |
1045 | | |
1046 | | /* static */ void |
1047 | | ReflowInput::ApplyRelativePositioning(nsIFrame* aFrame, |
1048 | | const nsMargin& aComputedOffsets, |
1049 | | nsPoint* aPosition) |
1050 | 0 | { |
1051 | 0 | if (!aFrame->IsRelativelyPositioned()) { |
1052 | 0 | NS_ASSERTION(!aFrame->GetProperty(nsIFrame::NormalPositionProperty()), |
1053 | 0 | "We assume that changing the 'position' property causes " |
1054 | 0 | "frame reconstruction. If that ever changes, this code " |
1055 | 0 | "should call " |
1056 | 0 | "aFrame->DeleteProperty(nsIFrame::NormalPositionProperty())"); |
1057 | 0 | return; |
1058 | 0 | } |
1059 | 0 |
|
1060 | 0 | // Store the normal position |
1061 | 0 | nsPoint* normalPosition = |
1062 | 0 | aFrame->GetProperty(nsIFrame::NormalPositionProperty()); |
1063 | 0 | if (normalPosition) { |
1064 | 0 | *normalPosition = *aPosition; |
1065 | 0 | } else { |
1066 | 0 | aFrame->AddProperty(nsIFrame::NormalPositionProperty(), |
1067 | 0 | new nsPoint(*aPosition)); |
1068 | 0 | } |
1069 | 0 |
|
1070 | 0 | const nsStyleDisplay* display = aFrame->StyleDisplay(); |
1071 | 0 | if (NS_STYLE_POSITION_RELATIVE == display->mPosition) { |
1072 | 0 | *aPosition += nsPoint(aComputedOffsets.left, aComputedOffsets.top); |
1073 | 0 | } else if (NS_STYLE_POSITION_STICKY == display->mPosition && |
1074 | 0 | !aFrame->GetNextContinuation() && |
1075 | 0 | !aFrame->GetPrevContinuation() && |
1076 | 0 | !(aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) { |
1077 | 0 | // Sticky positioning for elements with multiple frames needs to be |
1078 | 0 | // computed all at once. We can't safely do that here because we might be |
1079 | 0 | // partway through (re)positioning the frames, so leave it until the scroll |
1080 | 0 | // container reflows and calls StickyScrollContainer::UpdatePositions. |
1081 | 0 | // For single-frame sticky positioned elements, though, go ahead and apply |
1082 | 0 | // it now to avoid unnecessary overflow updates later. |
1083 | 0 | StickyScrollContainer* ssc = |
1084 | 0 | StickyScrollContainer::GetStickyScrollContainerForFrame(aFrame); |
1085 | 0 | if (ssc) { |
1086 | 0 | *aPosition = ssc->ComputePosition(aFrame); |
1087 | 0 | } |
1088 | 0 | } |
1089 | 0 | } |
1090 | | |
1091 | | // Returns true if aFrame is non-null, a XUL frame, and "XUL-collapsed" (which |
1092 | | // only becomes a valid question to ask if we know it's a XUL frame). |
1093 | | static bool |
1094 | | IsXULCollapsedXULFrame(nsIFrame* aFrame) |
1095 | 0 | { |
1096 | 0 | return aFrame && aFrame->IsXULBoxFrame() && aFrame->IsXULCollapsed(); |
1097 | 0 | } |
1098 | | |
1099 | | nsIFrame* |
1100 | | ReflowInput::GetHypotheticalBoxContainer(nsIFrame* aFrame, |
1101 | | nscoord& aCBIStartEdge, |
1102 | | LogicalSize& aCBSize) const |
1103 | 0 | { |
1104 | 0 | aFrame = aFrame->GetContainingBlock(); |
1105 | 0 | NS_ASSERTION(aFrame != mFrame, "How did that happen?"); |
1106 | 0 |
|
1107 | 0 | /* Now aFrame is the containing block we want */ |
1108 | 0 |
|
1109 | 0 | /* Check whether the containing block is currently being reflowed. |
1110 | 0 | If so, use the info from the reflow input. */ |
1111 | 0 | const ReflowInput* reflowInput; |
1112 | 0 | if (aFrame->GetStateBits() & NS_FRAME_IN_REFLOW) { |
1113 | 0 | for (reflowInput = mParentReflowInput; |
1114 | 0 | reflowInput && reflowInput->mFrame != aFrame; |
1115 | 0 | reflowInput = reflowInput->mParentReflowInput) { |
1116 | 0 | /* do nothing */ |
1117 | 0 | } |
1118 | 0 | } else { |
1119 | 0 | reflowInput = nullptr; |
1120 | 0 | } |
1121 | 0 |
|
1122 | 0 | if (reflowInput) { |
1123 | 0 | WritingMode wm = reflowInput->GetWritingMode(); |
1124 | 0 | NS_ASSERTION(wm == aFrame->GetWritingMode(), "unexpected writing mode"); |
1125 | 0 | aCBIStartEdge = reflowInput->ComputedLogicalBorderPadding().IStart(wm); |
1126 | 0 | aCBSize = reflowInput->ComputedSize(wm); |
1127 | 0 | } else { |
1128 | 0 | /* Didn't find a reflow reflowInput for aFrame. Just compute the information we |
1129 | 0 | want, on the assumption that aFrame already knows its size. This really |
1130 | 0 | ought to be true by now. */ |
1131 | 0 | NS_ASSERTION(!(aFrame->GetStateBits() & NS_FRAME_IN_REFLOW), |
1132 | 0 | "aFrame shouldn't be in reflow; we'll lie if it is"); |
1133 | 0 | WritingMode wm = aFrame->GetWritingMode(); |
1134 | 0 | // Compute CB's offset & content-box size by subtracting borderpadding from |
1135 | 0 | // frame size. Exception: if the CB is 0-sized, it *might* be a child of a |
1136 | 0 | // XUL-collapsed frame and might have nonzero borderpadding that was simply |
1137 | 0 | // discarded during its layout. (See the child-zero-sizing in |
1138 | 0 | // nsSprocketLayout::XULLayout()). In that case, we ignore the |
1139 | 0 | // borderpadding here (just like we did when laying it out), or else we'd |
1140 | 0 | // produce a bogus negative content-box size. |
1141 | 0 | aCBIStartEdge = 0; |
1142 | 0 | aCBSize = aFrame->GetLogicalSize(wm); |
1143 | 0 | if (!aCBSize.IsAllZero() || |
1144 | 0 | (!IsXULCollapsedXULFrame(aFrame->GetParent()))) { |
1145 | 0 | // aFrame is not XUL-collapsed (nor is it a child of a XUL-collapsed |
1146 | 0 | // frame), so we can go ahead and subtract out border padding. |
1147 | 0 | LogicalMargin borderPadding = aFrame->GetLogicalUsedBorderAndPadding(wm); |
1148 | 0 | aCBIStartEdge += borderPadding.IStart(wm); |
1149 | 0 | aCBSize -= borderPadding.Size(wm); |
1150 | 0 | } |
1151 | 0 | } |
1152 | 0 |
|
1153 | 0 | return aFrame; |
1154 | 0 | } |
1155 | | |
1156 | | struct nsHypotheticalPosition { |
1157 | | // offset from inline-start edge of containing block (which is a padding edge) |
1158 | | nscoord mIStart; |
1159 | | // offset from block-start edge of containing block (which is a padding edge) |
1160 | | nscoord mBStart; |
1161 | | WritingMode mWritingMode; |
1162 | | }; |
1163 | | |
1164 | | static bool |
1165 | | GetIntrinsicSizeFor(nsIFrame* aFrame, |
1166 | | nsSize& aIntrinsicSize, |
1167 | | LayoutFrameType aFrameType) |
1168 | 0 | { |
1169 | 0 | // See if it is an image frame |
1170 | 0 | bool success = false; |
1171 | 0 |
|
1172 | 0 | // Currently the only type of replaced frame that we can get the intrinsic |
1173 | 0 | // size for is an image frame |
1174 | 0 | // XXX We should add back the GetReflowOutput() function and one of the |
1175 | 0 | // things should be the intrinsic size... |
1176 | 0 | if (aFrameType == LayoutFrameType::Image) { |
1177 | 0 | nsImageFrame* imageFrame = (nsImageFrame*)aFrame; |
1178 | 0 |
|
1179 | 0 | if (NS_SUCCEEDED(imageFrame->GetIntrinsicImageSize(aIntrinsicSize))) { |
1180 | 0 | success = (aIntrinsicSize != nsSize(0, 0)); |
1181 | 0 | } |
1182 | 0 | } |
1183 | 0 | return success; |
1184 | 0 | } |
1185 | | |
1186 | | /** |
1187 | | * aInsideBoxSizing returns the part of the padding, border, and margin |
1188 | | * in the aAxis dimension that goes inside the edge given by box-sizing; |
1189 | | * aOutsideBoxSizing returns the rest. |
1190 | | */ |
1191 | | void |
1192 | | ReflowInput::CalculateBorderPaddingMargin( |
1193 | | LogicalAxis aAxis, |
1194 | | nscoord aContainingBlockSize, |
1195 | | nscoord* aInsideBoxSizing, |
1196 | | nscoord* aOutsideBoxSizing) const |
1197 | 0 | { |
1198 | 0 | WritingMode wm = GetWritingMode(); |
1199 | 0 | mozilla::Side startSide = |
1200 | 0 | wm.PhysicalSide(MakeLogicalSide(aAxis, eLogicalEdgeStart)); |
1201 | 0 | mozilla::Side endSide = |
1202 | 0 | wm.PhysicalSide(MakeLogicalSide(aAxis, eLogicalEdgeEnd)); |
1203 | 0 |
|
1204 | 0 | nsMargin styleBorder = mStyleBorder->GetComputedBorder(); |
1205 | 0 | nscoord borderStartEnd = |
1206 | 0 | styleBorder.Side(startSide) + styleBorder.Side(endSide); |
1207 | 0 |
|
1208 | 0 | nscoord paddingStartEnd, marginStartEnd; |
1209 | 0 |
|
1210 | 0 | // See if the style system can provide us the padding directly |
1211 | 0 | nsMargin stylePadding; |
1212 | 0 | if (mStylePadding->GetPadding(stylePadding)) { |
1213 | 0 | paddingStartEnd = |
1214 | 0 | stylePadding.Side(startSide) + stylePadding.Side(endSide); |
1215 | 0 | } else { |
1216 | 0 | // We have to compute the start and end values |
1217 | 0 | nscoord start, end; |
1218 | 0 | start = nsLayoutUtils:: |
1219 | 0 | ComputeCBDependentValue(aContainingBlockSize, |
1220 | 0 | mStylePadding->mPadding.Get(startSide)); |
1221 | 0 | end = nsLayoutUtils:: |
1222 | 0 | ComputeCBDependentValue(aContainingBlockSize, |
1223 | 0 | mStylePadding->mPadding.Get(endSide)); |
1224 | 0 | paddingStartEnd = start + end; |
1225 | 0 | } |
1226 | 0 |
|
1227 | 0 | // See if the style system can provide us the margin directly |
1228 | 0 | nsMargin styleMargin; |
1229 | 0 | if (mStyleMargin->GetMargin(styleMargin)) { |
1230 | 0 | marginStartEnd = |
1231 | 0 | styleMargin.Side(startSide) + styleMargin.Side(endSide); |
1232 | 0 | } else { |
1233 | 0 | nscoord start, end; |
1234 | 0 | // We have to compute the start and end values |
1235 | 0 | if (eStyleUnit_Auto == mStyleMargin->mMargin.GetUnit(startSide)) { |
1236 | 0 | // We set this to 0 for now, and fix it up later in |
1237 | 0 | // InitAbsoluteConstraints (which is caller of this function, via |
1238 | 0 | // CalculateHypotheticalPosition). |
1239 | 0 | start = 0; |
1240 | 0 | } else { |
1241 | 0 | start = nsLayoutUtils:: |
1242 | 0 | ComputeCBDependentValue(aContainingBlockSize, |
1243 | 0 | mStyleMargin->mMargin.Get(startSide)); |
1244 | 0 | } |
1245 | 0 | if (eStyleUnit_Auto == mStyleMargin->mMargin.GetUnit(endSide)) { |
1246 | 0 | // We set this to 0 for now, and fix it up later in |
1247 | 0 | // InitAbsoluteConstraints (which is caller of this function, via |
1248 | 0 | // CalculateHypotheticalPosition). |
1249 | 0 | end = 0; |
1250 | 0 | } else { |
1251 | 0 | end = nsLayoutUtils:: |
1252 | 0 | ComputeCBDependentValue(aContainingBlockSize, |
1253 | 0 | mStyleMargin->mMargin.Get(endSide)); |
1254 | 0 | } |
1255 | 0 | marginStartEnd = start + end; |
1256 | 0 | } |
1257 | 0 |
|
1258 | 0 | nscoord outside = paddingStartEnd + borderStartEnd + marginStartEnd; |
1259 | 0 | nscoord inside = 0; |
1260 | 0 | if (mStylePosition->mBoxSizing == StyleBoxSizing::Border) { |
1261 | 0 | inside = borderStartEnd + paddingStartEnd; |
1262 | 0 | } |
1263 | 0 | outside -= inside; |
1264 | 0 | *aInsideBoxSizing = inside; |
1265 | 0 | *aOutsideBoxSizing = outside; |
1266 | 0 | } |
1267 | | |
1268 | | /** |
1269 | | * Returns true iff a pre-order traversal of the normal child |
1270 | | * frames rooted at aFrame finds no non-empty frame before aDescendant. |
1271 | | */ |
1272 | | static bool |
1273 | | AreAllEarlierInFlowFramesEmpty(nsIFrame* aFrame, |
1274 | | nsIFrame* aDescendant, |
1275 | | bool* aFound) |
1276 | 0 | { |
1277 | 0 | if (aFrame == aDescendant) { |
1278 | 0 | *aFound = true; |
1279 | 0 | return true; |
1280 | 0 | } |
1281 | 0 | if (aFrame->IsPlaceholderFrame()) { |
1282 | 0 | auto ph = static_cast<nsPlaceholderFrame*>(aFrame); |
1283 | 0 | MOZ_ASSERT(ph->IsSelfEmpty() && ph->PrincipalChildList().IsEmpty()); |
1284 | 0 | ph->SetLineIsEmptySoFar(true); |
1285 | 0 | } else { |
1286 | 0 | if (!aFrame->IsSelfEmpty()) { |
1287 | 0 | *aFound = false; |
1288 | 0 | return false; |
1289 | 0 | } |
1290 | 0 | for (nsIFrame* f : aFrame->PrincipalChildList()) { |
1291 | 0 | bool allEmpty = AreAllEarlierInFlowFramesEmpty(f, aDescendant, aFound); |
1292 | 0 | if (*aFound || !allEmpty) { |
1293 | 0 | return allEmpty; |
1294 | 0 | } |
1295 | 0 | } |
1296 | 0 | } |
1297 | 0 | *aFound = false; |
1298 | 0 | return true; |
1299 | 0 | } |
1300 | | |
1301 | | // Calculate the position of the hypothetical box that the element would have |
1302 | | // if it were in the flow. |
1303 | | // The values returned are relative to the padding edge of the absolute |
1304 | | // containing block. The writing-mode of the hypothetical box position will |
1305 | | // have the same block direction as the absolute containing block, but may |
1306 | | // differ in inline-bidi direction. |
1307 | | // In the code below, |aReflowInput->frame| is the absolute containing block, while |
1308 | | // |containingBlock| is the nearest block container of the placeholder frame, |
1309 | | // which may be different from the absolute containing block. |
1310 | | void |
1311 | | ReflowInput::CalculateHypotheticalPosition( |
1312 | | nsPresContext* aPresContext, |
1313 | | nsPlaceholderFrame* aPlaceholderFrame, |
1314 | | const ReflowInput* aReflowInput, |
1315 | | nsHypotheticalPosition& aHypotheticalPos, |
1316 | | LayoutFrameType aFrameType) const |
1317 | 0 | { |
1318 | 0 | NS_ASSERTION(mStyleDisplay->mOriginalDisplay != StyleDisplay::None, |
1319 | 0 | "mOriginalDisplay has not been properly initialized"); |
1320 | 0 |
|
1321 | 0 | // Find the nearest containing block frame to the placeholder frame, |
1322 | 0 | // and its inline-start edge and width. |
1323 | 0 | nscoord blockIStartContentEdge; |
1324 | 0 | // Dummy writing mode for blockContentSize, will be changed as needed by |
1325 | 0 | // GetHypotheticalBoxContainer. |
1326 | 0 | WritingMode cbwm = aReflowInput->GetWritingMode(); |
1327 | 0 | LogicalSize blockContentSize(cbwm); |
1328 | 0 | nsIFrame* containingBlock = |
1329 | 0 | GetHypotheticalBoxContainer(aPlaceholderFrame, blockIStartContentEdge, |
1330 | 0 | blockContentSize); |
1331 | 0 | // Now blockContentSize is in containingBlock's writing mode. |
1332 | 0 |
|
1333 | 0 | // If it's a replaced element and it has a 'auto' value for |
1334 | 0 | //'inline size', see if we can get the intrinsic size. This will allow |
1335 | 0 | // us to exactly determine both the inline edges |
1336 | 0 | WritingMode wm = containingBlock->GetWritingMode(); |
1337 | 0 |
|
1338 | 0 | nsStyleCoord styleISize = mStylePosition->ISize(wm); |
1339 | 0 | bool isAutoISize = styleISize.GetUnit() == eStyleUnit_Auto; |
1340 | 0 | nsSize intrinsicSize; |
1341 | 0 | bool knowIntrinsicSize = false; |
1342 | 0 | if (NS_FRAME_IS_REPLACED(mFrameType) && isAutoISize) { |
1343 | 0 | // See if we can get the intrinsic size of the element |
1344 | 0 | knowIntrinsicSize = GetIntrinsicSizeFor(mFrame, intrinsicSize, aFrameType); |
1345 | 0 | } |
1346 | 0 |
|
1347 | 0 | // See if we can calculate what the box inline size would have been if |
1348 | 0 | // the element had been in the flow |
1349 | 0 | nscoord boxISize; |
1350 | 0 | bool knowBoxISize = false; |
1351 | 0 | if ((StyleDisplay::Inline == mStyleDisplay->mOriginalDisplay) && |
1352 | 0 | !NS_FRAME_IS_REPLACED(mFrameType)) { |
1353 | 0 | // For non-replaced inline-level elements the 'inline size' property |
1354 | 0 | // doesn't apply, so we don't know what the inline size would have |
1355 | 0 | // been without reflowing it |
1356 | 0 |
|
1357 | 0 | } else { |
1358 | 0 | // It's either a replaced inline-level element or a block-level element |
1359 | 0 |
|
1360 | 0 | // Determine the total amount of inline direction |
1361 | 0 | // border/padding/margin that the element would have had if it had |
1362 | 0 | // been in the flow. Note that we ignore any 'auto' and 'inherit' |
1363 | 0 | // values |
1364 | 0 | nscoord insideBoxSizing, outsideBoxSizing; |
1365 | 0 | CalculateBorderPaddingMargin(eLogicalAxisInline, |
1366 | 0 | blockContentSize.ISize(wm), |
1367 | 0 | &insideBoxSizing, &outsideBoxSizing); |
1368 | 0 |
|
1369 | 0 | if (NS_FRAME_IS_REPLACED(mFrameType) && isAutoISize) { |
1370 | 0 | // It's a replaced element with an 'auto' inline size so the box |
1371 | 0 | // inline size is its intrinsic size plus any border/padding/margin |
1372 | 0 | if (knowIntrinsicSize) { |
1373 | 0 | boxISize = LogicalSize(wm, intrinsicSize).ISize(wm) + |
1374 | 0 | outsideBoxSizing + insideBoxSizing; |
1375 | 0 | knowBoxISize = true; |
1376 | 0 | } |
1377 | 0 |
|
1378 | 0 | } else if (isAutoISize) { |
1379 | 0 | // The box inline size is the containing block inline size |
1380 | 0 | boxISize = blockContentSize.ISize(wm); |
1381 | 0 | knowBoxISize = true; |
1382 | 0 |
|
1383 | 0 | } else { |
1384 | 0 | // We need to compute it. It's important we do this, because if it's |
1385 | 0 | // percentage based this computed value may be different from the computed |
1386 | 0 | // value calculated using the absolute containing block width |
1387 | 0 | boxISize = ComputeISizeValue(blockContentSize.ISize(wm), |
1388 | 0 | insideBoxSizing, outsideBoxSizing, |
1389 | 0 | styleISize) + |
1390 | 0 | insideBoxSizing + outsideBoxSizing; |
1391 | 0 | knowBoxISize = true; |
1392 | 0 | } |
1393 | 0 | } |
1394 | 0 |
|
1395 | 0 | // Get the placeholder x-offset and y-offset in the coordinate |
1396 | 0 | // space of its containing block |
1397 | 0 | // XXXbz the placeholder is not fully reflowed yet if our containing block is |
1398 | 0 | // relatively positioned... |
1399 | 0 | nsSize containerSize = containingBlock->GetStateBits() & NS_FRAME_IN_REFLOW |
1400 | 0 | ? aReflowInput->ComputedSizeAsContainerIfConstrained() |
1401 | 0 | : containingBlock->GetSize(); |
1402 | 0 | LogicalPoint |
1403 | 0 | placeholderOffset(wm, |
1404 | 0 | aPlaceholderFrame->GetOffsetToIgnoringScrolling(containingBlock), |
1405 | 0 | containerSize); |
1406 | 0 |
|
1407 | 0 | // First, determine the hypothetical box's mBStart. We want to check the |
1408 | 0 | // content insertion frame of containingBlock for block-ness, but make |
1409 | 0 | // sure to compute all coordinates in the coordinate system of |
1410 | 0 | // containingBlock. |
1411 | 0 | nsBlockFrame* blockFrame = |
1412 | 0 | nsLayoutUtils::GetAsBlock(containingBlock->GetContentInsertionFrame()); |
1413 | 0 | if (blockFrame) { |
1414 | 0 | // Use a null containerSize to convert a LogicalPoint functioning as a |
1415 | 0 | // vector into a physical nsPoint vector. |
1416 | 0 | const nsSize nullContainerSize; |
1417 | 0 | LogicalPoint blockOffset(wm, |
1418 | 0 | blockFrame->GetOffsetToIgnoringScrolling(containingBlock), |
1419 | 0 | nullContainerSize); |
1420 | 0 | bool isValid; |
1421 | 0 | nsBlockInFlowLineIterator iter(blockFrame, aPlaceholderFrame, &isValid); |
1422 | 0 | if (!isValid) { |
1423 | 0 | // Give up. We're probably dealing with somebody using |
1424 | 0 | // position:absolute inside native-anonymous content anyway. |
1425 | 0 | aHypotheticalPos.mBStart = placeholderOffset.B(wm); |
1426 | 0 | } else { |
1427 | 0 | NS_ASSERTION(iter.GetContainer() == blockFrame, |
1428 | 0 | "Found placeholder in wrong block!"); |
1429 | 0 | nsBlockFrame::LineIterator lineBox = iter.GetLine(); |
1430 | 0 |
|
1431 | 0 | // How we determine the hypothetical box depends on whether the element |
1432 | 0 | // would have been inline-level or block-level |
1433 | 0 | LogicalRect lineBounds = |
1434 | 0 | lineBox->GetBounds().ConvertTo(wm, lineBox->mWritingMode, |
1435 | 0 | lineBox->mContainerSize); |
1436 | 0 | if (mStyleDisplay->IsOriginalDisplayInlineOutsideStyle()) { |
1437 | 0 | // Use the block-start of the inline box which the placeholder lives in |
1438 | 0 | // as the hypothetical box's block-start. |
1439 | 0 | aHypotheticalPos.mBStart = lineBounds.BStart(wm) + blockOffset.B(wm); |
1440 | 0 | } else { |
1441 | 0 | // The element would have been block-level which means it would |
1442 | 0 | // be below the line containing the placeholder frame, unless |
1443 | 0 | // all the frames before it are empty. In that case, it would |
1444 | 0 | // have been just before this line. |
1445 | 0 | // XXXbz the line box is not fully reflowed yet if our |
1446 | 0 | // containing block is relatively positioned... |
1447 | 0 | if (lineBox != iter.End()) { |
1448 | 0 | nsIFrame* firstFrame = lineBox->mFirstChild; |
1449 | 0 | bool allEmpty = false; |
1450 | 0 | if (firstFrame == aPlaceholderFrame) { |
1451 | 0 | aPlaceholderFrame->SetLineIsEmptySoFar(true); |
1452 | 0 | allEmpty = true; |
1453 | 0 | } else { |
1454 | 0 | auto prev = aPlaceholderFrame->GetPrevSibling(); |
1455 | 0 | if (prev && prev->IsPlaceholderFrame()) { |
1456 | 0 | auto ph = static_cast<nsPlaceholderFrame*>(prev); |
1457 | 0 | if (ph->GetLineIsEmptySoFar(&allEmpty)) { |
1458 | 0 | aPlaceholderFrame->SetLineIsEmptySoFar(allEmpty); |
1459 | 0 | } |
1460 | 0 | } |
1461 | 0 | } |
1462 | 0 | if (!allEmpty) { |
1463 | 0 | bool found = false; |
1464 | 0 | while (firstFrame) { // See bug 223064 |
1465 | 0 | allEmpty = AreAllEarlierInFlowFramesEmpty(firstFrame, |
1466 | 0 | aPlaceholderFrame, &found); |
1467 | 0 | if (found || !allEmpty) { |
1468 | 0 | break; |
1469 | 0 | } |
1470 | 0 | firstFrame = firstFrame->GetNextSibling(); |
1471 | 0 | } |
1472 | 0 | aPlaceholderFrame->SetLineIsEmptySoFar(allEmpty); |
1473 | 0 | } |
1474 | 0 | NS_ASSERTION(firstFrame, "Couldn't find placeholder!"); |
1475 | 0 |
|
1476 | 0 | if (allEmpty) { |
1477 | 0 | // The top of the hypothetical box is the top of the line |
1478 | 0 | // containing the placeholder, since there is nothing in the |
1479 | 0 | // line before our placeholder except empty frames. |
1480 | 0 | aHypotheticalPos.mBStart = |
1481 | 0 | lineBounds.BStart(wm) + blockOffset.B(wm); |
1482 | 0 | } else { |
1483 | 0 | // The top of the hypothetical box is just below the line |
1484 | 0 | // containing the placeholder. |
1485 | 0 | aHypotheticalPos.mBStart = |
1486 | 0 | lineBounds.BEnd(wm) + blockOffset.B(wm); |
1487 | 0 | } |
1488 | 0 | } else { |
1489 | 0 | // Just use the placeholder's block-offset wrt the containing block |
1490 | 0 | aHypotheticalPos.mBStart = placeholderOffset.B(wm); |
1491 | 0 | } |
1492 | 0 | } |
1493 | 0 | } |
1494 | 0 | } else { |
1495 | 0 | // The containing block is not a block, so it's probably something |
1496 | 0 | // like a XUL box, etc. |
1497 | 0 | // Just use the placeholder's block-offset |
1498 | 0 | aHypotheticalPos.mBStart = placeholderOffset.B(wm); |
1499 | 0 | } |
1500 | 0 |
|
1501 | 0 | // Second, determine the hypothetical box's mIStart. |
1502 | 0 | // How we determine the hypothetical box depends on whether the element |
1503 | 0 | // would have been inline-level or block-level |
1504 | 0 | if (mStyleDisplay->IsOriginalDisplayInlineOutsideStyle() || |
1505 | 0 | mFlags.mIOffsetsNeedCSSAlign) { |
1506 | 0 | // The placeholder represents the IStart edge of the hypothetical box. |
1507 | 0 | // (Or if mFlags.mIOffsetsNeedCSSAlign is set, it represents the IStart |
1508 | 0 | // edge of the Alignment Container.) |
1509 | 0 | aHypotheticalPos.mIStart = placeholderOffset.I(wm); |
1510 | 0 | } else { |
1511 | 0 | aHypotheticalPos.mIStart = blockIStartContentEdge; |
1512 | 0 | } |
1513 | 0 |
|
1514 | 0 | // The current coordinate space is that of the nearest block to the placeholder. |
1515 | 0 | // Convert to the coordinate space of the absolute containing block. |
1516 | 0 | nsPoint cbOffset = |
1517 | 0 | containingBlock->GetOffsetToIgnoringScrolling(aReflowInput->mFrame); |
1518 | 0 |
|
1519 | 0 | nsSize reflowSize = aReflowInput->ComputedSizeAsContainerIfConstrained(); |
1520 | 0 | LogicalPoint logCBOffs(wm, cbOffset, reflowSize - containerSize); |
1521 | 0 | aHypotheticalPos.mIStart += logCBOffs.I(wm); |
1522 | 0 | aHypotheticalPos.mBStart += logCBOffs.B(wm); |
1523 | 0 |
|
1524 | 0 | // The specified offsets are relative to the absolute containing block's |
1525 | 0 | // padding edge and our current values are relative to the border edge, so |
1526 | 0 | // translate. |
1527 | 0 | LogicalMargin border = |
1528 | 0 | aReflowInput->ComputedLogicalBorderPadding() - |
1529 | 0 | aReflowInput->ComputedLogicalPadding(); |
1530 | 0 | border = border.ConvertTo(wm, aReflowInput->GetWritingMode()); |
1531 | 0 | aHypotheticalPos.mIStart -= border.IStart(wm); |
1532 | 0 | aHypotheticalPos.mBStart -= border.BStart(wm); |
1533 | 0 |
|
1534 | 0 | // At this point, we have computed aHypotheticalPos using the writing mode |
1535 | 0 | // of the placeholder's containing block. |
1536 | 0 |
|
1537 | 0 | if (cbwm.GetBlockDir() != wm.GetBlockDir()) { |
1538 | 0 | // If the block direction we used in calculating aHypotheticalPos does not |
1539 | 0 | // match the absolute containing block's, we need to convert here so that |
1540 | 0 | // aHypotheticalPos is usable in relation to the absolute containing block. |
1541 | 0 | // This requires computing or measuring the abspos frame's block-size, |
1542 | 0 | // which is not otherwise required/used here (as aHypotheticalPos |
1543 | 0 | // records only the block-start coordinate). |
1544 | 0 |
|
1545 | 0 | // This is similar to the inline-size calculation for a replaced |
1546 | 0 | // inline-level element or a block-level element (above), except that |
1547 | 0 | // 'auto' sizing is handled differently in the block direction for non- |
1548 | 0 | // replaced elements and replaced elements lacking an intrinsic size. |
1549 | 0 |
|
1550 | 0 | // Determine the total amount of block direction |
1551 | 0 | // border/padding/margin that the element would have had if it had |
1552 | 0 | // been in the flow. Note that we ignore any 'auto' and 'inherit' |
1553 | 0 | // values. |
1554 | 0 | nscoord insideBoxSizing, outsideBoxSizing; |
1555 | 0 | CalculateBorderPaddingMargin(eLogicalAxisBlock, |
1556 | 0 | blockContentSize.BSize(wm), |
1557 | 0 | &insideBoxSizing, &outsideBoxSizing); |
1558 | 0 |
|
1559 | 0 | nscoord boxBSize; |
1560 | 0 | nsStyleCoord styleBSize = mStylePosition->BSize(wm); |
1561 | 0 | bool isAutoBSize = styleBSize.GetUnit() == eStyleUnit_Auto; |
1562 | 0 | if (isAutoBSize) { |
1563 | 0 | if (NS_FRAME_IS_REPLACED(mFrameType) && knowIntrinsicSize) { |
1564 | 0 | // It's a replaced element with an 'auto' block size so the box |
1565 | 0 | // block size is its intrinsic size plus any border/padding/margin |
1566 | 0 | boxBSize = LogicalSize(wm, intrinsicSize).BSize(wm) + |
1567 | 0 | outsideBoxSizing + insideBoxSizing; |
1568 | 0 | } else { |
1569 | 0 | // XXX Bug 1191801 |
1570 | 0 | // Figure out how to get the correct boxBSize here (need to reflow the |
1571 | 0 | // positioned frame?) |
1572 | 0 | boxBSize = 0; |
1573 | 0 | } |
1574 | 0 | } else { |
1575 | 0 | // We need to compute it. It's important we do this, because if it's |
1576 | 0 | // percentage-based this computed value may be different from the |
1577 | 0 | // computed value calculated using the absolute containing block height. |
1578 | 0 | boxBSize = nsLayoutUtils::ComputeBSizeValue(blockContentSize.BSize(wm), |
1579 | 0 | insideBoxSizing, styleBSize) + |
1580 | 0 | insideBoxSizing + outsideBoxSizing; |
1581 | 0 | } |
1582 | 0 |
|
1583 | 0 | LogicalSize boxSize(wm, knowBoxISize ? boxISize : 0, boxBSize); |
1584 | 0 |
|
1585 | 0 | LogicalPoint origin(wm, aHypotheticalPos.mIStart, |
1586 | 0 | aHypotheticalPos.mBStart); |
1587 | 0 | origin = origin.ConvertTo(cbwm, wm, reflowSize - |
1588 | 0 | boxSize.GetPhysicalSize(wm)); |
1589 | 0 |
|
1590 | 0 | aHypotheticalPos.mIStart = origin.I(cbwm); |
1591 | 0 | aHypotheticalPos.mBStart = origin.B(cbwm); |
1592 | 0 | aHypotheticalPos.mWritingMode = cbwm; |
1593 | 0 | } else { |
1594 | 0 | aHypotheticalPos.mWritingMode = wm; |
1595 | 0 | } |
1596 | 0 | } |
1597 | | |
1598 | | void |
1599 | | ReflowInput::InitAbsoluteConstraints(nsPresContext* aPresContext, |
1600 | | const ReflowInput* aReflowInput, |
1601 | | const LogicalSize& aCBSize, |
1602 | | LayoutFrameType aFrameType) |
1603 | 0 | { |
1604 | 0 | WritingMode wm = GetWritingMode(); |
1605 | 0 | WritingMode cbwm = aReflowInput->GetWritingMode(); |
1606 | 0 | NS_WARNING_ASSERTION(aCBSize.BSize(cbwm) != NS_AUTOHEIGHT, |
1607 | 0 | "containing block bsize must be constrained"); |
1608 | 0 |
|
1609 | 0 | NS_ASSERTION(aFrameType != LayoutFrameType::Table, |
1610 | 0 | "InitAbsoluteConstraints should not be called on table frames"); |
1611 | 0 | NS_ASSERTION(mFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW, |
1612 | 0 | "Why are we here?"); |
1613 | 0 |
|
1614 | 0 | const auto& styleOffset = mStylePosition->mOffset; |
1615 | 0 | bool iStartIsAuto = styleOffset.GetIStartUnit(cbwm) == eStyleUnit_Auto; |
1616 | 0 | bool iEndIsAuto = styleOffset.GetIEndUnit(cbwm) == eStyleUnit_Auto; |
1617 | 0 | bool bStartIsAuto = styleOffset.GetBStartUnit(cbwm) == eStyleUnit_Auto; |
1618 | 0 | bool bEndIsAuto = styleOffset.GetBEndUnit(cbwm) == eStyleUnit_Auto; |
1619 | 0 |
|
1620 | 0 | // If both 'left' and 'right' are 'auto' or both 'top' and 'bottom' are |
1621 | 0 | // 'auto', then compute the hypothetical box position where the element would |
1622 | 0 | // have been if it had been in the flow |
1623 | 0 | nsHypotheticalPosition hypotheticalPos; |
1624 | 0 | if ((iStartIsAuto && iEndIsAuto) || (bStartIsAuto && bEndIsAuto)) { |
1625 | 0 | nsPlaceholderFrame* placeholderFrame = mFrame->GetPlaceholderFrame(); |
1626 | 0 | MOZ_ASSERT(placeholderFrame, "no placeholder frame"); |
1627 | 0 |
|
1628 | 0 | if (placeholderFrame->HasAnyStateBits( |
1629 | 0 | PLACEHOLDER_STATICPOS_NEEDS_CSSALIGN)) { |
1630 | 0 | DebugOnly<nsIFrame*> placeholderParent = placeholderFrame->GetParent(); |
1631 | 0 | MOZ_ASSERT(placeholderParent, "shouldn't have unparented placeholders"); |
1632 | 0 | MOZ_ASSERT(placeholderParent->IsFlexOrGridContainer(), |
1633 | 0 | "This flag should only be set on grid/flex children"); |
1634 | 0 |
|
1635 | 0 | // If the (as-yet unknown) static position will determine the inline |
1636 | 0 | // and/or block offsets, set flags to note those offsets aren't valid |
1637 | 0 | // until we can do CSS Box Alignment on the OOF frame. |
1638 | 0 | mFlags.mIOffsetsNeedCSSAlign = (iStartIsAuto && iEndIsAuto); |
1639 | 0 | mFlags.mBOffsetsNeedCSSAlign = (bStartIsAuto && bEndIsAuto); |
1640 | 0 | } |
1641 | 0 |
|
1642 | 0 | if (mFlags.mStaticPosIsCBOrigin) { |
1643 | 0 | hypotheticalPos.mWritingMode = cbwm; |
1644 | 0 | hypotheticalPos.mIStart = nscoord(0); |
1645 | 0 | hypotheticalPos.mBStart = nscoord(0); |
1646 | 0 | } else { |
1647 | 0 | CalculateHypotheticalPosition(aPresContext, placeholderFrame, |
1648 | 0 | aReflowInput, hypotheticalPos, aFrameType); |
1649 | 0 | } |
1650 | 0 | } |
1651 | 0 |
|
1652 | 0 | // Initialize the 'left' and 'right' computed offsets |
1653 | 0 | // XXX Handle new 'static-position' value... |
1654 | 0 |
|
1655 | 0 | // Size of the containing block in its writing mode |
1656 | 0 | LogicalSize cbSize = aCBSize; |
1657 | 0 | LogicalMargin offsets = ComputedLogicalOffsets().ConvertTo(cbwm, wm); |
1658 | 0 |
|
1659 | 0 | if (iStartIsAuto) { |
1660 | 0 | offsets.IStart(cbwm) = 0; |
1661 | 0 | } else { |
1662 | 0 | offsets.IStart(cbwm) = nsLayoutUtils:: |
1663 | 0 | ComputeCBDependentValue(cbSize.ISize(cbwm), styleOffset.GetIStart(cbwm)); |
1664 | 0 | } |
1665 | 0 | if (iEndIsAuto) { |
1666 | 0 | offsets.IEnd(cbwm) = 0; |
1667 | 0 | } else { |
1668 | 0 | offsets.IEnd(cbwm) = nsLayoutUtils:: |
1669 | 0 | ComputeCBDependentValue(cbSize.ISize(cbwm), styleOffset.GetIEnd(cbwm)); |
1670 | 0 | } |
1671 | 0 |
|
1672 | 0 | if (iStartIsAuto && iEndIsAuto) { |
1673 | 0 | if (cbwm.IsBidiLTR() != hypotheticalPos.mWritingMode.IsBidiLTR()) { |
1674 | 0 | offsets.IEnd(cbwm) = hypotheticalPos.mIStart; |
1675 | 0 | iEndIsAuto = false; |
1676 | 0 | } else { |
1677 | 0 | offsets.IStart(cbwm) = hypotheticalPos.mIStart; |
1678 | 0 | iStartIsAuto = false; |
1679 | 0 | } |
1680 | 0 | } |
1681 | 0 |
|
1682 | 0 | if (bStartIsAuto) { |
1683 | 0 | offsets.BStart(cbwm) = 0; |
1684 | 0 | } else { |
1685 | 0 | offsets.BStart(cbwm) = nsLayoutUtils:: |
1686 | 0 | ComputeBSizeDependentValue(cbSize.BSize(cbwm), |
1687 | 0 | styleOffset.GetBStart(cbwm)); |
1688 | 0 | } |
1689 | 0 | if (bEndIsAuto) { |
1690 | 0 | offsets.BEnd(cbwm) = 0; |
1691 | 0 | } else { |
1692 | 0 | offsets.BEnd(cbwm) = nsLayoutUtils:: |
1693 | 0 | ComputeBSizeDependentValue(cbSize.BSize(cbwm), |
1694 | 0 | styleOffset.GetBEnd(cbwm)); |
1695 | 0 | } |
1696 | 0 |
|
1697 | 0 | if (bStartIsAuto && bEndIsAuto) { |
1698 | 0 | // Treat 'top' like 'static-position' |
1699 | 0 | offsets.BStart(cbwm) = hypotheticalPos.mBStart; |
1700 | 0 | bStartIsAuto = false; |
1701 | 0 | } |
1702 | 0 |
|
1703 | 0 | SetComputedLogicalOffsets(offsets.ConvertTo(wm, cbwm)); |
1704 | 0 |
|
1705 | 0 | typedef nsIFrame::ComputeSizeFlags ComputeSizeFlags; |
1706 | 0 | ComputeSizeFlags computeSizeFlags = ComputeSizeFlags::eDefault; |
1707 | 0 | if (mFlags.mIClampMarginBoxMinSize) { |
1708 | 0 | computeSizeFlags = ComputeSizeFlags(computeSizeFlags | |
1709 | 0 | ComputeSizeFlags::eIClampMarginBoxMinSize); |
1710 | 0 | } |
1711 | 0 | if (mFlags.mBClampMarginBoxMinSize) { |
1712 | 0 | computeSizeFlags = ComputeSizeFlags(computeSizeFlags | |
1713 | 0 | ComputeSizeFlags::eBClampMarginBoxMinSize); |
1714 | 0 | } |
1715 | 0 | if (mFlags.mApplyAutoMinSize) { |
1716 | 0 | computeSizeFlags = ComputeSizeFlags(computeSizeFlags | |
1717 | 0 | ComputeSizeFlags::eIApplyAutoMinSize); |
1718 | 0 | } |
1719 | 0 | if (mFlags.mShrinkWrap) { |
1720 | 0 | computeSizeFlags = |
1721 | 0 | ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eShrinkWrap); |
1722 | 0 | } |
1723 | 0 | if (mFlags.mUseAutoBSize) { |
1724 | 0 | computeSizeFlags = |
1725 | 0 | ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eUseAutoBSize); |
1726 | 0 | } |
1727 | 0 | if (wm.IsOrthogonalTo(cbwm)) { |
1728 | 0 | if (bStartIsAuto || bEndIsAuto) { |
1729 | 0 | computeSizeFlags = |
1730 | 0 | ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eShrinkWrap); |
1731 | 0 | } |
1732 | 0 | } else { |
1733 | 0 | if (iStartIsAuto || iEndIsAuto) { |
1734 | 0 | computeSizeFlags = |
1735 | 0 | ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eShrinkWrap); |
1736 | 0 | } |
1737 | 0 | } |
1738 | 0 |
|
1739 | 0 | LogicalSize computedSize(wm); |
1740 | 0 | { |
1741 | 0 | AutoMaybeDisableFontInflation an(mFrame); |
1742 | 0 |
|
1743 | 0 | computedSize = |
1744 | 0 | mFrame->ComputeSize(mRenderingContext, wm, cbSize.ConvertTo(wm, cbwm), |
1745 | 0 | cbSize.ConvertTo(wm, cbwm).ISize(wm), // XXX or AvailableISize()? |
1746 | 0 | ComputedLogicalMargin().Size(wm) + |
1747 | 0 | ComputedLogicalOffsets().Size(wm), |
1748 | 0 | ComputedLogicalBorderPadding().Size(wm) - |
1749 | 0 | ComputedLogicalPadding().Size(wm), |
1750 | 0 | ComputedLogicalPadding().Size(wm), |
1751 | 0 | computeSizeFlags); |
1752 | 0 | ComputedISize() = computedSize.ISize(wm); |
1753 | 0 | ComputedBSize() = computedSize.BSize(wm); |
1754 | 0 | NS_ASSERTION(ComputedISize() >= 0, "Bogus inline-size"); |
1755 | 0 | NS_ASSERTION(ComputedBSize() == NS_UNCONSTRAINEDSIZE || |
1756 | 0 | ComputedBSize() >= 0, "Bogus block-size"); |
1757 | 0 | } |
1758 | 0 | computedSize = computedSize.ConvertTo(cbwm, wm); |
1759 | 0 |
|
1760 | 0 | // XXX Now that we have ComputeSize, can we condense many of the |
1761 | 0 | // branches off of widthIsAuto? |
1762 | 0 |
|
1763 | 0 | LogicalMargin margin = ComputedLogicalMargin().ConvertTo(cbwm, wm); |
1764 | 0 | const LogicalMargin borderPadding = |
1765 | 0 | ComputedLogicalBorderPadding().ConvertTo(cbwm, wm); |
1766 | 0 |
|
1767 | 0 | bool iSizeIsAuto = eStyleUnit_Auto == mStylePosition->ISize(cbwm).GetUnit(); |
1768 | 0 | bool marginIStartIsAuto = false; |
1769 | 0 | bool marginIEndIsAuto = false; |
1770 | 0 | bool marginBStartIsAuto = false; |
1771 | 0 | bool marginBEndIsAuto = false; |
1772 | 0 | if (iStartIsAuto) { |
1773 | 0 | // We know 'right' is not 'auto' anymore thanks to the hypothetical |
1774 | 0 | // box code above. |
1775 | 0 | // Solve for 'left'. |
1776 | 0 | if (iSizeIsAuto) { |
1777 | 0 | // XXXldb This, and the corresponding code in |
1778 | 0 | // nsAbsoluteContainingBlock.cpp, could probably go away now that |
1779 | 0 | // we always compute widths. |
1780 | 0 | offsets.IStart(cbwm) = NS_AUTOOFFSET; |
1781 | 0 | } else { |
1782 | 0 | offsets.IStart(cbwm) = |
1783 | 0 | cbSize.ISize(cbwm) - offsets.IEnd(cbwm) - |
1784 | 0 | computedSize.ISize(cbwm) - margin.IStartEnd(cbwm) - |
1785 | 0 | borderPadding.IStartEnd(cbwm); |
1786 | 0 | } |
1787 | 0 | } else if (iEndIsAuto) { |
1788 | 0 | // We know 'left' is not 'auto' anymore thanks to the hypothetical |
1789 | 0 | // box code above. |
1790 | 0 | // Solve for 'right'. |
1791 | 0 | if (iSizeIsAuto) { |
1792 | 0 | // XXXldb This, and the corresponding code in |
1793 | 0 | // nsAbsoluteContainingBlock.cpp, could probably go away now that |
1794 | 0 | // we always compute widths. |
1795 | 0 | offsets.IEnd(cbwm) = NS_AUTOOFFSET; |
1796 | 0 | } else { |
1797 | 0 | offsets.IEnd(cbwm) = |
1798 | 0 | cbSize.ISize(cbwm) - offsets.IStart(cbwm) - |
1799 | 0 | computedSize.ISize(cbwm) - margin.IStartEnd(cbwm) - |
1800 | 0 | borderPadding.IStartEnd(cbwm); |
1801 | 0 | } |
1802 | 0 | } else { |
1803 | 0 | // Neither 'inline-start' nor 'inline-end' is 'auto'. |
1804 | 0 |
|
1805 | 0 | if (wm.IsOrthogonalTo(cbwm)) { |
1806 | 0 | // For orthogonal blocks, we need to handle the case where the block had |
1807 | 0 | // unconstrained block-size, which mapped to unconstrained inline-size |
1808 | 0 | // in the containing block's writing mode. |
1809 | 0 | nscoord autoISize = cbSize.ISize(cbwm) - margin.IStartEnd(cbwm) - |
1810 | 0 | borderPadding.IStartEnd(cbwm) - offsets.IStartEnd(cbwm); |
1811 | 0 | if (autoISize < 0) { |
1812 | 0 | autoISize = 0; |
1813 | 0 | } |
1814 | 0 |
|
1815 | 0 | if (computedSize.ISize(cbwm) == NS_UNCONSTRAINEDSIZE) { |
1816 | 0 | // For non-replaced elements with block-size auto, the block-size |
1817 | 0 | // fills the remaining space. |
1818 | 0 | computedSize.ISize(cbwm) = autoISize; |
1819 | 0 |
|
1820 | 0 | // XXX Do these need box-sizing adjustments? |
1821 | 0 | LogicalSize maxSize = ComputedMaxSize(cbwm); |
1822 | 0 | LogicalSize minSize = ComputedMinSize(cbwm); |
1823 | 0 | if (computedSize.ISize(cbwm) > maxSize.ISize(cbwm)) { |
1824 | 0 | computedSize.ISize(cbwm) = maxSize.ISize(cbwm); |
1825 | 0 | } |
1826 | 0 | if (computedSize.ISize(cbwm) < minSize.ISize(cbwm)) { |
1827 | 0 | computedSize.ISize(cbwm) = minSize.ISize(cbwm); |
1828 | 0 | } |
1829 | 0 | } |
1830 | 0 | } |
1831 | 0 |
|
1832 | 0 | // However, the inline-size might |
1833 | 0 | // still not fill all the available space (even though we didn't |
1834 | 0 | // shrink-wrap) in case: |
1835 | 0 | // * inline-size was specified |
1836 | 0 | // * we're dealing with a replaced element |
1837 | 0 | // * width was constrained by min- or max-inline-size. |
1838 | 0 |
|
1839 | 0 | nscoord availMarginSpace = |
1840 | 0 | aCBSize.ISize(cbwm) - offsets.IStartEnd(cbwm) - margin.IStartEnd(cbwm) - |
1841 | 0 | borderPadding.IStartEnd(cbwm) - computedSize.ISize(cbwm); |
1842 | 0 | marginIStartIsAuto = |
1843 | 0 | eStyleUnit_Auto == mStyleMargin->mMargin.GetIStartUnit(cbwm); |
1844 | 0 | marginIEndIsAuto = |
1845 | 0 | eStyleUnit_Auto == mStyleMargin->mMargin.GetIEndUnit(cbwm); |
1846 | 0 |
|
1847 | 0 | if (marginIStartIsAuto) { |
1848 | 0 | if (marginIEndIsAuto) { |
1849 | 0 | if (availMarginSpace < 0) { |
1850 | 0 | // Note that this case is different from the neither-'auto' |
1851 | 0 | // case below, where the spec says to ignore 'left'/'right'. |
1852 | 0 | // Ignore the specified value for 'margin-right'. |
1853 | 0 | margin.IEnd(cbwm) = availMarginSpace; |
1854 | 0 | } else { |
1855 | 0 | // Both 'margin-left' and 'margin-right' are 'auto', so they get |
1856 | 0 | // equal values |
1857 | 0 | margin.IStart(cbwm) = availMarginSpace / 2; |
1858 | 0 | margin.IEnd(cbwm) = availMarginSpace - margin.IStart(cbwm); |
1859 | 0 | } |
1860 | 0 | } else { |
1861 | 0 | // Just 'margin-left' is 'auto' |
1862 | 0 | margin.IStart(cbwm) = availMarginSpace; |
1863 | 0 | } |
1864 | 0 | } else { |
1865 | 0 | if (marginIEndIsAuto) { |
1866 | 0 | // Just 'margin-right' is 'auto' |
1867 | 0 | margin.IEnd(cbwm) = availMarginSpace; |
1868 | 0 | } else { |
1869 | 0 | // We're over-constrained so use the direction of the containing |
1870 | 0 | // block to dictate which value to ignore. (And note that the |
1871 | 0 | // spec says to ignore 'left' or 'right' rather than |
1872 | 0 | // 'margin-left' or 'margin-right'.) |
1873 | 0 | // Note that this case is different from the both-'auto' case |
1874 | 0 | // above, where the spec says to ignore |
1875 | 0 | // 'margin-left'/'margin-right'. |
1876 | 0 | // Ignore the specified value for 'right'. |
1877 | 0 | offsets.IEnd(cbwm) += availMarginSpace; |
1878 | 0 | } |
1879 | 0 | } |
1880 | 0 | } |
1881 | 0 |
|
1882 | 0 | bool bSizeIsAuto = eStyleUnit_Auto == mStylePosition->BSize(cbwm).GetUnit(); |
1883 | 0 | if (bStartIsAuto) { |
1884 | 0 | // solve for block-start |
1885 | 0 | if (bSizeIsAuto) { |
1886 | 0 | offsets.BStart(cbwm) = NS_AUTOOFFSET; |
1887 | 0 | } else { |
1888 | 0 | offsets.BStart(cbwm) = cbSize.BSize(cbwm) - margin.BStartEnd(cbwm) - |
1889 | 0 | borderPadding.BStartEnd(cbwm) - computedSize.BSize(cbwm) - |
1890 | 0 | offsets.BEnd(cbwm); |
1891 | 0 | } |
1892 | 0 | } else if (bEndIsAuto) { |
1893 | 0 | // solve for block-end |
1894 | 0 | if (bSizeIsAuto) { |
1895 | 0 | offsets.BEnd(cbwm) = NS_AUTOOFFSET; |
1896 | 0 | } else { |
1897 | 0 | offsets.BEnd(cbwm) = cbSize.BSize(cbwm) - margin.BStartEnd(cbwm) - |
1898 | 0 | borderPadding.BStartEnd(cbwm) - computedSize.BSize(cbwm) - |
1899 | 0 | offsets.BStart(cbwm); |
1900 | 0 | } |
1901 | 0 | } else { |
1902 | 0 | // Neither block-start nor -end is 'auto'. |
1903 | 0 | nscoord autoBSize = cbSize.BSize(cbwm) - margin.BStartEnd(cbwm) - |
1904 | 0 | borderPadding.BStartEnd(cbwm) - offsets.BStartEnd(cbwm); |
1905 | 0 | if (autoBSize < 0) { |
1906 | 0 | autoBSize = 0; |
1907 | 0 | } |
1908 | 0 |
|
1909 | 0 | if (computedSize.BSize(cbwm) == NS_UNCONSTRAINEDSIZE) { |
1910 | 0 | // For non-replaced elements with block-size auto, the block-size |
1911 | 0 | // fills the remaining space. |
1912 | 0 | computedSize.BSize(cbwm) = autoBSize; |
1913 | 0 |
|
1914 | 0 | // XXX Do these need box-sizing adjustments? |
1915 | 0 | LogicalSize maxSize = ComputedMaxSize(cbwm); |
1916 | 0 | LogicalSize minSize = ComputedMinSize(cbwm); |
1917 | 0 | if (computedSize.BSize(cbwm) > maxSize.BSize(cbwm)) { |
1918 | 0 | computedSize.BSize(cbwm) = maxSize.BSize(cbwm); |
1919 | 0 | } |
1920 | 0 | if (computedSize.BSize(cbwm) < minSize.BSize(cbwm)) { |
1921 | 0 | computedSize.BSize(cbwm) = minSize.BSize(cbwm); |
1922 | 0 | } |
1923 | 0 | } |
1924 | 0 |
|
1925 | 0 | // The block-size might still not fill all the available space in case: |
1926 | 0 | // * bsize was specified |
1927 | 0 | // * we're dealing with a replaced element |
1928 | 0 | // * bsize was constrained by min- or max-bsize. |
1929 | 0 | nscoord availMarginSpace = autoBSize - computedSize.BSize(cbwm); |
1930 | 0 | marginBStartIsAuto = |
1931 | 0 | eStyleUnit_Auto == mStyleMargin->mMargin.GetBStartUnit(cbwm); |
1932 | 0 | marginBEndIsAuto = |
1933 | 0 | eStyleUnit_Auto == mStyleMargin->mMargin.GetBEndUnit(cbwm); |
1934 | 0 |
|
1935 | 0 | if (marginBStartIsAuto) { |
1936 | 0 | if (marginBEndIsAuto) { |
1937 | 0 | // Both 'margin-top' and 'margin-bottom' are 'auto', so they get |
1938 | 0 | // equal values |
1939 | 0 | margin.BStart(cbwm) = availMarginSpace / 2; |
1940 | 0 | margin.BEnd(cbwm) = availMarginSpace - margin.BStart(cbwm); |
1941 | 0 | } else { |
1942 | 0 | // Just margin-block-start is 'auto' |
1943 | 0 | margin.BStart(cbwm) = availMarginSpace; |
1944 | 0 | } |
1945 | 0 | } else { |
1946 | 0 | if (marginBEndIsAuto) { |
1947 | 0 | // Just margin-block-end is 'auto' |
1948 | 0 | margin.BEnd(cbwm) = availMarginSpace; |
1949 | 0 | } else { |
1950 | 0 | // We're over-constrained so ignore the specified value for |
1951 | 0 | // block-end. (And note that the spec says to ignore 'bottom' |
1952 | 0 | // rather than 'margin-bottom'.) |
1953 | 0 | offsets.BEnd(cbwm) += availMarginSpace; |
1954 | 0 | } |
1955 | 0 | } |
1956 | 0 | } |
1957 | 0 | ComputedBSize() = computedSize.ConvertTo(wm, cbwm).BSize(wm); |
1958 | 0 | ComputedISize() = computedSize.ConvertTo(wm, cbwm).ISize(wm); |
1959 | 0 |
|
1960 | 0 | SetComputedLogicalOffsets(offsets.ConvertTo(wm, cbwm)); |
1961 | 0 |
|
1962 | 0 | LogicalMargin marginInOurWM = margin.ConvertTo(wm, cbwm); |
1963 | 0 | SetComputedLogicalMargin(marginInOurWM); |
1964 | 0 |
|
1965 | 0 | // If we have auto margins, update our UsedMarginProperty. The property |
1966 | 0 | // will have already been created by InitOffsets if it is needed. |
1967 | 0 | if (marginIStartIsAuto || marginIEndIsAuto || |
1968 | 0 | marginBStartIsAuto || marginBEndIsAuto) { |
1969 | 0 | nsMargin* propValue = mFrame->GetProperty(nsIFrame::UsedMarginProperty()); |
1970 | 0 | MOZ_ASSERT(propValue, "UsedMarginProperty should have been created " |
1971 | 0 | "by InitOffsets."); |
1972 | 0 | *propValue = marginInOurWM.GetPhysicalMargin(wm); |
1973 | 0 | } |
1974 | 0 | } |
1975 | | |
1976 | | // This will not be converted to abstract coordinates because it's only |
1977 | | // used in CalcQuirkContainingBlockHeight |
1978 | | static nscoord |
1979 | | GetBlockMarginBorderPadding(const ReflowInput* aReflowInput) |
1980 | 0 | { |
1981 | 0 | nscoord result = 0; |
1982 | 0 | if (!aReflowInput) return result; |
1983 | 0 | |
1984 | 0 | // zero auto margins |
1985 | 0 | nsMargin margin = aReflowInput->ComputedPhysicalMargin(); |
1986 | 0 | if (NS_AUTOMARGIN == margin.top) |
1987 | 0 | margin.top = 0; |
1988 | 0 | if (NS_AUTOMARGIN == margin.bottom) |
1989 | 0 | margin.bottom = 0; |
1990 | 0 |
|
1991 | 0 | result += margin.top + margin.bottom; |
1992 | 0 | result += aReflowInput->ComputedPhysicalBorderPadding().top + |
1993 | 0 | aReflowInput->ComputedPhysicalBorderPadding().bottom; |
1994 | 0 |
|
1995 | 0 | return result; |
1996 | 0 | } |
1997 | | |
1998 | | /* Get the height based on the viewport of the containing block specified |
1999 | | * in aReflowInput when the containing block has mComputedHeight == NS_AUTOHEIGHT |
2000 | | * This will walk up the chain of containing blocks looking for a computed height |
2001 | | * until it finds the canvas frame, or it encounters a frame that is not a block, |
2002 | | * area, or scroll frame. This handles compatibility with IE (see bug 85016 and bug 219693) |
2003 | | * |
2004 | | * When we encounter scrolledContent block frames, we skip over them, |
2005 | | * since they are guaranteed to not be useful for computing the containing block. |
2006 | | * |
2007 | | * See also IsQuirkContainingBlockHeight. |
2008 | | */ |
2009 | | static nscoord |
2010 | | CalcQuirkContainingBlockHeight(const ReflowInput* aCBReflowInput) |
2011 | 0 | { |
2012 | 0 | const ReflowInput* firstAncestorRI = nullptr; // a candidate for html frame |
2013 | 0 | const ReflowInput* secondAncestorRI = nullptr; // a candidate for body frame |
2014 | 0 |
|
2015 | 0 | // initialize the default to NS_AUTOHEIGHT as this is the containings block |
2016 | 0 | // computed height when this function is called. It is possible that we |
2017 | 0 | // don't alter this height especially if we are restricted to one level |
2018 | 0 | nscoord result = NS_AUTOHEIGHT; |
2019 | 0 |
|
2020 | 0 | const ReflowInput* ri = aCBReflowInput; |
2021 | 0 | for (; ri; ri = ri->mParentReflowInput) { |
2022 | 0 | LayoutFrameType frameType = ri->mFrame->Type(); |
2023 | 0 | // if the ancestor is auto height then skip it and continue up if it |
2024 | 0 | // is the first block frame and possibly the body/html |
2025 | 0 | if (LayoutFrameType::Block == frameType || |
2026 | 0 | #ifdef MOZ_XUL |
2027 | 0 | LayoutFrameType::XULLabel == frameType || |
2028 | 0 | #endif |
2029 | 0 | LayoutFrameType::Scroll == frameType) { |
2030 | 0 |
|
2031 | 0 | secondAncestorRI = firstAncestorRI; |
2032 | 0 | firstAncestorRI = ri; |
2033 | 0 |
|
2034 | 0 | // If the current frame we're looking at is positioned, we don't want to |
2035 | 0 | // go any further (see bug 221784). The behavior we want here is: 1) If |
2036 | 0 | // not auto-height, use this as the percentage base. 2) If auto-height, |
2037 | 0 | // keep looking, unless the frame is positioned. |
2038 | 0 | if (NS_AUTOHEIGHT == ri->ComputedHeight()) { |
2039 | 0 | if (ri->mFrame->IsAbsolutelyPositioned(ri->mStyleDisplay)) { |
2040 | 0 | break; |
2041 | 0 | } else { |
2042 | 0 | continue; |
2043 | 0 | } |
2044 | 0 | } |
2045 | 0 | } else if (LayoutFrameType::Canvas == frameType) { |
2046 | 0 | // Always continue on to the height calculation |
2047 | 0 | } else if (LayoutFrameType::PageContent == frameType) { |
2048 | 0 | nsIFrame* prevInFlow = ri->mFrame->GetPrevInFlow(); |
2049 | 0 | // only use the page content frame for a height basis if it is the first in flow |
2050 | 0 | if (prevInFlow) |
2051 | 0 | break; |
2052 | 0 | } |
2053 | 0 | else { |
2054 | 0 | break; |
2055 | 0 | } |
2056 | 0 | |
2057 | 0 | // if the ancestor is the page content frame then the percent base is |
2058 | 0 | // the avail height, otherwise it is the computed height |
2059 | 0 | result = (LayoutFrameType::PageContent == frameType) ? ri->AvailableHeight() |
2060 | 0 | : ri->ComputedHeight(); |
2061 | 0 | // if unconstrained - don't sutract borders - would result in huge height |
2062 | 0 | if (NS_AUTOHEIGHT == result) return result; |
2063 | 0 | |
2064 | 0 | // if we got to the canvas or page content frame, then subtract out |
2065 | 0 | // margin/border/padding for the BODY and HTML elements |
2066 | 0 | if ((LayoutFrameType::Canvas == frameType) || |
2067 | 0 | (LayoutFrameType::PageContent == frameType)) { |
2068 | 0 |
|
2069 | 0 | result -= GetBlockMarginBorderPadding(firstAncestorRI); |
2070 | 0 | result -= GetBlockMarginBorderPadding(secondAncestorRI); |
2071 | 0 |
|
2072 | | #ifdef DEBUG |
2073 | | // make sure the first ancestor is the HTML and the second is the BODY |
2074 | | if (firstAncestorRI) { |
2075 | | nsIContent* frameContent = firstAncestorRI->mFrame->GetContent(); |
2076 | | if (frameContent) { |
2077 | | NS_ASSERTION(frameContent->IsHTMLElement(nsGkAtoms::html), |
2078 | | "First ancestor is not HTML"); |
2079 | | } |
2080 | | } |
2081 | | if (secondAncestorRI) { |
2082 | | nsIContent* frameContent = secondAncestorRI->mFrame->GetContent(); |
2083 | | if (frameContent) { |
2084 | | NS_ASSERTION(frameContent->IsHTMLElement(nsGkAtoms::body), |
2085 | | "Second ancestor is not BODY"); |
2086 | | } |
2087 | | } |
2088 | | #endif |
2089 | |
|
2090 | 0 | } |
2091 | 0 | // if we got to the html frame (a block child of the canvas) ... |
2092 | 0 | else if (LayoutFrameType::Block == frameType && ri->mParentReflowInput && |
2093 | 0 | ri->mParentReflowInput->mFrame->IsCanvasFrame()) { |
2094 | 0 | // ... then subtract out margin/border/padding for the BODY element |
2095 | 0 | result -= GetBlockMarginBorderPadding(secondAncestorRI); |
2096 | 0 | } |
2097 | 0 | break; |
2098 | 0 | } |
2099 | 0 |
|
2100 | 0 | // Make sure not to return a negative height here! |
2101 | 0 | return std::max(result, 0); |
2102 | 0 | } |
2103 | | |
2104 | | // Called by InitConstraints() to compute the containing block rectangle for |
2105 | | // the element. Handles the special logic for absolutely positioned elements |
2106 | | LogicalSize |
2107 | | ReflowInput::ComputeContainingBlockRectangle( |
2108 | | nsPresContext* aPresContext, |
2109 | | const ReflowInput* aContainingBlockRI) const |
2110 | 0 | { |
2111 | 0 | // Unless the element is absolutely positioned, the containing block is |
2112 | 0 | // formed by the content edge of the nearest block-level ancestor |
2113 | 0 | LogicalSize cbSize = aContainingBlockRI->ComputedSize(); |
2114 | 0 |
|
2115 | 0 | WritingMode wm = aContainingBlockRI->GetWritingMode(); |
2116 | 0 |
|
2117 | 0 | // mFrameType for abs-pos tables is NS_CSS_FRAME_TYPE_BLOCK, so we need to |
2118 | 0 | // special case them here. |
2119 | 0 | if (NS_FRAME_GET_TYPE(mFrameType) == NS_CSS_FRAME_TYPE_ABSOLUTE || |
2120 | 0 | (mFrame->IsTableFrame() && mFrame->IsAbsolutelyPositioned(mStyleDisplay) && |
2121 | 0 | (mFrame->GetParent()->GetStateBits() & NS_FRAME_OUT_OF_FLOW))) { |
2122 | 0 | // See if the ancestor is block-level or inline-level |
2123 | 0 | if (NS_FRAME_GET_TYPE(aContainingBlockRI->mFrameType) == NS_CSS_FRAME_TYPE_INLINE) { |
2124 | 0 | // Base our size on the actual size of the frame. In cases when this is |
2125 | 0 | // completely bogus (eg initial reflow), this code shouldn't even be |
2126 | 0 | // called, since the code in nsInlineFrame::Reflow will pass in |
2127 | 0 | // the containing block dimensions to our constructor. |
2128 | 0 | // XXXbz we should be taking the in-flows into account too, but |
2129 | 0 | // that's very hard. |
2130 | 0 |
|
2131 | 0 | LogicalMargin computedBorder = |
2132 | 0 | aContainingBlockRI->ComputedLogicalBorderPadding() - |
2133 | 0 | aContainingBlockRI->ComputedLogicalPadding(); |
2134 | 0 | cbSize.ISize(wm) = aContainingBlockRI->mFrame->ISize(wm) - |
2135 | 0 | computedBorder.IStartEnd(wm); |
2136 | 0 | NS_ASSERTION(cbSize.ISize(wm) >= 0, |
2137 | 0 | "Negative containing block isize!"); |
2138 | 0 | cbSize.BSize(wm) = aContainingBlockRI->mFrame->BSize(wm) - |
2139 | 0 | computedBorder.BStartEnd(wm); |
2140 | 0 | NS_ASSERTION(cbSize.BSize(wm) >= 0, |
2141 | 0 | "Negative containing block bsize!"); |
2142 | 0 | } else { |
2143 | 0 | // If the ancestor is block-level, the containing block is formed by the |
2144 | 0 | // padding edge of the ancestor |
2145 | 0 | cbSize.ISize(wm) += |
2146 | 0 | aContainingBlockRI->ComputedLogicalPadding().IStartEnd(wm); |
2147 | 0 | cbSize.BSize(wm) += |
2148 | 0 | aContainingBlockRI->ComputedLogicalPadding().BStartEnd(wm); |
2149 | 0 | } |
2150 | 0 | } else { |
2151 | 0 | // an element in quirks mode gets a containing block based on looking for a |
2152 | 0 | // parent with a non-auto height if the element has a percent height |
2153 | 0 | // Note: We don't emulate this quirk for percents in calc() or in |
2154 | 0 | // vertical writing modes. |
2155 | 0 | if (!wm.IsVertical() && |
2156 | 0 | NS_AUTOHEIGHT == cbSize.BSize(wm)) { |
2157 | 0 | if (eCompatibility_NavQuirks == aPresContext->CompatibilityMode() && |
2158 | 0 | (mStylePosition->mHeight.GetUnit() == eStyleUnit_Percent || |
2159 | 0 | (mFrame->IsTableWrapperFrame() && |
2160 | 0 | mFrame->PrincipalChildList().FirstChild()->StylePosition()-> |
2161 | 0 | mHeight.GetUnit() == eStyleUnit_Percent))) { |
2162 | 0 | cbSize.BSize(wm) = CalcQuirkContainingBlockHeight(aContainingBlockRI); |
2163 | 0 | } |
2164 | 0 | } |
2165 | 0 | } |
2166 | 0 |
|
2167 | 0 | return cbSize.ConvertTo(GetWritingMode(), wm); |
2168 | 0 | } |
2169 | | |
2170 | | static eNormalLineHeightControl GetNormalLineHeightCalcControl(void) |
2171 | 0 | { |
2172 | 0 | if (sNormalLineHeightControl == eUninitialized) { |
2173 | 0 | // browser.display.normal_lineheight_calc_control is not user |
2174 | 0 | // changeable, so no need to register callback for it. |
2175 | 0 | int32_t val = |
2176 | 0 | Preferences::GetInt("browser.display.normal_lineheight_calc_control", |
2177 | 0 | eNoExternalLeading); |
2178 | 0 | sNormalLineHeightControl = static_cast<eNormalLineHeightControl>(val); |
2179 | 0 | } |
2180 | 0 | return sNormalLineHeightControl; |
2181 | 0 | } |
2182 | | |
2183 | | static inline bool |
2184 | | IsSideCaption(nsIFrame* aFrame, const nsStyleDisplay* aStyleDisplay, |
2185 | | WritingMode aWM) |
2186 | 0 | { |
2187 | 0 | if (aStyleDisplay->mDisplay != StyleDisplay::TableCaption) { |
2188 | 0 | return false; |
2189 | 0 | } |
2190 | 0 | uint8_t captionSide = aFrame->StyleTableBorder()->mCaptionSide; |
2191 | 0 | return captionSide == NS_STYLE_CAPTION_SIDE_LEFT || |
2192 | 0 | captionSide == NS_STYLE_CAPTION_SIDE_RIGHT; |
2193 | 0 | } |
2194 | | |
2195 | | // XXX refactor this code to have methods for each set of properties |
2196 | | // we are computing: width,height,line-height; margin; offsets |
2197 | | |
2198 | | void |
2199 | | ReflowInput::InitConstraints(nsPresContext* aPresContext, |
2200 | | const LogicalSize& aContainingBlockSize, |
2201 | | const nsMargin* aBorder, |
2202 | | const nsMargin* aPadding, |
2203 | | LayoutFrameType aFrameType) |
2204 | 0 | { |
2205 | 0 | WritingMode wm = GetWritingMode(); |
2206 | 0 | DISPLAY_INIT_CONSTRAINTS(mFrame, this, |
2207 | 0 | aContainingBlockSize.ISize(wm), |
2208 | 0 | aContainingBlockSize.BSize(wm), |
2209 | 0 | aBorder, aPadding); |
2210 | 0 |
|
2211 | 0 | // If this is a reflow root, then set the computed width and |
2212 | 0 | // height equal to the available space |
2213 | 0 | if (nullptr == mParentReflowInput || mFlags.mDummyParentReflowInput) { |
2214 | 0 | // XXXldb This doesn't mean what it used to! |
2215 | 0 | InitOffsets(wm, aContainingBlockSize.ISize(wm), |
2216 | 0 | aFrameType, mFlags, aBorder, aPadding, mStyleDisplay); |
2217 | 0 | // Override mComputedMargin since reflow roots start from the |
2218 | 0 | // frame's boundary, which is inside the margin. |
2219 | 0 | ComputedPhysicalMargin().SizeTo(0, 0, 0, 0); |
2220 | 0 | ComputedPhysicalOffsets().SizeTo(0, 0, 0, 0); |
2221 | 0 |
|
2222 | 0 | ComputedISize() = |
2223 | 0 | AvailableISize() - ComputedLogicalBorderPadding().IStartEnd(wm); |
2224 | 0 | if (ComputedISize() < 0) { |
2225 | 0 | ComputedISize() = 0; |
2226 | 0 | } |
2227 | 0 | if (AvailableBSize() != NS_UNCONSTRAINEDSIZE) { |
2228 | 0 | ComputedBSize() = |
2229 | 0 | AvailableBSize() - ComputedLogicalBorderPadding().BStartEnd(wm); |
2230 | 0 | if (ComputedBSize() < 0) { |
2231 | 0 | ComputedBSize() = 0; |
2232 | 0 | } |
2233 | 0 | } else { |
2234 | 0 | ComputedBSize() = NS_UNCONSTRAINEDSIZE; |
2235 | 0 | } |
2236 | 0 |
|
2237 | 0 | ComputedMinWidth() = ComputedMinHeight() = 0; |
2238 | 0 | ComputedMaxWidth() = ComputedMaxHeight() = NS_UNCONSTRAINEDSIZE; |
2239 | 0 | } else { |
2240 | 0 | // Get the containing block reflow state |
2241 | 0 | const ReflowInput* cbri = mCBReflowInput; |
2242 | 0 | MOZ_ASSERT(cbri, "no containing block"); |
2243 | 0 | MOZ_ASSERT(mFrame->GetParent()); |
2244 | 0 |
|
2245 | 0 | // If we weren't given a containing block width and height, then |
2246 | 0 | // compute one |
2247 | 0 | LogicalSize cbSize = (aContainingBlockSize == LogicalSize(wm, -1, -1)) |
2248 | 0 | ? ComputeContainingBlockRectangle(aPresContext, cbri) |
2249 | 0 | : aContainingBlockSize; |
2250 | 0 |
|
2251 | 0 | // See if the containing block height is based on the size of its |
2252 | 0 | // content |
2253 | 0 | if (NS_AUTOHEIGHT == cbSize.BSize(wm)) { |
2254 | 0 | // See if the containing block is a cell frame which needs |
2255 | 0 | // to use the mComputedHeight of the cell instead of what the cell block passed in. |
2256 | 0 | // XXX It seems like this could lead to bugs with min-height and friends |
2257 | 0 | if (cbri->mParentReflowInput) { |
2258 | 0 | if (IsTableCell(cbri->mFrame->Type())) { |
2259 | 0 | // use the cell's computed block size |
2260 | 0 | cbSize.BSize(wm) = cbri->ComputedSize(wm).BSize(wm); |
2261 | 0 | } |
2262 | 0 | } |
2263 | 0 | } |
2264 | 0 |
|
2265 | 0 | // XXX Might need to also pass the CB height (not width) for page boxes, |
2266 | 0 | // too, if we implement them. |
2267 | 0 |
|
2268 | 0 | // For calculating positioning offsets, margins, borders and |
2269 | 0 | // padding, we use the writing mode of the containing block |
2270 | 0 | WritingMode cbwm = cbri->GetWritingMode(); |
2271 | 0 | InitOffsets(cbwm, cbSize.ConvertTo(cbwm, wm).ISize(cbwm), |
2272 | 0 | aFrameType, mFlags, aBorder, aPadding, mStyleDisplay); |
2273 | 0 |
|
2274 | 0 | // For calculating the size of this box, we use its own writing mode |
2275 | 0 | const nsStyleCoord &blockSize = mStylePosition->BSize(wm); |
2276 | 0 | nsStyleUnit blockSizeUnit = blockSize.GetUnit(); |
2277 | 0 |
|
2278 | 0 | // Check for a percentage based block size and a containing block |
2279 | 0 | // block size that depends on the content block size |
2280 | 0 | // XXX twiddling blockSizeUnit doesn't help anymore |
2281 | 0 | // FIXME Shouldn't we fix that? |
2282 | 0 | if (blockSize.HasPercent()) { |
2283 | 0 | if (NS_AUTOHEIGHT == cbSize.BSize(wm)) { |
2284 | 0 | // this if clause enables %-blockSize on replaced inline frames, |
2285 | 0 | // such as images. See bug 54119. The else clause "blockSizeUnit = eStyleUnit_Auto;" |
2286 | 0 | // used to be called exclusively. |
2287 | 0 | if (NS_FRAME_REPLACED(NS_CSS_FRAME_TYPE_INLINE) == mFrameType || |
2288 | 0 | NS_FRAME_REPLACED_CONTAINS_BLOCK( |
2289 | 0 | NS_CSS_FRAME_TYPE_INLINE) == mFrameType) { |
2290 | 0 | // Get the containing block reflow state |
2291 | 0 | NS_ASSERTION(nullptr != cbri, "no containing block"); |
2292 | 0 | // in quirks mode, get the cb height using the special quirk method |
2293 | 0 | if (!wm.IsVertical() && |
2294 | 0 | eCompatibility_NavQuirks == aPresContext->CompatibilityMode()) { |
2295 | 0 | if (!IsTableCell(cbri->mFrame->Type())) { |
2296 | 0 | cbSize.BSize(wm) = CalcQuirkContainingBlockHeight(cbri); |
2297 | 0 | if (cbSize.BSize(wm) == NS_AUTOHEIGHT) { |
2298 | 0 | blockSizeUnit = eStyleUnit_Auto; |
2299 | 0 | } |
2300 | 0 | } |
2301 | 0 | else { |
2302 | 0 | blockSizeUnit = eStyleUnit_Auto; |
2303 | 0 | } |
2304 | 0 | } |
2305 | 0 | // in standard mode, use the cb block size. if it's "auto", |
2306 | 0 | // as will be the case by default in BODY, use auto block size |
2307 | 0 | // as per CSS2 spec. |
2308 | 0 | else |
2309 | 0 | { |
2310 | 0 | nscoord computedBSize = cbri->ComputedSize(wm).BSize(wm); |
2311 | 0 | if (NS_AUTOHEIGHT != computedBSize) { |
2312 | 0 | cbSize.BSize(wm) = computedBSize; |
2313 | 0 | } |
2314 | 0 | else { |
2315 | 0 | blockSizeUnit = eStyleUnit_Auto; |
2316 | 0 | } |
2317 | 0 | } |
2318 | 0 | } |
2319 | 0 | else { |
2320 | 0 | // default to interpreting the blockSize like 'auto' |
2321 | 0 | blockSizeUnit = eStyleUnit_Auto; |
2322 | 0 | } |
2323 | 0 | } |
2324 | 0 | } |
2325 | 0 |
|
2326 | 0 | // Compute our offsets if the element is relatively positioned. We |
2327 | 0 | // need the correct containing block inline-size and block-size |
2328 | 0 | // here, which is why we need to do it after all the quirks-n-such |
2329 | 0 | // above. (If the element is sticky positioned, we need to wait |
2330 | 0 | // until the scroll container knows its size, so we compute offsets |
2331 | 0 | // from StickyScrollContainer::UpdatePositions.) |
2332 | 0 | if (mStyleDisplay->IsRelativelyPositioned(mFrame) && |
2333 | 0 | NS_STYLE_POSITION_RELATIVE == mStyleDisplay->mPosition) { |
2334 | 0 | ComputeRelativeOffsets(cbwm, mFrame, cbSize.ConvertTo(cbwm, wm), |
2335 | 0 | ComputedPhysicalOffsets()); |
2336 | 0 | } else { |
2337 | 0 | // Initialize offsets to 0 |
2338 | 0 | ComputedPhysicalOffsets().SizeTo(0, 0, 0, 0); |
2339 | 0 | } |
2340 | 0 |
|
2341 | 0 | // Calculate the computed values for min and max properties. Note that |
2342 | 0 | // this MUST come after we've computed our border and padding. |
2343 | 0 | ComputeMinMaxValues(cbSize); |
2344 | 0 |
|
2345 | 0 | // Calculate the computed inlineSize and blockSize. |
2346 | 0 | // This varies by frame type. |
2347 | 0 |
|
2348 | 0 | if (NS_CSS_FRAME_TYPE_INTERNAL_TABLE == mFrameType) { |
2349 | 0 | // Internal table elements. The rules vary depending on the type. |
2350 | 0 | // Calculate the computed isize |
2351 | 0 | bool rowOrRowGroup = false; |
2352 | 0 | const nsStyleCoord &inlineSize = mStylePosition->ISize(wm); |
2353 | 0 | nsStyleUnit inlineSizeUnit = inlineSize.GetUnit(); |
2354 | 0 | if ((StyleDisplay::TableRow == mStyleDisplay->mDisplay) || |
2355 | 0 | (StyleDisplay::TableRowGroup == mStyleDisplay->mDisplay)) { |
2356 | 0 | // 'inlineSize' property doesn't apply to table rows and row groups |
2357 | 0 | inlineSizeUnit = eStyleUnit_Auto; |
2358 | 0 | rowOrRowGroup = true; |
2359 | 0 | } |
2360 | 0 |
|
2361 | 0 | // calc() with percentages acts like auto on internal table elements |
2362 | 0 | if (eStyleUnit_Auto == inlineSizeUnit || |
2363 | 0 | (inlineSize.IsCalcUnit() && inlineSize.CalcHasPercent())) { |
2364 | 0 | ComputedISize() = AvailableISize(); |
2365 | 0 |
|
2366 | 0 | if ((ComputedISize() != NS_UNCONSTRAINEDSIZE) && !rowOrRowGroup){ |
2367 | 0 | // Internal table elements don't have margins. Only tables and |
2368 | 0 | // cells have border and padding |
2369 | 0 | ComputedISize() -= ComputedLogicalBorderPadding().IStartEnd(wm); |
2370 | 0 | if (ComputedISize() < 0) |
2371 | 0 | ComputedISize() = 0; |
2372 | 0 | } |
2373 | 0 | NS_ASSERTION(ComputedISize() >= 0, "Bogus computed isize"); |
2374 | 0 |
|
2375 | 0 | } else { |
2376 | 0 | NS_ASSERTION(inlineSizeUnit == inlineSize.GetUnit(), |
2377 | 0 | "unexpected inline size unit change"); |
2378 | 0 | ComputedISize() = ComputeISizeValue(cbSize.ISize(wm), |
2379 | 0 | mStylePosition->mBoxSizing, |
2380 | 0 | inlineSize); |
2381 | 0 | } |
2382 | 0 |
|
2383 | 0 | // Calculate the computed block size |
2384 | 0 | if ((StyleDisplay::TableColumn == mStyleDisplay->mDisplay) || |
2385 | 0 | (StyleDisplay::TableColumnGroup == mStyleDisplay->mDisplay)) { |
2386 | 0 | // 'blockSize' property doesn't apply to table columns and column groups |
2387 | 0 | blockSizeUnit = eStyleUnit_Auto; |
2388 | 0 | } |
2389 | 0 | // calc() with percentages acts like 'auto' on internal table elements |
2390 | 0 | if (eStyleUnit_Auto == blockSizeUnit || |
2391 | 0 | (blockSize.IsCalcUnit() && blockSize.CalcHasPercent())) { |
2392 | 0 | ComputedBSize() = NS_AUTOHEIGHT; |
2393 | 0 | } else { |
2394 | 0 | NS_ASSERTION(blockSizeUnit == blockSize.GetUnit(), |
2395 | 0 | "unexpected block size unit change"); |
2396 | 0 | ComputedBSize() = ComputeBSizeValue(cbSize.BSize(wm), |
2397 | 0 | mStylePosition->mBoxSizing, |
2398 | 0 | blockSize); |
2399 | 0 | } |
2400 | 0 |
|
2401 | 0 | // Doesn't apply to table elements |
2402 | 0 | ComputedMinWidth() = ComputedMinHeight() = 0; |
2403 | 0 | ComputedMaxWidth() = ComputedMaxHeight() = NS_UNCONSTRAINEDSIZE; |
2404 | 0 |
|
2405 | 0 | } else if (NS_FRAME_GET_TYPE(mFrameType) == NS_CSS_FRAME_TYPE_ABSOLUTE) { |
2406 | 0 | // XXX not sure if this belongs here or somewhere else - cwk |
2407 | 0 | InitAbsoluteConstraints(aPresContext, cbri, |
2408 | 0 | cbSize.ConvertTo(cbri->GetWritingMode(), wm), |
2409 | 0 | aFrameType); |
2410 | 0 | } else { |
2411 | 0 | AutoMaybeDisableFontInflation an(mFrame); |
2412 | 0 |
|
2413 | 0 | bool isBlock = NS_CSS_FRAME_TYPE_BLOCK == NS_FRAME_GET_TYPE(mFrameType); |
2414 | 0 | typedef nsIFrame::ComputeSizeFlags ComputeSizeFlags; |
2415 | 0 | ComputeSizeFlags computeSizeFlags = |
2416 | 0 | isBlock ? ComputeSizeFlags::eDefault : ComputeSizeFlags::eShrinkWrap; |
2417 | 0 | if (mFlags.mIClampMarginBoxMinSize) { |
2418 | 0 | computeSizeFlags = ComputeSizeFlags(computeSizeFlags | |
2419 | 0 | ComputeSizeFlags::eIClampMarginBoxMinSize); |
2420 | 0 | } |
2421 | 0 | if (mFlags.mBClampMarginBoxMinSize) { |
2422 | 0 | computeSizeFlags = ComputeSizeFlags(computeSizeFlags | |
2423 | 0 | ComputeSizeFlags::eBClampMarginBoxMinSize); |
2424 | 0 | } |
2425 | 0 | if (mFlags.mApplyAutoMinSize) { |
2426 | 0 | computeSizeFlags = ComputeSizeFlags(computeSizeFlags | |
2427 | 0 | ComputeSizeFlags::eIApplyAutoMinSize); |
2428 | 0 | } |
2429 | 0 | if (mFlags.mShrinkWrap) { |
2430 | 0 | computeSizeFlags = |
2431 | 0 | ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eShrinkWrap); |
2432 | 0 | } |
2433 | 0 | if (mFlags.mUseAutoBSize) { |
2434 | 0 | computeSizeFlags = |
2435 | 0 | ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eUseAutoBSize); |
2436 | 0 | } |
2437 | 0 |
|
2438 | 0 | nsIFrame* alignCB = mFrame->GetParent(); |
2439 | 0 | if (alignCB->IsTableWrapperFrame() && alignCB->GetParent()) { |
2440 | 0 | // XXX grid-specific for now; maybe remove this check after we address bug 799725 |
2441 | 0 | if (alignCB->GetParent()->IsGridContainerFrame()) { |
2442 | 0 | alignCB = alignCB->GetParent(); |
2443 | 0 | } |
2444 | 0 | } |
2445 | 0 | if (alignCB->IsGridContainerFrame()) { |
2446 | 0 | // Shrink-wrap grid items that will be aligned (rather than stretched) |
2447 | 0 | // in its inline axis. |
2448 | 0 | auto inlineAxisAlignment = |
2449 | 0 | wm.IsOrthogonalTo(cbwm) |
2450 | 0 | ? mStylePosition->UsedAlignSelf(alignCB->Style()) |
2451 | 0 | : mStylePosition->UsedJustifySelf(alignCB->Style()); |
2452 | 0 | if ((inlineAxisAlignment != NS_STYLE_ALIGN_STRETCH && |
2453 | 0 | inlineAxisAlignment != NS_STYLE_ALIGN_NORMAL) || |
2454 | 0 | mStyleMargin->mMargin.GetIStartUnit(wm) == eStyleUnit_Auto || |
2455 | 0 | mStyleMargin->mMargin.GetIEndUnit(wm) == eStyleUnit_Auto) { |
2456 | 0 | computeSizeFlags = |
2457 | 0 | ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eShrinkWrap); |
2458 | 0 | } |
2459 | 0 | } else { |
2460 | 0 | // Make sure legend frames with display:block and width:auto still |
2461 | 0 | // shrink-wrap. |
2462 | 0 | // Also shrink-wrap blocks that are orthogonal to their container. |
2463 | 0 | if (isBlock && |
2464 | 0 | ((aFrameType == LayoutFrameType::Legend && |
2465 | 0 | mFrame->Style()->GetPseudo() != nsCSSAnonBoxes::scrolledContent()) || |
2466 | 0 | (aFrameType == LayoutFrameType::Scroll && |
2467 | 0 | mFrame->GetContentInsertionFrame()->IsLegendFrame()) || |
2468 | 0 | (mCBReflowInput && |
2469 | 0 | mCBReflowInput->GetWritingMode().IsOrthogonalTo(mWritingMode)))) { |
2470 | 0 | computeSizeFlags = |
2471 | 0 | ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eShrinkWrap); |
2472 | 0 | } |
2473 | 0 |
|
2474 | 0 | if (alignCB->IsFlexContainerFrame()) { |
2475 | 0 | computeSizeFlags = |
2476 | 0 | ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eShrinkWrap); |
2477 | 0 |
|
2478 | 0 | // If we're inside of a flex container that needs to measure our |
2479 | 0 | // auto BSize, pass that information along to ComputeSize(). |
2480 | 0 | if (mFlags.mIsFlexContainerMeasuringBSize) { |
2481 | 0 | computeSizeFlags = |
2482 | 0 | ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eUseAutoBSize); |
2483 | 0 | } |
2484 | 0 | } else { |
2485 | 0 | MOZ_ASSERT(!mFlags.mIsFlexContainerMeasuringBSize, |
2486 | 0 | "We're not in a flex container, so the flag " |
2487 | 0 | "'mIsFlexContainerMeasuringBSize' shouldn't be set"); |
2488 | 0 | } |
2489 | 0 | } |
2490 | 0 |
|
2491 | 0 | if (cbSize.ISize(wm) == NS_UNCONSTRAINEDSIZE) { |
2492 | 0 | // For orthogonal flows, where we found a parent orthogonal-limit |
2493 | 0 | // for AvailableISize() in Init(), we'll use the same here as well. |
2494 | 0 | cbSize.ISize(wm) = AvailableISize(); |
2495 | 0 | } |
2496 | 0 |
|
2497 | 0 | LogicalSize size = |
2498 | 0 | mFrame->ComputeSize(mRenderingContext, wm, cbSize, AvailableISize(), |
2499 | 0 | ComputedLogicalMargin().Size(wm), |
2500 | 0 | ComputedLogicalBorderPadding().Size(wm) - |
2501 | 0 | ComputedLogicalPadding().Size(wm), |
2502 | 0 | ComputedLogicalPadding().Size(wm), |
2503 | 0 | computeSizeFlags); |
2504 | 0 |
|
2505 | 0 | ComputedISize() = size.ISize(wm); |
2506 | 0 | ComputedBSize() = size.BSize(wm); |
2507 | 0 | NS_ASSERTION(ComputedISize() >= 0, "Bogus inline-size"); |
2508 | 0 | NS_ASSERTION(ComputedBSize() == NS_UNCONSTRAINEDSIZE || |
2509 | 0 | ComputedBSize() >= 0, "Bogus block-size"); |
2510 | 0 |
|
2511 | 0 | // Exclude inline tables, side captions, flex and grid items from block |
2512 | 0 | // margin calculations. |
2513 | 0 | if (isBlock && !IsSideCaption(mFrame, mStyleDisplay, cbwm) && |
2514 | 0 | mStyleDisplay->mDisplay != StyleDisplay::InlineTable && |
2515 | 0 | !alignCB->IsFlexOrGridContainer()) { |
2516 | 0 | CalculateBlockSideMargins(aFrameType); |
2517 | 0 | } |
2518 | 0 | } |
2519 | 0 | } |
2520 | 0 |
|
2521 | 0 | // Save our containing block dimensions |
2522 | 0 | mContainingBlockSize = aContainingBlockSize; |
2523 | 0 | } |
2524 | | |
2525 | | static void |
2526 | | UpdateProp(nsIFrame* aFrame, |
2527 | | const FramePropertyDescriptor<nsMargin>* aProperty, |
2528 | | bool aNeeded, |
2529 | | const nsMargin& aNewValue) |
2530 | 0 | { |
2531 | 0 | if (aNeeded) { |
2532 | 0 | nsMargin* propValue = aFrame->GetProperty(aProperty); |
2533 | 0 | if (propValue) { |
2534 | 0 | *propValue = aNewValue; |
2535 | 0 | } else { |
2536 | 0 | aFrame->AddProperty(aProperty, new nsMargin(aNewValue)); |
2537 | 0 | } |
2538 | 0 | } else { |
2539 | 0 | aFrame->DeleteProperty(aProperty); |
2540 | 0 | } |
2541 | 0 | } |
2542 | | |
2543 | | void |
2544 | | SizeComputationInput::InitOffsets(WritingMode aWM, |
2545 | | nscoord aPercentBasis, |
2546 | | LayoutFrameType aFrameType, |
2547 | | ReflowInputFlags aFlags, |
2548 | | const nsMargin* aBorder, |
2549 | | const nsMargin* aPadding, |
2550 | | const nsStyleDisplay* aDisplay) |
2551 | 0 | { |
2552 | 0 | DISPLAY_INIT_OFFSETS(mFrame, this, aPercentBasis, aWM, aBorder, aPadding); |
2553 | 0 |
|
2554 | 0 | // Since we are in reflow, we don't need to store these properties anymore |
2555 | 0 | // unless they are dependent on width, in which case we store the new value. |
2556 | 0 | nsPresContext *presContext = mFrame->PresContext(); |
2557 | 0 | mFrame->DeleteProperty(nsIFrame::UsedBorderProperty()); |
2558 | 0 |
|
2559 | 0 | // Compute margins from the specified margin style information. These |
2560 | 0 | // become the default computed values, and may be adjusted below |
2561 | 0 | // XXX fix to provide 0,0 for the top&bottom margins for |
2562 | 0 | // inline-non-replaced elements |
2563 | 0 | bool needMarginProp = ComputeMargin(aWM, aPercentBasis); |
2564 | 0 | // Note that ComputeMargin() simplistically resolves 'auto' margins to 0. |
2565 | 0 | // In formatting contexts where this isn't correct, some later code will |
2566 | 0 | // need to update the UsedMargin() property with the actual resolved value. |
2567 | 0 | // One example of this is ::CalculateBlockSideMargins(). |
2568 | 0 | ::UpdateProp(mFrame, nsIFrame::UsedMarginProperty(), needMarginProp, |
2569 | 0 | ComputedPhysicalMargin()); |
2570 | 0 |
|
2571 | 0 |
|
2572 | 0 | const nsStyleDisplay* disp = mFrame->StyleDisplayWithOptionalParam(aDisplay); |
2573 | 0 | bool isThemed = mFrame->IsThemed(disp); |
2574 | 0 | bool needPaddingProp; |
2575 | 0 | LayoutDeviceIntMargin widgetPadding; |
2576 | 0 | if (isThemed && |
2577 | 0 | presContext->GetTheme()->GetWidgetPadding(presContext->DeviceContext(), |
2578 | 0 | mFrame, disp->mAppearance, |
2579 | 0 | &widgetPadding)) { |
2580 | 0 | ComputedPhysicalPadding() = |
2581 | 0 | LayoutDevicePixel::ToAppUnits(widgetPadding, |
2582 | 0 | presContext->AppUnitsPerDevPixel()); |
2583 | 0 | needPaddingProp = false; |
2584 | 0 | } |
2585 | 0 | else if (nsSVGUtils::IsInSVGTextSubtree(mFrame)) { |
2586 | 0 | ComputedPhysicalPadding().SizeTo(0, 0, 0, 0); |
2587 | 0 | needPaddingProp = false; |
2588 | 0 | } |
2589 | 0 | else if (aPadding) { // padding is an input arg |
2590 | 0 | ComputedPhysicalPadding() = *aPadding; |
2591 | 0 | needPaddingProp = mFrame->StylePadding()->IsWidthDependent() || |
2592 | 0 | (mFrame->GetStateBits() & NS_FRAME_REFLOW_ROOT); |
2593 | 0 | } |
2594 | 0 | else { |
2595 | 0 | needPaddingProp = ComputePadding(aWM, aPercentBasis, aFrameType); |
2596 | 0 | } |
2597 | 0 |
|
2598 | 0 | // Add [align|justify]-content:baseline padding contribution. |
2599 | 0 | typedef const FramePropertyDescriptor<SmallValueHolder<nscoord>>* Prop; |
2600 | 0 | auto ApplyBaselinePadding = [this, &needPaddingProp] |
2601 | 0 | (LogicalAxis aAxis, Prop aProp) { |
2602 | 0 | bool found; |
2603 | 0 | nscoord val = mFrame->GetProperty(aProp, &found); |
2604 | 0 | if (found) { |
2605 | 0 | NS_ASSERTION(val != nscoord(0), "zero in this property is useless"); |
2606 | 0 | WritingMode wm = GetWritingMode(); |
2607 | 0 | LogicalSide side; |
2608 | 0 | if (val > 0) { |
2609 | 0 | side = MakeLogicalSide(aAxis, eLogicalEdgeStart); |
2610 | 0 | } else { |
2611 | 0 | side = MakeLogicalSide(aAxis, eLogicalEdgeEnd); |
2612 | 0 | val = -val; |
2613 | 0 | } |
2614 | 0 | mComputedPadding.Side(wm.PhysicalSide(side)) += val; |
2615 | 0 | needPaddingProp = true; |
2616 | 0 | } |
2617 | 0 | }; |
2618 | 0 | if (!aFlags.mUseAutoBSize) { |
2619 | 0 | ApplyBaselinePadding(eLogicalAxisBlock, nsIFrame::BBaselinePadProperty()); |
2620 | 0 | } |
2621 | 0 | if (!aFlags.mShrinkWrap) { |
2622 | 0 | ApplyBaselinePadding(eLogicalAxisInline, nsIFrame::IBaselinePadProperty()); |
2623 | 0 | } |
2624 | 0 |
|
2625 | 0 | if (isThemed) { |
2626 | 0 | LayoutDeviceIntMargin border = |
2627 | 0 | presContext->GetTheme()->GetWidgetBorder(presContext->DeviceContext(), |
2628 | 0 | mFrame, disp->mAppearance); |
2629 | 0 | ComputedPhysicalBorderPadding() = |
2630 | 0 | LayoutDevicePixel::ToAppUnits(border, |
2631 | 0 | presContext->AppUnitsPerDevPixel()); |
2632 | 0 | } |
2633 | 0 | else if (nsSVGUtils::IsInSVGTextSubtree(mFrame)) { |
2634 | 0 | ComputedPhysicalBorderPadding().SizeTo(0, 0, 0, 0); |
2635 | 0 | } |
2636 | 0 | else if (aBorder) { // border is an input arg |
2637 | 0 | ComputedPhysicalBorderPadding() = *aBorder; |
2638 | 0 | } |
2639 | 0 | else { |
2640 | 0 | ComputedPhysicalBorderPadding() = mFrame->StyleBorder()->GetComputedBorder(); |
2641 | 0 | } |
2642 | 0 | ComputedPhysicalBorderPadding() += ComputedPhysicalPadding(); |
2643 | 0 |
|
2644 | 0 | if (aFrameType == LayoutFrameType::Table) { |
2645 | 0 | nsTableFrame *tableFrame = static_cast<nsTableFrame*>(mFrame); |
2646 | 0 |
|
2647 | 0 | if (tableFrame->IsBorderCollapse()) { |
2648 | 0 | // border-collapsed tables don't use any of their padding, and |
2649 | 0 | // only part of their border. We need to do this here before we |
2650 | 0 | // try to do anything like handling 'auto' widths, |
2651 | 0 | // 'box-sizing', or 'auto' margins. |
2652 | 0 | ComputedPhysicalPadding().SizeTo(0,0,0,0); |
2653 | 0 | SetComputedLogicalBorderPadding( |
2654 | 0 | tableFrame->GetIncludedOuterBCBorder(mWritingMode)); |
2655 | 0 | } |
2656 | 0 |
|
2657 | 0 | // The margin is inherited to the table wrapper frame via |
2658 | 0 | // the ::-moz-table-wrapper rule in ua.css. |
2659 | 0 | ComputedPhysicalMargin().SizeTo(0, 0, 0, 0); |
2660 | 0 | } else if (aFrameType == LayoutFrameType::Scrollbar) { |
2661 | 0 | // scrollbars may have had their width or height smashed to zero |
2662 | 0 | // by the associated scrollframe, in which case we must not report |
2663 | 0 | // any padding or border. |
2664 | 0 | nsSize size(mFrame->GetSize()); |
2665 | 0 | if (size.width == 0 || size.height == 0) { |
2666 | 0 | ComputedPhysicalPadding().SizeTo(0,0,0,0); |
2667 | 0 | ComputedPhysicalBorderPadding().SizeTo(0,0,0,0); |
2668 | 0 | } |
2669 | 0 | } |
2670 | 0 | ::UpdateProp(mFrame, nsIFrame::UsedPaddingProperty(), needPaddingProp, |
2671 | 0 | ComputedPhysicalPadding()); |
2672 | 0 | } |
2673 | | |
2674 | | // This code enforces section 10.3.3 of the CSS2 spec for this formula: |
2675 | | // |
2676 | | // 'margin-left' + 'border-left-width' + 'padding-left' + 'width' + |
2677 | | // 'padding-right' + 'border-right-width' + 'margin-right' |
2678 | | // = width of containing block |
2679 | | // |
2680 | | // Note: the width unit is not auto when this is called |
2681 | | void |
2682 | | ReflowInput::CalculateBlockSideMargins(LayoutFrameType aFrameType) |
2683 | 0 | { |
2684 | 0 | // Calculations here are done in the containing block's writing mode, |
2685 | 0 | // which is where margins will eventually be applied: we're calculating |
2686 | 0 | // margins that will be used by the container in its inline direction, |
2687 | 0 | // which in the case of an orthogonal contained block will correspond to |
2688 | 0 | // the block direction of this reflow state. So in the orthogonal-flow |
2689 | 0 | // case, "CalculateBlock*Side*Margins" will actually end up adjusting |
2690 | 0 | // the BStart/BEnd margins; those are the "sides" of the block from its |
2691 | 0 | // container's point of view. |
2692 | 0 | WritingMode cbWM = |
2693 | 0 | mCBReflowInput ? mCBReflowInput->GetWritingMode(): GetWritingMode(); |
2694 | 0 |
|
2695 | 0 | nscoord availISizeCBWM = AvailableSize(cbWM).ISize(cbWM); |
2696 | 0 | nscoord computedISizeCBWM = ComputedSize(cbWM).ISize(cbWM); |
2697 | 0 | if (computedISizeCBWM == NS_UNCONSTRAINEDSIZE) { |
2698 | 0 | // For orthogonal flows, where we found a parent orthogonal-limit |
2699 | 0 | // for AvailableISize() in Init(), we'll use the same here as well. |
2700 | 0 | computedISizeCBWM = availISizeCBWM; |
2701 | 0 | } |
2702 | 0 |
|
2703 | 0 | LAYOUT_WARN_IF_FALSE(NS_UNCONSTRAINEDSIZE != computedISizeCBWM && |
2704 | 0 | NS_UNCONSTRAINEDSIZE != availISizeCBWM, |
2705 | 0 | "have unconstrained inline-size; this should only " |
2706 | 0 | "result from very large sizes, not attempts at " |
2707 | 0 | "intrinsic inline-size calculation"); |
2708 | 0 |
|
2709 | 0 | LogicalMargin margin = |
2710 | 0 | ComputedLogicalMargin().ConvertTo(cbWM, mWritingMode); |
2711 | 0 | LogicalMargin borderPadding = |
2712 | 0 | ComputedLogicalBorderPadding().ConvertTo(cbWM, mWritingMode); |
2713 | 0 | nscoord sum = margin.IStartEnd(cbWM) + |
2714 | 0 | borderPadding.IStartEnd(cbWM) + computedISizeCBWM; |
2715 | 0 | if (sum == availISizeCBWM) { |
2716 | 0 | // The sum is already correct |
2717 | 0 | return; |
2718 | 0 | } |
2719 | 0 | |
2720 | 0 | // Determine the start and end margin values. The isize value |
2721 | 0 | // remains constant while we do this. |
2722 | 0 | |
2723 | 0 | // Calculate how much space is available for margins |
2724 | 0 | nscoord availMarginSpace = availISizeCBWM - sum; |
2725 | 0 |
|
2726 | 0 | // If the available margin space is negative, then don't follow the |
2727 | 0 | // usual overconstraint rules. |
2728 | 0 | if (availMarginSpace < 0) { |
2729 | 0 | margin.IEnd(cbWM) += availMarginSpace; |
2730 | 0 | SetComputedLogicalMargin(margin.ConvertTo(mWritingMode, cbWM)); |
2731 | 0 | return; |
2732 | 0 | } |
2733 | 0 | |
2734 | 0 | // The css2 spec clearly defines how block elements should behave |
2735 | 0 | // in section 10.3.3. |
2736 | 0 | const nsStyleSides& styleSides = mStyleMargin->mMargin; |
2737 | 0 | bool isAutoStartMargin = eStyleUnit_Auto == styleSides.GetIStartUnit(cbWM); |
2738 | 0 | bool isAutoEndMargin = eStyleUnit_Auto == styleSides.GetIEndUnit(cbWM); |
2739 | 0 | if (!isAutoStartMargin && !isAutoEndMargin) { |
2740 | 0 | // Neither margin is 'auto' so we're over constrained. Use the |
2741 | 0 | // 'direction' property of the parent to tell which margin to |
2742 | 0 | // ignore |
2743 | 0 | // First check if there is an HTML alignment that we should honor |
2744 | 0 | const ReflowInput* pri = mParentReflowInput; |
2745 | 0 | if (aFrameType == LayoutFrameType::Table) { |
2746 | 0 | NS_ASSERTION(pri->mFrame->IsTableWrapperFrame(), |
2747 | 0 | "table not inside table wrapper"); |
2748 | 0 | // Center the table within the table wrapper based on the alignment |
2749 | 0 | // of the table wrapper's parent. |
2750 | 0 | pri = pri->mParentReflowInput; |
2751 | 0 | } |
2752 | 0 | if (pri && |
2753 | 0 | (pri->mStyleText->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_LEFT || |
2754 | 0 | pri->mStyleText->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_CENTER || |
2755 | 0 | pri->mStyleText->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_RIGHT)) { |
2756 | 0 | if (pri->mWritingMode.IsBidiLTR()) { |
2757 | 0 | isAutoStartMargin = |
2758 | 0 | pri->mStyleText->mTextAlign != NS_STYLE_TEXT_ALIGN_MOZ_LEFT; |
2759 | 0 | isAutoEndMargin = |
2760 | 0 | pri->mStyleText->mTextAlign != NS_STYLE_TEXT_ALIGN_MOZ_RIGHT; |
2761 | 0 | } else { |
2762 | 0 | isAutoStartMargin = |
2763 | 0 | pri->mStyleText->mTextAlign != NS_STYLE_TEXT_ALIGN_MOZ_RIGHT; |
2764 | 0 | isAutoEndMargin = |
2765 | 0 | pri->mStyleText->mTextAlign != NS_STYLE_TEXT_ALIGN_MOZ_LEFT; |
2766 | 0 | } |
2767 | 0 | } |
2768 | 0 | // Otherwise apply the CSS rules, and ignore one margin by forcing |
2769 | 0 | // it to 'auto', depending on 'direction'. |
2770 | 0 | else { |
2771 | 0 | isAutoEndMargin = true; |
2772 | 0 | } |
2773 | 0 | } |
2774 | 0 |
|
2775 | 0 | // Logic which is common to blocks and tables |
2776 | 0 | // The computed margins need not be zero because the 'auto' could come from |
2777 | 0 | // overconstraint or from HTML alignment so values need to be accumulated |
2778 | 0 |
|
2779 | 0 | if (isAutoStartMargin) { |
2780 | 0 | if (isAutoEndMargin) { |
2781 | 0 | // Both margins are 'auto' so the computed addition should be equal |
2782 | 0 | nscoord forStart = availMarginSpace / 2; |
2783 | 0 | margin.IStart(cbWM) += forStart; |
2784 | 0 | margin.IEnd(cbWM) += availMarginSpace - forStart; |
2785 | 0 | } else { |
2786 | 0 | margin.IStart(cbWM) += availMarginSpace; |
2787 | 0 | } |
2788 | 0 | } else if (isAutoEndMargin) { |
2789 | 0 | margin.IEnd(cbWM) += availMarginSpace; |
2790 | 0 | } |
2791 | 0 | LogicalMargin marginInOurWM = margin.ConvertTo(mWritingMode, cbWM); |
2792 | 0 | SetComputedLogicalMargin(marginInOurWM); |
2793 | 0 |
|
2794 | 0 | if (isAutoStartMargin || isAutoEndMargin) { |
2795 | 0 | // Update the UsedMargin property if we were tracking it already. |
2796 | 0 | nsMargin* propValue = mFrame->GetProperty(nsIFrame::UsedMarginProperty()); |
2797 | 0 | if (propValue) { |
2798 | 0 | *propValue = marginInOurWM.GetPhysicalMargin(mWritingMode); |
2799 | 0 | } |
2800 | 0 | } |
2801 | 0 | } |
2802 | | |
2803 | 0 | #define NORMAL_LINE_HEIGHT_FACTOR 1.2f // in term of emHeight |
2804 | | // For "normal" we use the font's normal line height (em height + leading). |
2805 | | // If both internal leading and external leading specified by font itself |
2806 | | // are zeros, we should compensate this by creating extra (external) leading |
2807 | | // in eCompensateLeading mode. This is necessary because without this |
2808 | | // compensation, normal line height might looks too tight. |
2809 | | |
2810 | | // For risk management, we use preference to control the behavior, and |
2811 | | // eNoExternalLeading is the old behavior. |
2812 | | static nscoord |
2813 | | GetNormalLineHeight(nsFontMetrics* aFontMetrics) |
2814 | 0 | { |
2815 | 0 | MOZ_ASSERT(nullptr != aFontMetrics, "no font metrics"); |
2816 | 0 |
|
2817 | 0 | nscoord normalLineHeight; |
2818 | 0 |
|
2819 | 0 | nscoord externalLeading = aFontMetrics->ExternalLeading(); |
2820 | 0 | nscoord internalLeading = aFontMetrics->InternalLeading(); |
2821 | 0 | nscoord emHeight = aFontMetrics->EmHeight(); |
2822 | 0 | switch (GetNormalLineHeightCalcControl()) { |
2823 | 0 | case eIncludeExternalLeading: |
2824 | 0 | normalLineHeight = emHeight+ internalLeading + externalLeading; |
2825 | 0 | break; |
2826 | 0 | case eCompensateLeading: |
2827 | 0 | if (!internalLeading && !externalLeading) |
2828 | 0 | normalLineHeight = NSToCoordRound(emHeight * NORMAL_LINE_HEIGHT_FACTOR); |
2829 | 0 | else |
2830 | 0 | normalLineHeight = emHeight+ internalLeading + externalLeading; |
2831 | 0 | break; |
2832 | 0 | default: |
2833 | 0 | //case eNoExternalLeading: |
2834 | 0 | normalLineHeight = emHeight + internalLeading; |
2835 | 0 | } |
2836 | 0 | return normalLineHeight; |
2837 | 0 | } |
2838 | | |
2839 | | static inline nscoord |
2840 | | ComputeLineHeight(ComputedStyle* aComputedStyle, |
2841 | | nsPresContext* aPresContext, |
2842 | | nscoord aBlockBSize, |
2843 | | float aFontSizeInflation) |
2844 | 0 | { |
2845 | 0 | const nsStyleCoord& lhCoord = aComputedStyle->StyleText()->mLineHeight; |
2846 | 0 |
|
2847 | 0 | if (lhCoord.GetUnit() == eStyleUnit_Coord) { |
2848 | 0 | nscoord result = lhCoord.GetCoordValue(); |
2849 | 0 | if (aFontSizeInflation != 1.0f) { |
2850 | 0 | result = NSToCoordRound(result * aFontSizeInflation); |
2851 | 0 | } |
2852 | 0 | return result; |
2853 | 0 | } |
2854 | 0 |
|
2855 | 0 | if (lhCoord.GetUnit() == eStyleUnit_Factor) |
2856 | 0 | // For factor units the computed value of the line-height property |
2857 | 0 | // is found by multiplying the factor by the font's computed size |
2858 | 0 | // (adjusted for min-size prefs and text zoom). |
2859 | 0 | return NSToCoordRound(lhCoord.GetFactorValue() * aFontSizeInflation * |
2860 | 0 | aComputedStyle->StyleFont()->mFont.size); |
2861 | 0 | |
2862 | 0 | NS_ASSERTION(lhCoord.GetUnit() == eStyleUnit_Normal || |
2863 | 0 | lhCoord.GetUnit() == eStyleUnit_Enumerated, |
2864 | 0 | "bad line-height unit"); |
2865 | 0 |
|
2866 | 0 | if (lhCoord.GetUnit() == eStyleUnit_Enumerated) { |
2867 | 0 | NS_ASSERTION(lhCoord.GetIntValue() == NS_STYLE_LINE_HEIGHT_BLOCK_HEIGHT, |
2868 | 0 | "bad line-height value"); |
2869 | 0 | if (aBlockBSize != NS_AUTOHEIGHT) { |
2870 | 0 | return aBlockBSize; |
2871 | 0 | } |
2872 | 0 | } |
2873 | 0 | |
2874 | 0 | RefPtr<nsFontMetrics> fm = nsLayoutUtils:: |
2875 | 0 | GetFontMetricsForComputedStyle(aComputedStyle, aPresContext, aFontSizeInflation); |
2876 | 0 | return GetNormalLineHeight(fm); |
2877 | 0 | } |
2878 | | |
2879 | | nscoord |
2880 | | ReflowInput::CalcLineHeight() const |
2881 | 0 | { |
2882 | 0 | nscoord blockBSize = |
2883 | 0 | nsLayoutUtils::IsNonWrapperBlock(mFrame) ? ComputedBSize() : |
2884 | 0 | (mCBReflowInput ? mCBReflowInput->ComputedBSize() : NS_AUTOHEIGHT); |
2885 | 0 |
|
2886 | 0 | return CalcLineHeight(mFrame->GetContent(), |
2887 | 0 | mFrame->Style(), |
2888 | 0 | mFrame->PresContext(), |
2889 | 0 | blockBSize, |
2890 | 0 | nsLayoutUtils::FontSizeInflationFor(mFrame)); |
2891 | 0 | } |
2892 | | |
2893 | | /* static */ nscoord |
2894 | | ReflowInput::CalcLineHeight(nsIContent* aContent, |
2895 | | ComputedStyle* aComputedStyle, |
2896 | | nsPresContext* aPresContext, |
2897 | | nscoord aBlockBSize, |
2898 | | float aFontSizeInflation) |
2899 | 0 | { |
2900 | 0 | MOZ_ASSERT(aComputedStyle, "Must have a ComputedStyle"); |
2901 | 0 |
|
2902 | 0 | nscoord lineHeight = |
2903 | 0 | ComputeLineHeight(aComputedStyle, |
2904 | 0 | aPresContext, |
2905 | 0 | aBlockBSize, |
2906 | 0 | aFontSizeInflation); |
2907 | 0 |
|
2908 | 0 | NS_ASSERTION(lineHeight >= 0, "ComputeLineHeight screwed up"); |
2909 | 0 |
|
2910 | 0 | HTMLInputElement* input = HTMLInputElement::FromNodeOrNull(aContent); |
2911 | 0 | if (input && input->IsSingleLineTextControl()) { |
2912 | 0 | // For Web-compatibility, single-line text input elements cannot |
2913 | 0 | // have a line-height smaller than one. |
2914 | 0 | nscoord lineHeightOne = |
2915 | 0 | aFontSizeInflation * aComputedStyle->StyleFont()->mFont.size; |
2916 | 0 | if (lineHeight < lineHeightOne) { |
2917 | 0 | lineHeight = lineHeightOne; |
2918 | 0 | } |
2919 | 0 | } |
2920 | 0 |
|
2921 | 0 | return lineHeight; |
2922 | 0 | } |
2923 | | |
2924 | | bool |
2925 | | SizeComputationInput::ComputeMargin(WritingMode aWM, |
2926 | | nscoord aPercentBasis) |
2927 | 0 | { |
2928 | 0 | // SVG text frames have no margin. |
2929 | 0 | if (nsSVGUtils::IsInSVGTextSubtree(mFrame)) { |
2930 | 0 | return false; |
2931 | 0 | } |
2932 | 0 | |
2933 | 0 | // If style style can provide us the margin directly, then use it. |
2934 | 0 | const nsStyleMargin *styleMargin = mFrame->StyleMargin(); |
2935 | 0 |
|
2936 | 0 | bool isCBDependent = !styleMargin->GetMargin(ComputedPhysicalMargin()); |
2937 | 0 | if (isCBDependent) { |
2938 | 0 | // We have to compute the value. Note that this calculation is |
2939 | 0 | // performed according to the writing mode of the containing block |
2940 | 0 | // (http://dev.w3.org/csswg/css-writing-modes-3/#orthogonal-flows) |
2941 | 0 | LogicalMargin m(aWM); |
2942 | 0 | m.IStart(aWM) = nsLayoutUtils:: |
2943 | 0 | ComputeCBDependentValue(aPercentBasis, |
2944 | 0 | styleMargin->mMargin.GetIStart(aWM)); |
2945 | 0 | m.IEnd(aWM) = nsLayoutUtils:: |
2946 | 0 | ComputeCBDependentValue(aPercentBasis, |
2947 | 0 | styleMargin->mMargin.GetIEnd(aWM)); |
2948 | 0 |
|
2949 | 0 | m.BStart(aWM) = nsLayoutUtils:: |
2950 | 0 | ComputeCBDependentValue(aPercentBasis, |
2951 | 0 | styleMargin->mMargin.GetBStart(aWM)); |
2952 | 0 | m.BEnd(aWM) = nsLayoutUtils:: |
2953 | 0 | ComputeCBDependentValue(aPercentBasis, |
2954 | 0 | styleMargin->mMargin.GetBEnd(aWM)); |
2955 | 0 |
|
2956 | 0 | SetComputedLogicalMargin(aWM, m); |
2957 | 0 | } |
2958 | 0 |
|
2959 | 0 | // ... but font-size-inflation-based margin adjustment uses the |
2960 | 0 | // frame's writing mode |
2961 | 0 | nscoord marginAdjustment = FontSizeInflationListMarginAdjustment(mFrame); |
2962 | 0 |
|
2963 | 0 | if (marginAdjustment > 0) { |
2964 | 0 | LogicalMargin m = ComputedLogicalMargin(); |
2965 | 0 | m.IStart(mWritingMode) += marginAdjustment; |
2966 | 0 | SetComputedLogicalMargin(m); |
2967 | 0 | } |
2968 | 0 |
|
2969 | 0 | return isCBDependent; |
2970 | 0 | } |
2971 | | |
2972 | | bool |
2973 | | SizeComputationInput::ComputePadding(WritingMode aWM, |
2974 | | nscoord aPercentBasis, |
2975 | | LayoutFrameType aFrameType) |
2976 | 0 | { |
2977 | 0 | // If style can provide us the padding directly, then use it. |
2978 | 0 | const nsStylePadding *stylePadding = mFrame->StylePadding(); |
2979 | 0 | bool isCBDependent = !stylePadding->GetPadding(ComputedPhysicalPadding()); |
2980 | 0 | // a table row/col group, row/col doesn't have padding |
2981 | 0 | // XXXldb Neither do border-collapse tables. |
2982 | 0 | if (LayoutFrameType::TableRowGroup == aFrameType || |
2983 | 0 | LayoutFrameType::TableColGroup == aFrameType || |
2984 | 0 | LayoutFrameType::TableRow == aFrameType || |
2985 | 0 | LayoutFrameType::TableCol == aFrameType) { |
2986 | 0 | ComputedPhysicalPadding().SizeTo(0,0,0,0); |
2987 | 0 | } |
2988 | 0 | else if (isCBDependent) { |
2989 | 0 | // We have to compute the value. This calculation is performed |
2990 | 0 | // according to the writing mode of the containing block |
2991 | 0 | // (http://dev.w3.org/csswg/css-writing-modes-3/#orthogonal-flows) |
2992 | 0 | // clamp negative calc() results to 0 |
2993 | 0 | LogicalMargin p(aWM); |
2994 | 0 | p.IStart(aWM) = std::max(0, nsLayoutUtils:: |
2995 | 0 | ComputeCBDependentValue(aPercentBasis, |
2996 | 0 | stylePadding->mPadding.GetIStart(aWM))); |
2997 | 0 | p.IEnd(aWM) = std::max(0, nsLayoutUtils:: |
2998 | 0 | ComputeCBDependentValue(aPercentBasis, |
2999 | 0 | stylePadding->mPadding.GetIEnd(aWM))); |
3000 | 0 |
|
3001 | 0 | p.BStart(aWM) = std::max(0, nsLayoutUtils:: |
3002 | 0 | ComputeCBDependentValue(aPercentBasis, |
3003 | 0 | stylePadding->mPadding.GetBStart(aWM))); |
3004 | 0 | p.BEnd(aWM) = std::max(0, nsLayoutUtils:: |
3005 | 0 | ComputeCBDependentValue(aPercentBasis, |
3006 | 0 | stylePadding->mPadding.GetBEnd(aWM))); |
3007 | 0 |
|
3008 | 0 | SetComputedLogicalPadding(aWM, p); |
3009 | 0 | } |
3010 | 0 | return isCBDependent; |
3011 | 0 | } |
3012 | | |
3013 | | void |
3014 | | ReflowInput::ComputeMinMaxValues(const LogicalSize&aCBSize) |
3015 | 0 | { |
3016 | 0 | WritingMode wm = GetWritingMode(); |
3017 | 0 |
|
3018 | 0 | const nsStyleCoord& minISize = mStylePosition->MinISize(wm); |
3019 | 0 | const nsStyleCoord& maxISize = mStylePosition->MaxISize(wm); |
3020 | 0 | const nsStyleCoord& minBSize = mStylePosition->MinBSize(wm); |
3021 | 0 | const nsStyleCoord& maxBSize = mStylePosition->MaxBSize(wm); |
3022 | 0 |
|
3023 | 0 | // NOTE: min-width:auto resolves to 0, except on a flex item. (But |
3024 | 0 | // even there, it's supposed to be ignored (i.e. treated as 0) until |
3025 | 0 | // the flex container explicitly resolves & considers it.) |
3026 | 0 | if (eStyleUnit_Auto == minISize.GetUnit()) { |
3027 | 0 | ComputedMinISize() = 0; |
3028 | 0 | } else { |
3029 | 0 | ComputedMinISize() = ComputeISizeValue(aCBSize.ISize(wm), |
3030 | 0 | mStylePosition->mBoxSizing, |
3031 | 0 | minISize); |
3032 | 0 | } |
3033 | 0 |
|
3034 | 0 | if (eStyleUnit_None == maxISize.GetUnit()) { |
3035 | 0 | // Specified value of 'none' |
3036 | 0 | ComputedMaxISize() = NS_UNCONSTRAINEDSIZE; // no limit |
3037 | 0 | } else { |
3038 | 0 | ComputedMaxISize() = ComputeISizeValue(aCBSize.ISize(wm), |
3039 | 0 | mStylePosition->mBoxSizing, |
3040 | 0 | maxISize); |
3041 | 0 | } |
3042 | 0 |
|
3043 | 0 | // If the computed value of 'min-width' is greater than the value of |
3044 | 0 | // 'max-width', 'max-width' is set to the value of 'min-width' |
3045 | 0 | if (ComputedMinISize() > ComputedMaxISize()) { |
3046 | 0 | ComputedMaxISize() = ComputedMinISize(); |
3047 | 0 | } |
3048 | 0 |
|
3049 | 0 | // Check for percentage based values and a containing block height that |
3050 | 0 | // depends on the content height. Treat them like 'auto' |
3051 | 0 | // Likewise, check for calc() with percentages on internal table elements; |
3052 | 0 | // that's treated as 'auto' too. |
3053 | 0 | // Likewise, if we're a child of a flex container who's measuring our |
3054 | 0 | // intrinsic height, then we want to disregard our min-height. |
3055 | 0 |
|
3056 | 0 | // NOTE: min-height:auto resolves to 0, except on a flex item. (But |
3057 | 0 | // even there, it's supposed to be ignored (i.e. treated as 0) until |
3058 | 0 | // the flex container explicitly resolves & considers it.) |
3059 | 0 | if (eStyleUnit_Auto == minBSize.GetUnit() || |
3060 | 0 | (NS_AUTOHEIGHT == aCBSize.BSize(wm) && |
3061 | 0 | minBSize.HasPercent()) || |
3062 | 0 | (mFrameType == NS_CSS_FRAME_TYPE_INTERNAL_TABLE && |
3063 | 0 | minBSize.IsCalcUnit() && minBSize.CalcHasPercent()) || |
3064 | 0 | mFlags.mIsFlexContainerMeasuringBSize) { |
3065 | 0 | ComputedMinBSize() = 0; |
3066 | 0 | } else { |
3067 | 0 | ComputedMinBSize() = ComputeBSizeValue(aCBSize.BSize(wm), |
3068 | 0 | mStylePosition->mBoxSizing, |
3069 | 0 | minBSize); |
3070 | 0 | } |
3071 | 0 | nsStyleUnit maxBSizeUnit = maxBSize.GetUnit(); |
3072 | 0 | if (eStyleUnit_None == maxBSizeUnit) { |
3073 | 0 | // Specified value of 'none' |
3074 | 0 | ComputedMaxBSize() = NS_UNCONSTRAINEDSIZE; // no limit |
3075 | 0 | } else { |
3076 | 0 | // Check for percentage based values and a containing block height that |
3077 | 0 | // depends on the content height. Treat them like 'none' |
3078 | 0 | // Likewise, check for calc() with percentages on internal table elements; |
3079 | 0 | // that's treated as 'auto' too. |
3080 | 0 | // Likewise, if we're a child of a flex container who's measuring our |
3081 | 0 | // intrinsic height, then we want to disregard our max-height. |
3082 | 0 | if ((NS_AUTOHEIGHT == aCBSize.BSize(wm) && |
3083 | 0 | maxBSize.HasPercent()) || |
3084 | 0 | (mFrameType == NS_CSS_FRAME_TYPE_INTERNAL_TABLE && |
3085 | 0 | maxBSize.IsCalcUnit() && maxBSize.CalcHasPercent()) || |
3086 | 0 | mFlags.mIsFlexContainerMeasuringBSize) { |
3087 | 0 | ComputedMaxBSize() = NS_UNCONSTRAINEDSIZE; |
3088 | 0 | } else { |
3089 | 0 | ComputedMaxBSize() = ComputeBSizeValue(aCBSize.BSize(wm), |
3090 | 0 | mStylePosition->mBoxSizing, |
3091 | 0 | maxBSize); |
3092 | 0 | } |
3093 | 0 | } |
3094 | 0 |
|
3095 | 0 | // If the computed value of 'min-height' is greater than the value of |
3096 | 0 | // 'max-height', 'max-height' is set to the value of 'min-height' |
3097 | 0 | if (ComputedMinBSize() > ComputedMaxBSize()) { |
3098 | 0 | ComputedMaxBSize() = ComputedMinBSize(); |
3099 | 0 | } |
3100 | 0 | } |
3101 | | |
3102 | | bool |
3103 | | ReflowInput::IsFloating() const |
3104 | 0 | { |
3105 | 0 | return mStyleDisplay->IsFloating(mFrame); |
3106 | 0 | } |
3107 | | |
3108 | | mozilla::StyleDisplay |
3109 | | ReflowInput::GetDisplay() const |
3110 | 0 | { |
3111 | 0 | return mStyleDisplay->GetDisplay(mFrame); |
3112 | 0 | } |