Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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 | } |