diff options
Diffstat (limited to 'layout/generic')
-rw-r--r-- | layout/generic/AspectRatio.h | 81 | ||||
-rw-r--r-- | layout/generic/crashtests/1633434.html | 15 | ||||
-rw-r--r-- | layout/generic/crashtests/crashtests.list | 1 | ||||
-rw-r--r-- | layout/generic/moz.build | 1 | ||||
-rw-r--r-- | layout/generic/nsFlexContainerFrame.cpp | 31 | ||||
-rw-r--r-- | layout/generic/nsFrame.cpp | 57 | ||||
-rw-r--r-- | layout/generic/nsFrame.h | 4 | ||||
-rw-r--r-- | layout/generic/nsHTMLCanvasFrame.cpp | 31 | ||||
-rw-r--r-- | layout/generic/nsHTMLCanvasFrame.h | 2 | ||||
-rw-r--r-- | layout/generic/nsIFrame.h | 14 | ||||
-rw-r--r-- | layout/generic/nsImageFrame.cpp | 265 | ||||
-rw-r--r-- | layout/generic/nsImageFrame.h | 24 | ||||
-rw-r--r-- | layout/generic/nsLineLayout.cpp | 2 | ||||
-rw-r--r-- | layout/generic/nsSubDocumentFrame.cpp | 4 | ||||
-rw-r--r-- | layout/generic/nsSubDocumentFrame.h | 2 | ||||
-rw-r--r-- | layout/generic/nsVideoFrame.cpp | 18 | ||||
-rw-r--r-- | layout/generic/nsVideoFrame.h | 2 |
17 files changed, 330 insertions, 224 deletions
diff --git a/layout/generic/AspectRatio.h b/layout/generic/AspectRatio.h new file mode 100644 index 000000000..0056c0620 --- /dev/null +++ b/layout/generic/AspectRatio.h @@ -0,0 +1,81 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_AspectRatio_h +#define mozilla_AspectRatio_h + +/* The aspect ratio of a box, in a "width / height" format. */ + +#include "mozilla/Attributes.h" +#include "nsCoord.h" +#include <algorithm> +#include <limits> + +namespace mozilla { + +struct AspectRatio { + AspectRatio() : mRatio(0.0f) {} + explicit AspectRatio(float aRatio) : mRatio(std::max(aRatio, 0.0f)) {} + + static AspectRatio FromSize(float aWidth, float aHeight) { + if (aWidth == 0.0f || aHeight == 0.0f) { + return AspectRatio(); + } + return AspectRatio(aWidth / aHeight); + } + + static AspectRatio FromSize(nsSize aSize) { + return FromSize(aSize.width, aSize.height); + } + + static AspectRatio FromSize(nsIntSize aSize) { + return FromSize(aSize.width, aSize.height); + } + + explicit operator bool() const { return mRatio != 0.0f; } + + nscoord ApplyTo(nscoord aCoord) const { + MOZ_DIAGNOSTIC_ASSERT(*this); + return NSCoordSaturatingNonnegativeMultiply(aCoord, mRatio); + } + + float ApplyToFloat(float aFloat) const { + MOZ_DIAGNOSTIC_ASSERT(*this); + return mRatio * aFloat; + } + + // Inverts the ratio, in order to get the height / width ratio. + MOZ_MUST_USE AspectRatio Inverted() const { + if (!*this) { + return AspectRatio(); + } + // Clamp to a small epsilon, in case mRatio is absurdly large & produces + // 0.0f in the division here (so that valid ratios always generate other + // valid ratios when inverted). + return AspectRatio( + std::max(std::numeric_limits<float>::epsilon(), 1.0f / mRatio)); + } + + bool operator==(const AspectRatio& aOther) const { + return mRatio == aOther.mRatio; + } + + bool operator!=(const AspectRatio& aOther) const { + return !(*this == aOther); + } + + bool operator<(const AspectRatio& aOther) const { + return mRatio < aOther.mRatio; + } + + private: + // 0.0f represents no aspect ratio. + float mRatio; +}; + +} // namespace mozilla + +#endif // mozilla_AspectRatio_h diff --git a/layout/generic/crashtests/1633434.html b/layout/generic/crashtests/1633434.html new file mode 100644 index 000000000..8a60b2072 --- /dev/null +++ b/layout/generic/crashtests/1633434.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<html> +<head> + <script> + document.addEventListener('DOMContentLoaded', () => { + const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg') + svg.setAttribute('height', '6') + svg.setAttribute('width', '1pc') + document.documentElement.appendChild(svg) + svg.style.setProperty('height', '5%', undefined) + svg.width.baseVal.valueInSpecifiedUnits = 1.988164037240853e+38 + }) + </script> +</head> +</html> diff --git a/layout/generic/crashtests/crashtests.list b/layout/generic/crashtests/crashtests.list index 183556ab9..ab371429c 100644 --- a/layout/generic/crashtests/crashtests.list +++ b/layout/generic/crashtests/crashtests.list @@ -642,3 +642,4 @@ load 1278461-1.html load 1278461-2.html load 1304441.html load 1316649.html +load 1633434.html diff --git a/layout/generic/moz.build b/layout/generic/moz.build index ad186ef7a..b830470a3 100644 --- a/layout/generic/moz.build +++ b/layout/generic/moz.build @@ -109,6 +109,7 @@ EXPORTS += [ ] EXPORTS.mozilla += [ + 'AspectRatio.h', 'CSSAlignUtils.h', 'ReflowInput.h', 'ReflowOutput.h', diff --git a/layout/generic/nsFlexContainerFrame.cpp b/layout/generic/nsFlexContainerFrame.cpp index 69200117b..a03d777e7 100644 --- a/layout/generic/nsFlexContainerFrame.cpp +++ b/layout/generic/nsFlexContainerFrame.cpp @@ -557,8 +557,8 @@ public: return mFlexShrink * mFlexBaseSize; } - const nsSize& IntrinsicRatio() const { return mIntrinsicRatio; } - bool HasIntrinsicRatio() const { return mIntrinsicRatio != nsSize(); } + const AspectRatio IntrinsicRatio() const { return mIntrinsicRatio; } + bool HasIntrinsicRatio() const { return !!mIntrinsicRatio; } // Getters for margin: // =================== @@ -756,7 +756,7 @@ protected: const float mFlexGrow; const float mFlexShrink; - const nsSize mIntrinsicRatio; + const AspectRatio mIntrinsicRatio; const nsMargin mBorderPadding; nsMargin mMargin; // non-const because we need to resolve auto margins @@ -1520,22 +1520,20 @@ CrossSizeToUseWithRatio(const FlexItem& aFlexItem, } // Convenience function; returns a main-size, given a cross-size and an -// intrinsic ratio. The intrinsic ratio must not have 0 in its cross-axis -// component (or else we'll divide by 0). +// intrinsic ratio. The caller is responsible for ensuring that the passed-in +// intrinsic ratio is not zero. static nscoord MainSizeFromAspectRatio(nscoord aCrossSize, - const nsSize& aIntrinsicRatio, + const AspectRatio& aIntrinsicRatio, const FlexboxAxisTracker& aAxisTracker) { - MOZ_ASSERT(aAxisTracker.GetCrossComponent(aIntrinsicRatio) != 0, + MOZ_ASSERT(aIntrinsicRatio, "Invalid ratio; will divide by 0! Caller should've checked..."); + AspectRatio ratio = aAxisTracker.IsMainAxisHorizontal() ? + aIntrinsicRatio : + aIntrinsicRatio.Inverted(); - if (aAxisTracker.IsCrossAxisHorizontal()) { - // cross axis horiz --> aCrossSize is a width. Converting to height. - return NSCoordMulDiv(aCrossSize, aIntrinsicRatio.height, aIntrinsicRatio.width); - } - // cross axis vert --> aCrossSize is a height. Converting to width. - return NSCoordMulDiv(aCrossSize, aIntrinsicRatio.width, aIntrinsicRatio.height); + return ratio.ApplyTo(aCrossSize); } // Partially resolves "min-[width|height]:auto" and returns the resulting value. @@ -1584,7 +1582,7 @@ PartiallyResolveAutoMinSize(const FlexItem& aFlexItem, // * if the item has an intrinsic aspect ratio, the width (height) calculated // from the aspect ratio and any definite size constraints in the opposite // dimension. - if (aAxisTracker.GetCrossComponent(aFlexItem.IntrinsicRatio()) != 0) { + if (aFlexItem.IntrinsicRatio()) { // We have a usable aspect ratio. (not going to divide by 0) const bool useMinSizeIfCrossSizeIsIndefinite = true; nscoord crossSizeToUseWithRatio = @@ -1617,7 +1615,7 @@ ResolveAutoFlexBasisFromRatio(FlexItem& aFlexItem, // - a definite cross size // then the flex base size is calculated from its inner cross size and the // flex item’s intrinsic aspect ratio. - if (aAxisTracker.GetCrossComponent(aFlexItem.IntrinsicRatio()) != 0) { + if (aFlexItem.IntrinsicRatio()) { // We have a usable aspect ratio. (not going to divide by 0) const bool useMinSizeIfCrossSizeIsIndefinite = false; nscoord crossSizeToUseWithRatio = @@ -1693,8 +1691,7 @@ nsFlexContainerFrame:: // (We'll consider that later, if we need to.) resolvedMinSize = PartiallyResolveAutoMinSize(aFlexItem, aItemReflowInput, aAxisTracker); - if (resolvedMinSize > 0 && - aAxisTracker.GetCrossComponent(aFlexItem.IntrinsicRatio()) == 0) { + if (resolvedMinSize > 0 && !aFlexItem.IntrinsicRatio()) { // We don't have a usable aspect ratio, so we need to consider our // min-content size as another candidate min-size, which we'll have to // min() with the current resolvedMinSize. diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 07cdbd7e3..cb70f8b1e 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -4643,10 +4643,10 @@ nsFrame::GetIntrinsicSize() return IntrinsicSize(); // default is width/height set to eStyleUnit_None } -/* virtual */ nsSize +/* virtual */ AspectRatio nsFrame::GetIntrinsicRatio() { - return nsSize(0, 0); + return AspectRatio(); } /* virtual */ @@ -4660,7 +4660,7 @@ nsFrame::ComputeSize(nsRenderingContext* aRenderingContext, const LogicalSize& aPadding, ComputeSizeFlags aFlags) { - MOZ_ASSERT(GetIntrinsicRatio() == nsSize(0,0), + MOZ_ASSERT(!GetIntrinsicRatio(), "Please override this method and call " "nsFrame::ComputeSizeWithIntrinsicDimensions instead."); LogicalSize result = ComputeAutoSize(aRenderingContext, aWM, @@ -4916,13 +4916,15 @@ LogicalSize nsFrame::ComputeSizeWithIntrinsicDimensions(nsRenderingContext* aRenderingContext, WritingMode aWM, const IntrinsicSize& aIntrinsicSize, - nsSize aIntrinsicRatio, + const AspectRatio& aIntrinsicRatio, const LogicalSize& aCBSize, const LogicalSize& aMargin, const LogicalSize& aBorder, const LogicalSize& aPadding, ComputeSizeFlags aFlags) { + auto logicalRatio = + aWM.IsVertical() ? aIntrinsicRatio.Inverted() : aIntrinsicRatio; const nsStylePosition* stylePos = StylePosition(); const nsStyleCoord* inlineStyleCoord = &stylePos->ISize(aWM); const nsStyleCoord* blockStyleCoord = &stylePos->BSize(aWM); @@ -5183,10 +5185,6 @@ nsFrame::ComputeSizeWithIntrinsicDimensions(nsRenderingContext* aRenderingConte intrinsicBSize = 0; } - NS_ASSERTION(aIntrinsicRatio.width >= 0 && aIntrinsicRatio.height >= 0, - "Intrinsic ratio has a negative component!"); - LogicalSize logicalRatio(aWM, aIntrinsicRatio); - // Now calculate the used values for iSize and bSize: if (isAutoISize) { @@ -5200,9 +5198,9 @@ nsFrame::ComputeSizeWithIntrinsicDimensions(nsRenderingContext* aRenderingConte if (hasIntrinsicISize) { tentISize = intrinsicISize; - } else if (hasIntrinsicBSize && logicalRatio.BSize(aWM) > 0) { - tentISize = NSCoordMulDiv(intrinsicBSize, logicalRatio.ISize(aWM), logicalRatio.BSize(aWM)); - } else if (logicalRatio.ISize(aWM) > 0) { + } else if (hasIntrinsicBSize && logicalRatio) { + tentISize = logicalRatio.ApplyTo(intrinsicBSize); + } else if (logicalRatio) { tentISize = aCBSize.ISize(aWM) - boxSizingToMarginEdgeISize; // XXX scrollbar? if (tentISize < 0) tentISize = 0; } else { @@ -5219,8 +5217,8 @@ nsFrame::ComputeSizeWithIntrinsicDimensions(nsRenderingContext* aRenderingConte if (hasIntrinsicBSize) { tentBSize = intrinsicBSize; - } else if (logicalRatio.ISize(aWM) > 0) { - tentBSize = NSCoordMulDiv(tentISize, logicalRatio.BSize(aWM), logicalRatio.ISize(aWM)); + } else if (logicalRatio) { + tentBSize = logicalRatio.Inverted().ApplyTo(tentISize); } else { tentBSize = nsPresContext::CSSPixelsToAppUnits(150); } @@ -5231,46 +5229,39 @@ nsFrame::ComputeSizeWithIntrinsicDimensions(nsRenderingContext* aRenderingConte stretchB = (stretchI == eStretch ? eStretch : eStretchPreservingRatio); } - if (aIntrinsicRatio != nsSize(0, 0)) { + if (logicalRatio) { if (stretchI == eStretch) { tentISize = iSize; // * / 'stretch' if (stretchB == eStretch) { tentBSize = bSize; // 'stretch' / 'stretch' - } else if (stretchB == eStretchPreservingRatio && logicalRatio.ISize(aWM) > 0) { + } else if (stretchB == eStretchPreservingRatio) { // 'normal' / 'stretch' - tentBSize = NSCoordMulDiv(iSize, logicalRatio.BSize(aWM), logicalRatio.ISize(aWM)); + tentBSize = logicalRatio.Inverted().ApplyTo(iSize); } } else if (stretchB == eStretch) { tentBSize = bSize; // 'stretch' / * (except 'stretch') - if (stretchI == eStretchPreservingRatio && logicalRatio.BSize(aWM) > 0) { + if (stretchI == eStretchPreservingRatio) { // 'stretch' / 'normal' - tentISize = NSCoordMulDiv(bSize, logicalRatio.ISize(aWM), logicalRatio.BSize(aWM)); + tentISize = logicalRatio.ApplyTo(bSize); } } else if (stretchI == eStretchPreservingRatio) { tentISize = iSize; // * (except 'stretch') / 'normal' - if (logicalRatio.ISize(aWM) > 0) { - tentBSize = NSCoordMulDiv(iSize, logicalRatio.BSize(aWM), logicalRatio.ISize(aWM)); - } + tentBSize = logicalRatio.Inverted().ApplyTo(iSize); if (stretchB == eStretchPreservingRatio && tentBSize > bSize) { // Stretch within the CB size with preserved intrinsic ratio. tentBSize = bSize; // 'normal' / 'normal' - if (logicalRatio.BSize(aWM) > 0) { - tentISize = NSCoordMulDiv(bSize, logicalRatio.ISize(aWM), logicalRatio.BSize(aWM)); - } + tentISize = logicalRatio.ApplyTo(bSize); } } else if (stretchB == eStretchPreservingRatio) { tentBSize = bSize; // 'normal' / * (except 'normal' and 'stretch') - if (logicalRatio.BSize(aWM) > 0) { - tentISize = NSCoordMulDiv(bSize, logicalRatio.ISize(aWM), logicalRatio.BSize(aWM)); - } + tentISize = logicalRatio.ApplyTo(bSize); } } // ComputeAutoSizeWithIntrinsicDimensions preserves the ratio when applying // the min/max-size. We don't want that when we have 'stretch' in either // axis because tentISize/tentBSize is likely not according to ratio now. - if (aIntrinsicRatio != nsSize(0, 0) && - stretchI != eStretch && stretchB != eStretch) { + if (logicalRatio && stretchI != eStretch && stretchB != eStretch) { nsSize autoSize = nsLayoutUtils:: ComputeAutoSizeWithIntrinsicDimensions(minISize, minBSize, maxISize, maxBSize, @@ -5291,8 +5282,8 @@ nsFrame::ComputeSizeWithIntrinsicDimensions(nsRenderingContext* aRenderingConte // 'auto' iSize, non-'auto' bSize bSize = NS_CSS_MINMAX(bSize, minBSize, maxBSize); if (stretchI != eStretch) { - if (logicalRatio.BSize(aWM) > 0) { - iSize = NSCoordMulDiv(bSize, logicalRatio.ISize(aWM), logicalRatio.BSize(aWM)); + if (logicalRatio) { + iSize = logicalRatio.ApplyTo(bSize); } else if (hasIntrinsicISize) { if (!((aFlags & ComputeSizeFlags::eIClampMarginBoxMinSize) && intrinsicISize > iSize)) { @@ -5311,8 +5302,8 @@ nsFrame::ComputeSizeWithIntrinsicDimensions(nsRenderingContext* aRenderingConte // non-'auto' iSize, 'auto' bSize iSize = NS_CSS_MINMAX(iSize, minISize, maxISize); if (stretchB != eStretch) { - if (logicalRatio.ISize(aWM) > 0) { - bSize = NSCoordMulDiv(iSize, logicalRatio.BSize(aWM), logicalRatio.ISize(aWM)); + if (logicalRatio) { + bSize = logicalRatio.Inverted().ApplyTo(iSize); } else if (hasIntrinsicBSize) { if (!((aFlags & ComputeSizeFlags::eBClampMarginBoxMinSize) && intrinsicBSize > bSize)) { diff --git a/layout/generic/nsFrame.h b/layout/generic/nsFrame.h index 439e39856..d75555fec 100644 --- a/layout/generic/nsFrame.h +++ b/layout/generic/nsFrame.h @@ -264,7 +264,7 @@ public: IntrinsicISizeOffsetData IntrinsicISizeOffsets(nscoord aPercentageBasis = NS_UNCONSTRAINEDSIZE) override; virtual mozilla::IntrinsicSize GetIntrinsicSize() override; - virtual nsSize GetIntrinsicRatio() override; + virtual mozilla::AspectRatio GetIntrinsicRatio() override; virtual mozilla::LogicalSize ComputeSize(nsRenderingContext* aRenderingContext, @@ -285,7 +285,7 @@ public: nsRenderingContext* aRenderingContext, mozilla::WritingMode aWM, const mozilla::IntrinsicSize& aIntrinsicSize, - nsSize aIntrinsicRatio, + const mozilla::AspectRatio& aIntrinsicRatio, const mozilla::LogicalSize& aCBSize, const mozilla::LogicalSize& aMargin, const mozilla::LogicalSize& aBorder, diff --git a/layout/generic/nsHTMLCanvasFrame.cpp b/layout/generic/nsHTMLCanvasFrame.cpp index f86ec1136..f40f799ba 100644 --- a/layout/generic/nsHTMLCanvasFrame.cpp +++ b/layout/generic/nsHTMLCanvasFrame.cpp @@ -44,21 +44,6 @@ IntrinsicSizeFromCanvasSize(const nsIntSize& aCanvasSizeInPx) return intrinsicSize; } -/* Helper for our nsIFrame::GetIntrinsicRatio() impl. Takes the result of - * "GetCanvasSize()" as a parameter, which may help avoid redundant - * indirect calls to GetCanvasSize(). - * - * @param aCanvasSizeInPx The canvas's size in CSS pixels, as returned - * by GetCanvasSize(). - * @return The canvas's intrinsic ratio, as a nsSize. - */ -static nsSize -IntrinsicRatioFromCanvasSize(const nsIntSize& aCanvasSizeInPx) -{ - return nsSize(nsPresContext::CSSPixelsToAppUnits(aCanvasSizeInPx.width), - nsPresContext::CSSPixelsToAppUnits(aCanvasSizeInPx.height)); -} - class nsDisplayCanvas : public nsDisplayItem { public: nsDisplayCanvas(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) @@ -92,7 +77,7 @@ public: // Need intrinsic size & ratio, for ComputeObjectDestRect: nsIntSize canvasSize = f->GetCanvasSize(); IntrinsicSize intrinsicSize = IntrinsicSizeFromCanvasSize(canvasSize); - nsSize intrinsicRatio = IntrinsicRatioFromCanvasSize(canvasSize); + AspectRatio intrinsicRatio = AspectRatio::FromSize(canvasSize); const nsRect destRect = nsLayoutUtils::ComputeObjectDestRect(constraintRect, @@ -211,10 +196,16 @@ nsHTMLCanvasFrame::GetIntrinsicSize() return IntrinsicSizeFromCanvasSize(GetCanvasSize()); } -/* virtual */ nsSize +/* virtual */ AspectRatio nsHTMLCanvasFrame::GetIntrinsicRatio() { - return IntrinsicRatioFromCanvasSize(GetCanvasSize()); + // When 'contain: size' is implemented, make sure to check for it. +/* + if (StyleDisplay()->IsContainSize()) { + return AspectRatio(); + } + */ + return AspectRatio::FromSize(GetCanvasSize()); } /* virtual */ @@ -234,7 +225,7 @@ nsHTMLCanvasFrame::ComputeSize(nsRenderingContext *aRenderingContext, intrinsicSize.width.SetCoordValue(nsPresContext::CSSPixelsToAppUnits(size.width)); intrinsicSize.height.SetCoordValue(nsPresContext::CSSPixelsToAppUnits(size.height)); - nsSize intrinsicRatio = GetIntrinsicRatio(); // won't actually be used + AspectRatio intrinsicRatio = GetIntrinsicRatio(); return ComputeSizeWithIntrinsicDimensions(aRenderingContext, aWM, intrinsicSize, intrinsicRatio, @@ -340,7 +331,7 @@ nsHTMLCanvasFrame::BuildLayer(nsDisplayListBuilder* aBuilder, return nullptr; IntrinsicSize intrinsicSize = IntrinsicSizeFromCanvasSize(canvasSizeInPx); - nsSize intrinsicRatio = IntrinsicRatioFromCanvasSize(canvasSizeInPx); + AspectRatio intrinsicRatio = AspectRatio::FromSize(canvasSizeInPx); nsRect dest = nsLayoutUtils::ComputeObjectDestRect(area, intrinsicSize, intrinsicRatio, diff --git a/layout/generic/nsHTMLCanvasFrame.h b/layout/generic/nsHTMLCanvasFrame.h index b2d159627..8432ad224 100644 --- a/layout/generic/nsHTMLCanvasFrame.h +++ b/layout/generic/nsHTMLCanvasFrame.h @@ -58,7 +58,7 @@ public: virtual nscoord GetMinISize(nsRenderingContext *aRenderingContext) override; virtual nscoord GetPrefISize(nsRenderingContext *aRenderingContext) override; virtual mozilla::IntrinsicSize GetIntrinsicSize() override; - virtual nsSize GetIntrinsicRatio() override; + virtual mozilla::AspectRatio GetIntrinsicRatio() override; virtual mozilla::LogicalSize ComputeSize(nsRenderingContext *aRenderingContext, diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h index 82bcf563a..b3606d0b4 100644 --- a/layout/generic/nsIFrame.h +++ b/layout/generic/nsIFrame.h @@ -27,6 +27,7 @@ #include "FrameProperties.h" #include "LayoutConstants.h" #include "mozilla/layout/FrameChildList.h" +#include "mozilla/AspectRatio.h" #include "mozilla/Maybe.h" #include "mozilla/WritingModes.h" #include "nsDirection.h" @@ -2047,15 +2048,14 @@ public: virtual mozilla::IntrinsicSize GetIntrinsicSize() = 0; /** - * Get the intrinsic ratio of this element, or nsSize(0,0) if it has - * no intrinsic ratio. The intrinsic ratio is the ratio of the - * height/width of a box with an intrinsic size or the intrinsic - * aspect ratio of a scalable vector image without an intrinsic size. + * Get the intrinsic ratio of this element, or a default-constructed + * AspectRatio if it has no intrinsic ratio. * - * Either one of the sides may be zero, indicating a zero or infinite - * ratio. + * The intrinsic ratio is the ratio of the width/height of a box with an + * intrinsic size or the intrinsic aspect ratio of a scalable vector image + * without an intrinsic size. */ - virtual nsSize GetIntrinsicRatio() = 0; + virtual mozilla::AspectRatio GetIntrinsicRatio() = 0; /** * Bit-flags to pass to ComputeSize in |aFlags| parameter. diff --git a/layout/generic/nsImageFrame.cpp b/layout/generic/nsImageFrame.cpp index 03fcbd8e3..2646125c9 100644 --- a/layout/generic/nsImageFrame.cpp +++ b/layout/generic/nsImageFrame.cpp @@ -14,6 +14,7 @@ #include "mozilla/gfx/2D.h" #include "mozilla/gfx/Helpers.h" #include "mozilla/gfx/PathHelpers.h" +#include "mozilla/dom/HTMLImageElement.h" #include "mozilla/MouseEvents.h" #include "mozilla/Unused.h" @@ -138,7 +139,6 @@ NS_IMPL_FRAMEARENA_HELPERS(nsImageFrame) nsImageFrame::nsImageFrame(nsStyleContext* aContext) : nsAtomicContainerFrame(aContext), mComputedSize(0, 0), - mIntrinsicRatio(0, 0), mDisplayingIcon(false), mFirstFrameComplete(false), mReflowCallbackPosted(false), @@ -230,26 +230,26 @@ nsImageFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) { nsAtomicContainerFrame::DidSetStyleContext(aOldStyleContext); - if (!mImage) { - // We'll pick this change up whenever we do get an image. - return; - } - nsStyleImageOrientation newOrientation = StyleVisibility()->mImageOrientation; // We need to update our orientation either if we had no style context before // because this is the first time it's been set, or if the image-orientation // property changed from its previous value. bool shouldUpdateOrientation = - !aOldStyleContext || - aOldStyleContext->StyleVisibility()->mImageOrientation != newOrientation; + mImage && + (!aOldStyleContext || + aOldStyleContext->StyleVisibility()->mImageOrientation != newOrientation); if (shouldUpdateOrientation) { nsCOMPtr<imgIContainer> image(mImage->Unwrap()); mImage = nsLayoutUtils::OrientImage(image, newOrientation); - UpdateIntrinsicSize(mImage); - UpdateIntrinsicRatio(mImage); + UpdateIntrinsicSize(); + UpdateIntrinsicRatio(); + } else if (!aOldStyleContext || + aOldStyleContext->StylePosition()->mAspectRatio != + StylePosition()->mAspectRatio) { + UpdateIntrinsicRatio(); } } @@ -287,50 +287,110 @@ nsImageFrame::Init(nsIContent* aContent, p->AdjustPriority(-1); } -bool -nsImageFrame::UpdateIntrinsicSize(imgIContainer* aImage) +static IntrinsicSize +ComputeIntrinsicSize(imgIContainer* aImage, + bool aUseMappedRatio, + const nsImageFrame& aFrame) { - NS_PRECONDITION(aImage, "null image"); - if (!aImage) - return false; + // When 'contain: size' is implemented, make sure to check for it. +/* + const ComputedStyle& style = *aFrame.Style(); + if (style.StyleDisplay()->IsContainSize()) { + return AspectRatio(); + } + */ + nsSize size; + IntrinsicSize intrinsicSize; + if (aImage && NS_SUCCEEDED(aImage->GetIntrinsicSize(&size))) { + if (size.width != -1) + intrinsicSize.width.SetCoordValue(size.width); + if (size.height != -1) + intrinsicSize.height.SetCoordValue(size.height); + return intrinsicSize; + } - IntrinsicSize oldIntrinsicSize = mIntrinsicSize; - mIntrinsicSize = IntrinsicSize(); - - // Set intrinsic size to match aImage's reported intrinsic width & height. - nsSize intrinsicSize; - if (NS_SUCCEEDED(aImage->GetIntrinsicSize(&intrinsicSize))) { - // If the image has no intrinsic width, intrinsicSize.width will be -1, and - // we can leave mIntrinsicSize.width at its default value of eStyleUnit_None. - // Otherwise we use intrinsicSize.width. Height works the same way. - if (intrinsicSize.width != -1) - mIntrinsicSize.width.SetCoordValue(intrinsicSize.width); - if (intrinsicSize.height != -1) - mIntrinsicSize.height.SetCoordValue(intrinsicSize.height); - } else { - // Failure means that the image hasn't loaded enough to report a result. We - // treat this case as if the image's intrinsic size was 0x0. - mIntrinsicSize.width.SetCoordValue(0); - mIntrinsicSize.height.SetCoordValue(0); + // If broken images should ever lose their size + /* + if (aFrame.ShouldShowBrokenImageIcon()) { + nscoord edgeLengthToUse = nsPresContext::CSSPixelsToAppUnits( + ICON_SIZE + (2 * (ICON_PADDING + ALT_BORDER_WIDTH))); + intrinsicSize.width.SetCoordValue(edgeLengthToUse); + intrinsicSize.height.SetCoordValue(edgeLengthToUse); + return intrinsicSize; } + */ - return mIntrinsicSize != oldIntrinsicSize; + if (aUseMappedRatio && aFrame.StylePosition()->mAspectRatio != 0.0f) { + return IntrinsicSize(); + } + + intrinsicSize.width.SetCoordValue(0); + intrinsicSize.height.SetCoordValue(0); + return intrinsicSize; +} + +// For compat reasons, see bug 1602047, we don't use the intrinsic ratio from +// width="" and height="" for images with no src attribute (no request). +// +// If <img loading=lazy> ever gets implemented, this will need to check for it. +bool nsImageFrame::ShouldUseMappedAspectRatio() const { + nsCOMPtr<imgIRequest> currentRequest; + nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent); + if (imageLoader) { + imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, + getter_AddRefs(currentRequest)); + } + if (!!currentRequest) { + return true; + } + // TODO(emilio): Investigate the compat situation of the above check, maybe we + // can just check for empty src attribute or something... + auto* image = static_cast<HTMLImageElement*>(mContent); + return image && image->IsAwaitingLoad(); } bool -nsImageFrame::UpdateIntrinsicRatio(imgIContainer* aImage) +nsImageFrame::UpdateIntrinsicSize() { - NS_PRECONDITION(aImage, "null image"); - - if (!aImage) - return false; + IntrinsicSize oldIntrinsicSize = mIntrinsicSize; + mIntrinsicSize = ComputeIntrinsicSize(mImage, ShouldUseMappedAspectRatio(), *this); + return mIntrinsicSize != oldIntrinsicSize; +} - nsSize oldIntrinsicRatio = mIntrinsicRatio; +static AspectRatio +ComputeAspectRatio(imgIContainer* aImage, + bool aUseMappedRatio, + const nsImageFrame& aFrame) +{ + // When 'contain: size' is implemented, make sure to check for it. +/* + const ComputedStyle& style = *aFrame.Style(); + if (style.StyleDisplay()->IsContainSize()) { + return AspectRatio(); + } + */ + if (aImage) { + AspectRatio fromImage; + if (NS_SUCCEEDED(aImage->GetIntrinsicRatio(&fromImage))) { + return fromImage; + } + } + if (aUseMappedRatio && aFrame.StylePosition()->mAspectRatio != 0.0f) { + return AspectRatio(aFrame.StylePosition()->mAspectRatio); + } + if (aFrame.ShouldShowBrokenImageIcon()) { + return AspectRatio(1.0f); + } + return AspectRatio(); +} - // Set intrinsic ratio to match aImage's reported intrinsic ratio. - if (NS_FAILED(aImage->GetIntrinsicRatio(&mIntrinsicRatio))) - mIntrinsicRatio.SizeTo(0, 0); +bool +nsImageFrame::UpdateIntrinsicRatio() +{ + AspectRatio oldIntrinsicRatio = mIntrinsicRatio; + mIntrinsicRatio = + ComputeAspectRatio(mImage, ShouldUseMappedAspectRatio(), *this); return mIntrinsicRatio != oldIntrinsicRatio; } @@ -542,30 +602,38 @@ nsImageFrame::OnSizeAvailable(imgIRequest* aRequest, imgIContainer* aImage) return NS_OK; } - bool intrinsicSizeChanged = false; + UpdateImage(aRequest, aImage); + return NS_OK; +} + +void nsImageFrame::UpdateImage(imgIRequest* aRequest, imgIContainer* aImage) { + MOZ_ASSERT(aRequest); if (SizeIsAvailable(aRequest)) { // This is valid and for the current request, so update our stored image // container, orienting according to our style. - mImage = nsLayoutUtils::OrientImage(aImage, StyleVisibility()->mImageOrientation); - - intrinsicSizeChanged = UpdateIntrinsicSize(mImage); - intrinsicSizeChanged = UpdateIntrinsicRatio(mImage) || intrinsicSizeChanged; + mImage = nsLayoutUtils::OrientImage(aImage, + StyleVisibility()->mImageOrientation); + MOZ_ASSERT(mImage); } else { // We no longer have a valid image, so release our stored image container. mImage = mPrevImage = nullptr; - - // Have to size to 0,0 so that GetDesiredSize recalculates the size. - mIntrinsicSize.width.SetCoordValue(0); - mIntrinsicSize.height.SetCoordValue(0); - mIntrinsicRatio.SizeTo(0, 0); - intrinsicSizeChanged = true; + } + // NOTE(emilio): Intentionally using `|` instead of `||` to avoid + // short-circuiting. + bool intrinsicSizeChanged = + UpdateIntrinsicSize() | UpdateIntrinsicRatio(); + if (!(mState & IMAGE_GOTINITIALREFLOW)) { + return; } - if (intrinsicSizeChanged && (mState & IMAGE_GOTINITIALREFLOW)) { + // We're going to need to repaint now either way. + InvalidateFrame(); + + if (intrinsicSizeChanged) { // Now we need to reflow if we have an unconstrained size and have - // already gotten the initial reflow + // already gotten the initial reflow. if (!(mState & IMAGE_SIZECONSTRAINED)) { - nsIPresShell *presShell = presContext->GetPresShell(); + nsIPresShell *presShell = PresContext()->GetPresShell(); NS_ASSERTION(presShell, "No PresShell."); if (presShell) { presShell->FrameNeedsReflow(this, nsIPresShell::eStyleChange, @@ -579,8 +647,6 @@ nsImageFrame::OnSizeAvailable(imgIRequest* aRequest, imgIContainer* aImage) mPrevImage = nullptr; } - - return NS_OK; } nsresult @@ -655,45 +721,9 @@ nsImageFrame::NotifyNewCurrentRequest(imgIRequest *aRequest, { nsCOMPtr<imgIContainer> image; aRequest->GetImage(getter_AddRefs(image)); - NS_ASSERTION(image || NS_FAILED(aStatus), "Successful load with no container?"); - - // May have to switch sizes here! - bool intrinsicSizeChanged = true; - if (NS_SUCCEEDED(aStatus) && image && SizeIsAvailable(aRequest)) { - // Update our stored image container, orienting according to our style. - mImage = nsLayoutUtils::OrientImage(image, StyleVisibility()->mImageOrientation); - - intrinsicSizeChanged = UpdateIntrinsicSize(mImage); - intrinsicSizeChanged = UpdateIntrinsicRatio(mImage) || intrinsicSizeChanged; - } else { - // We no longer have a valid image, so release our stored image container. - mImage = mPrevImage = nullptr; - - // Have to size to 0,0 so that GetDesiredSize recalculates the size - mIntrinsicSize.width.SetCoordValue(0); - mIntrinsicSize.height.SetCoordValue(0); - mIntrinsicRatio.SizeTo(0, 0); - } - - if (mState & IMAGE_GOTINITIALREFLOW) { // do nothing if we haven't gotten the initial reflow yet - if (intrinsicSizeChanged) { - if (!(mState & IMAGE_SIZECONSTRAINED)) { - nsIPresShell *presShell = PresContext()->GetPresShell(); - if (presShell) { - presShell->FrameNeedsReflow(this, nsIPresShell::eStyleChange, - NS_FRAME_IS_DIRTY); - } - } else { - // We've already gotten the initial reflow, and our size hasn't changed, - // so we're ready to request a decode. - MaybeDecodeForPredictedSize(); - } - - mPrevImage = nullptr; - } - // Update border+content to account for image change - InvalidateFrame(); - } + NS_ASSERTION(image || NS_FAILED(aStatus), + "Successful load with no container?"); + UpdateImage(aRequest, image); } void @@ -787,32 +817,27 @@ bool nsImageFrame::ShouldShowBrokenImageIcon() const void nsImageFrame::EnsureIntrinsicSizeAndRatio() { + // When 'contain: size' is implemented, make sure to check for it. +/* + if (StyleDisplay()->IsContainSize()) { + // If we have 'contain:size', then our intrinsic size and ratio are 0,0 + // regardless of what our underlying image may think. + mIntrinsicSize = IntrinsicSize(0, 0); + mIntrinsicRatio = AspectRatio(); + return; + } + */ + // If mIntrinsicSize.width and height are 0, then we need to update from the // image container. - if (mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord && + if (!(mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord && mIntrinsicSize.width.GetCoordValue() == 0 && mIntrinsicSize.height.GetUnit() == eStyleUnit_Coord && - mIntrinsicSize.height.GetCoordValue() == 0) { - - if (mImage) { - UpdateIntrinsicSize(mImage); - UpdateIntrinsicRatio(mImage); - } else { - // Image request is null or image size not known. - if (!(GetStateBits() & NS_FRAME_GENERATED_CONTENT)) { - // Likely an invalid image. Check if we should display it as broken. - if (ShouldShowBrokenImageIcon()) { - // Invalid image specified. make the image big enough for the "broken" icon - nscoord edgeLengthToUse = - nsPresContext::CSSPixelsToAppUnits( - ICON_SIZE + (2 * (ICON_PADDING + ALT_BORDER_WIDTH))); - mIntrinsicSize.width.SetCoordValue(edgeLengthToUse); - mIntrinsicSize.height.SetCoordValue(edgeLengthToUse); - mIntrinsicRatio.SizeTo(1, 1); - } - } - } + mIntrinsicSize.height.GetCoordValue() == 0)) { + return; } + UpdateIntrinsicSize(); + UpdateIntrinsicRatio(); } /* virtual */ @@ -932,7 +957,7 @@ nsImageFrame::GetIntrinsicSize() return mIntrinsicSize; } -/* virtual */ nsSize +/* virtual */ AspectRatio nsImageFrame::GetIntrinsicRatio() { return mIntrinsicRatio; diff --git a/layout/generic/nsImageFrame.h b/layout/generic/nsImageFrame.h index 59af1be32..5e9b67274 100644 --- a/layout/generic/nsImageFrame.h +++ b/layout/generic/nsImageFrame.h @@ -86,7 +86,7 @@ public: virtual nscoord GetMinISize(nsRenderingContext *aRenderingContext) override; virtual nscoord GetPrefISize(nsRenderingContext *aRenderingContext) override; virtual mozilla::IntrinsicSize GetIntrinsicSize() override; - virtual nsSize GetIntrinsicRatio() override; + virtual mozilla::AspectRatio GetIntrinsicRatio() override; virtual void Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize, const ReflowInput& aReflowInput, @@ -273,21 +273,19 @@ private: void GetDocumentCharacterSet(nsACString& aCharset) const; bool ShouldDisplaySelection(); + // Whether the image frame should use the mapped aspect ratio from width="" + // and height="". + bool ShouldUseMappedAspectRatio() const; + /** * Recalculate mIntrinsicSize from the image. - * - * @return whether aImage's size did _not_ - * match our previous intrinsic size. */ - bool UpdateIntrinsicSize(imgIContainer* aImage); + bool UpdateIntrinsicSize(); /** * Recalculate mIntrinsicRatio from the image. - * - * @return whether aImage's ratio did _not_ - * match our previous intrinsic ratio. */ - bool UpdateIntrinsicRatio(imgIContainer* aImage); + bool UpdateIntrinsicRatio(); /** * This function calculates the transform for converting between @@ -308,6 +306,12 @@ private: bool IsPendingLoad(imgIRequest* aRequest) const; /** + * Updates mImage based on the current image request (cannot be null), and the + * image passed in (can be null), and invalidate layout and paint as needed. + */ + void UpdateImage(imgIRequest* aRequest, imgIContainer* aImage); + + /** * Function to convert a dirty rect in the source image to a dirty * rect for the image frame. */ @@ -333,7 +337,7 @@ private: nsCOMPtr<imgIContainer> mPrevImage; nsSize mComputedSize; mozilla::IntrinsicSize mIntrinsicSize; - nsSize mIntrinsicRatio; + mozilla::AspectRatio mIntrinsicRatio; bool mDisplayingIcon; bool mFirstFrameComplete; diff --git a/layout/generic/nsLineLayout.cpp b/layout/generic/nsLineLayout.cpp index 6a15a9cfa..60e4e8c96 100644 --- a/layout/generic/nsLineLayout.cpp +++ b/layout/generic/nsLineLayout.cpp @@ -784,7 +784,7 @@ IsPercentageAware(const nsIFrame* aFrame) // is calculated from the constraint equation used for // block-level, non-replaced elements in normal flow. nsIFrame *f = const_cast<nsIFrame*>(aFrame); - if (f->GetIntrinsicRatio() != nsSize(0, 0) && + if (f->GetIntrinsicRatio() && // Some percents are treated like 'auto', so check != coord pos->mHeight.GetUnit() != eStyleUnit_Coord) { const IntrinsicSize &intrinsicSize = f->GetIntrinsicSize(); diff --git a/layout/generic/nsSubDocumentFrame.cpp b/layout/generic/nsSubDocumentFrame.cpp index fd9a7d32c..31d04a335 100644 --- a/layout/generic/nsSubDocumentFrame.cpp +++ b/layout/generic/nsSubDocumentFrame.cpp @@ -671,7 +671,7 @@ nsSubDocumentFrame::GetIntrinsicSize() return nsAtomicContainerFrame::GetIntrinsicSize(); } -/* virtual */ nsSize +/* virtual */ AspectRatio nsSubDocumentFrame::GetIntrinsicRatio() { nsIFrame* subDocRoot = ObtainIntrinsicSizeFrame(); @@ -771,7 +771,7 @@ nsSubDocumentFrame::Reflow(nsPresContext* aPresContext, // Size & position the view according to 'object-fit' & 'object-position'. nsIFrame* subDocRoot = ObtainIntrinsicSizeFrame(); IntrinsicSize intrinsSize; - nsSize intrinsRatio; + AspectRatio intrinsRatio; if (subDocRoot) { intrinsSize = subDocRoot->GetIntrinsicSize(); intrinsRatio = subDocRoot->GetIntrinsicRatio(); diff --git a/layout/generic/nsSubDocumentFrame.h b/layout/generic/nsSubDocumentFrame.h index 93d908dcc..907d33710 100644 --- a/layout/generic/nsSubDocumentFrame.h +++ b/layout/generic/nsSubDocumentFrame.h @@ -51,7 +51,7 @@ public: virtual nscoord GetPrefISize(nsRenderingContext *aRenderingContext) override; virtual mozilla::IntrinsicSize GetIntrinsicSize() override; - virtual nsSize GetIntrinsicRatio() override; + virtual mozilla::AspectRatio GetIntrinsicRatio() override; virtual mozilla::LogicalSize ComputeAutoSize(nsRenderingContext* aRenderingContext, diff --git a/layout/generic/nsVideoFrame.cpp b/layout/generic/nsVideoFrame.cpp index cea209cb5..0472ebe62 100644 --- a/layout/generic/nsVideoFrame.cpp +++ b/layout/generic/nsVideoFrame.cpp @@ -217,12 +217,10 @@ nsVideoFrame::BuildLayer(nsDisplayListBuilder* aBuilder, // Convert video size from pixel units into app units, to get an aspect-ratio // (which has to be represented as a nsSize) and an IntrinsicSize that we // can pass to ComputeObjectRenderRect. - nsSize aspectRatio(nsPresContext::CSSPixelsToAppUnits(videoSizeInPx.width), - nsPresContext::CSSPixelsToAppUnits(videoSizeInPx.height)); + auto aspectRatio = AspectRatio::FromSize(videoSizeInPx); IntrinsicSize intrinsicSize; - intrinsicSize.width.SetCoordValue(aspectRatio.width); - intrinsicSize.height.SetCoordValue(aspectRatio.height); - + intrinsicSize.width.SetCoordValue(nsPresContext::CSSPixelsToAppUnits(videoSizeInPx.width)); + intrinsicSize.height.SetCoordValue(nsPresContext::CSSPixelsToAppUnits(videoSizeInPx.height)); nsRect dest = nsLayoutUtils::ComputeObjectDestRect(area, intrinsicSize, aspectRatio, @@ -533,7 +531,9 @@ nsVideoFrame::ComputeSize(nsRenderingContext *aRenderingContext, intrinsicSize.height.SetCoordValue(size.height); // Only video elements have an intrinsic ratio. - nsSize intrinsicRatio = HasVideoElement() ? size : nsSize(0, 0); + auto intrinsicRatio = HasVideoElement() ? + AspectRatio::FromSize(size) : + AspectRatio(); return ComputeSizeWithIntrinsicDimensions(aRenderingContext, aWM, intrinsicSize, intrinsicRatio, @@ -557,14 +557,14 @@ nscoord nsVideoFrame::GetPrefISize(nsRenderingContext *aRenderingContext) return result; } -nsSize nsVideoFrame::GetIntrinsicRatio() +AspectRatio nsVideoFrame::GetIntrinsicRatio() { if (!HasVideoElement()) { // Audio elements have no intrinsic ratio. - return nsSize(0, 0); + return AspectRatio(); } - return GetVideoIntrinsicSize(nullptr); + return AspectRatio::FromSize(GetVideoIntrinsicSize(nullptr)); } bool nsVideoFrame::ShouldDisplayPoster() diff --git a/layout/generic/nsVideoFrame.h b/layout/generic/nsVideoFrame.h index d624ae6b9..f52cd8277 100644 --- a/layout/generic/nsVideoFrame.h +++ b/layout/generic/nsVideoFrame.h @@ -56,7 +56,7 @@ public: /* get the size of the video's display */ nsSize GetVideoIntrinsicSize(nsRenderingContext *aRenderingContext); - virtual nsSize GetIntrinsicRatio() override; + virtual mozilla::AspectRatio GetIntrinsicRatio() override; virtual mozilla::LogicalSize ComputeSize(nsRenderingContext *aRenderingContext, mozilla::WritingMode aWritingMode, |