|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 /* struct containing the input to nsIFrame::Reflow */ |
|
7 |
|
8 #include "nsHTMLReflowState.h" |
|
9 |
|
10 #include "nsStyleConsts.h" |
|
11 #include "nsCSSAnonBoxes.h" |
|
12 #include "nsFrame.h" |
|
13 #include "nsIContent.h" |
|
14 #include "nsGkAtoms.h" |
|
15 #include "nsPresContext.h" |
|
16 #include "nsIPresShell.h" |
|
17 #include "nsFontMetrics.h" |
|
18 #include "nsBlockFrame.h" |
|
19 #include "nsLineBox.h" |
|
20 #include "nsFlexContainerFrame.h" |
|
21 #include "nsImageFrame.h" |
|
22 #include "nsTableFrame.h" |
|
23 #include "nsTableCellFrame.h" |
|
24 #include "nsIPercentHeightObserver.h" |
|
25 #include "nsLayoutUtils.h" |
|
26 #include "mozilla/Preferences.h" |
|
27 #include "nsFontInflationData.h" |
|
28 #include "StickyScrollContainer.h" |
|
29 #include "nsIFrameInlines.h" |
|
30 #include <algorithm> |
|
31 #include "mozilla/dom/HTMLInputElement.h" |
|
32 |
|
33 #ifdef DEBUG |
|
34 #undef NOISY_VERTICAL_ALIGN |
|
35 #else |
|
36 #undef NOISY_VERTICAL_ALIGN |
|
37 #endif |
|
38 |
|
39 using namespace mozilla; |
|
40 using namespace mozilla::css; |
|
41 using namespace mozilla::dom; |
|
42 using namespace mozilla::layout; |
|
43 |
|
44 enum eNormalLineHeightControl { |
|
45 eUninitialized = -1, |
|
46 eNoExternalLeading = 0, // does not include external leading |
|
47 eIncludeExternalLeading, // use whatever value font vendor provides |
|
48 eCompensateLeading // compensate leading if leading provided by font vendor is not enough |
|
49 }; |
|
50 |
|
51 static eNormalLineHeightControl sNormalLineHeightControl = eUninitialized; |
|
52 |
|
53 // Initialize a <b>root</b> reflow state with a rendering context to |
|
54 // use for measuring things. |
|
55 nsHTMLReflowState::nsHTMLReflowState(nsPresContext* aPresContext, |
|
56 nsIFrame* aFrame, |
|
57 nsRenderingContext* aRenderingContext, |
|
58 const nsSize& aAvailableSpace, |
|
59 uint32_t aFlags) |
|
60 : nsCSSOffsetState(aFrame, aRenderingContext) |
|
61 , mBlockDelta(0) |
|
62 , mReflowDepth(0) |
|
63 { |
|
64 NS_PRECONDITION(aRenderingContext, "no rendering context"); |
|
65 MOZ_ASSERT(aPresContext, "no pres context"); |
|
66 MOZ_ASSERT(aFrame, "no frame"); |
|
67 MOZ_ASSERT(aPresContext == aFrame->PresContext(), "wrong pres context"); |
|
68 parentReflowState = nullptr; |
|
69 AvailableWidth() = aAvailableSpace.width; |
|
70 AvailableHeight() = aAvailableSpace.height; |
|
71 mFloatManager = nullptr; |
|
72 mLineLayout = nullptr; |
|
73 memset(&mFlags, 0, sizeof(mFlags)); |
|
74 mDiscoveredClearance = nullptr; |
|
75 mPercentHeightObserver = nullptr; |
|
76 |
|
77 if (aFlags & DUMMY_PARENT_REFLOW_STATE) { |
|
78 mFlags.mDummyParentReflowState = true; |
|
79 } |
|
80 |
|
81 if (!(aFlags & CALLER_WILL_INIT)) { |
|
82 Init(aPresContext); |
|
83 } |
|
84 } |
|
85 |
|
86 static bool CheckNextInFlowParenthood(nsIFrame* aFrame, nsIFrame* aParent) |
|
87 { |
|
88 nsIFrame* frameNext = aFrame->GetNextInFlow(); |
|
89 nsIFrame* parentNext = aParent->GetNextInFlow(); |
|
90 return frameNext && parentNext && frameNext->GetParent() == parentNext; |
|
91 } |
|
92 |
|
93 /** |
|
94 * Adjusts the margin for a list (ol, ul), if necessary, depending on |
|
95 * font inflation settings. Unfortunately, because bullets from a list are |
|
96 * placed in the margin area, we only have ~40px in which to place the |
|
97 * bullets. When they are inflated, however, this causes problems, since |
|
98 * the text takes up more space than is available in the margin. |
|
99 * |
|
100 * This method will return a small amount (in app units) by which the |
|
101 * margin can be adjusted, so that the space is available for list |
|
102 * bullets to be rendered with font inflation enabled. |
|
103 */ |
|
104 static nscoord |
|
105 FontSizeInflationListMarginAdjustment(const nsIFrame* aFrame) |
|
106 { |
|
107 float inflation = nsLayoutUtils::FontSizeInflationFor(aFrame); |
|
108 if (aFrame->IsFrameOfType(nsIFrame::eBlockFrame)) { |
|
109 const nsBlockFrame* blockFrame = static_cast<const nsBlockFrame*>(aFrame); |
|
110 const nsStyleList* styleList = aFrame->StyleList(); |
|
111 |
|
112 // We only want to adjust the margins if we're dealing with an ordered |
|
113 // list. |
|
114 if (inflation > 1.0f && |
|
115 blockFrame->HasBullet() && |
|
116 styleList->mListStyleType != NS_STYLE_LIST_STYLE_NONE && |
|
117 styleList->mListStyleType != NS_STYLE_LIST_STYLE_DISC && |
|
118 styleList->mListStyleType != NS_STYLE_LIST_STYLE_CIRCLE && |
|
119 styleList->mListStyleType != NS_STYLE_LIST_STYLE_SQUARE && |
|
120 inflation > 1.0f) { |
|
121 |
|
122 // The HTML spec states that the default padding for ordered lists begins |
|
123 // at 40px, indicating that we have 40px of space to place a bullet. When |
|
124 // performing font inflation calculations, we add space equivalent to this, |
|
125 // but simply inflated at the same amount as the text, in app units. |
|
126 return nsPresContext::CSSPixelsToAppUnits(40) * (inflation - 1); |
|
127 } |
|
128 } |
|
129 |
|
130 return 0; |
|
131 } |
|
132 |
|
133 // NOTE: If we ever want to use nsCSSOffsetState for a flex item or a grid |
|
134 // item, we need to make it take the containing-block height as well as the |
|
135 // width, since flex items and grid items resolve vertical percent margins |
|
136 // and padding against the containing-block height, rather than its width. |
|
137 nsCSSOffsetState::nsCSSOffsetState(nsIFrame *aFrame, |
|
138 nsRenderingContext *aRenderingContext, |
|
139 nscoord aContainingBlockWidth) |
|
140 : frame(aFrame) |
|
141 , rendContext(aRenderingContext) |
|
142 , mWritingMode(aFrame->GetWritingMode()) |
|
143 { |
|
144 MOZ_ASSERT(!aFrame->IsFlexItem(), |
|
145 "We're about to resolve vertical percent margin & padding " |
|
146 "values against CB width, which is incorrect for flex items"); |
|
147 InitOffsets(aContainingBlockWidth, aContainingBlockWidth, frame->GetType()); |
|
148 } |
|
149 |
|
150 // Initialize a reflow state for a child frame's reflow. Some state |
|
151 // is copied from the parent reflow state; the remaining state is |
|
152 // computed. |
|
153 nsHTMLReflowState::nsHTMLReflowState(nsPresContext* aPresContext, |
|
154 const nsHTMLReflowState& aParentReflowState, |
|
155 nsIFrame* aFrame, |
|
156 const nsSize& aAvailableSpace, |
|
157 nscoord aContainingBlockWidth, |
|
158 nscoord aContainingBlockHeight, |
|
159 uint32_t aFlags) |
|
160 : nsCSSOffsetState(aFrame, aParentReflowState.rendContext) |
|
161 , mBlockDelta(0) |
|
162 , mReflowDepth(aParentReflowState.mReflowDepth + 1) |
|
163 , mFlags(aParentReflowState.mFlags) |
|
164 { |
|
165 MOZ_ASSERT(aPresContext, "no pres context"); |
|
166 MOZ_ASSERT(aFrame, "no frame"); |
|
167 MOZ_ASSERT(aPresContext == aFrame->PresContext(), "wrong pres context"); |
|
168 NS_PRECONDITION((aContainingBlockWidth == -1) == |
|
169 (aContainingBlockHeight == -1), |
|
170 "cb width and height should only be non-default together"); |
|
171 NS_PRECONDITION(!mFlags.mSpecialHeightReflow || |
|
172 !NS_SUBTREE_DIRTY(aFrame), |
|
173 "frame should be clean when getting special height reflow"); |
|
174 |
|
175 parentReflowState = &aParentReflowState; |
|
176 |
|
177 // If the parent is dirty, then the child is as well. |
|
178 // XXX Are the other cases where the parent reflows a child a second |
|
179 // time, as a resize? |
|
180 if (!mFlags.mSpecialHeightReflow) |
|
181 frame->AddStateBits(parentReflowState->frame->GetStateBits() & |
|
182 NS_FRAME_IS_DIRTY); |
|
183 |
|
184 AvailableWidth() = aAvailableSpace.width; |
|
185 AvailableHeight() = aAvailableSpace.height; |
|
186 |
|
187 mFloatManager = aParentReflowState.mFloatManager; |
|
188 if (frame->IsFrameOfType(nsIFrame::eLineParticipant)) |
|
189 mLineLayout = aParentReflowState.mLineLayout; |
|
190 else |
|
191 mLineLayout = nullptr; |
|
192 |
|
193 // Note: mFlags was initialized as a copy of aParentReflowState.mFlags up in |
|
194 // this constructor's init list, so the only flags that we need to explicitly |
|
195 // initialize here are those that may need a value other than our parent's. |
|
196 mFlags.mNextInFlowUntouched = aParentReflowState.mFlags.mNextInFlowUntouched && |
|
197 CheckNextInFlowParenthood(aFrame, aParentReflowState.frame); |
|
198 mFlags.mAssumingHScrollbar = mFlags.mAssumingVScrollbar = false; |
|
199 mFlags.mHasClearance = false; |
|
200 mFlags.mIsColumnBalancing = false; |
|
201 mFlags.mIsFlexContainerMeasuringHeight = false; |
|
202 mFlags.mDummyParentReflowState = false; |
|
203 |
|
204 mDiscoveredClearance = nullptr; |
|
205 mPercentHeightObserver = (aParentReflowState.mPercentHeightObserver && |
|
206 aParentReflowState.mPercentHeightObserver->NeedsToObserve(*this)) |
|
207 ? aParentReflowState.mPercentHeightObserver : nullptr; |
|
208 |
|
209 if (aFlags & DUMMY_PARENT_REFLOW_STATE) { |
|
210 mFlags.mDummyParentReflowState = true; |
|
211 } |
|
212 |
|
213 if (!(aFlags & CALLER_WILL_INIT)) { |
|
214 Init(aPresContext, aContainingBlockWidth, aContainingBlockHeight); |
|
215 } |
|
216 } |
|
217 |
|
218 inline nscoord |
|
219 nsCSSOffsetState::ComputeWidthValue(nscoord aContainingBlockWidth, |
|
220 nscoord aContentEdgeToBoxSizing, |
|
221 nscoord aBoxSizingToMarginEdge, |
|
222 const nsStyleCoord& aCoord) |
|
223 { |
|
224 return nsLayoutUtils::ComputeWidthValue(rendContext, frame, |
|
225 aContainingBlockWidth, |
|
226 aContentEdgeToBoxSizing, |
|
227 aBoxSizingToMarginEdge, |
|
228 aCoord); |
|
229 } |
|
230 |
|
231 nscoord |
|
232 nsCSSOffsetState::ComputeWidthValue(nscoord aContainingBlockWidth, |
|
233 uint8_t aBoxSizing, |
|
234 const nsStyleCoord& aCoord) |
|
235 { |
|
236 nscoord inside = 0, outside = ComputedPhysicalBorderPadding().LeftRight() + |
|
237 ComputedPhysicalMargin().LeftRight(); |
|
238 switch (aBoxSizing) { |
|
239 case NS_STYLE_BOX_SIZING_BORDER: |
|
240 inside = ComputedPhysicalBorderPadding().LeftRight(); |
|
241 break; |
|
242 case NS_STYLE_BOX_SIZING_PADDING: |
|
243 inside = ComputedPhysicalPadding().LeftRight(); |
|
244 break; |
|
245 } |
|
246 outside -= inside; |
|
247 |
|
248 return ComputeWidthValue(aContainingBlockWidth, inside, |
|
249 outside, aCoord); |
|
250 } |
|
251 |
|
252 nscoord |
|
253 nsCSSOffsetState::ComputeHeightValue(nscoord aContainingBlockHeight, |
|
254 uint8_t aBoxSizing, |
|
255 const nsStyleCoord& aCoord) |
|
256 { |
|
257 nscoord inside = 0; |
|
258 switch (aBoxSizing) { |
|
259 case NS_STYLE_BOX_SIZING_BORDER: |
|
260 inside = ComputedPhysicalBorderPadding().TopBottom(); |
|
261 break; |
|
262 case NS_STYLE_BOX_SIZING_PADDING: |
|
263 inside = ComputedPhysicalPadding().TopBottom(); |
|
264 break; |
|
265 } |
|
266 return nsLayoutUtils::ComputeHeightValue(aContainingBlockHeight, |
|
267 inside, aCoord); |
|
268 } |
|
269 |
|
270 void |
|
271 nsHTMLReflowState::SetComputedWidth(nscoord aComputedWidth) |
|
272 { |
|
273 NS_ASSERTION(frame, "Must have a frame!"); |
|
274 // It'd be nice to assert that |frame| is not in reflow, but this fails for |
|
275 // two reasons: |
|
276 // |
|
277 // 1) Viewport frames reset the computed width on a copy of their reflow |
|
278 // state when reflowing fixed-pos kids. In that case we actually don't |
|
279 // want to mess with the resize flags, because comparing the frame's rect |
|
280 // to the munged computed width is pointless. |
|
281 // 2) nsFrame::BoxReflow creates a reflow state for its parent. This reflow |
|
282 // state is not used to reflow the parent, but just as a parent for the |
|
283 // frame's own reflow state. So given a nsBoxFrame inside some non-XUL |
|
284 // (like a text control, for example), we'll end up creating a reflow |
|
285 // state for the parent while the parent is reflowing. |
|
286 |
|
287 NS_PRECONDITION(aComputedWidth >= 0, "Invalid computed width"); |
|
288 if (ComputedWidth() != aComputedWidth) { |
|
289 ComputedWidth() = aComputedWidth; |
|
290 nsIAtom* frameType = frame->GetType(); |
|
291 if (frameType != nsGkAtoms::viewportFrame) { // Or check GetParent()? |
|
292 InitResizeFlags(frame->PresContext(), frameType); |
|
293 } |
|
294 } |
|
295 } |
|
296 |
|
297 void |
|
298 nsHTMLReflowState::SetComputedHeight(nscoord aComputedHeight) |
|
299 { |
|
300 NS_ASSERTION(frame, "Must have a frame!"); |
|
301 // It'd be nice to assert that |frame| is not in reflow, but this fails |
|
302 // because: |
|
303 // |
|
304 // nsFrame::BoxReflow creates a reflow state for its parent. This reflow |
|
305 // state is not used to reflow the parent, but just as a parent for the |
|
306 // frame's own reflow state. So given a nsBoxFrame inside some non-XUL |
|
307 // (like a text control, for example), we'll end up creating a reflow |
|
308 // state for the parent while the parent is reflowing. |
|
309 |
|
310 NS_PRECONDITION(aComputedHeight >= 0, "Invalid computed height"); |
|
311 if (ComputedHeight() != aComputedHeight) { |
|
312 ComputedHeight() = aComputedHeight; |
|
313 InitResizeFlags(frame->PresContext(), frame->GetType()); |
|
314 } |
|
315 } |
|
316 |
|
317 void |
|
318 nsHTMLReflowState::Init(nsPresContext* aPresContext, |
|
319 nscoord aContainingBlockWidth, |
|
320 nscoord aContainingBlockHeight, |
|
321 const nsMargin* aBorder, |
|
322 const nsMargin* aPadding) |
|
323 { |
|
324 NS_WARN_IF_FALSE(AvailableWidth() != NS_UNCONSTRAINEDSIZE, |
|
325 "have unconstrained width; this should only result from " |
|
326 "very large sizes, not attempts at intrinsic width " |
|
327 "calculation"); |
|
328 |
|
329 mStylePosition = frame->StylePosition(); |
|
330 mStyleDisplay = frame->StyleDisplay(); |
|
331 mStyleVisibility = frame->StyleVisibility(); |
|
332 mStyleBorder = frame->StyleBorder(); |
|
333 mStyleMargin = frame->StyleMargin(); |
|
334 mStylePadding = frame->StylePadding(); |
|
335 mStyleText = frame->StyleText(); |
|
336 |
|
337 nsIAtom* type = frame->GetType(); |
|
338 |
|
339 InitFrameType(type); |
|
340 InitCBReflowState(); |
|
341 |
|
342 InitConstraints(aPresContext, aContainingBlockWidth, aContainingBlockHeight, |
|
343 aBorder, aPadding, type); |
|
344 |
|
345 InitResizeFlags(aPresContext, type); |
|
346 |
|
347 nsIFrame *parent = frame->GetParent(); |
|
348 if (parent && |
|
349 (parent->GetStateBits() & NS_FRAME_IN_CONSTRAINED_HEIGHT) && |
|
350 !(parent->GetType() == nsGkAtoms::scrollFrame && |
|
351 parent->StyleDisplay()->mOverflowY != NS_STYLE_OVERFLOW_HIDDEN)) { |
|
352 frame->AddStateBits(NS_FRAME_IN_CONSTRAINED_HEIGHT); |
|
353 } else if (type == nsGkAtoms::svgForeignObjectFrame) { |
|
354 // An SVG foreignObject frame is inherently constrained height. |
|
355 frame->AddStateBits(NS_FRAME_IN_CONSTRAINED_HEIGHT); |
|
356 } else if ((mStylePosition->mHeight.GetUnit() != eStyleUnit_Auto || |
|
357 mStylePosition->mMaxHeight.GetUnit() != eStyleUnit_None) && |
|
358 // Don't set NS_FRAME_IN_CONSTRAINED_HEIGHT on body or html |
|
359 // elements. |
|
360 (frame->GetContent() && |
|
361 !(frame->GetContent()->IsHTML(nsGkAtoms::body) || |
|
362 frame->GetContent()->IsHTML(nsGkAtoms::html)))) { |
|
363 |
|
364 // If our height was specified as a percentage, then this could |
|
365 // actually resolve to 'auto', based on: |
|
366 // http://www.w3.org/TR/CSS21/visudet.html#the-height-property |
|
367 nsIFrame* containingBlk = frame; |
|
368 while (containingBlk) { |
|
369 const nsStylePosition* stylePos = containingBlk->StylePosition(); |
|
370 if ((stylePos->mHeight.IsCoordPercentCalcUnit() && |
|
371 !stylePos->mHeight.HasPercent()) || |
|
372 (stylePos->mMaxHeight.IsCoordPercentCalcUnit() && |
|
373 !stylePos->mMaxHeight.HasPercent())) { |
|
374 frame->AddStateBits(NS_FRAME_IN_CONSTRAINED_HEIGHT); |
|
375 break; |
|
376 } else if ((stylePos->mHeight.IsCoordPercentCalcUnit() && |
|
377 stylePos->mHeight.HasPercent()) || |
|
378 (stylePos->mMaxHeight.IsCoordPercentCalcUnit() && |
|
379 stylePos->mMaxHeight.HasPercent())) { |
|
380 if (!(containingBlk = containingBlk->GetContainingBlock())) { |
|
381 // If we've reached the top of the tree, then we don't have |
|
382 // a constrained height. |
|
383 frame->RemoveStateBits(NS_FRAME_IN_CONSTRAINED_HEIGHT); |
|
384 break; |
|
385 } |
|
386 |
|
387 continue; |
|
388 } else { |
|
389 frame->RemoveStateBits(NS_FRAME_IN_CONSTRAINED_HEIGHT); |
|
390 break; |
|
391 } |
|
392 } |
|
393 } else { |
|
394 frame->RemoveStateBits(NS_FRAME_IN_CONSTRAINED_HEIGHT); |
|
395 } |
|
396 |
|
397 NS_WARN_IF_FALSE((mFrameType == NS_CSS_FRAME_TYPE_INLINE && |
|
398 !frame->IsFrameOfType(nsIFrame::eReplaced)) || |
|
399 type == nsGkAtoms::textFrame || |
|
400 ComputedWidth() != NS_UNCONSTRAINEDSIZE, |
|
401 "have unconstrained width; this should only result from " |
|
402 "very large sizes, not attempts at intrinsic width " |
|
403 "calculation"); |
|
404 } |
|
405 |
|
406 void nsHTMLReflowState::InitCBReflowState() |
|
407 { |
|
408 if (!parentReflowState) { |
|
409 mCBReflowState = nullptr; |
|
410 return; |
|
411 } |
|
412 |
|
413 if (parentReflowState->frame == frame->GetContainingBlock()) { |
|
414 // Inner table frames need to use the containing block of the outer |
|
415 // table frame. |
|
416 if (frame->GetType() == nsGkAtoms::tableFrame) { |
|
417 mCBReflowState = parentReflowState->mCBReflowState; |
|
418 } else { |
|
419 mCBReflowState = parentReflowState; |
|
420 } |
|
421 } else { |
|
422 mCBReflowState = parentReflowState->mCBReflowState; |
|
423 } |
|
424 } |
|
425 |
|
426 /* Check whether CalcQuirkContainingBlockHeight would stop on the |
|
427 * given reflow state, using its block as a height. (essentially |
|
428 * returns false for any case in which CalcQuirkContainingBlockHeight |
|
429 * has a "continue" in its main loop.) |
|
430 * |
|
431 * XXX Maybe refactor CalcQuirkContainingBlockHeight so it uses |
|
432 * this function as well |
|
433 */ |
|
434 static bool |
|
435 IsQuirkContainingBlockHeight(const nsHTMLReflowState* rs, nsIAtom* aFrameType) |
|
436 { |
|
437 if (nsGkAtoms::blockFrame == aFrameType || |
|
438 #ifdef MOZ_XUL |
|
439 nsGkAtoms::XULLabelFrame == aFrameType || |
|
440 #endif |
|
441 nsGkAtoms::scrollFrame == aFrameType) { |
|
442 // Note: This next condition could change due to a style change, |
|
443 // but that would cause a style reflow anyway, which means we're ok. |
|
444 if (NS_AUTOHEIGHT == rs->ComputedHeight()) { |
|
445 if (!rs->frame->IsAbsolutelyPositioned()) { |
|
446 return false; |
|
447 } |
|
448 } |
|
449 } |
|
450 return true; |
|
451 } |
|
452 |
|
453 |
|
454 void |
|
455 nsHTMLReflowState::InitResizeFlags(nsPresContext* aPresContext, nsIAtom* aFrameType) |
|
456 { |
|
457 bool isHResize = (frame->GetSize().width != |
|
458 ComputedWidth() + ComputedPhysicalBorderPadding().LeftRight()) || |
|
459 aPresContext->PresShell()->IsReflowOnZoomPending(); |
|
460 |
|
461 if ((frame->GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT) && |
|
462 nsLayoutUtils::FontSizeInflationEnabled(aPresContext)) { |
|
463 // Create our font inflation data if we don't have it already, and |
|
464 // give it our current width information. |
|
465 bool dirty = nsFontInflationData::UpdateFontInflationDataWidthFor(*this) && |
|
466 // Avoid running this at the box-to-block interface |
|
467 // (where we shouldn't be inflating anyway, and where |
|
468 // reflow state construction is probably to construct a |
|
469 // dummy parent reflow state anyway). |
|
470 !mFlags.mDummyParentReflowState; |
|
471 |
|
472 if (dirty || (!frame->GetParent() && isHResize)) { |
|
473 // When font size inflation is enabled, a change in either: |
|
474 // * the effective width of a font inflation flow root |
|
475 // * the width of the frame |
|
476 // needs to cause a dirty reflow since they change the font size |
|
477 // inflation calculations, which in turn change the size of text, |
|
478 // line-heights, etc. This is relatively similar to a classic |
|
479 // case of style change reflow, except that because inflation |
|
480 // doesn't affect the intrinsic sizing codepath, there's no need |
|
481 // to invalidate intrinsic sizes. |
|
482 // |
|
483 // Note that this makes horizontal resizing a good bit more |
|
484 // expensive. However, font size inflation is targeted at a set of |
|
485 // devices (zoom-and-pan devices) where the main use case for |
|
486 // horizontal resizing needing to be efficient (window resizing) is |
|
487 // not present. It does still increase the cost of dynamic changes |
|
488 // caused by script where a style or content change in one place |
|
489 // causes a resize in another (e.g., rebalancing a table). |
|
490 |
|
491 // FIXME: This isn't so great for the cases where |
|
492 // nsHTMLReflowState::SetComputedWidth is called, if the first time |
|
493 // we go through InitResizeFlags we set mHResize to true, and then |
|
494 // the second time we'd set it to false even without the |
|
495 // NS_FRAME_IS_DIRTY bit already set. |
|
496 if (frame->GetType() == nsGkAtoms::svgForeignObjectFrame) { |
|
497 // Foreign object frames use dirty bits in a special way. |
|
498 frame->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN); |
|
499 nsIFrame *kid = frame->GetFirstPrincipalChild(); |
|
500 if (kid) { |
|
501 kid->AddStateBits(NS_FRAME_IS_DIRTY); |
|
502 } |
|
503 } else { |
|
504 frame->AddStateBits(NS_FRAME_IS_DIRTY); |
|
505 } |
|
506 |
|
507 // Mark intrinsic widths on all descendants dirty. We need to do |
|
508 // this (1) since we're changing the size of text and need to |
|
509 // clear text runs on text frames and (2) since we actually are |
|
510 // changing some intrinsic widths, but only those that live inside |
|
511 // of containers. |
|
512 |
|
513 // It makes sense to do this for descendants but not ancestors |
|
514 // (which is unusual) because we're only changing the unusual |
|
515 // inflation-dependent intrinsic widths (i.e., ones computed with |
|
516 // nsPresContext::mInflationDisabledForShrinkWrap set to false), |
|
517 // which should never affect anything outside of their inflation |
|
518 // flow root (or, for that matter, even their inflation |
|
519 // container). |
|
520 |
|
521 // This is also different from what PresShell::FrameNeedsReflow |
|
522 // does because it doesn't go through placeholders. It doesn't |
|
523 // need to because we're actually doing something that cares about |
|
524 // frame tree geometry (the width on an ancestor) rather than |
|
525 // style. |
|
526 |
|
527 nsAutoTArray<nsIFrame*, 32> stack; |
|
528 stack.AppendElement(frame); |
|
529 |
|
530 do { |
|
531 nsIFrame *f = stack.ElementAt(stack.Length() - 1); |
|
532 stack.RemoveElementAt(stack.Length() - 1); |
|
533 |
|
534 nsIFrame::ChildListIterator lists(f); |
|
535 for (; !lists.IsDone(); lists.Next()) { |
|
536 nsFrameList::Enumerator childFrames(lists.CurrentList()); |
|
537 for (; !childFrames.AtEnd(); childFrames.Next()) { |
|
538 nsIFrame* kid = childFrames.get(); |
|
539 kid->MarkIntrinsicWidthsDirty(); |
|
540 stack.AppendElement(kid); |
|
541 } |
|
542 } |
|
543 } while (stack.Length() != 0); |
|
544 } |
|
545 } |
|
546 |
|
547 mFlags.mHResize = !(frame->GetStateBits() & NS_FRAME_IS_DIRTY) && |
|
548 isHResize; |
|
549 |
|
550 // XXX Should we really need to null check mCBReflowState? (We do for |
|
551 // at least nsBoxFrame). |
|
552 if (IS_TABLE_CELL(aFrameType) && |
|
553 (mFlags.mSpecialHeightReflow || |
|
554 (frame->FirstInFlow()->GetStateBits() & |
|
555 NS_TABLE_CELL_HAD_SPECIAL_REFLOW)) && |
|
556 (frame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT)) { |
|
557 // Need to set the bit on the cell so that |
|
558 // mCBReflowState->mFlags.mVResize is set correctly below when |
|
559 // reflowing descendant. |
|
560 mFlags.mVResize = true; |
|
561 } else if (mCBReflowState && !nsLayoutUtils::IsNonWrapperBlock(frame)) { |
|
562 // XXX Is this problematic for relatively positioned inlines acting |
|
563 // as containing block for absolutely positioned elements? |
|
564 // Possibly; in that case we should at least be checking |
|
565 // NS_SUBTREE_DIRTY, I'd think. |
|
566 mFlags.mVResize = mCBReflowState->mFlags.mVResize; |
|
567 } else if (ComputedHeight() == NS_AUTOHEIGHT) { |
|
568 if (eCompatibility_NavQuirks == aPresContext->CompatibilityMode() && |
|
569 mCBReflowState) { |
|
570 mFlags.mVResize = mCBReflowState->mFlags.mVResize; |
|
571 } else { |
|
572 mFlags.mVResize = mFlags.mHResize; |
|
573 } |
|
574 mFlags.mVResize = mFlags.mVResize || NS_SUBTREE_DIRTY(frame); |
|
575 } else { |
|
576 // not 'auto' height |
|
577 mFlags.mVResize = frame->GetSize().height != |
|
578 ComputedHeight() + ComputedPhysicalBorderPadding().TopBottom(); |
|
579 } |
|
580 |
|
581 bool dependsOnCBHeight = |
|
582 (mStylePosition->HeightDependsOnContainer() && |
|
583 // FIXME: condition this on not-abspos? |
|
584 mStylePosition->mHeight.GetUnit() != eStyleUnit_Auto) || |
|
585 mStylePosition->MinHeightDependsOnContainer() || |
|
586 mStylePosition->MaxHeightDependsOnContainer() || |
|
587 mStylePosition->OffsetHasPercent(NS_SIDE_TOP) || |
|
588 mStylePosition->mOffset.GetBottomUnit() != eStyleUnit_Auto || |
|
589 frame->IsBoxFrame(); |
|
590 |
|
591 if (mStyleText->mLineHeight.GetUnit() == eStyleUnit_Enumerated) { |
|
592 NS_ASSERTION(mStyleText->mLineHeight.GetIntValue() == |
|
593 NS_STYLE_LINE_HEIGHT_BLOCK_HEIGHT, |
|
594 "bad line-height value"); |
|
595 |
|
596 // line-height depends on block height |
|
597 frame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT); |
|
598 // but only on containing blocks if this frame is not a suitable block |
|
599 dependsOnCBHeight |= !nsLayoutUtils::IsNonWrapperBlock(frame); |
|
600 } |
|
601 |
|
602 // If we're the descendant of a table cell that performs special height |
|
603 // reflows and we could be the child that requires them, always set |
|
604 // the vertical resize in case this is the first pass before the |
|
605 // special height reflow. However, don't do this if it actually is |
|
606 // the special height reflow, since in that case it will already be |
|
607 // set correctly above if we need it set. |
|
608 if (!mFlags.mVResize && mCBReflowState && |
|
609 (IS_TABLE_CELL(mCBReflowState->frame->GetType()) || |
|
610 mCBReflowState->mFlags.mHeightDependsOnAncestorCell) && |
|
611 !mCBReflowState->mFlags.mSpecialHeightReflow && |
|
612 dependsOnCBHeight) { |
|
613 mFlags.mVResize = true; |
|
614 mFlags.mHeightDependsOnAncestorCell = true; |
|
615 } |
|
616 |
|
617 // Set NS_FRAME_CONTAINS_RELATIVE_HEIGHT if it's needed. |
|
618 |
|
619 // It would be nice to check that |mComputedHeight != NS_AUTOHEIGHT| |
|
620 // &&ed with the percentage height check. However, this doesn't get |
|
621 // along with table special height reflows, since a special height |
|
622 // reflow (a quirk that makes such percentage heights work on children |
|
623 // of table cells) can cause not just a single percentage height to |
|
624 // become fixed, but an entire descendant chain of percentage heights |
|
625 // to become fixed. |
|
626 if (dependsOnCBHeight && mCBReflowState) { |
|
627 const nsHTMLReflowState *rs = this; |
|
628 bool hitCBReflowState = false; |
|
629 do { |
|
630 rs = rs->parentReflowState; |
|
631 if (!rs) { |
|
632 break; |
|
633 } |
|
634 |
|
635 if (rs->frame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT) |
|
636 break; // no need to go further |
|
637 rs->frame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT); |
|
638 |
|
639 // Keep track of whether we've hit the containing block, because |
|
640 // we need to go at least that far. |
|
641 if (rs == mCBReflowState) { |
|
642 hitCBReflowState = true; |
|
643 } |
|
644 |
|
645 } while (!hitCBReflowState || |
|
646 (eCompatibility_NavQuirks == aPresContext->CompatibilityMode() && |
|
647 !IsQuirkContainingBlockHeight(rs, rs->frame->GetType()))); |
|
648 // Note: We actually don't need to set the |
|
649 // NS_FRAME_CONTAINS_RELATIVE_HEIGHT bit for the cases |
|
650 // where we hit the early break statements in |
|
651 // CalcQuirkContainingBlockHeight. But it doesn't hurt |
|
652 // us to set the bit in these cases. |
|
653 |
|
654 } |
|
655 if (frame->GetStateBits() & NS_FRAME_IS_DIRTY) { |
|
656 // If we're reflowing everything, then we'll find out if we need |
|
657 // to re-set this. |
|
658 frame->RemoveStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT); |
|
659 } |
|
660 } |
|
661 |
|
662 /* static */ |
|
663 nscoord |
|
664 nsHTMLReflowState::GetContainingBlockContentWidth(const nsHTMLReflowState* aReflowState) |
|
665 { |
|
666 const nsHTMLReflowState* rs = aReflowState->mCBReflowState; |
|
667 if (!rs) |
|
668 return 0; |
|
669 return rs->ComputedWidth(); |
|
670 } |
|
671 |
|
672 void |
|
673 nsHTMLReflowState::InitFrameType(nsIAtom* aFrameType) |
|
674 { |
|
675 const nsStyleDisplay *disp = mStyleDisplay; |
|
676 nsCSSFrameType frameType; |
|
677 |
|
678 // Section 9.7 of the CSS2 spec indicates that absolute position |
|
679 // takes precedence over float which takes precedence over display. |
|
680 // XXXldb nsRuleNode::ComputeDisplayData should take care of this, right? |
|
681 // Make sure the frame was actually moved out of the flow, and don't |
|
682 // just assume what the style says, because we might not have had a |
|
683 // useful float/absolute containing block |
|
684 |
|
685 DISPLAY_INIT_TYPE(frame, this); |
|
686 |
|
687 if (aFrameType == nsGkAtoms::tableFrame) { |
|
688 mFrameType = NS_CSS_FRAME_TYPE_BLOCK; |
|
689 return; |
|
690 } |
|
691 |
|
692 NS_ASSERTION(frame->StyleDisplay()->IsAbsolutelyPositionedStyle() == |
|
693 disp->IsAbsolutelyPositionedStyle(), |
|
694 "Unexpected position style"); |
|
695 NS_ASSERTION(frame->StyleDisplay()->IsFloatingStyle() == |
|
696 disp->IsFloatingStyle(), "Unexpected float style"); |
|
697 if (frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) { |
|
698 if (disp->IsAbsolutelyPositioned(frame)) { |
|
699 frameType = NS_CSS_FRAME_TYPE_ABSOLUTE; |
|
700 //XXXfr hack for making frames behave properly when in overflow container lists |
|
701 // see bug 154892; need to revisit later |
|
702 if (frame->GetPrevInFlow()) |
|
703 frameType = NS_CSS_FRAME_TYPE_BLOCK; |
|
704 } |
|
705 else if (disp->IsFloating(frame)) { |
|
706 frameType = NS_CSS_FRAME_TYPE_FLOATING; |
|
707 } else { |
|
708 NS_ASSERTION(disp->mDisplay == NS_STYLE_DISPLAY_POPUP, |
|
709 "unknown out of flow frame type"); |
|
710 frameType = NS_CSS_FRAME_TYPE_UNKNOWN; |
|
711 } |
|
712 } |
|
713 else { |
|
714 switch (GetDisplay()) { |
|
715 case NS_STYLE_DISPLAY_BLOCK: |
|
716 case NS_STYLE_DISPLAY_LIST_ITEM: |
|
717 case NS_STYLE_DISPLAY_TABLE: |
|
718 case NS_STYLE_DISPLAY_TABLE_CAPTION: |
|
719 case NS_STYLE_DISPLAY_FLEX: |
|
720 frameType = NS_CSS_FRAME_TYPE_BLOCK; |
|
721 break; |
|
722 |
|
723 case NS_STYLE_DISPLAY_INLINE: |
|
724 case NS_STYLE_DISPLAY_INLINE_BLOCK: |
|
725 case NS_STYLE_DISPLAY_INLINE_TABLE: |
|
726 case NS_STYLE_DISPLAY_INLINE_BOX: |
|
727 case NS_STYLE_DISPLAY_INLINE_XUL_GRID: |
|
728 case NS_STYLE_DISPLAY_INLINE_STACK: |
|
729 case NS_STYLE_DISPLAY_INLINE_FLEX: |
|
730 frameType = NS_CSS_FRAME_TYPE_INLINE; |
|
731 break; |
|
732 |
|
733 case NS_STYLE_DISPLAY_TABLE_CELL: |
|
734 case NS_STYLE_DISPLAY_TABLE_ROW_GROUP: |
|
735 case NS_STYLE_DISPLAY_TABLE_COLUMN: |
|
736 case NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP: |
|
737 case NS_STYLE_DISPLAY_TABLE_HEADER_GROUP: |
|
738 case NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP: |
|
739 case NS_STYLE_DISPLAY_TABLE_ROW: |
|
740 frameType = NS_CSS_FRAME_TYPE_INTERNAL_TABLE; |
|
741 break; |
|
742 |
|
743 case NS_STYLE_DISPLAY_NONE: |
|
744 default: |
|
745 frameType = NS_CSS_FRAME_TYPE_UNKNOWN; |
|
746 break; |
|
747 } |
|
748 } |
|
749 |
|
750 // See if the frame is replaced |
|
751 if (frame->IsFrameOfType(nsIFrame::eReplacedContainsBlock)) { |
|
752 frameType = NS_FRAME_REPLACED_CONTAINS_BLOCK(frameType); |
|
753 } else if (frame->IsFrameOfType(nsIFrame::eReplaced)) { |
|
754 frameType = NS_FRAME_REPLACED(frameType); |
|
755 } |
|
756 |
|
757 mFrameType = frameType; |
|
758 } |
|
759 |
|
760 /* static */ void |
|
761 nsHTMLReflowState::ComputeRelativeOffsets(uint8_t aCBDirection, |
|
762 nsIFrame* aFrame, |
|
763 nscoord aContainingBlockWidth, |
|
764 nscoord aContainingBlockHeight, |
|
765 nsMargin& aComputedOffsets) |
|
766 { |
|
767 const nsStylePosition* position = aFrame->StylePosition(); |
|
768 |
|
769 // Compute the 'left' and 'right' values. 'Left' moves the boxes to the right, |
|
770 // and 'right' moves the boxes to the left. The computed values are always: |
|
771 // left=-right |
|
772 bool leftIsAuto = eStyleUnit_Auto == position->mOffset.GetLeftUnit(); |
|
773 bool rightIsAuto = eStyleUnit_Auto == position->mOffset.GetRightUnit(); |
|
774 |
|
775 // If neither 'left' not 'right' are auto, then we're over-constrained and |
|
776 // we ignore one of them |
|
777 if (!leftIsAuto && !rightIsAuto) { |
|
778 if (aCBDirection == NS_STYLE_DIRECTION_RTL) { |
|
779 leftIsAuto = true; |
|
780 } else { |
|
781 rightIsAuto = true; |
|
782 } |
|
783 } |
|
784 |
|
785 if (leftIsAuto) { |
|
786 if (rightIsAuto) { |
|
787 // If both are 'auto' (their initial values), the computed values are 0 |
|
788 aComputedOffsets.left = aComputedOffsets.right = 0; |
|
789 } else { |
|
790 // 'Right' isn't 'auto' so compute its value |
|
791 aComputedOffsets.right = nsLayoutUtils:: |
|
792 ComputeCBDependentValue(aContainingBlockWidth, |
|
793 position->mOffset.GetRight()); |
|
794 |
|
795 // Computed value for 'left' is minus the value of 'right' |
|
796 aComputedOffsets.left = -aComputedOffsets.right; |
|
797 } |
|
798 |
|
799 } else { |
|
800 NS_ASSERTION(rightIsAuto, "unexpected specified constraint"); |
|
801 |
|
802 // 'Left' isn't 'auto' so compute its value |
|
803 aComputedOffsets.left = nsLayoutUtils:: |
|
804 ComputeCBDependentValue(aContainingBlockWidth, |
|
805 position->mOffset.GetLeft()); |
|
806 |
|
807 // Computed value for 'right' is minus the value of 'left' |
|
808 aComputedOffsets.right = -aComputedOffsets.left; |
|
809 } |
|
810 |
|
811 // Compute the 'top' and 'bottom' values. The 'top' and 'bottom' properties |
|
812 // move relatively positioned elements up and down. They also must be each |
|
813 // other's negative |
|
814 bool topIsAuto = eStyleUnit_Auto == position->mOffset.GetTopUnit(); |
|
815 bool bottomIsAuto = eStyleUnit_Auto == position->mOffset.GetBottomUnit(); |
|
816 |
|
817 // Check for percentage based values and a containing block height that |
|
818 // depends on the content height. Treat them like 'auto' |
|
819 if (NS_AUTOHEIGHT == aContainingBlockHeight) { |
|
820 if (position->OffsetHasPercent(NS_SIDE_TOP)) { |
|
821 topIsAuto = true; |
|
822 } |
|
823 if (position->OffsetHasPercent(NS_SIDE_BOTTOM)) { |
|
824 bottomIsAuto = true; |
|
825 } |
|
826 } |
|
827 |
|
828 // If neither is 'auto', 'bottom' is ignored |
|
829 if (!topIsAuto && !bottomIsAuto) { |
|
830 bottomIsAuto = true; |
|
831 } |
|
832 |
|
833 if (topIsAuto) { |
|
834 if (bottomIsAuto) { |
|
835 // If both are 'auto' (their initial values), the computed values are 0 |
|
836 aComputedOffsets.top = aComputedOffsets.bottom = 0; |
|
837 } else { |
|
838 // 'Bottom' isn't 'auto' so compute its value |
|
839 aComputedOffsets.bottom = nsLayoutUtils:: |
|
840 ComputeHeightDependentValue(aContainingBlockHeight, |
|
841 position->mOffset.GetBottom()); |
|
842 |
|
843 // Computed value for 'top' is minus the value of 'bottom' |
|
844 aComputedOffsets.top = -aComputedOffsets.bottom; |
|
845 } |
|
846 |
|
847 } else { |
|
848 NS_ASSERTION(bottomIsAuto, "unexpected specified constraint"); |
|
849 |
|
850 // 'Top' isn't 'auto' so compute its value |
|
851 aComputedOffsets.top = nsLayoutUtils:: |
|
852 ComputeHeightDependentValue(aContainingBlockHeight, |
|
853 position->mOffset.GetTop()); |
|
854 |
|
855 // Computed value for 'bottom' is minus the value of 'top' |
|
856 aComputedOffsets.bottom = -aComputedOffsets.top; |
|
857 } |
|
858 |
|
859 // Store the offset |
|
860 FrameProperties props = aFrame->Properties(); |
|
861 nsMargin* offsets = static_cast<nsMargin*> |
|
862 (props.Get(nsIFrame::ComputedOffsetProperty())); |
|
863 if (offsets) { |
|
864 *offsets = aComputedOffsets; |
|
865 } else { |
|
866 props.Set(nsIFrame::ComputedOffsetProperty(), |
|
867 new nsMargin(aComputedOffsets)); |
|
868 } |
|
869 } |
|
870 |
|
871 /* static */ void |
|
872 nsHTMLReflowState::ApplyRelativePositioning(nsIFrame* aFrame, |
|
873 const nsMargin& aComputedOffsets, |
|
874 nsPoint* aPosition) |
|
875 { |
|
876 if (!aFrame->IsRelativelyPositioned()) { |
|
877 NS_ASSERTION(!aFrame->Properties().Get(nsIFrame::NormalPositionProperty()), |
|
878 "We assume that changing the 'position' property causes " |
|
879 "frame reconstruction. If that ever changes, this code " |
|
880 "should call " |
|
881 "props.Delete(nsIFrame::NormalPositionProperty())"); |
|
882 return; |
|
883 } |
|
884 |
|
885 // Store the normal position |
|
886 FrameProperties props = aFrame->Properties(); |
|
887 nsPoint* normalPosition = static_cast<nsPoint*> |
|
888 (props.Get(nsIFrame::NormalPositionProperty())); |
|
889 if (normalPosition) { |
|
890 *normalPosition = *aPosition; |
|
891 } else { |
|
892 props.Set(nsIFrame::NormalPositionProperty(), new nsPoint(*aPosition)); |
|
893 } |
|
894 |
|
895 const nsStyleDisplay* display = aFrame->StyleDisplay(); |
|
896 if (NS_STYLE_POSITION_RELATIVE == display->mPosition) { |
|
897 *aPosition += nsPoint(aComputedOffsets.left, aComputedOffsets.top); |
|
898 } else if (NS_STYLE_POSITION_STICKY == display->mPosition && |
|
899 !aFrame->GetNextContinuation() && |
|
900 !aFrame->GetPrevContinuation() && |
|
901 !(aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) { |
|
902 // Sticky positioning for elements with multiple frames needs to be |
|
903 // computed all at once. We can't safely do that here because we might be |
|
904 // partway through (re)positioning the frames, so leave it until the scroll |
|
905 // container reflows and calls StickyScrollContainer::UpdatePositions. |
|
906 // For single-frame sticky positioned elements, though, go ahead and apply |
|
907 // it now to avoid unnecessary overflow updates later. |
|
908 StickyScrollContainer* ssc = |
|
909 StickyScrollContainer::GetStickyScrollContainerForFrame(aFrame); |
|
910 if (ssc) { |
|
911 *aPosition = ssc->ComputePosition(aFrame); |
|
912 } |
|
913 } |
|
914 } |
|
915 |
|
916 nsIFrame* |
|
917 nsHTMLReflowState::GetHypotheticalBoxContainer(nsIFrame* aFrame, |
|
918 nscoord& aCBLeftEdge, |
|
919 nscoord& aCBWidth) |
|
920 { |
|
921 aFrame = aFrame->GetContainingBlock(); |
|
922 NS_ASSERTION(aFrame != frame, "How did that happen?"); |
|
923 |
|
924 /* Now aFrame is the containing block we want */ |
|
925 |
|
926 /* Check whether the containing block is currently being reflowed. |
|
927 If so, use the info from the reflow state. */ |
|
928 const nsHTMLReflowState* state; |
|
929 if (aFrame->GetStateBits() & NS_FRAME_IN_REFLOW) { |
|
930 for (state = parentReflowState; state && state->frame != aFrame; |
|
931 state = state->parentReflowState) { |
|
932 /* do nothing */ |
|
933 } |
|
934 } else { |
|
935 state = nullptr; |
|
936 } |
|
937 |
|
938 if (state) { |
|
939 aCBLeftEdge = state->ComputedPhysicalBorderPadding().left; |
|
940 aCBWidth = state->ComputedWidth(); |
|
941 } else { |
|
942 /* Didn't find a reflow state for aFrame. Just compute the information we |
|
943 want, on the assumption that aFrame already knows its size. This really |
|
944 ought to be true by now. */ |
|
945 NS_ASSERTION(!(aFrame->GetStateBits() & NS_FRAME_IN_REFLOW), |
|
946 "aFrame shouldn't be in reflow; we'll lie if it is"); |
|
947 nsMargin borderPadding = aFrame->GetUsedBorderAndPadding(); |
|
948 aCBLeftEdge = borderPadding.left; |
|
949 aCBWidth = aFrame->GetSize().width - borderPadding.LeftRight(); |
|
950 } |
|
951 |
|
952 return aFrame; |
|
953 } |
|
954 |
|
955 // When determining the hypothetical box that would have been if the element |
|
956 // had been in the flow we may not be able to exactly determine both the left |
|
957 // and right edges. For example, if the element is a non-replaced inline-level |
|
958 // element we would have to reflow it in order to determine it desired width. |
|
959 // In that case depending on the progression direction either the left or |
|
960 // right edge would be marked as not being exact |
|
961 struct nsHypotheticalBox { |
|
962 // offsets from left edge of containing block (which is a padding edge) |
|
963 nscoord mLeft, mRight; |
|
964 // offset from top edge of containing block (which is a padding edge) |
|
965 nscoord mTop; |
|
966 #ifdef DEBUG |
|
967 bool mLeftIsExact, mRightIsExact; |
|
968 #endif |
|
969 |
|
970 nsHypotheticalBox() { |
|
971 #ifdef DEBUG |
|
972 mLeftIsExact = mRightIsExact = false; |
|
973 #endif |
|
974 } |
|
975 }; |
|
976 |
|
977 static bool |
|
978 GetIntrinsicSizeFor(nsIFrame* aFrame, nsSize& aIntrinsicSize, nsIAtom* aFrameType) |
|
979 { |
|
980 // See if it is an image frame |
|
981 bool success = false; |
|
982 |
|
983 // Currently the only type of replaced frame that we can get the intrinsic |
|
984 // size for is an image frame |
|
985 // XXX We should add back the GetReflowMetrics() function and one of the |
|
986 // things should be the intrinsic size... |
|
987 if (aFrameType == nsGkAtoms::imageFrame) { |
|
988 nsImageFrame* imageFrame = (nsImageFrame*)aFrame; |
|
989 |
|
990 if (NS_SUCCEEDED(imageFrame->GetIntrinsicImageSize(aIntrinsicSize))) { |
|
991 success = (aIntrinsicSize != nsSize(0, 0)); |
|
992 } |
|
993 } |
|
994 return success; |
|
995 } |
|
996 |
|
997 /** |
|
998 * aInsideBoxSizing returns the part of the horizontal padding, border, |
|
999 * and margin that goes inside the edge given by box-sizing; |
|
1000 * aOutsideBoxSizing returns the rest. |
|
1001 */ |
|
1002 void |
|
1003 nsHTMLReflowState::CalculateHorizBorderPaddingMargin( |
|
1004 nscoord aContainingBlockWidth, |
|
1005 nscoord* aInsideBoxSizing, |
|
1006 nscoord* aOutsideBoxSizing) |
|
1007 { |
|
1008 const nsMargin& border = mStyleBorder->GetComputedBorder(); |
|
1009 nsMargin padding, margin; |
|
1010 |
|
1011 // See if the style system can provide us the padding directly |
|
1012 if (!mStylePadding->GetPadding(padding)) { |
|
1013 // We have to compute the left and right values |
|
1014 padding.left = nsLayoutUtils:: |
|
1015 ComputeCBDependentValue(aContainingBlockWidth, |
|
1016 mStylePadding->mPadding.GetLeft()); |
|
1017 padding.right = nsLayoutUtils:: |
|
1018 ComputeCBDependentValue(aContainingBlockWidth, |
|
1019 mStylePadding->mPadding.GetRight()); |
|
1020 } |
|
1021 |
|
1022 // See if the style system can provide us the margin directly |
|
1023 if (!mStyleMargin->GetMargin(margin)) { |
|
1024 // We have to compute the left and right values |
|
1025 if (eStyleUnit_Auto == mStyleMargin->mMargin.GetLeftUnit()) { |
|
1026 // XXX FIXME (or does CalculateBlockSideMargins do this?) |
|
1027 margin.left = 0; // just ignore |
|
1028 } else { |
|
1029 margin.left = nsLayoutUtils:: |
|
1030 ComputeCBDependentValue(aContainingBlockWidth, |
|
1031 mStyleMargin->mMargin.GetLeft()); |
|
1032 } |
|
1033 if (eStyleUnit_Auto == mStyleMargin->mMargin.GetRightUnit()) { |
|
1034 // XXX FIXME (or does CalculateBlockSideMargins do this?) |
|
1035 margin.right = 0; // just ignore |
|
1036 } else { |
|
1037 margin.right = nsLayoutUtils:: |
|
1038 ComputeCBDependentValue(aContainingBlockWidth, |
|
1039 mStyleMargin->mMargin.GetRight()); |
|
1040 } |
|
1041 } |
|
1042 |
|
1043 nscoord outside = |
|
1044 padding.LeftRight() + border.LeftRight() + margin.LeftRight(); |
|
1045 nscoord inside = 0; |
|
1046 switch (mStylePosition->mBoxSizing) { |
|
1047 case NS_STYLE_BOX_SIZING_BORDER: |
|
1048 inside += border.LeftRight(); |
|
1049 // fall through |
|
1050 case NS_STYLE_BOX_SIZING_PADDING: |
|
1051 inside += padding.LeftRight(); |
|
1052 } |
|
1053 outside -= inside; |
|
1054 *aInsideBoxSizing = inside; |
|
1055 *aOutsideBoxSizing = outside; |
|
1056 return; |
|
1057 } |
|
1058 |
|
1059 /** |
|
1060 * Returns true iff a pre-order traversal of the normal child |
|
1061 * frames rooted at aFrame finds no non-empty frame before aDescendant. |
|
1062 */ |
|
1063 static bool AreAllEarlierInFlowFramesEmpty(nsIFrame* aFrame, |
|
1064 nsIFrame* aDescendant, bool* aFound) { |
|
1065 if (aFrame == aDescendant) { |
|
1066 *aFound = true; |
|
1067 return true; |
|
1068 } |
|
1069 if (!aFrame->IsSelfEmpty()) { |
|
1070 *aFound = false; |
|
1071 return false; |
|
1072 } |
|
1073 for (nsIFrame* f = aFrame->GetFirstPrincipalChild(); f; f = f->GetNextSibling()) { |
|
1074 bool allEmpty = AreAllEarlierInFlowFramesEmpty(f, aDescendant, aFound); |
|
1075 if (*aFound || !allEmpty) { |
|
1076 return allEmpty; |
|
1077 } |
|
1078 } |
|
1079 *aFound = false; |
|
1080 return true; |
|
1081 } |
|
1082 |
|
1083 // Calculate the hypothetical box that the element would have if it were in |
|
1084 // the flow. The values returned are relative to the padding edge of the |
|
1085 // absolute containing block |
|
1086 // aContainingBlock is the placeholder's containing block (XXX rename it?) |
|
1087 // cbrs->frame is the actual containing block |
|
1088 void |
|
1089 nsHTMLReflowState::CalculateHypotheticalBox(nsPresContext* aPresContext, |
|
1090 nsIFrame* aPlaceholderFrame, |
|
1091 nsIFrame* aContainingBlock, |
|
1092 nscoord aBlockLeftContentEdge, |
|
1093 nscoord aBlockContentWidth, |
|
1094 const nsHTMLReflowState* cbrs, |
|
1095 nsHypotheticalBox& aHypotheticalBox, |
|
1096 nsIAtom* aFrameType) |
|
1097 { |
|
1098 NS_ASSERTION(mStyleDisplay->mOriginalDisplay != NS_STYLE_DISPLAY_NONE, |
|
1099 "mOriginalDisplay has not been properly initialized"); |
|
1100 |
|
1101 // If it's a replaced element and it has a 'auto' value for 'width', see if we |
|
1102 // can get the intrinsic size. This will allow us to exactly determine both the |
|
1103 // left and right edges |
|
1104 bool isAutoWidth = mStylePosition->mWidth.GetUnit() == eStyleUnit_Auto; |
|
1105 nsSize intrinsicSize; |
|
1106 bool knowIntrinsicSize = false; |
|
1107 if (NS_FRAME_IS_REPLACED(mFrameType) && isAutoWidth) { |
|
1108 // See if we can get the intrinsic size of the element |
|
1109 knowIntrinsicSize = GetIntrinsicSizeFor(frame, intrinsicSize, aFrameType); |
|
1110 } |
|
1111 |
|
1112 // See if we can calculate what the box width would have been if the |
|
1113 // element had been in the flow |
|
1114 nscoord boxWidth; |
|
1115 bool knowBoxWidth = false; |
|
1116 if ((NS_STYLE_DISPLAY_INLINE == mStyleDisplay->mOriginalDisplay) && |
|
1117 !NS_FRAME_IS_REPLACED(mFrameType)) { |
|
1118 // For non-replaced inline-level elements the 'width' property doesn't apply, |
|
1119 // so we don't know what the width would have been without reflowing it |
|
1120 |
|
1121 } else { |
|
1122 // It's either a replaced inline-level element or a block-level element |
|
1123 |
|
1124 // Determine the total amount of horizontal border/padding/margin that |
|
1125 // the element would have had if it had been in the flow. Note that we |
|
1126 // ignore any 'auto' and 'inherit' values |
|
1127 nscoord insideBoxSizing, outsideBoxSizing; |
|
1128 CalculateHorizBorderPaddingMargin(aBlockContentWidth, |
|
1129 &insideBoxSizing, &outsideBoxSizing); |
|
1130 |
|
1131 if (NS_FRAME_IS_REPLACED(mFrameType) && isAutoWidth) { |
|
1132 // It's a replaced element with an 'auto' width so the box width is |
|
1133 // its intrinsic size plus any border/padding/margin |
|
1134 if (knowIntrinsicSize) { |
|
1135 boxWidth = intrinsicSize.width + outsideBoxSizing + insideBoxSizing; |
|
1136 knowBoxWidth = true; |
|
1137 } |
|
1138 |
|
1139 } else if (isAutoWidth) { |
|
1140 // The box width is the containing block width |
|
1141 boxWidth = aBlockContentWidth; |
|
1142 knowBoxWidth = true; |
|
1143 |
|
1144 } else { |
|
1145 // We need to compute it. It's important we do this, because if it's |
|
1146 // percentage based this computed value may be different from the computed |
|
1147 // value calculated using the absolute containing block width |
|
1148 boxWidth = ComputeWidthValue(aBlockContentWidth, |
|
1149 insideBoxSizing, outsideBoxSizing, |
|
1150 mStylePosition->mWidth) + |
|
1151 insideBoxSizing + outsideBoxSizing; |
|
1152 knowBoxWidth = true; |
|
1153 } |
|
1154 } |
|
1155 |
|
1156 // Get the 'direction' of the block |
|
1157 const nsStyleVisibility* blockVis = aContainingBlock->StyleVisibility(); |
|
1158 |
|
1159 // Get the placeholder x-offset and y-offset in the coordinate |
|
1160 // space of its containing block |
|
1161 // XXXbz the placeholder is not fully reflowed yet if our containing block is |
|
1162 // relatively positioned... |
|
1163 nsPoint placeholderOffset = aPlaceholderFrame->GetOffsetTo(aContainingBlock); |
|
1164 |
|
1165 // First, determine the hypothetical box's mTop. We want to check the |
|
1166 // content insertion frame of aContainingBlock for block-ness, but make |
|
1167 // sure to compute all coordinates in the coordinate system of |
|
1168 // aContainingBlock. |
|
1169 nsBlockFrame* blockFrame = |
|
1170 nsLayoutUtils::GetAsBlock(aContainingBlock->GetContentInsertionFrame()); |
|
1171 if (blockFrame) { |
|
1172 nscoord blockYOffset = blockFrame->GetOffsetTo(aContainingBlock).y; |
|
1173 bool isValid; |
|
1174 nsBlockInFlowLineIterator iter(blockFrame, aPlaceholderFrame, &isValid); |
|
1175 if (!isValid) { |
|
1176 // Give up. We're probably dealing with somebody using |
|
1177 // position:absolute inside native-anonymous content anyway. |
|
1178 aHypotheticalBox.mTop = placeholderOffset.y; |
|
1179 } else { |
|
1180 NS_ASSERTION(iter.GetContainer() == blockFrame, |
|
1181 "Found placeholder in wrong block!"); |
|
1182 nsBlockFrame::line_iterator lineBox = iter.GetLine(); |
|
1183 |
|
1184 // How we determine the hypothetical box depends on whether the element |
|
1185 // would have been inline-level or block-level |
|
1186 if (mStyleDisplay->IsOriginalDisplayInlineOutsideStyle()) { |
|
1187 // Use the top of the inline box which the placeholder lives in |
|
1188 // as the hypothetical box's top. |
|
1189 aHypotheticalBox.mTop = lineBox->GetPhysicalBounds().y + blockYOffset; |
|
1190 } else { |
|
1191 // The element would have been block-level which means it would |
|
1192 // be below the line containing the placeholder frame, unless |
|
1193 // all the frames before it are empty. In that case, it would |
|
1194 // have been just before this line. |
|
1195 // XXXbz the line box is not fully reflowed yet if our |
|
1196 // containing block is relatively positioned... |
|
1197 if (lineBox != iter.End()) { |
|
1198 nsIFrame * firstFrame = lineBox->mFirstChild; |
|
1199 bool found = false; |
|
1200 bool allEmpty = true; |
|
1201 while (firstFrame) { // See bug 223064 |
|
1202 allEmpty = AreAllEarlierInFlowFramesEmpty(firstFrame, |
|
1203 aPlaceholderFrame, &found); |
|
1204 if (found || !allEmpty) |
|
1205 break; |
|
1206 firstFrame = firstFrame->GetNextSibling(); |
|
1207 } |
|
1208 NS_ASSERTION(firstFrame, "Couldn't find placeholder!"); |
|
1209 |
|
1210 if (allEmpty) { |
|
1211 // The top of the hypothetical box is the top of the line |
|
1212 // containing the placeholder, since there is nothing in the |
|
1213 // line before our placeholder except empty frames. |
|
1214 aHypotheticalBox.mTop = lineBox->GetPhysicalBounds().y + blockYOffset; |
|
1215 } else { |
|
1216 // The top of the hypothetical box is just below the line |
|
1217 // containing the placeholder. |
|
1218 aHypotheticalBox.mTop = lineBox->GetPhysicalBounds().YMost() + blockYOffset; |
|
1219 } |
|
1220 } else { |
|
1221 // Just use the placeholder's y-offset wrt the containing block |
|
1222 aHypotheticalBox.mTop = placeholderOffset.y; |
|
1223 } |
|
1224 } |
|
1225 } |
|
1226 } else { |
|
1227 // The containing block is not a block, so it's probably something |
|
1228 // like a XUL box, etc. |
|
1229 // Just use the placeholder's y-offset |
|
1230 aHypotheticalBox.mTop = placeholderOffset.y; |
|
1231 } |
|
1232 |
|
1233 // Second, determine the hypothetical box's mLeft & mRight |
|
1234 // To determine the left and right offsets we need to look at the block's 'direction' |
|
1235 if (NS_STYLE_DIRECTION_LTR == blockVis->mDirection) { |
|
1236 // How we determine the hypothetical box depends on whether the element |
|
1237 // would have been inline-level or block-level |
|
1238 if (mStyleDisplay->IsOriginalDisplayInlineOutsideStyle()) { |
|
1239 // The placeholder represents the left edge of the hypothetical box |
|
1240 aHypotheticalBox.mLeft = placeholderOffset.x; |
|
1241 } else { |
|
1242 aHypotheticalBox.mLeft = aBlockLeftContentEdge; |
|
1243 } |
|
1244 #ifdef DEBUG |
|
1245 aHypotheticalBox.mLeftIsExact = true; |
|
1246 #endif |
|
1247 |
|
1248 if (knowBoxWidth) { |
|
1249 aHypotheticalBox.mRight = aHypotheticalBox.mLeft + boxWidth; |
|
1250 #ifdef DEBUG |
|
1251 aHypotheticalBox.mRightIsExact = true; |
|
1252 #endif |
|
1253 } else { |
|
1254 // We can't compute the right edge because we don't know the desired |
|
1255 // width. So instead use the right content edge of the block parent, |
|
1256 // but remember it's not exact |
|
1257 aHypotheticalBox.mRight = aBlockLeftContentEdge + aBlockContentWidth; |
|
1258 #ifdef DEBUG |
|
1259 aHypotheticalBox.mRightIsExact = false; |
|
1260 #endif |
|
1261 } |
|
1262 |
|
1263 } else { |
|
1264 // The placeholder represents the right edge of the hypothetical box |
|
1265 if (mStyleDisplay->IsOriginalDisplayInlineOutsideStyle()) { |
|
1266 aHypotheticalBox.mRight = placeholderOffset.x; |
|
1267 } else { |
|
1268 aHypotheticalBox.mRight = aBlockLeftContentEdge + aBlockContentWidth; |
|
1269 } |
|
1270 #ifdef DEBUG |
|
1271 aHypotheticalBox.mRightIsExact = true; |
|
1272 #endif |
|
1273 |
|
1274 if (knowBoxWidth) { |
|
1275 aHypotheticalBox.mLeft = aHypotheticalBox.mRight - boxWidth; |
|
1276 #ifdef DEBUG |
|
1277 aHypotheticalBox.mLeftIsExact = true; |
|
1278 #endif |
|
1279 } else { |
|
1280 // We can't compute the left edge because we don't know the desired |
|
1281 // width. So instead use the left content edge of the block parent, |
|
1282 // but remember it's not exact |
|
1283 aHypotheticalBox.mLeft = aBlockLeftContentEdge; |
|
1284 #ifdef DEBUG |
|
1285 aHypotheticalBox.mLeftIsExact = false; |
|
1286 #endif |
|
1287 } |
|
1288 |
|
1289 } |
|
1290 |
|
1291 // The current coordinate space is that of the nearest block to the placeholder. |
|
1292 // Convert to the coordinate space of the absolute containing block |
|
1293 // One weird thing here is that for fixed-positioned elements we want to do |
|
1294 // the conversion incorrectly; specifically we want to ignore any scrolling |
|
1295 // that may have happened; |
|
1296 nsPoint cbOffset; |
|
1297 if (mStyleDisplay->mPosition == NS_STYLE_POSITION_FIXED && |
|
1298 // Exclude cases inside -moz-transform where fixed is like absolute. |
|
1299 nsLayoutUtils::IsReallyFixedPos(frame)) { |
|
1300 // In this case, cbrs->frame will always be an ancestor of |
|
1301 // aContainingBlock, so can just walk our way up the frame tree. |
|
1302 // Make sure to not add positions of frames whose parent is a |
|
1303 // scrollFrame, since we're doing fixed positioning, which assumes |
|
1304 // everything is scrolled to (0,0). |
|
1305 cbOffset.MoveTo(0, 0); |
|
1306 do { |
|
1307 NS_ASSERTION(aContainingBlock, |
|
1308 "Should hit cbrs->frame before we run off the frame tree!"); |
|
1309 cbOffset += aContainingBlock->GetPositionIgnoringScrolling(); |
|
1310 aContainingBlock = aContainingBlock->GetParent(); |
|
1311 } while (aContainingBlock != cbrs->frame); |
|
1312 } else { |
|
1313 // XXXldb We need to either ignore scrolling for the absolute |
|
1314 // positioning case too (and take the incompatibility) or figure out |
|
1315 // how to make these positioned elements actually *move* when we |
|
1316 // scroll, and thus avoid the resulting incremental reflow bugs. |
|
1317 cbOffset = aContainingBlock->GetOffsetTo(cbrs->frame); |
|
1318 } |
|
1319 aHypotheticalBox.mLeft += cbOffset.x; |
|
1320 aHypotheticalBox.mTop += cbOffset.y; |
|
1321 aHypotheticalBox.mRight += cbOffset.x; |
|
1322 |
|
1323 // The specified offsets are relative to the absolute containing block's |
|
1324 // padding edge and our current values are relative to the border edge, so |
|
1325 // translate. |
|
1326 nsMargin border = cbrs->ComputedPhysicalBorderPadding() - cbrs->ComputedPhysicalPadding(); |
|
1327 aHypotheticalBox.mLeft -= border.left; |
|
1328 aHypotheticalBox.mRight -= border.left; |
|
1329 aHypotheticalBox.mTop -= border.top; |
|
1330 } |
|
1331 |
|
1332 void |
|
1333 nsHTMLReflowState::InitAbsoluteConstraints(nsPresContext* aPresContext, |
|
1334 const nsHTMLReflowState* cbrs, |
|
1335 nscoord containingBlockWidth, |
|
1336 nscoord containingBlockHeight, |
|
1337 nsIAtom* aFrameType) |
|
1338 { |
|
1339 NS_PRECONDITION(containingBlockHeight != NS_AUTOHEIGHT, |
|
1340 "containing block height must be constrained"); |
|
1341 |
|
1342 NS_ASSERTION(aFrameType != nsGkAtoms::tableFrame, |
|
1343 "InitAbsoluteConstraints should not be called on table frames"); |
|
1344 NS_ASSERTION(frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW, |
|
1345 "Why are we here?"); |
|
1346 |
|
1347 // Get the placeholder frame |
|
1348 nsIFrame* placeholderFrame; |
|
1349 |
|
1350 placeholderFrame = aPresContext->PresShell()->GetPlaceholderFrameFor(frame); |
|
1351 NS_ASSERTION(nullptr != placeholderFrame, "no placeholder frame"); |
|
1352 |
|
1353 // If both 'left' and 'right' are 'auto' or both 'top' and 'bottom' are |
|
1354 // 'auto', then compute the hypothetical box of where the element would |
|
1355 // have been if it had been in the flow |
|
1356 nsHypotheticalBox hypotheticalBox; |
|
1357 if (((eStyleUnit_Auto == mStylePosition->mOffset.GetLeftUnit()) && |
|
1358 (eStyleUnit_Auto == mStylePosition->mOffset.GetRightUnit())) || |
|
1359 ((eStyleUnit_Auto == mStylePosition->mOffset.GetTopUnit()) && |
|
1360 (eStyleUnit_Auto == mStylePosition->mOffset.GetBottomUnit()))) { |
|
1361 // Find the nearest containing block frame to the placeholder frame, |
|
1362 // and return its left edge and width. |
|
1363 nscoord cbLeftEdge, cbWidth; |
|
1364 nsIFrame* cbFrame = GetHypotheticalBoxContainer(placeholderFrame, |
|
1365 cbLeftEdge, |
|
1366 cbWidth); |
|
1367 |
|
1368 CalculateHypotheticalBox(aPresContext, placeholderFrame, cbFrame, |
|
1369 cbLeftEdge, cbWidth, cbrs, hypotheticalBox, aFrameType); |
|
1370 } |
|
1371 |
|
1372 // Initialize the 'left' and 'right' computed offsets |
|
1373 // XXX Handle new 'static-position' value... |
|
1374 bool leftIsAuto = false, rightIsAuto = false; |
|
1375 if (eStyleUnit_Auto == mStylePosition->mOffset.GetLeftUnit()) { |
|
1376 ComputedPhysicalOffsets().left = 0; |
|
1377 leftIsAuto = true; |
|
1378 } else { |
|
1379 ComputedPhysicalOffsets().left = nsLayoutUtils:: |
|
1380 ComputeCBDependentValue(containingBlockWidth, |
|
1381 mStylePosition->mOffset.GetLeft()); |
|
1382 } |
|
1383 if (eStyleUnit_Auto == mStylePosition->mOffset.GetRightUnit()) { |
|
1384 ComputedPhysicalOffsets().right = 0; |
|
1385 rightIsAuto = true; |
|
1386 } else { |
|
1387 ComputedPhysicalOffsets().right = nsLayoutUtils:: |
|
1388 ComputeCBDependentValue(containingBlockWidth, |
|
1389 mStylePosition->mOffset.GetRight()); |
|
1390 } |
|
1391 |
|
1392 // Use the horizontal component of the hypothetical box in the cases |
|
1393 // where it's needed. |
|
1394 if (leftIsAuto && rightIsAuto) { |
|
1395 // Use the direction of the original ("static-position") containing block |
|
1396 // to dictate whether 'left' or 'right' is treated like 'static-position'. |
|
1397 if (NS_STYLE_DIRECTION_LTR == placeholderFrame->GetContainingBlock() |
|
1398 ->StyleVisibility()->mDirection) { |
|
1399 NS_ASSERTION(hypotheticalBox.mLeftIsExact, "should always have " |
|
1400 "exact value on containing block's start side"); |
|
1401 ComputedPhysicalOffsets().left = hypotheticalBox.mLeft; |
|
1402 leftIsAuto = false; |
|
1403 } else { |
|
1404 NS_ASSERTION(hypotheticalBox.mRightIsExact, "should always have " |
|
1405 "exact value on containing block's start side"); |
|
1406 ComputedPhysicalOffsets().right = containingBlockWidth - hypotheticalBox.mRight; |
|
1407 rightIsAuto = false; |
|
1408 } |
|
1409 } |
|
1410 |
|
1411 // Initialize the 'top' and 'bottom' computed offsets |
|
1412 bool topIsAuto = false, bottomIsAuto = false; |
|
1413 if (eStyleUnit_Auto == mStylePosition->mOffset.GetTopUnit()) { |
|
1414 ComputedPhysicalOffsets().top = 0; |
|
1415 topIsAuto = true; |
|
1416 } else { |
|
1417 ComputedPhysicalOffsets().top = nsLayoutUtils:: |
|
1418 ComputeHeightDependentValue(containingBlockHeight, |
|
1419 mStylePosition->mOffset.GetTop()); |
|
1420 } |
|
1421 if (eStyleUnit_Auto == mStylePosition->mOffset.GetBottomUnit()) { |
|
1422 ComputedPhysicalOffsets().bottom = 0; |
|
1423 bottomIsAuto = true; |
|
1424 } else { |
|
1425 ComputedPhysicalOffsets().bottom = nsLayoutUtils:: |
|
1426 ComputeHeightDependentValue(containingBlockHeight, |
|
1427 mStylePosition->mOffset.GetBottom()); |
|
1428 } |
|
1429 |
|
1430 if (topIsAuto && bottomIsAuto) { |
|
1431 // Treat 'top' like 'static-position' |
|
1432 ComputedPhysicalOffsets().top = hypotheticalBox.mTop; |
|
1433 topIsAuto = false; |
|
1434 } |
|
1435 |
|
1436 bool widthIsAuto = eStyleUnit_Auto == mStylePosition->mWidth.GetUnit(); |
|
1437 bool heightIsAuto = eStyleUnit_Auto == mStylePosition->mHeight.GetUnit(); |
|
1438 |
|
1439 uint32_t computeSizeFlags = 0; |
|
1440 if (leftIsAuto || rightIsAuto) { |
|
1441 computeSizeFlags |= nsIFrame::eShrinkWrap; |
|
1442 } |
|
1443 |
|
1444 { |
|
1445 AutoMaybeDisableFontInflation an(frame); |
|
1446 |
|
1447 nsSize size = |
|
1448 frame->ComputeSize(rendContext, |
|
1449 nsSize(containingBlockWidth, |
|
1450 containingBlockHeight), |
|
1451 containingBlockWidth, // XXX or mAvailableWidth? |
|
1452 nsSize(ComputedPhysicalMargin().LeftRight() + |
|
1453 ComputedPhysicalOffsets().LeftRight(), |
|
1454 ComputedPhysicalMargin().TopBottom() + |
|
1455 ComputedPhysicalOffsets().TopBottom()), |
|
1456 nsSize(ComputedPhysicalBorderPadding().LeftRight() - |
|
1457 ComputedPhysicalPadding().LeftRight(), |
|
1458 ComputedPhysicalBorderPadding().TopBottom() - |
|
1459 ComputedPhysicalPadding().TopBottom()), |
|
1460 nsSize(ComputedPhysicalPadding().LeftRight(), |
|
1461 ComputedPhysicalPadding().TopBottom()), |
|
1462 computeSizeFlags); |
|
1463 ComputedWidth() = size.width; |
|
1464 ComputedHeight() = size.height; |
|
1465 } |
|
1466 NS_ASSERTION(ComputedWidth() >= 0, "Bogus width"); |
|
1467 NS_ASSERTION(ComputedHeight() == NS_UNCONSTRAINEDSIZE || |
|
1468 ComputedHeight() >= 0, "Bogus height"); |
|
1469 |
|
1470 // XXX Now that we have ComputeSize, can we condense many of the |
|
1471 // branches off of widthIsAuto? |
|
1472 |
|
1473 if (leftIsAuto) { |
|
1474 // We know 'right' is not 'auto' anymore thanks to the hypothetical |
|
1475 // box code above. |
|
1476 // Solve for 'left'. |
|
1477 if (widthIsAuto) { |
|
1478 // XXXldb This, and the corresponding code in |
|
1479 // nsAbsoluteContainingBlock.cpp, could probably go away now that |
|
1480 // we always compute widths. |
|
1481 ComputedPhysicalOffsets().left = NS_AUTOOFFSET; |
|
1482 } else { |
|
1483 ComputedPhysicalOffsets().left = containingBlockWidth - ComputedPhysicalMargin().left - |
|
1484 ComputedPhysicalBorderPadding().left - ComputedWidth() - ComputedPhysicalBorderPadding().right - |
|
1485 ComputedPhysicalMargin().right - ComputedPhysicalOffsets().right; |
|
1486 |
|
1487 } |
|
1488 } else if (rightIsAuto) { |
|
1489 // We know 'left' is not 'auto' anymore thanks to the hypothetical |
|
1490 // box code above. |
|
1491 // Solve for 'right'. |
|
1492 if (widthIsAuto) { |
|
1493 // XXXldb This, and the corresponding code in |
|
1494 // nsAbsoluteContainingBlock.cpp, could probably go away now that |
|
1495 // we always compute widths. |
|
1496 ComputedPhysicalOffsets().right = NS_AUTOOFFSET; |
|
1497 } else { |
|
1498 ComputedPhysicalOffsets().right = containingBlockWidth - ComputedPhysicalOffsets().left - |
|
1499 ComputedPhysicalMargin().left - ComputedPhysicalBorderPadding().left - ComputedWidth() - |
|
1500 ComputedPhysicalBorderPadding().right - ComputedPhysicalMargin().right; |
|
1501 } |
|
1502 } else { |
|
1503 // Neither 'left' nor 'right' is 'auto'. However, the width might |
|
1504 // still not fill all the available space (even though we didn't |
|
1505 // shrink-wrap) in case: |
|
1506 // * width was specified |
|
1507 // * we're dealing with a replaced element |
|
1508 // * width was constrained by min-width or max-width. |
|
1509 |
|
1510 nscoord availMarginSpace = containingBlockWidth - |
|
1511 ComputedPhysicalOffsets().LeftRight() - |
|
1512 ComputedPhysicalMargin().LeftRight() - |
|
1513 ComputedPhysicalBorderPadding().LeftRight() - |
|
1514 ComputedWidth(); |
|
1515 bool marginLeftIsAuto = |
|
1516 eStyleUnit_Auto == mStyleMargin->mMargin.GetLeftUnit(); |
|
1517 bool marginRightIsAuto = |
|
1518 eStyleUnit_Auto == mStyleMargin->mMargin.GetRightUnit(); |
|
1519 |
|
1520 if (marginLeftIsAuto) { |
|
1521 if (marginRightIsAuto) { |
|
1522 if (availMarginSpace < 0) { |
|
1523 // Note that this case is different from the neither-'auto' |
|
1524 // case below, where the spec says to ignore 'left'/'right'. |
|
1525 if (cbrs && |
|
1526 NS_STYLE_DIRECTION_RTL == cbrs->mStyleVisibility->mDirection) { |
|
1527 // Ignore the specified value for 'margin-left'. |
|
1528 ComputedPhysicalMargin().left = availMarginSpace; |
|
1529 } else { |
|
1530 // Ignore the specified value for 'margin-right'. |
|
1531 ComputedPhysicalMargin().right = availMarginSpace; |
|
1532 } |
|
1533 } else { |
|
1534 // Both 'margin-left' and 'margin-right' are 'auto', so they get |
|
1535 // equal values |
|
1536 ComputedPhysicalMargin().left = availMarginSpace / 2; |
|
1537 ComputedPhysicalMargin().right = availMarginSpace - ComputedPhysicalMargin().left; |
|
1538 } |
|
1539 } else { |
|
1540 // Just 'margin-left' is 'auto' |
|
1541 ComputedPhysicalMargin().left = availMarginSpace; |
|
1542 } |
|
1543 } else { |
|
1544 if (marginRightIsAuto) { |
|
1545 // Just 'margin-right' is 'auto' |
|
1546 ComputedPhysicalMargin().right = availMarginSpace; |
|
1547 } else { |
|
1548 // We're over-constrained so use the direction of the containing |
|
1549 // block to dictate which value to ignore. (And note that the |
|
1550 // spec says to ignore 'left' or 'right' rather than |
|
1551 // 'margin-left' or 'margin-right'.) |
|
1552 // Note that this case is different from the both-'auto' case |
|
1553 // above, where the spec says to ignore |
|
1554 // 'margin-left'/'margin-right'. |
|
1555 if (cbrs && |
|
1556 NS_STYLE_DIRECTION_RTL == cbrs->mStyleVisibility->mDirection) { |
|
1557 // Ignore the specified value for 'left'. |
|
1558 ComputedPhysicalOffsets().left += availMarginSpace; |
|
1559 } else { |
|
1560 // Ignore the specified value for 'right'. |
|
1561 ComputedPhysicalOffsets().right += availMarginSpace; |
|
1562 } |
|
1563 } |
|
1564 } |
|
1565 } |
|
1566 |
|
1567 if (topIsAuto) { |
|
1568 // solve for 'top' |
|
1569 if (heightIsAuto) { |
|
1570 ComputedPhysicalOffsets().top = NS_AUTOOFFSET; |
|
1571 } else { |
|
1572 ComputedPhysicalOffsets().top = containingBlockHeight - ComputedPhysicalMargin().top - |
|
1573 ComputedPhysicalBorderPadding().top - ComputedHeight() - ComputedPhysicalBorderPadding().bottom - |
|
1574 ComputedPhysicalMargin().bottom - ComputedPhysicalOffsets().bottom; |
|
1575 } |
|
1576 } else if (bottomIsAuto) { |
|
1577 // solve for 'bottom' |
|
1578 if (heightIsAuto) { |
|
1579 ComputedPhysicalOffsets().bottom = NS_AUTOOFFSET; |
|
1580 } else { |
|
1581 ComputedPhysicalOffsets().bottom = containingBlockHeight - ComputedPhysicalOffsets().top - |
|
1582 ComputedPhysicalMargin().top - ComputedPhysicalBorderPadding().top - ComputedHeight() - |
|
1583 ComputedPhysicalBorderPadding().bottom - ComputedPhysicalMargin().bottom; |
|
1584 } |
|
1585 } else { |
|
1586 // Neither 'top' nor 'bottom' is 'auto'. |
|
1587 nscoord autoHeight = containingBlockHeight - |
|
1588 ComputedPhysicalOffsets().TopBottom() - |
|
1589 ComputedPhysicalMargin().TopBottom() - |
|
1590 ComputedPhysicalBorderPadding().TopBottom(); |
|
1591 if (autoHeight < 0) { |
|
1592 autoHeight = 0; |
|
1593 } |
|
1594 |
|
1595 if (ComputedHeight() == NS_UNCONSTRAINEDSIZE) { |
|
1596 // For non-replaced elements with 'height' auto, the 'height' |
|
1597 // fills the remaining space. |
|
1598 ComputedHeight() = autoHeight; |
|
1599 |
|
1600 // XXX Do these need box-sizing adjustments? |
|
1601 if (ComputedHeight() > ComputedMaxHeight()) |
|
1602 ComputedHeight() = ComputedMaxHeight(); |
|
1603 if (ComputedHeight() < ComputedMinHeight()) |
|
1604 ComputedHeight() = ComputedMinHeight(); |
|
1605 } |
|
1606 |
|
1607 // The height might still not fill all the available space in case: |
|
1608 // * height was specified |
|
1609 // * we're dealing with a replaced element |
|
1610 // * height was constrained by min-height or max-height. |
|
1611 nscoord availMarginSpace = autoHeight - ComputedHeight(); |
|
1612 bool marginTopIsAuto = |
|
1613 eStyleUnit_Auto == mStyleMargin->mMargin.GetTopUnit(); |
|
1614 bool marginBottomIsAuto = |
|
1615 eStyleUnit_Auto == mStyleMargin->mMargin.GetBottomUnit(); |
|
1616 |
|
1617 if (marginTopIsAuto) { |
|
1618 if (marginBottomIsAuto) { |
|
1619 if (availMarginSpace < 0) { |
|
1620 // FIXME: Note that the spec doesn't actually say we should do this! |
|
1621 ComputedPhysicalMargin().bottom = availMarginSpace; |
|
1622 } else { |
|
1623 // Both 'margin-top' and 'margin-bottom' are 'auto', so they get |
|
1624 // equal values |
|
1625 ComputedPhysicalMargin().top = availMarginSpace / 2; |
|
1626 ComputedPhysicalMargin().bottom = availMarginSpace - ComputedPhysicalMargin().top; |
|
1627 } |
|
1628 } else { |
|
1629 // Just 'margin-top' is 'auto' |
|
1630 ComputedPhysicalMargin().top = availMarginSpace; |
|
1631 } |
|
1632 } else { |
|
1633 if (marginBottomIsAuto) { |
|
1634 // Just 'margin-bottom' is 'auto' |
|
1635 ComputedPhysicalMargin().bottom = availMarginSpace; |
|
1636 } else { |
|
1637 // We're over-constrained so ignore the specified value for |
|
1638 // 'bottom'. (And note that the spec says to ignore 'bottom' |
|
1639 // rather than 'margin-bottom'.) |
|
1640 ComputedPhysicalOffsets().bottom += availMarginSpace; |
|
1641 } |
|
1642 } |
|
1643 } |
|
1644 } |
|
1645 |
|
1646 nscoord |
|
1647 GetVerticalMarginBorderPadding(const nsHTMLReflowState* aReflowState) |
|
1648 { |
|
1649 nscoord result = 0; |
|
1650 if (!aReflowState) return result; |
|
1651 |
|
1652 // zero auto margins |
|
1653 nsMargin margin = aReflowState->ComputedPhysicalMargin(); |
|
1654 if (NS_AUTOMARGIN == margin.top) |
|
1655 margin.top = 0; |
|
1656 if (NS_AUTOMARGIN == margin.bottom) |
|
1657 margin.bottom = 0; |
|
1658 |
|
1659 result += margin.top + margin.bottom; |
|
1660 result += aReflowState->ComputedPhysicalBorderPadding().top + |
|
1661 aReflowState->ComputedPhysicalBorderPadding().bottom; |
|
1662 |
|
1663 return result; |
|
1664 } |
|
1665 |
|
1666 /* Get the height based on the viewport of the containing block specified |
|
1667 * in aReflowState when the containing block has mComputedHeight == NS_AUTOHEIGHT |
|
1668 * This will walk up the chain of containing blocks looking for a computed height |
|
1669 * until it finds the canvas frame, or it encounters a frame that is not a block, |
|
1670 * area, or scroll frame. This handles compatibility with IE (see bug 85016 and bug 219693) |
|
1671 * |
|
1672 * When we encounter scrolledContent block frames, we skip over them, since they are guaranteed to not be useful for computing the containing block. |
|
1673 * |
|
1674 * See also IsQuirkContainingBlockHeight. |
|
1675 */ |
|
1676 static nscoord |
|
1677 CalcQuirkContainingBlockHeight(const nsHTMLReflowState* aCBReflowState) |
|
1678 { |
|
1679 const nsHTMLReflowState* firstAncestorRS = nullptr; // a candidate for html frame |
|
1680 const nsHTMLReflowState* secondAncestorRS = nullptr; // a candidate for body frame |
|
1681 |
|
1682 // initialize the default to NS_AUTOHEIGHT as this is the containings block |
|
1683 // computed height when this function is called. It is possible that we |
|
1684 // don't alter this height especially if we are restricted to one level |
|
1685 nscoord result = NS_AUTOHEIGHT; |
|
1686 |
|
1687 const nsHTMLReflowState* rs = aCBReflowState; |
|
1688 for (; rs; rs = rs->parentReflowState) { |
|
1689 nsIAtom* frameType = rs->frame->GetType(); |
|
1690 // if the ancestor is auto height then skip it and continue up if it |
|
1691 // is the first block frame and possibly the body/html |
|
1692 if (nsGkAtoms::blockFrame == frameType || |
|
1693 #ifdef MOZ_XUL |
|
1694 nsGkAtoms::XULLabelFrame == frameType || |
|
1695 #endif |
|
1696 nsGkAtoms::scrollFrame == frameType) { |
|
1697 |
|
1698 secondAncestorRS = firstAncestorRS; |
|
1699 firstAncestorRS = rs; |
|
1700 |
|
1701 // If the current frame we're looking at is positioned, we don't want to |
|
1702 // go any further (see bug 221784). The behavior we want here is: 1) If |
|
1703 // not auto-height, use this as the percentage base. 2) If auto-height, |
|
1704 // keep looking, unless the frame is positioned. |
|
1705 if (NS_AUTOHEIGHT == rs->ComputedHeight()) { |
|
1706 if (rs->frame->IsAbsolutelyPositioned()) { |
|
1707 break; |
|
1708 } else { |
|
1709 continue; |
|
1710 } |
|
1711 } |
|
1712 } |
|
1713 else if (nsGkAtoms::canvasFrame == frameType) { |
|
1714 // Always continue on to the height calculation |
|
1715 } |
|
1716 else if (nsGkAtoms::pageContentFrame == frameType) { |
|
1717 nsIFrame* prevInFlow = rs->frame->GetPrevInFlow(); |
|
1718 // only use the page content frame for a height basis if it is the first in flow |
|
1719 if (prevInFlow) |
|
1720 break; |
|
1721 } |
|
1722 else { |
|
1723 break; |
|
1724 } |
|
1725 |
|
1726 // if the ancestor is the page content frame then the percent base is |
|
1727 // the avail height, otherwise it is the computed height |
|
1728 result = (nsGkAtoms::pageContentFrame == frameType) |
|
1729 ? rs->AvailableHeight() : rs->ComputedHeight(); |
|
1730 // if unconstrained - don't sutract borders - would result in huge height |
|
1731 if (NS_AUTOHEIGHT == result) return result; |
|
1732 |
|
1733 // if we got to the canvas or page content frame, then subtract out |
|
1734 // margin/border/padding for the BODY and HTML elements |
|
1735 if ((nsGkAtoms::canvasFrame == frameType) || |
|
1736 (nsGkAtoms::pageContentFrame == frameType)) { |
|
1737 |
|
1738 result -= GetVerticalMarginBorderPadding(firstAncestorRS); |
|
1739 result -= GetVerticalMarginBorderPadding(secondAncestorRS); |
|
1740 |
|
1741 #ifdef DEBUG |
|
1742 // make sure the first ancestor is the HTML and the second is the BODY |
|
1743 if (firstAncestorRS) { |
|
1744 nsIContent* frameContent = firstAncestorRS->frame->GetContent(); |
|
1745 if (frameContent) { |
|
1746 nsIAtom *contentTag = frameContent->Tag(); |
|
1747 NS_ASSERTION(contentTag == nsGkAtoms::html, "First ancestor is not HTML"); |
|
1748 } |
|
1749 } |
|
1750 if (secondAncestorRS) { |
|
1751 nsIContent* frameContent = secondAncestorRS->frame->GetContent(); |
|
1752 if (frameContent) { |
|
1753 nsIAtom *contentTag = frameContent->Tag(); |
|
1754 NS_ASSERTION(contentTag == nsGkAtoms::body, "Second ancestor is not BODY"); |
|
1755 } |
|
1756 } |
|
1757 #endif |
|
1758 |
|
1759 } |
|
1760 // if we got to the html frame (a block child of the canvas) ... |
|
1761 else if (nsGkAtoms::blockFrame == frameType && |
|
1762 rs->parentReflowState && |
|
1763 nsGkAtoms::canvasFrame == |
|
1764 rs->parentReflowState->frame->GetType()) { |
|
1765 // ... then subtract out margin/border/padding for the BODY element |
|
1766 result -= GetVerticalMarginBorderPadding(secondAncestorRS); |
|
1767 } |
|
1768 break; |
|
1769 } |
|
1770 |
|
1771 // Make sure not to return a negative height here! |
|
1772 return std::max(result, 0); |
|
1773 } |
|
1774 |
|
1775 // Called by InitConstraints() to compute the containing block rectangle for |
|
1776 // the element. Handles the special logic for absolutely positioned elements |
|
1777 void |
|
1778 nsHTMLReflowState::ComputeContainingBlockRectangle(nsPresContext* aPresContext, |
|
1779 const nsHTMLReflowState* aContainingBlockRS, |
|
1780 nscoord& aContainingBlockWidth, |
|
1781 nscoord& aContainingBlockHeight) |
|
1782 { |
|
1783 // Unless the element is absolutely positioned, the containing block is |
|
1784 // formed by the content edge of the nearest block-level ancestor |
|
1785 aContainingBlockWidth = aContainingBlockRS->ComputedWidth(); |
|
1786 aContainingBlockHeight = aContainingBlockRS->ComputedHeight(); |
|
1787 |
|
1788 // mFrameType for abs-pos tables is NS_CSS_FRAME_TYPE_BLOCK, so we need to |
|
1789 // special case them here. |
|
1790 if (NS_FRAME_GET_TYPE(mFrameType) == NS_CSS_FRAME_TYPE_ABSOLUTE || |
|
1791 (frame->GetType() == nsGkAtoms::tableFrame && |
|
1792 frame->IsAbsolutelyPositioned() && |
|
1793 (frame->GetParent()->GetStateBits() & NS_FRAME_OUT_OF_FLOW))) { |
|
1794 // See if the ancestor is block-level or inline-level |
|
1795 if (NS_FRAME_GET_TYPE(aContainingBlockRS->mFrameType) == NS_CSS_FRAME_TYPE_INLINE) { |
|
1796 // Base our size on the actual size of the frame. In cases when this is |
|
1797 // completely bogus (eg initial reflow), this code shouldn't even be |
|
1798 // called, since the code in nsInlineFrame::Reflow will pass in |
|
1799 // the containing block dimensions to our constructor. |
|
1800 // XXXbz we should be taking the in-flows into account too, but |
|
1801 // that's very hard. |
|
1802 nsMargin computedBorder = aContainingBlockRS->ComputedPhysicalBorderPadding() - |
|
1803 aContainingBlockRS->ComputedPhysicalPadding(); |
|
1804 aContainingBlockWidth = aContainingBlockRS->frame->GetRect().width - |
|
1805 computedBorder.LeftRight(); |
|
1806 NS_ASSERTION(aContainingBlockWidth >= 0, |
|
1807 "Negative containing block width!"); |
|
1808 aContainingBlockHeight = aContainingBlockRS->frame->GetRect().height - |
|
1809 computedBorder.TopBottom(); |
|
1810 NS_ASSERTION(aContainingBlockHeight >= 0, |
|
1811 "Negative containing block height!"); |
|
1812 } else { |
|
1813 // If the ancestor is block-level, the containing block is formed by the |
|
1814 // padding edge of the ancestor |
|
1815 aContainingBlockWidth += aContainingBlockRS->ComputedPhysicalPadding().LeftRight(); |
|
1816 aContainingBlockHeight += aContainingBlockRS->ComputedPhysicalPadding().TopBottom(); |
|
1817 } |
|
1818 } else { |
|
1819 // an element in quirks mode gets a containing block based on looking for a |
|
1820 // parent with a non-auto height if the element has a percent height |
|
1821 // Note: We don't emulate this quirk for percents in calc(). |
|
1822 if (NS_AUTOHEIGHT == aContainingBlockHeight) { |
|
1823 if (eCompatibility_NavQuirks == aPresContext->CompatibilityMode() && |
|
1824 mStylePosition->mHeight.GetUnit() == eStyleUnit_Percent) { |
|
1825 aContainingBlockHeight = CalcQuirkContainingBlockHeight(aContainingBlockRS); |
|
1826 } |
|
1827 } |
|
1828 } |
|
1829 } |
|
1830 |
|
1831 static eNormalLineHeightControl GetNormalLineHeightCalcControl(void) |
|
1832 { |
|
1833 if (sNormalLineHeightControl == eUninitialized) { |
|
1834 // browser.display.normal_lineheight_calc_control is not user |
|
1835 // changeable, so no need to register callback for it. |
|
1836 int32_t val = |
|
1837 Preferences::GetInt("browser.display.normal_lineheight_calc_control", |
|
1838 eNoExternalLeading); |
|
1839 sNormalLineHeightControl = static_cast<eNormalLineHeightControl>(val); |
|
1840 } |
|
1841 return sNormalLineHeightControl; |
|
1842 } |
|
1843 |
|
1844 static inline bool |
|
1845 IsSideCaption(nsIFrame* aFrame, const nsStyleDisplay* aStyleDisplay) |
|
1846 { |
|
1847 if (aStyleDisplay->mDisplay != NS_STYLE_DISPLAY_TABLE_CAPTION) |
|
1848 return false; |
|
1849 uint8_t captionSide = aFrame->StyleTableBorder()->mCaptionSide; |
|
1850 return captionSide == NS_STYLE_CAPTION_SIDE_LEFT || |
|
1851 captionSide == NS_STYLE_CAPTION_SIDE_RIGHT; |
|
1852 } |
|
1853 |
|
1854 static nsFlexContainerFrame* |
|
1855 GetFlexContainer(nsIFrame* aFrame) |
|
1856 { |
|
1857 nsIFrame* parent = aFrame->GetParent(); |
|
1858 if (!parent || |
|
1859 parent->GetType() != nsGkAtoms::flexContainerFrame) { |
|
1860 return nullptr; |
|
1861 } |
|
1862 |
|
1863 return static_cast<nsFlexContainerFrame*>(parent); |
|
1864 } |
|
1865 |
|
1866 // Flex items resolve percentage margin & padding against the flex |
|
1867 // container's height (which is the containing block height). |
|
1868 // For everything else: the CSS21 spec requires that margin and padding |
|
1869 // percentage values are calculated with respect to the *width* of the |
|
1870 // containing block, even for margin & padding in the vertical axis. |
|
1871 static nscoord |
|
1872 VerticalOffsetPercentBasis(const nsIFrame* aFrame, |
|
1873 nscoord aContainingBlockWidth, |
|
1874 nscoord aContainingBlockHeight) |
|
1875 { |
|
1876 if (!aFrame->IsFlexItem()) { |
|
1877 return aContainingBlockWidth; |
|
1878 } |
|
1879 |
|
1880 if (aContainingBlockHeight == NS_AUTOHEIGHT) { |
|
1881 return 0; |
|
1882 } |
|
1883 |
|
1884 return aContainingBlockHeight; |
|
1885 } |
|
1886 |
|
1887 // XXX refactor this code to have methods for each set of properties |
|
1888 // we are computing: width,height,line-height; margin; offsets |
|
1889 |
|
1890 void |
|
1891 nsHTMLReflowState::InitConstraints(nsPresContext* aPresContext, |
|
1892 nscoord aContainingBlockWidth, |
|
1893 nscoord aContainingBlockHeight, |
|
1894 const nsMargin* aBorder, |
|
1895 const nsMargin* aPadding, |
|
1896 nsIAtom* aFrameType) |
|
1897 { |
|
1898 DISPLAY_INIT_CONSTRAINTS(frame, this, |
|
1899 aContainingBlockWidth, aContainingBlockHeight, |
|
1900 aBorder, aPadding); |
|
1901 |
|
1902 // If this is a reflow root, then set the computed width and |
|
1903 // height equal to the available space |
|
1904 if (nullptr == parentReflowState || mFlags.mDummyParentReflowState) { |
|
1905 // XXXldb This doesn't mean what it used to! |
|
1906 InitOffsets(aContainingBlockWidth, |
|
1907 VerticalOffsetPercentBasis(frame, aContainingBlockWidth, |
|
1908 aContainingBlockHeight), |
|
1909 aFrameType, aBorder, aPadding); |
|
1910 // Override mComputedMargin since reflow roots start from the |
|
1911 // frame's boundary, which is inside the margin. |
|
1912 ComputedPhysicalMargin().SizeTo(0, 0, 0, 0); |
|
1913 ComputedPhysicalOffsets().SizeTo(0, 0, 0, 0); |
|
1914 |
|
1915 ComputedWidth() = AvailableWidth() - ComputedPhysicalBorderPadding().LeftRight(); |
|
1916 if (ComputedWidth() < 0) |
|
1917 ComputedWidth() = 0; |
|
1918 if (AvailableHeight() != NS_UNCONSTRAINEDSIZE) { |
|
1919 ComputedHeight() = AvailableHeight() - ComputedPhysicalBorderPadding().TopBottom(); |
|
1920 if (ComputedHeight() < 0) |
|
1921 ComputedHeight() = 0; |
|
1922 } else { |
|
1923 ComputedHeight() = NS_UNCONSTRAINEDSIZE; |
|
1924 } |
|
1925 |
|
1926 ComputedMinWidth() = ComputedMinHeight() = 0; |
|
1927 ComputedMaxWidth() = ComputedMaxHeight() = NS_UNCONSTRAINEDSIZE; |
|
1928 } else { |
|
1929 // Get the containing block reflow state |
|
1930 const nsHTMLReflowState* cbrs = mCBReflowState; |
|
1931 NS_ASSERTION(nullptr != cbrs, "no containing block"); |
|
1932 |
|
1933 // If we weren't given a containing block width and height, then |
|
1934 // compute one |
|
1935 if (aContainingBlockWidth == -1) { |
|
1936 ComputeContainingBlockRectangle(aPresContext, cbrs, aContainingBlockWidth, |
|
1937 aContainingBlockHeight); |
|
1938 } |
|
1939 |
|
1940 // See if the containing block height is based on the size of its |
|
1941 // content |
|
1942 nsIAtom* fType; |
|
1943 if (NS_AUTOHEIGHT == aContainingBlockHeight) { |
|
1944 // See if the containing block is a cell frame which needs |
|
1945 // to use the mComputedHeight of the cell instead of what the cell block passed in. |
|
1946 // XXX It seems like this could lead to bugs with min-height and friends |
|
1947 if (cbrs->parentReflowState) { |
|
1948 fType = cbrs->frame->GetType(); |
|
1949 if (IS_TABLE_CELL(fType)) { |
|
1950 // use the cell's computed height |
|
1951 aContainingBlockHeight = cbrs->ComputedHeight(); |
|
1952 } |
|
1953 } |
|
1954 } |
|
1955 |
|
1956 // XXX Might need to also pass the CB height (not width) for page boxes, |
|
1957 // too, if we implement them. |
|
1958 InitOffsets(aContainingBlockWidth, |
|
1959 VerticalOffsetPercentBasis(frame, aContainingBlockWidth, |
|
1960 aContainingBlockHeight), |
|
1961 aFrameType, aBorder, aPadding); |
|
1962 |
|
1963 const nsStyleCoord &height = mStylePosition->mHeight; |
|
1964 nsStyleUnit heightUnit = height.GetUnit(); |
|
1965 |
|
1966 // Check for a percentage based height and a containing block height |
|
1967 // that depends on the content height |
|
1968 // XXX twiddling heightUnit doesn't help anymore |
|
1969 // FIXME Shouldn't we fix that? |
|
1970 if (height.HasPercent()) { |
|
1971 if (NS_AUTOHEIGHT == aContainingBlockHeight) { |
|
1972 // this if clause enables %-height on replaced inline frames, |
|
1973 // such as images. See bug 54119. The else clause "heightUnit = eStyleUnit_Auto;" |
|
1974 // used to be called exclusively. |
|
1975 if (NS_FRAME_REPLACED(NS_CSS_FRAME_TYPE_INLINE) == mFrameType || |
|
1976 NS_FRAME_REPLACED_CONTAINS_BLOCK( |
|
1977 NS_CSS_FRAME_TYPE_INLINE) == mFrameType) { |
|
1978 // Get the containing block reflow state |
|
1979 NS_ASSERTION(nullptr != cbrs, "no containing block"); |
|
1980 // in quirks mode, get the cb height using the special quirk method |
|
1981 if (eCompatibility_NavQuirks == aPresContext->CompatibilityMode()) { |
|
1982 if (!IS_TABLE_CELL(fType)) { |
|
1983 aContainingBlockHeight = CalcQuirkContainingBlockHeight(cbrs); |
|
1984 if (aContainingBlockHeight == NS_AUTOHEIGHT) { |
|
1985 heightUnit = eStyleUnit_Auto; |
|
1986 } |
|
1987 } |
|
1988 else { |
|
1989 heightUnit = eStyleUnit_Auto; |
|
1990 } |
|
1991 } |
|
1992 // in standard mode, use the cb height. if it's "auto", as will be the case |
|
1993 // by default in BODY, use auto height as per CSS2 spec. |
|
1994 else |
|
1995 { |
|
1996 if (NS_AUTOHEIGHT != cbrs->ComputedHeight()) |
|
1997 aContainingBlockHeight = cbrs->ComputedHeight(); |
|
1998 else |
|
1999 heightUnit = eStyleUnit_Auto; |
|
2000 } |
|
2001 } |
|
2002 else { |
|
2003 // default to interpreting the height like 'auto' |
|
2004 heightUnit = eStyleUnit_Auto; |
|
2005 } |
|
2006 } |
|
2007 } |
|
2008 |
|
2009 // Compute our offsets if the element is relatively positioned. We need |
|
2010 // the correct containing block width and height here, which is why we need |
|
2011 // to do it after all the quirks-n-such above. (If the element is sticky |
|
2012 // positioned, we need to wait until the scroll container knows its size, |
|
2013 // so we compute offsets from StickyScrollContainer::UpdatePositions.) |
|
2014 if (mStyleDisplay->IsRelativelyPositioned(frame) && |
|
2015 NS_STYLE_POSITION_RELATIVE == mStyleDisplay->mPosition) { |
|
2016 uint8_t direction = NS_STYLE_DIRECTION_LTR; |
|
2017 if (cbrs && NS_STYLE_DIRECTION_RTL == cbrs->mStyleVisibility->mDirection) { |
|
2018 direction = NS_STYLE_DIRECTION_RTL; |
|
2019 } |
|
2020 ComputeRelativeOffsets(direction, frame, aContainingBlockWidth, |
|
2021 aContainingBlockHeight, ComputedPhysicalOffsets()); |
|
2022 } else { |
|
2023 // Initialize offsets to 0 |
|
2024 ComputedPhysicalOffsets().SizeTo(0, 0, 0, 0); |
|
2025 } |
|
2026 |
|
2027 // Calculate the computed values for min and max properties. Note that |
|
2028 // this MUST come after we've computed our border and padding. |
|
2029 ComputeMinMaxValues(aContainingBlockWidth, aContainingBlockHeight, cbrs); |
|
2030 |
|
2031 // Calculate the computed width and height. This varies by frame type |
|
2032 |
|
2033 if (NS_CSS_FRAME_TYPE_INTERNAL_TABLE == mFrameType) { |
|
2034 // Internal table elements. The rules vary depending on the type. |
|
2035 // Calculate the computed width |
|
2036 bool rowOrRowGroup = false; |
|
2037 const nsStyleCoord &width = mStylePosition->mWidth; |
|
2038 nsStyleUnit widthUnit = width.GetUnit(); |
|
2039 if ((NS_STYLE_DISPLAY_TABLE_ROW == mStyleDisplay->mDisplay) || |
|
2040 (NS_STYLE_DISPLAY_TABLE_ROW_GROUP == mStyleDisplay->mDisplay)) { |
|
2041 // 'width' property doesn't apply to table rows and row groups |
|
2042 widthUnit = eStyleUnit_Auto; |
|
2043 rowOrRowGroup = true; |
|
2044 } |
|
2045 |
|
2046 // calc() with percentages acts like auto on internal table elements |
|
2047 if (eStyleUnit_Auto == widthUnit || |
|
2048 (width.IsCalcUnit() && width.CalcHasPercent())) { |
|
2049 ComputedWidth() = AvailableWidth(); |
|
2050 |
|
2051 if ((ComputedWidth() != NS_UNCONSTRAINEDSIZE) && !rowOrRowGroup){ |
|
2052 // Internal table elements don't have margins. Only tables and |
|
2053 // cells have border and padding |
|
2054 ComputedWidth() -= ComputedPhysicalBorderPadding().left + |
|
2055 ComputedPhysicalBorderPadding().right; |
|
2056 if (ComputedWidth() < 0) |
|
2057 ComputedWidth() = 0; |
|
2058 } |
|
2059 NS_ASSERTION(ComputedWidth() >= 0, "Bogus computed width"); |
|
2060 |
|
2061 } else { |
|
2062 NS_ASSERTION(widthUnit == mStylePosition->mWidth.GetUnit(), |
|
2063 "unexpected width unit change"); |
|
2064 ComputedWidth() = ComputeWidthValue(aContainingBlockWidth, |
|
2065 mStylePosition->mBoxSizing, |
|
2066 mStylePosition->mWidth); |
|
2067 } |
|
2068 |
|
2069 // Calculate the computed height |
|
2070 if ((NS_STYLE_DISPLAY_TABLE_COLUMN == mStyleDisplay->mDisplay) || |
|
2071 (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == mStyleDisplay->mDisplay)) { |
|
2072 // 'height' property doesn't apply to table columns and column groups |
|
2073 heightUnit = eStyleUnit_Auto; |
|
2074 } |
|
2075 // calc() with percentages acts like 'auto' on internal table elements |
|
2076 if (eStyleUnit_Auto == heightUnit || |
|
2077 (height.IsCalcUnit() && height.CalcHasPercent())) { |
|
2078 ComputedHeight() = NS_AUTOHEIGHT; |
|
2079 } else { |
|
2080 NS_ASSERTION(heightUnit == mStylePosition->mHeight.GetUnit(), |
|
2081 "unexpected height unit change"); |
|
2082 ComputedHeight() = ComputeHeightValue(aContainingBlockHeight, |
|
2083 mStylePosition->mBoxSizing, |
|
2084 mStylePosition->mHeight); |
|
2085 } |
|
2086 |
|
2087 // Doesn't apply to table elements |
|
2088 ComputedMinWidth() = ComputedMinHeight() = 0; |
|
2089 ComputedMaxWidth() = ComputedMaxHeight() = NS_UNCONSTRAINEDSIZE; |
|
2090 |
|
2091 } else if (NS_FRAME_GET_TYPE(mFrameType) == NS_CSS_FRAME_TYPE_ABSOLUTE) { |
|
2092 // XXX not sure if this belongs here or somewhere else - cwk |
|
2093 InitAbsoluteConstraints(aPresContext, cbrs, aContainingBlockWidth, |
|
2094 aContainingBlockHeight, aFrameType); |
|
2095 } else { |
|
2096 AutoMaybeDisableFontInflation an(frame); |
|
2097 |
|
2098 bool isBlock = NS_CSS_FRAME_TYPE_BLOCK == NS_FRAME_GET_TYPE(mFrameType); |
|
2099 uint32_t computeSizeFlags = isBlock ? 0 : nsIFrame::eShrinkWrap; |
|
2100 |
|
2101 // Make sure legend frames with display:block and width:auto still |
|
2102 // shrink-wrap. |
|
2103 if (isBlock && |
|
2104 ((aFrameType == nsGkAtoms::legendFrame && |
|
2105 frame->StyleContext()->GetPseudo() != nsCSSAnonBoxes::scrolledContent) || |
|
2106 (aFrameType == nsGkAtoms::scrollFrame && |
|
2107 frame->GetContentInsertionFrame()->GetType() == nsGkAtoms::legendFrame))) { |
|
2108 computeSizeFlags |= nsIFrame::eShrinkWrap; |
|
2109 } |
|
2110 |
|
2111 const nsFlexContainerFrame* flexContainerFrame = GetFlexContainer(frame); |
|
2112 if (flexContainerFrame) { |
|
2113 computeSizeFlags |= nsIFrame::eShrinkWrap; |
|
2114 |
|
2115 // If we're inside of a flex container that needs to measure our |
|
2116 // auto height, pass that information along to ComputeSize(). |
|
2117 if (mFlags.mIsFlexContainerMeasuringHeight) { |
|
2118 computeSizeFlags |= nsIFrame::eUseAutoHeight; |
|
2119 } |
|
2120 } else { |
|
2121 MOZ_ASSERT(!mFlags.mIsFlexContainerMeasuringHeight, |
|
2122 "We're not in a flex container, so the flag " |
|
2123 "'mIsFlexContainerMeasuringHeight' shouldn't be set"); |
|
2124 } |
|
2125 |
|
2126 nsSize size = |
|
2127 frame->ComputeSize(rendContext, |
|
2128 nsSize(aContainingBlockWidth, |
|
2129 aContainingBlockHeight), |
|
2130 AvailableWidth(), |
|
2131 nsSize(ComputedPhysicalMargin().LeftRight(), |
|
2132 ComputedPhysicalMargin().TopBottom()), |
|
2133 nsSize(ComputedPhysicalBorderPadding().LeftRight() - |
|
2134 ComputedPhysicalPadding().LeftRight(), |
|
2135 ComputedPhysicalBorderPadding().TopBottom() - |
|
2136 ComputedPhysicalPadding().TopBottom()), |
|
2137 nsSize(ComputedPhysicalPadding().LeftRight(), |
|
2138 ComputedPhysicalPadding().TopBottom()), |
|
2139 computeSizeFlags); |
|
2140 |
|
2141 ComputedWidth() = size.width; |
|
2142 ComputedHeight() = size.height; |
|
2143 NS_ASSERTION(ComputedWidth() >= 0, "Bogus width"); |
|
2144 NS_ASSERTION(ComputedHeight() == NS_UNCONSTRAINEDSIZE || |
|
2145 ComputedHeight() >= 0, "Bogus height"); |
|
2146 |
|
2147 // Exclude inline tables and flex items from the block margin calculations |
|
2148 if (isBlock && |
|
2149 !IsSideCaption(frame, mStyleDisplay) && |
|
2150 mStyleDisplay->mDisplay != NS_STYLE_DISPLAY_INLINE_TABLE && |
|
2151 !flexContainerFrame) { |
|
2152 CalculateBlockSideMargins(AvailableWidth(), ComputedWidth(), aFrameType); |
|
2153 } |
|
2154 } |
|
2155 } |
|
2156 } |
|
2157 |
|
2158 static void |
|
2159 UpdateProp(FrameProperties& aProps, |
|
2160 const FramePropertyDescriptor* aProperty, |
|
2161 bool aNeeded, |
|
2162 nsMargin& aNewValue) |
|
2163 { |
|
2164 if (aNeeded) { |
|
2165 nsMargin* propValue = static_cast<nsMargin*>(aProps.Get(aProperty)); |
|
2166 if (propValue) { |
|
2167 *propValue = aNewValue; |
|
2168 } else { |
|
2169 aProps.Set(aProperty, new nsMargin(aNewValue)); |
|
2170 } |
|
2171 } else { |
|
2172 aProps.Delete(aProperty); |
|
2173 } |
|
2174 } |
|
2175 |
|
2176 void |
|
2177 nsCSSOffsetState::InitOffsets(nscoord aHorizontalPercentBasis, |
|
2178 nscoord aVerticalPercentBasis, |
|
2179 nsIAtom* aFrameType, |
|
2180 const nsMargin *aBorder, |
|
2181 const nsMargin *aPadding) |
|
2182 { |
|
2183 DISPLAY_INIT_OFFSETS(frame, this, |
|
2184 aHorizontalPercentBasis, |
|
2185 aVerticalPercentBasis, |
|
2186 aBorder, aPadding); |
|
2187 |
|
2188 // Since we are in reflow, we don't need to store these properties anymore |
|
2189 // unless they are dependent on width, in which case we store the new value. |
|
2190 nsPresContext *presContext = frame->PresContext(); |
|
2191 FrameProperties props(presContext->PropertyTable(), frame); |
|
2192 props.Delete(nsIFrame::UsedBorderProperty()); |
|
2193 |
|
2194 // Compute margins from the specified margin style information. These |
|
2195 // become the default computed values, and may be adjusted below |
|
2196 // XXX fix to provide 0,0 for the top&bottom margins for |
|
2197 // inline-non-replaced elements |
|
2198 bool needMarginProp = ComputeMargin(aHorizontalPercentBasis, |
|
2199 aVerticalPercentBasis); |
|
2200 // XXX We need to include 'auto' horizontal margins in this too! |
|
2201 // ... but if we did that, we'd need to fix nsFrame::GetUsedMargin |
|
2202 // to use it even when the margins are all zero (since sometimes |
|
2203 // they get treated as auto) |
|
2204 ::UpdateProp(props, nsIFrame::UsedMarginProperty(), needMarginProp, |
|
2205 ComputedPhysicalMargin()); |
|
2206 |
|
2207 |
|
2208 const nsStyleDisplay *disp = frame->StyleDisplay(); |
|
2209 bool isThemed = frame->IsThemed(disp); |
|
2210 bool needPaddingProp; |
|
2211 nsIntMargin widget; |
|
2212 if (isThemed && |
|
2213 presContext->GetTheme()->GetWidgetPadding(presContext->DeviceContext(), |
|
2214 frame, disp->mAppearance, |
|
2215 &widget)) { |
|
2216 ComputedPhysicalPadding().top = presContext->DevPixelsToAppUnits(widget.top); |
|
2217 ComputedPhysicalPadding().right = presContext->DevPixelsToAppUnits(widget.right); |
|
2218 ComputedPhysicalPadding().bottom = presContext->DevPixelsToAppUnits(widget.bottom); |
|
2219 ComputedPhysicalPadding().left = presContext->DevPixelsToAppUnits(widget.left); |
|
2220 needPaddingProp = false; |
|
2221 } |
|
2222 else if (frame->IsSVGText()) { |
|
2223 ComputedPhysicalPadding().SizeTo(0, 0, 0, 0); |
|
2224 needPaddingProp = false; |
|
2225 } |
|
2226 else if (aPadding) { // padding is an input arg |
|
2227 ComputedPhysicalPadding() = *aPadding; |
|
2228 needPaddingProp = frame->StylePadding()->IsWidthDependent() || |
|
2229 (frame->GetStateBits() & NS_FRAME_REFLOW_ROOT); |
|
2230 } |
|
2231 else { |
|
2232 needPaddingProp = ComputePadding(aHorizontalPercentBasis, |
|
2233 aVerticalPercentBasis, aFrameType); |
|
2234 } |
|
2235 |
|
2236 if (isThemed) { |
|
2237 nsIntMargin widget; |
|
2238 presContext->GetTheme()->GetWidgetBorder(presContext->DeviceContext(), |
|
2239 frame, disp->mAppearance, |
|
2240 &widget); |
|
2241 ComputedPhysicalBorderPadding().top = |
|
2242 presContext->DevPixelsToAppUnits(widget.top); |
|
2243 ComputedPhysicalBorderPadding().right = |
|
2244 presContext->DevPixelsToAppUnits(widget.right); |
|
2245 ComputedPhysicalBorderPadding().bottom = |
|
2246 presContext->DevPixelsToAppUnits(widget.bottom); |
|
2247 ComputedPhysicalBorderPadding().left = |
|
2248 presContext->DevPixelsToAppUnits(widget.left); |
|
2249 } |
|
2250 else if (frame->IsSVGText()) { |
|
2251 ComputedPhysicalBorderPadding().SizeTo(0, 0, 0, 0); |
|
2252 } |
|
2253 else if (aBorder) { // border is an input arg |
|
2254 ComputedPhysicalBorderPadding() = *aBorder; |
|
2255 } |
|
2256 else { |
|
2257 ComputedPhysicalBorderPadding() = frame->StyleBorder()->GetComputedBorder(); |
|
2258 } |
|
2259 ComputedPhysicalBorderPadding() += ComputedPhysicalPadding(); |
|
2260 |
|
2261 if (aFrameType == nsGkAtoms::tableFrame) { |
|
2262 nsTableFrame *tableFrame = static_cast<nsTableFrame*>(frame); |
|
2263 |
|
2264 if (tableFrame->IsBorderCollapse()) { |
|
2265 // border-collapsed tables don't use any of their padding, and |
|
2266 // only part of their border. We need to do this here before we |
|
2267 // try to do anything like handling 'auto' widths, |
|
2268 // 'box-sizing', or 'auto' margins. |
|
2269 ComputedPhysicalPadding().SizeTo(0,0,0,0); |
|
2270 ComputedPhysicalBorderPadding() = tableFrame->GetIncludedOuterBCBorder(); |
|
2271 } |
|
2272 |
|
2273 // The margin is inherited to the outer table frame via |
|
2274 // the ::-moz-table-outer rule in ua.css. |
|
2275 ComputedPhysicalMargin().SizeTo(0, 0, 0, 0); |
|
2276 } else if (aFrameType == nsGkAtoms::scrollbarFrame) { |
|
2277 // scrollbars may have had their width or height smashed to zero |
|
2278 // by the associated scrollframe, in which case we must not report |
|
2279 // any padding or border. |
|
2280 nsSize size(frame->GetSize()); |
|
2281 if (size.width == 0 || size.height == 0) { |
|
2282 ComputedPhysicalPadding().SizeTo(0,0,0,0); |
|
2283 ComputedPhysicalBorderPadding().SizeTo(0,0,0,0); |
|
2284 } |
|
2285 } |
|
2286 ::UpdateProp(props, nsIFrame::UsedPaddingProperty(), needPaddingProp, |
|
2287 ComputedPhysicalPadding()); |
|
2288 } |
|
2289 |
|
2290 // This code enforces section 10.3.3 of the CSS2 spec for this formula: |
|
2291 // |
|
2292 // 'margin-left' + 'border-left-width' + 'padding-left' + 'width' + |
|
2293 // 'padding-right' + 'border-right-width' + 'margin-right' |
|
2294 // = width of containing block |
|
2295 // |
|
2296 // Note: the width unit is not auto when this is called |
|
2297 void |
|
2298 nsHTMLReflowState::CalculateBlockSideMargins(nscoord aAvailWidth, |
|
2299 nscoord aComputedWidth, |
|
2300 nsIAtom* aFrameType) |
|
2301 { |
|
2302 NS_WARN_IF_FALSE(NS_UNCONSTRAINEDSIZE != aComputedWidth && |
|
2303 NS_UNCONSTRAINEDSIZE != aAvailWidth, |
|
2304 "have unconstrained width; this should only result from " |
|
2305 "very large sizes, not attempts at intrinsic width " |
|
2306 "calculation"); |
|
2307 |
|
2308 nscoord sum = ComputedPhysicalMargin().left + ComputedPhysicalBorderPadding().left + |
|
2309 aComputedWidth + ComputedPhysicalBorderPadding().right + ComputedPhysicalMargin().right; |
|
2310 if (sum == aAvailWidth) |
|
2311 // The sum is already correct |
|
2312 return; |
|
2313 |
|
2314 // Determine the left and right margin values. The width value |
|
2315 // remains constant while we do this. |
|
2316 |
|
2317 // Calculate how much space is available for margins |
|
2318 nscoord availMarginSpace = aAvailWidth - sum; |
|
2319 |
|
2320 // If the available margin space is negative, then don't follow the |
|
2321 // usual overconstraint rules. |
|
2322 if (availMarginSpace < 0) { |
|
2323 if (mCBReflowState && |
|
2324 mCBReflowState->mStyleVisibility->mDirection == NS_STYLE_DIRECTION_RTL) { |
|
2325 ComputedPhysicalMargin().left += availMarginSpace; |
|
2326 } else { |
|
2327 ComputedPhysicalMargin().right += availMarginSpace; |
|
2328 } |
|
2329 return; |
|
2330 } |
|
2331 |
|
2332 // The css2 spec clearly defines how block elements should behave |
|
2333 // in section 10.3.3. |
|
2334 bool isAutoLeftMargin = |
|
2335 eStyleUnit_Auto == mStyleMargin->mMargin.GetLeftUnit(); |
|
2336 bool isAutoRightMargin = |
|
2337 eStyleUnit_Auto == mStyleMargin->mMargin.GetRightUnit(); |
|
2338 if (!isAutoLeftMargin && !isAutoRightMargin) { |
|
2339 // Neither margin is 'auto' so we're over constrained. Use the |
|
2340 // 'direction' property of the parent to tell which margin to |
|
2341 // ignore |
|
2342 // First check if there is an HTML alignment that we should honor |
|
2343 const nsHTMLReflowState* prs = parentReflowState; |
|
2344 if (aFrameType == nsGkAtoms::tableFrame) { |
|
2345 NS_ASSERTION(prs->frame->GetType() == nsGkAtoms::tableOuterFrame, |
|
2346 "table not inside outer table"); |
|
2347 // Center the table within the outer table based on the alignment |
|
2348 // of the outer table's parent. |
|
2349 prs = prs->parentReflowState; |
|
2350 } |
|
2351 if (prs && |
|
2352 (prs->mStyleText->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_LEFT || |
|
2353 prs->mStyleText->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_CENTER || |
|
2354 prs->mStyleText->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_RIGHT)) { |
|
2355 isAutoLeftMargin = |
|
2356 prs->mStyleText->mTextAlign != NS_STYLE_TEXT_ALIGN_MOZ_LEFT; |
|
2357 isAutoRightMargin = |
|
2358 prs->mStyleText->mTextAlign != NS_STYLE_TEXT_ALIGN_MOZ_RIGHT; |
|
2359 } |
|
2360 // Otherwise apply the CSS rules, and ignore one margin by forcing |
|
2361 // it to 'auto', depending on 'direction'. |
|
2362 else if (mCBReflowState && |
|
2363 NS_STYLE_DIRECTION_RTL == mCBReflowState->mStyleVisibility->mDirection) { |
|
2364 isAutoLeftMargin = true; |
|
2365 } |
|
2366 else { |
|
2367 isAutoRightMargin = true; |
|
2368 } |
|
2369 } |
|
2370 |
|
2371 // Logic which is common to blocks and tables |
|
2372 // The computed margins need not be zero because the 'auto' could come from |
|
2373 // overconstraint or from HTML alignment so values need to be accumulated |
|
2374 |
|
2375 if (isAutoLeftMargin) { |
|
2376 if (isAutoRightMargin) { |
|
2377 // Both margins are 'auto' so the computed addition should be equal |
|
2378 nscoord forLeft = availMarginSpace / 2; |
|
2379 ComputedPhysicalMargin().left += forLeft; |
|
2380 ComputedPhysicalMargin().right += availMarginSpace - forLeft; |
|
2381 } else { |
|
2382 ComputedPhysicalMargin().left += availMarginSpace; |
|
2383 } |
|
2384 } else if (isAutoRightMargin) { |
|
2385 ComputedPhysicalMargin().right += availMarginSpace; |
|
2386 } |
|
2387 } |
|
2388 |
|
2389 #define NORMAL_LINE_HEIGHT_FACTOR 1.2f // in term of emHeight |
|
2390 // For "normal" we use the font's normal line height (em height + leading). |
|
2391 // If both internal leading and external leading specified by font itself |
|
2392 // are zeros, we should compensate this by creating extra (external) leading |
|
2393 // in eCompensateLeading mode. This is necessary because without this |
|
2394 // compensation, normal line height might looks too tight. |
|
2395 |
|
2396 // For risk management, we use preference to control the behavior, and |
|
2397 // eNoExternalLeading is the old behavior. |
|
2398 static nscoord |
|
2399 GetNormalLineHeight(nsFontMetrics* aFontMetrics) |
|
2400 { |
|
2401 NS_PRECONDITION(nullptr != aFontMetrics, "no font metrics"); |
|
2402 |
|
2403 nscoord normalLineHeight; |
|
2404 |
|
2405 nscoord externalLeading = aFontMetrics->ExternalLeading(); |
|
2406 nscoord internalLeading = aFontMetrics->InternalLeading(); |
|
2407 nscoord emHeight = aFontMetrics->EmHeight(); |
|
2408 switch (GetNormalLineHeightCalcControl()) { |
|
2409 case eIncludeExternalLeading: |
|
2410 normalLineHeight = emHeight+ internalLeading + externalLeading; |
|
2411 break; |
|
2412 case eCompensateLeading: |
|
2413 if (!internalLeading && !externalLeading) |
|
2414 normalLineHeight = NSToCoordRound(emHeight * NORMAL_LINE_HEIGHT_FACTOR); |
|
2415 else |
|
2416 normalLineHeight = emHeight+ internalLeading + externalLeading; |
|
2417 break; |
|
2418 default: |
|
2419 //case eNoExternalLeading: |
|
2420 normalLineHeight = emHeight + internalLeading; |
|
2421 } |
|
2422 return normalLineHeight; |
|
2423 } |
|
2424 |
|
2425 static inline nscoord |
|
2426 ComputeLineHeight(nsStyleContext* aStyleContext, |
|
2427 nscoord aBlockHeight, |
|
2428 float aFontSizeInflation) |
|
2429 { |
|
2430 const nsStyleCoord& lhCoord = aStyleContext->StyleText()->mLineHeight; |
|
2431 |
|
2432 if (lhCoord.GetUnit() == eStyleUnit_Coord) { |
|
2433 nscoord result = lhCoord.GetCoordValue(); |
|
2434 if (aFontSizeInflation != 1.0f) { |
|
2435 result = NSToCoordRound(result * aFontSizeInflation); |
|
2436 } |
|
2437 return result; |
|
2438 } |
|
2439 |
|
2440 if (lhCoord.GetUnit() == eStyleUnit_Factor) |
|
2441 // For factor units the computed value of the line-height property |
|
2442 // is found by multiplying the factor by the font's computed size |
|
2443 // (adjusted for min-size prefs and text zoom). |
|
2444 return NSToCoordRound(lhCoord.GetFactorValue() * aFontSizeInflation * |
|
2445 aStyleContext->StyleFont()->mFont.size); |
|
2446 |
|
2447 NS_ASSERTION(lhCoord.GetUnit() == eStyleUnit_Normal || |
|
2448 lhCoord.GetUnit() == eStyleUnit_Enumerated, |
|
2449 "bad line-height unit"); |
|
2450 |
|
2451 if (lhCoord.GetUnit() == eStyleUnit_Enumerated) { |
|
2452 NS_ASSERTION(lhCoord.GetIntValue() == NS_STYLE_LINE_HEIGHT_BLOCK_HEIGHT, |
|
2453 "bad line-height value"); |
|
2454 if (aBlockHeight != NS_AUTOHEIGHT) { |
|
2455 return aBlockHeight; |
|
2456 } |
|
2457 } |
|
2458 |
|
2459 nsRefPtr<nsFontMetrics> fm; |
|
2460 nsLayoutUtils::GetFontMetricsForStyleContext(aStyleContext, |
|
2461 getter_AddRefs(fm), |
|
2462 aFontSizeInflation); |
|
2463 return GetNormalLineHeight(fm); |
|
2464 } |
|
2465 |
|
2466 nscoord |
|
2467 nsHTMLReflowState::CalcLineHeight() const |
|
2468 { |
|
2469 nscoord blockHeight = |
|
2470 nsLayoutUtils::IsNonWrapperBlock(frame) ? ComputedHeight() : |
|
2471 (mCBReflowState ? mCBReflowState->ComputedHeight() : NS_AUTOHEIGHT); |
|
2472 |
|
2473 return CalcLineHeight(frame->GetContent(), frame->StyleContext(), blockHeight, |
|
2474 nsLayoutUtils::FontSizeInflationFor(frame)); |
|
2475 } |
|
2476 |
|
2477 /* static */ nscoord |
|
2478 nsHTMLReflowState::CalcLineHeight(nsIContent* aContent, |
|
2479 nsStyleContext* aStyleContext, |
|
2480 nscoord aBlockHeight, |
|
2481 float aFontSizeInflation) |
|
2482 { |
|
2483 NS_PRECONDITION(aStyleContext, "Must have a style context"); |
|
2484 |
|
2485 nscoord lineHeight = |
|
2486 ComputeLineHeight(aStyleContext, aBlockHeight, aFontSizeInflation); |
|
2487 |
|
2488 NS_ASSERTION(lineHeight >= 0, "ComputeLineHeight screwed up"); |
|
2489 |
|
2490 HTMLInputElement* input = HTMLInputElement::FromContentOrNull(aContent); |
|
2491 if (input && input->IsSingleLineTextControl()) { |
|
2492 // For Web-compatibility, single-line text input elements cannot |
|
2493 // have a line-height smaller than one. |
|
2494 nscoord lineHeightOne = |
|
2495 aFontSizeInflation * aStyleContext->StyleFont()->mFont.size; |
|
2496 if (lineHeight < lineHeightOne) { |
|
2497 lineHeight = lineHeightOne; |
|
2498 } |
|
2499 } |
|
2500 |
|
2501 return lineHeight; |
|
2502 } |
|
2503 |
|
2504 bool |
|
2505 nsCSSOffsetState::ComputeMargin(nscoord aHorizontalPercentBasis, |
|
2506 nscoord aVerticalPercentBasis) |
|
2507 { |
|
2508 // SVG text frames have no margin. |
|
2509 if (frame->IsSVGText()) { |
|
2510 return false; |
|
2511 } |
|
2512 |
|
2513 // If style style can provide us the margin directly, then use it. |
|
2514 const nsStyleMargin *styleMargin = frame->StyleMargin(); |
|
2515 bool isCBDependent = !styleMargin->GetMargin(ComputedPhysicalMargin()); |
|
2516 if (isCBDependent) { |
|
2517 // We have to compute the value |
|
2518 ComputedPhysicalMargin().left = nsLayoutUtils:: |
|
2519 ComputeCBDependentValue(aHorizontalPercentBasis, |
|
2520 styleMargin->mMargin.GetLeft()); |
|
2521 ComputedPhysicalMargin().right = nsLayoutUtils:: |
|
2522 ComputeCBDependentValue(aHorizontalPercentBasis, |
|
2523 styleMargin->mMargin.GetRight()); |
|
2524 |
|
2525 ComputedPhysicalMargin().top = nsLayoutUtils:: |
|
2526 ComputeCBDependentValue(aVerticalPercentBasis, |
|
2527 styleMargin->mMargin.GetTop()); |
|
2528 ComputedPhysicalMargin().bottom = nsLayoutUtils:: |
|
2529 ComputeCBDependentValue(aVerticalPercentBasis, |
|
2530 styleMargin->mMargin.GetBottom()); |
|
2531 } |
|
2532 |
|
2533 nscoord marginAdjustment = FontSizeInflationListMarginAdjustment(frame); |
|
2534 |
|
2535 if (marginAdjustment > 0) { |
|
2536 const nsStyleVisibility* visibility = frame->StyleVisibility(); |
|
2537 if (visibility->mDirection == NS_STYLE_DIRECTION_RTL) { |
|
2538 ComputedPhysicalMargin().right = ComputedPhysicalMargin().right + marginAdjustment; |
|
2539 } else { |
|
2540 ComputedPhysicalMargin().left = ComputedPhysicalMargin().left + marginAdjustment; |
|
2541 } |
|
2542 } |
|
2543 |
|
2544 return isCBDependent; |
|
2545 } |
|
2546 |
|
2547 bool |
|
2548 nsCSSOffsetState::ComputePadding(nscoord aHorizontalPercentBasis, |
|
2549 nscoord aVerticalPercentBasis, |
|
2550 nsIAtom* aFrameType) |
|
2551 { |
|
2552 // If style can provide us the padding directly, then use it. |
|
2553 const nsStylePadding *stylePadding = frame->StylePadding(); |
|
2554 bool isCBDependent = !stylePadding->GetPadding(ComputedPhysicalPadding()); |
|
2555 // a table row/col group, row/col doesn't have padding |
|
2556 // XXXldb Neither do border-collapse tables. |
|
2557 if (nsGkAtoms::tableRowGroupFrame == aFrameType || |
|
2558 nsGkAtoms::tableColGroupFrame == aFrameType || |
|
2559 nsGkAtoms::tableRowFrame == aFrameType || |
|
2560 nsGkAtoms::tableColFrame == aFrameType) { |
|
2561 ComputedPhysicalPadding().SizeTo(0,0,0,0); |
|
2562 } |
|
2563 else if (isCBDependent) { |
|
2564 // We have to compute the value |
|
2565 // clamp negative calc() results to 0 |
|
2566 ComputedPhysicalPadding().left = std::max(0, nsLayoutUtils:: |
|
2567 ComputeCBDependentValue(aHorizontalPercentBasis, |
|
2568 stylePadding->mPadding.GetLeft())); |
|
2569 ComputedPhysicalPadding().right = std::max(0, nsLayoutUtils:: |
|
2570 ComputeCBDependentValue(aHorizontalPercentBasis, |
|
2571 stylePadding->mPadding.GetRight())); |
|
2572 |
|
2573 ComputedPhysicalPadding().top = std::max(0, nsLayoutUtils:: |
|
2574 ComputeCBDependentValue(aVerticalPercentBasis, |
|
2575 stylePadding->mPadding.GetTop())); |
|
2576 ComputedPhysicalPadding().bottom = std::max(0, nsLayoutUtils:: |
|
2577 ComputeCBDependentValue(aVerticalPercentBasis, |
|
2578 stylePadding->mPadding.GetBottom())); |
|
2579 } |
|
2580 return isCBDependent; |
|
2581 } |
|
2582 |
|
2583 void |
|
2584 nsHTMLReflowState::ComputeMinMaxValues(nscoord aContainingBlockWidth, |
|
2585 nscoord aContainingBlockHeight, |
|
2586 const nsHTMLReflowState* aContainingBlockRS) |
|
2587 { |
|
2588 ComputedMinWidth() = ComputeWidthValue(aContainingBlockWidth, |
|
2589 mStylePosition->mBoxSizing, |
|
2590 mStylePosition->mMinWidth); |
|
2591 |
|
2592 if (eStyleUnit_None == mStylePosition->mMaxWidth.GetUnit()) { |
|
2593 // Specified value of 'none' |
|
2594 ComputedMaxWidth() = NS_UNCONSTRAINEDSIZE; // no limit |
|
2595 } else { |
|
2596 ComputedMaxWidth() = ComputeWidthValue(aContainingBlockWidth, |
|
2597 mStylePosition->mBoxSizing, |
|
2598 mStylePosition->mMaxWidth); |
|
2599 } |
|
2600 |
|
2601 // If the computed value of 'min-width' is greater than the value of |
|
2602 // 'max-width', 'max-width' is set to the value of 'min-width' |
|
2603 if (ComputedMinWidth() > ComputedMaxWidth()) { |
|
2604 ComputedMaxWidth() = ComputedMinWidth(); |
|
2605 } |
|
2606 |
|
2607 // Check for percentage based values and a containing block height that |
|
2608 // depends on the content height. Treat them like 'auto' |
|
2609 // Likewise, check for calc() with percentages on internal table elements; |
|
2610 // that's treated as 'auto' too. |
|
2611 // Likewise, if we're a child of a flex container who's measuring our |
|
2612 // intrinsic height, then we want to disregard our min-height. |
|
2613 |
|
2614 const nsStyleCoord &minHeight = mStylePosition->mMinHeight; |
|
2615 if ((NS_AUTOHEIGHT == aContainingBlockHeight && |
|
2616 minHeight.HasPercent()) || |
|
2617 (mFrameType == NS_CSS_FRAME_TYPE_INTERNAL_TABLE && |
|
2618 minHeight.IsCalcUnit() && minHeight.CalcHasPercent()) || |
|
2619 mFlags.mIsFlexContainerMeasuringHeight) { |
|
2620 ComputedMinHeight() = 0; |
|
2621 } else { |
|
2622 ComputedMinHeight() = ComputeHeightValue(aContainingBlockHeight, |
|
2623 mStylePosition->mBoxSizing, |
|
2624 minHeight); |
|
2625 } |
|
2626 const nsStyleCoord &maxHeight = mStylePosition->mMaxHeight; |
|
2627 nsStyleUnit maxHeightUnit = maxHeight.GetUnit(); |
|
2628 if (eStyleUnit_None == maxHeightUnit) { |
|
2629 // Specified value of 'none' |
|
2630 ComputedMaxHeight() = NS_UNCONSTRAINEDSIZE; // no limit |
|
2631 } else { |
|
2632 // Check for percentage based values and a containing block height that |
|
2633 // depends on the content height. Treat them like 'none' |
|
2634 // Likewise, check for calc() with percentages on internal table elements; |
|
2635 // that's treated as 'auto' too. |
|
2636 // Likewise, if we're a child of a flex container who's measuring our |
|
2637 // intrinsic height, then we want to disregard our max-height. |
|
2638 if ((NS_AUTOHEIGHT == aContainingBlockHeight && |
|
2639 maxHeight.HasPercent()) || |
|
2640 (mFrameType == NS_CSS_FRAME_TYPE_INTERNAL_TABLE && |
|
2641 maxHeight.IsCalcUnit() && maxHeight.CalcHasPercent()) || |
|
2642 mFlags.mIsFlexContainerMeasuringHeight) { |
|
2643 ComputedMaxHeight() = NS_UNCONSTRAINEDSIZE; |
|
2644 } else { |
|
2645 ComputedMaxHeight() = ComputeHeightValue(aContainingBlockHeight, |
|
2646 mStylePosition->mBoxSizing, |
|
2647 maxHeight); |
|
2648 } |
|
2649 } |
|
2650 |
|
2651 // If the computed value of 'min-height' is greater than the value of |
|
2652 // 'max-height', 'max-height' is set to the value of 'min-height' |
|
2653 if (ComputedMinHeight() > ComputedMaxHeight()) { |
|
2654 ComputedMaxHeight() = ComputedMinHeight(); |
|
2655 } |
|
2656 } |
|
2657 |
|
2658 void |
|
2659 nsHTMLReflowState::SetTruncated(const nsHTMLReflowMetrics& aMetrics, |
|
2660 nsReflowStatus* aStatus) const |
|
2661 { |
|
2662 if (AvailableHeight() != NS_UNCONSTRAINEDSIZE && |
|
2663 AvailableHeight() < aMetrics.Height() && |
|
2664 !mFlags.mIsTopOfPage) { |
|
2665 *aStatus |= NS_FRAME_TRUNCATED; |
|
2666 } else { |
|
2667 *aStatus &= ~NS_FRAME_TRUNCATED; |
|
2668 } |
|
2669 } |
|
2670 |
|
2671 bool |
|
2672 nsHTMLReflowState::IsFloating() const |
|
2673 { |
|
2674 return mStyleDisplay->IsFloating(frame); |
|
2675 } |
|
2676 |
|
2677 uint8_t |
|
2678 nsHTMLReflowState::GetDisplay() const |
|
2679 { |
|
2680 return mStyleDisplay->GetDisplay(frame); |
|
2681 } |