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