layout/generic/nsHTMLReflowState.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial