LayoutUnit RenderReplaced::computeReplacedLogicalWidth(ShouldComputePreferred shouldComputePreferred) const { if (style()->logicalWidth().isSpecified() || style()->logicalWidth().isIntrinsic()) return computeReplacedLogicalWidthRespectingMinMaxWidth(computeReplacedLogicalWidthUsing(style()->logicalWidth()), shouldComputePreferred); // 10.3.2 Inline, replaced elements: http://www.w3.org/TR/CSS21/visudet.html#inline-replaced-width double intrinsicRatio = 0; FloatSize constrainedSize; computeAspectRatioInformationForRenderBox(constrainedSize, intrinsicRatio); if (style()->logicalWidth().isAuto()) { bool computedHeightIsAuto = hasAutoHeightOrContainingBlockWithAutoHeight(); bool hasIntrinsicWidth = constrainedSize.width() > 0; // If 'height' and 'width' both have computed values of 'auto' and the element also has an intrinsic width, then that intrinsic width is the used value of 'width'. if (computedHeightIsAuto && hasIntrinsicWidth) return computeReplacedLogicalWidthRespectingMinMaxWidth(constrainedSize.width(), shouldComputePreferred); bool hasIntrinsicHeight = constrainedSize.height() > 0; if (intrinsicRatio) { // If 'height' and 'width' both have computed values of 'auto' and the element has no intrinsic width, but does have an intrinsic height and intrinsic ratio; // or if 'width' has a computed value of 'auto', 'height' has some other computed value, and the element does have an intrinsic ratio; then the used value // of 'width' is: (used height) * (intrinsic ratio) if (intrinsicRatio && ((computedHeightIsAuto && !hasIntrinsicWidth && hasIntrinsicHeight) || !computedHeightIsAuto)) { LayoutUnit logicalHeight = computeReplacedLogicalHeight(); return computeReplacedLogicalWidthRespectingMinMaxWidth(roundToInt(round(logicalHeight * intrinsicRatio)), shouldComputePreferred); } // If 'height' and 'width' both have computed values of 'auto' and the element has an intrinsic ratio but no intrinsic height or width, then the used value of // 'width' is undefined in CSS 2.1. However, it is suggested that, if the containing block's width does not itself depend on the replaced element's width, then // the used value of 'width' is calculated from the constraint equation used for block-level, non-replaced elements in normal flow. if (computedHeightIsAuto && !hasIntrinsicWidth && !hasIntrinsicHeight) { if (shouldComputePreferred == ComputePreferred) return 0; // The aforementioned 'constraint equation' used for block-level, non-replaced elements in normal flow: // 'margin-left' + 'border-left-width' + 'padding-left' + 'width' + 'padding-right' + 'border-right-width' + 'margin-right' = width of containing block LayoutUnit logicalWidth = containingBlock()->availableLogicalWidth(); // This solves above equation for 'width' (== logicalWidth). LayoutUnit marginStart = minimumValueForLength(style()->marginStart(), logicalWidth); LayoutUnit marginEnd = minimumValueForLength(style()->marginEnd(), logicalWidth); logicalWidth = std::max<LayoutUnit>(0, logicalWidth - (marginStart + marginEnd + (width() - clientWidth()))); return computeReplacedLogicalWidthRespectingMinMaxWidth(logicalWidth, shouldComputePreferred); } } // Otherwise, if 'width' has a computed value of 'auto', and the element has an intrinsic width, then that intrinsic width is the used value of 'width'. if (hasIntrinsicWidth) return computeReplacedLogicalWidthRespectingMinMaxWidth(constrainedSize.width(), shouldComputePreferred); // Otherwise, if 'width' has a computed value of 'auto', but none of the conditions above are met, then the used value of 'width' becomes 300px. If 300px is too // wide to fit the device, UAs should use the width of the largest rectangle that has a 2:1 ratio and fits the device instead. // Note: We fall through and instead return intrinsicLogicalWidth() here - to preserve existing WebKit behavior, which might or might not be correct, or desired. // Changing this to return cDefaultWidth, will affect lots of test results. Eg. some tests assume that a blank <img> tag (which implies width/height=auto) // has no intrinsic size, which is wrong per CSS 2.1, but matches our behavior since a long time. } return computeReplacedLogicalWidthRespectingMinMaxWidth(intrinsicLogicalWidth(), shouldComputePreferred); }
LayoutUnit LayoutReplaced::computeReplacedLogicalWidth( ShouldComputePreferred shouldComputePreferred) const { if (style()->logicalWidth().isSpecified() || style()->logicalWidth().isIntrinsic()) return computeReplacedLogicalWidthRespectingMinMaxWidth( computeReplacedLogicalWidthUsing(MainOrPreferredSize, style()->logicalWidth()), shouldComputePreferred); LayoutReplaced* contentLayoutObject = embeddedReplacedContent(); // 10.3.2 Inline, replaced elements: // http://www.w3.org/TR/CSS21/visudet.html#inline-replaced-width IntrinsicSizingInfo intrinsicSizingInfo; computeIntrinsicSizingInfoForReplacedContent(contentLayoutObject, intrinsicSizingInfo); FloatSize constrainedSize = constrainIntrinsicSizeToMinMax(intrinsicSizingInfo); if (style()->logicalWidth().isAuto()) { bool computedHeightIsAuto = style()->logicalHeight().isAuto(); // If 'height' and 'width' both have computed values of 'auto' and the // element also has an intrinsic width, then that intrinsic width is the // used value of 'width'. if (computedHeightIsAuto && intrinsicSizingInfo.hasWidth) return computeReplacedLogicalWidthRespectingMinMaxWidth( LayoutUnit(constrainedSize.width()), shouldComputePreferred); if (!intrinsicSizingInfo.aspectRatio.isEmpty()) { // If 'height' and 'width' both have computed values of 'auto' and the // element has no intrinsic width, but does have an intrinsic height and // intrinsic ratio; or if 'width' has a computed value of 'auto', 'height' // has some other computed value, and the element does have an intrinsic // ratio; then the used value of 'width' is: (used height) * (intrinsic // ratio). if ((computedHeightIsAuto && !intrinsicSizingInfo.hasWidth && intrinsicSizingInfo.hasHeight) || !computedHeightIsAuto) { LayoutUnit estimatedUsedWidth = intrinsicSizingInfo.hasWidth ? LayoutUnit(constrainedSize.width()) : computeConstrainedLogicalWidth(shouldComputePreferred); LayoutUnit logicalHeight = computeReplacedLogicalHeight(estimatedUsedWidth); return computeReplacedLogicalWidthRespectingMinMaxWidth( resolveWidthForRatio(logicalHeight, intrinsicSizingInfo.aspectRatio), shouldComputePreferred); } // If 'height' and 'width' both have computed values of 'auto' and the // element has an intrinsic ratio but no intrinsic height or width, then // the used value of 'width' is undefined in CSS 2.1. However, it is // suggested that, if the containing block's width does not itself depend // on the replaced element's width, then the used value of 'width' is // calculated from the constraint equation used for block-level, // non-replaced elements in normal flow. if (computedHeightIsAuto && !intrinsicSizingInfo.hasWidth && !intrinsicSizingInfo.hasHeight) return computeConstrainedLogicalWidth(shouldComputePreferred); } // Otherwise, if 'width' has a computed value of 'auto', and the element has // an intrinsic width, then that intrinsic width is the used value of // 'width'. if (intrinsicSizingInfo.hasWidth) return computeReplacedLogicalWidthRespectingMinMaxWidth( LayoutUnit(constrainedSize.width()), shouldComputePreferred); // Otherwise, if 'width' has a computed value of 'auto', but none of the // conditions above are met, then the used value of 'width' becomes 300px. // If 300px is too wide to fit the device, UAs should use the width of the // largest rectangle that has a 2:1 ratio and fits the device instead. // Note: We fall through and instead return intrinsicLogicalWidth() here - // to preserve existing WebKit behavior, which might or might not be // correct, or desired. // Changing this to return cDefaultWidth, will affect lots of test results. // Eg. some tests assume that a blank <img> tag (which implies // width/height=auto) has no intrinsic size, which is wrong per CSS 2.1, but // matches our behavior since a long time. } return computeReplacedLogicalWidthRespectingMinMaxWidth( intrinsicLogicalWidth(), shouldComputePreferred); }
void LayoutReplaced::computePositionedLogicalHeight( LogicalExtentComputedValues& computedValues) const { // The following is based off of the W3C Working Draft from April 11, 2006 of // CSS 2.1: Section 10.6.5 "Absolutely positioned, replaced elements" // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-replaced-height> // (block-style-comments in this function correspond to text from the spec and // the numbers correspond to numbers in spec) // We don't use containingBlock(), since we may be positioned by an enclosing // relpositioned inline. const LayoutBoxModelObject* containerBlock = toLayoutBoxModelObject(container()); const LayoutUnit containerLogicalHeight = containingBlockLogicalHeightForPositioned(containerBlock); const LayoutUnit containerRelativeLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock, false); // Variables to solve. Length marginBefore = style()->marginBefore(); Length marginAfter = style()->marginAfter(); LayoutUnit& marginBeforeAlias = computedValues.m_margins.m_before; LayoutUnit& marginAfterAlias = computedValues.m_margins.m_after; Length logicalTop = style()->logicalTop(); Length logicalBottom = style()->logicalBottom(); // --------------------------------------------------------------------------- // 1. The used value of 'height' is determined as for inline replaced // elements. // --------------------------------------------------------------------------- // NOTE: This value of height is final in that the min/max height calculations // are dealt with in computeReplacedHeight(). This means that the steps to // produce correct max/min in the non-replaced version, are not necessary. computedValues.m_extent = computeReplacedLogicalHeight() + borderAndPaddingLogicalHeight(); const LayoutUnit availableSpace = containerLogicalHeight - computedValues.m_extent; // --------------------------------------------------------------------------- // 2. If both 'top' and 'bottom' have the value 'auto', replace 'top' with the // element's static position. // --------------------------------------------------------------------------- // see FIXME 1 computeBlockStaticDistance(logicalTop, logicalBottom, this, containerBlock); // --------------------------------------------------------------------------- // 3. If 'bottom' is 'auto', replace any 'auto' on 'margin-top' or // 'margin-bottom' with '0'. // --------------------------------------------------------------------------- // FIXME: The spec. says that this step should only be taken when bottom is // auto, but if only top is auto, this makes step 4 impossible. if (logicalTop.isAuto() || logicalBottom.isAuto()) { if (marginBefore.isAuto()) marginBefore.setValue(Fixed, 0); if (marginAfter.isAuto()) marginAfter.setValue(Fixed, 0); } // --------------------------------------------------------------------------- // 4. If at this point both 'margin-top' and 'margin-bottom' are still 'auto', // solve the equation under the extra constraint that the two margins must // get equal values. // --------------------------------------------------------------------------- LayoutUnit logicalTopValue; LayoutUnit logicalBottomValue; if (marginBefore.isAuto() && marginAfter.isAuto()) { // 'top' and 'bottom' cannot be 'auto' due to step 2 and 3 combined. ASSERT(!(logicalTop.isAuto() || logicalBottom.isAuto())); logicalTopValue = valueForLength(logicalTop, containerLogicalHeight); logicalBottomValue = valueForLength(logicalBottom, containerLogicalHeight); LayoutUnit difference = availableSpace - (logicalTopValue + logicalBottomValue); // NOTE: This may result in negative values. marginBeforeAlias = difference / 2; // split the difference marginAfterAlias = difference - marginBeforeAlias; // account for odd valued differences // ------------------------------------------------------------------------- // 5. If at this point there is only one 'auto' left, solve the equation // for that value. // ------------------------------------------------------------------------- } else if (logicalTop.isAuto()) { marginBeforeAlias = valueForLength(marginBefore, containerRelativeLogicalWidth); marginAfterAlias = valueForLength(marginAfter, containerRelativeLogicalWidth); logicalBottomValue = valueForLength(logicalBottom, containerLogicalHeight); // Solve for 'top' logicalTopValue = availableSpace - (logicalBottomValue + marginBeforeAlias + marginAfterAlias); } else if (logicalBottom.isAuto()) { marginBeforeAlias = valueForLength(marginBefore, containerRelativeLogicalWidth); marginAfterAlias = valueForLength(marginAfter, containerRelativeLogicalWidth); logicalTopValue = valueForLength(logicalTop, containerLogicalHeight); // Solve for 'bottom' // NOTE: It is not necessary to solve for 'bottom' because we don't ever // use the value. } else if (marginBefore.isAuto()) { marginAfterAlias = valueForLength(marginAfter, containerRelativeLogicalWidth); logicalTopValue = valueForLength(logicalTop, containerLogicalHeight); logicalBottomValue = valueForLength(logicalBottom, containerLogicalHeight); // Solve for 'margin-top' marginBeforeAlias = availableSpace - (logicalTopValue + logicalBottomValue + marginAfterAlias); } else if (marginAfter.isAuto()) { marginBeforeAlias = valueForLength(marginBefore, containerRelativeLogicalWidth); logicalTopValue = valueForLength(logicalTop, containerLogicalHeight); logicalBottomValue = valueForLength(logicalBottom, containerLogicalHeight); // Solve for 'margin-bottom' marginAfterAlias = availableSpace - (logicalTopValue + logicalBottomValue + marginBeforeAlias); } else { // Nothing is 'auto', just calculate the values. marginBeforeAlias = valueForLength(marginBefore, containerRelativeLogicalWidth); marginAfterAlias = valueForLength(marginAfter, containerRelativeLogicalWidth); logicalTopValue = valueForLength(logicalTop, containerLogicalHeight); // NOTE: It is not necessary to solve for 'bottom' because we don't ever // use the value. } // --------------------------------------------------------------------------- // 6. If at this point the values are over-constrained, ignore the value for // 'bottom' and solve for that value. // --------------------------------------------------------------------------- // NOTE: It is not necessary to do this step because we don't end up using the // value of 'bottom' regardless of whether the values are over-constrained or // not. // Use computed values to calculate the vertical position. LayoutUnit logicalTopPos = logicalTopValue + marginBeforeAlias; computeLogicalTopPositionedOffset(logicalTopPos, this, computedValues.m_extent, containerBlock, containerLogicalHeight); computedValues.m_position = logicalTopPos; }