From 1e9e9ef0cb4242c25f257f01bfb733a507c2596d Mon Sep 17 00:00:00 2001 From: Fedor Date: Thu, 17 Sep 2020 08:20:27 +0300 Subject: [PATCH] Use Intrinsic Aspect Ratio for Images. --- dom/html/HTMLImageElement.cpp | 2 +- dom/html/HTMLImageElement.h | 5 + dom/html/nsGenericHTMLElement.cpp | 58 +++- dom/html/nsGenericHTMLElement.h | 4 +- layout/generic/crashtests/1633434.html | 15 + layout/generic/crashtests/crashtests.list | 1 + layout/generic/nsImageFrame.cpp | 264 ++++++++++-------- layout/generic/nsImageFrame.h | 20 +- layout/style/nsCSSPropList.h | 13 + layout/style/nsRuleNode.cpp | 6 + layout/style/nsStyleStruct.cpp | 7 + layout/style/nsStyleStruct.h | 1 + layout/style/test/ListCSSProperties.cpp | 1 + layout/svg/nsSVGOuterSVGFrame.cpp | 16 +- modules/libpref/init/all.js | 6 + .../background-size-cover-003-ref.html | 21 ++ .../background-size-cover-003.html | 38 +++ .../img-aspect-ratio.html | 60 ++++ 18 files changed, 382 insertions(+), 156 deletions(-) create mode 100644 layout/generic/crashtests/1633434.html create mode 100644 testing/web-platform/tests/css-backgrounds/background-size-cover-003-ref.html create mode 100644 testing/web-platform/tests/css-backgrounds/background-size-cover-003.html create mode 100644 testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-aspect-ratio.html diff --git a/dom/html/HTMLImageElement.cpp b/dom/html/HTMLImageElement.cpp index 444c352e2..d042a9fe6 100644 --- a/dom/html/HTMLImageElement.cpp +++ b/dom/html/HTMLImageElement.cpp @@ -325,7 +325,7 @@ HTMLImageElement::MapAttributesIntoRule(const nsMappedAttributes* aAttributes, nsGenericHTMLElement::MapImageAlignAttributeInto(aAttributes, aData); nsGenericHTMLElement::MapImageBorderAttributeInto(aAttributes, aData); nsGenericHTMLElement::MapImageMarginAttributeInto(aAttributes, aData); - nsGenericHTMLElement::MapImageSizeAttributesInto(aAttributes, aData); + nsGenericHTMLElement::MapImageSizeAttributesInto(aAttributes, aData, true); nsGenericHTMLElement::MapCommonAttributesInto(aAttributes, aData); } diff --git a/dom/html/HTMLImageElement.h b/dom/html/HTMLImageElement.h index bb4a09882..23cac4afb 100644 --- a/dom/html/HTMLImageElement.h +++ b/dom/html/HTMLImageElement.h @@ -196,6 +196,11 @@ public: return GetReferrerPolicyAsEnum(); } + bool IsAwaitingLoad() const + { + return !!mPendingImageLoadTask; + } + int32_t X(); int32_t Y(); // Uses XPCOM GetLowsrc. diff --git a/dom/html/nsGenericHTMLElement.cpp b/dom/html/nsGenericHTMLElement.cpp index b52e61ce6..35ce8533b 100644 --- a/dom/html/nsGenericHTMLElement.cpp +++ b/dom/html/nsGenericHTMLElement.cpp @@ -1449,29 +1449,57 @@ nsGenericHTMLElement::MapImageMarginAttributeInto(const nsMappedAttributes* aAtt void nsGenericHTMLElement::MapImageSizeAttributesInto(const nsMappedAttributes* aAttributes, - nsRuleData* aData) + nsRuleData* aData, + bool aMapAspectRatio) { if (!(aData->mSIDs & NS_STYLE_INHERIT_BIT(Position))) return; + auto* aWidth = aAttributes->GetAttr(nsGkAtoms::width); + auto* aHeight = aAttributes->GetAttr(nsGkAtoms::height); + // width: value - nsCSSValue* width = aData->ValueForWidth(); - if (width->GetUnit() == eCSSUnit_Null) { - const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::width); - if (value && value->Type() == nsAttrValue::eInteger) - width->SetFloatValue((float)value->GetIntegerValue(), eCSSUnit_Pixel); - else if (value && value->Type() == nsAttrValue::ePercent) - width->SetPercentValue(value->GetPercentValue()); + if (aWidth) { + nsCSSValue* cWidth = aData->ValueForWidth(); + if (cWidth->GetUnit() == eCSSUnit_Null) { + if (aWidth->Type() == nsAttrValue::eInteger) + cWidth->SetFloatValue((float)aWidth->GetIntegerValue(), eCSSUnit_Pixel); + else if (aWidth->Type() == nsAttrValue::ePercent) + cWidth->SetPercentValue(aWidth->GetPercentValue()); + } } // height: value - nsCSSValue* height = aData->ValueForHeight(); - if (height->GetUnit() == eCSSUnit_Null) { - const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::height); - if (value && value->Type() == nsAttrValue::eInteger) - height->SetFloatValue((float)value->GetIntegerValue(), eCSSUnit_Pixel); - else if (value && value->Type() == nsAttrValue::ePercent) - height->SetPercentValue(value->GetPercentValue()); + if (aHeight) { + nsCSSValue* cHeight = aData->ValueForHeight(); + if (cHeight->GetUnit() == eCSSUnit_Null) { + if (aHeight->Type() == nsAttrValue::eInteger) + cHeight->SetFloatValue((float)aHeight->GetIntegerValue(), eCSSUnit_Pixel); + else if (aHeight->Type() == nsAttrValue::ePercent) + cHeight->SetPercentValue(aHeight->GetPercentValue()); + } + } + + if (Preferences::GetBool("layout.css.intrinsic-aspect-ratio.enabled") && + aMapAspectRatio && aWidth && aHeight) { + Maybe w; + if (aWidth->Type() == nsAttrValue::eInteger) { + w.emplace(aWidth->GetIntegerValue()); + } else if (aWidth->Type() == nsAttrValue::eDoubleValue) { + w.emplace(aWidth->GetDoubleValue()); + } + + Maybe h; + if (aHeight->Type() == nsAttrValue::eInteger) { + h.emplace(aHeight->GetIntegerValue()); + } else if (aHeight->Type() == nsAttrValue::eDoubleValue) { + h.emplace(aHeight->GetDoubleValue()); + } + + if (w && h && *w != 0 && *h != 0) { + nsCSSValue* aspect_ratio = aData->ValueForAspectRatio(); + aspect_ratio->SetFloatValue((float(*w) / float(*h)), eCSSUnit_Number); + } } } diff --git a/dom/html/nsGenericHTMLElement.h b/dom/html/nsGenericHTMLElement.h index c9169df11..8412ea0dc 100644 --- a/dom/html/nsGenericHTMLElement.h +++ b/dom/html/nsGenericHTMLElement.h @@ -702,10 +702,12 @@ public: * * @param aAttributes the list of attributes to map * @param aData the returned rule data [INOUT] + * @param aMapAspectRatio map width and height attributes on aspect-ratio * @see GetAttributeMappingFunction */ static void MapImageSizeAttributesInto(const nsMappedAttributes* aAttributes, - nsRuleData* aData); + nsRuleData* aData, + bool = false); /** * Helper to map the background attribute * into a style struct. 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 @@ + + + + + + diff --git a/layout/generic/crashtests/crashtests.list b/layout/generic/crashtests/crashtests.list index a6737fd7a..d16e0984c 100644 --- a/layout/generic/crashtests/crashtests.list +++ b/layout/generic/crashtests/crashtests.list @@ -644,3 +644,4 @@ load 1304441.html load 1316649.html load 1381134.html load 1381134-2.html +load 1633434.html diff --git a/layout/generic/nsImageFrame.cpp b/layout/generic/nsImageFrame.cpp index 365b7810b..d6f47f2e4 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" @@ -229,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 image(mImage->Unwrap()); mImage = nsLayoutUtils::OrientImage(image, newOrientation); - UpdateIntrinsicSize(mImage); - UpdateIntrinsicRatio(mImage); + UpdateIntrinsicSize(); + UpdateIntrinsicRatio(); + } else if (!aOldStyleContext || + aOldStyleContext->StylePosition()->mAspectRatio != + StylePosition()->mAspectRatio) { + UpdateIntrinsicRatio(); } } @@ -286,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; - - 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); + // 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; } - return mIntrinsicSize != oldIntrinsicSize; + // 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; + } + */ + + 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 ever gets implemented, this will need to check for it. +bool nsImageFrame::ShouldUseMappedAspectRatio() const { + nsCOMPtr currentRequest; + nsCOMPtr 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(mContent); + return image && image->IsAwaitingLoad(); } bool -nsImageFrame::UpdateIntrinsicRatio(imgIContainer* aImage) +nsImageFrame::UpdateIntrinsicSize() { - NS_PRECONDITION(aImage, "null image"); + IntrinsicSize oldIntrinsicSize = mIntrinsicSize; + mIntrinsicSize = ComputeIntrinsicSize(mImage, ShouldUseMappedAspectRatio(), *this); + return mIntrinsicSize != oldIntrinsicSize; +} - if (!aImage) - return false; +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(); +} + +bool +nsImageFrame::UpdateIntrinsicRatio() +{ AspectRatio oldIntrinsicRatio = mIntrinsicRatio; - - // Set intrinsic ratio to match aImage's reported intrinsic ratio. - if (NS_FAILED(aImage->GetIntrinsicRatio(&mIntrinsicRatio))) - mIntrinsicRatio = AspectRatio(); - + mIntrinsicRatio = + ComputeAspectRatio(mImage, ShouldUseMappedAspectRatio(), *this); return mIntrinsicRatio != oldIntrinsicRatio; } @@ -541,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 = AspectRatio(); - 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, @@ -578,8 +647,6 @@ nsImageFrame::OnSizeAvailable(imgIRequest* aRequest, imgIContainer* aImage) mPrevImage = nullptr; } - - return NS_OK; } nsresult @@ -654,45 +721,9 @@ nsImageFrame::NotifyNewCurrentRequest(imgIRequest *aRequest, { nsCOMPtr 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 = AspectRatio(); - } - - 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 @@ -786,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 = AspectRatio(1.0f); - } - } - } + mIntrinsicSize.height.GetCoordValue() == 0)) { + return; } + UpdateIntrinsicSize(); + UpdateIntrinsicRatio(); } /* virtual */ diff --git a/layout/generic/nsImageFrame.h b/layout/generic/nsImageFrame.h index 2414d89df..5e9b67274 100644 --- a/layout/generic/nsImageFrame.h +++ b/layout/generic/nsImageFrame.h @@ -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 @@ -307,6 +305,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. diff --git a/layout/style/nsCSSPropList.h b/layout/style/nsCSSPropList.h index 411f982a4..f62aa3827 100644 --- a/layout/style/nsCSSPropList.h +++ b/layout/style/nsCSSPropList.h @@ -470,6 +470,19 @@ CSS_PROP_DISPLAY( kAppearanceKTable, CSS_PROP_NO_OFFSET, eStyleAnimType_Discrete) +#ifndef CSS_PROP_LIST_EXCLUDE_INTERNAL +CSS_PROP_POSITION( + aspect-ratio, + aspect_ratio, + AspectRatio, + CSS_PROPERTY_INTERNAL | + CSS_PROPERTY_PARSE_INACCESSIBLE, + "", + VARIANT_NUMBER, + nullptr, + offsetof(nsStylePosition, mAspectRatio), + eStyleAnimType_None) +#endif // CSS_PROP_LIST_EXCLUDE_INTERNAL CSS_PROP_DISPLAY( backface-visibility, backface_visibility, diff --git a/layout/style/nsRuleNode.cpp b/layout/style/nsRuleNode.cpp index a0f65c069..1a451a2ef 100644 --- a/layout/style/nsRuleNode.cpp +++ b/layout/style/nsRuleNode.cpp @@ -8544,6 +8544,12 @@ nsRuleNode::ComputePositionData(void* aStartStruct, SETCOORD_UNSET_INITIAL, aContext, mPresContext, conditions); + // aspect-ratio: float, initial + SetFactor(*aRuleData->ValueForAspectRatio(), + pos->mAspectRatio, conditions, + parentPos->mAspectRatio, 0.0f, + SETFCT_UNSET_INITIAL | SETFCT_POSITIVE | SETFCT_NONE); + // box-sizing: enum, inherit, initial SetValue(*aRuleData->ValueForBoxSizing(), pos->mBoxSizing, conditions, diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp index 9270f2960..3b19a4418 100644 --- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -1408,6 +1408,7 @@ nsStylePosition::nsStylePosition(StyleStructContext aContext) , mGridAutoColumnsMax(eStyleUnit_Auto) , mGridAutoRowsMin(eStyleUnit_Auto) , mGridAutoRowsMax(eStyleUnit_Auto) + , mAspectRatio(0.0f) , mGridAutoFlow(NS_STYLE_GRID_AUTO_FLOW_ROW) , mBoxSizing(StyleBoxSizing::Content) , mAlignContent(NS_STYLE_ALIGN_NORMAL) @@ -1466,6 +1467,7 @@ nsStylePosition::nsStylePosition(const nsStylePosition& aSource) , mGridAutoColumnsMax(aSource.mGridAutoColumnsMax) , mGridAutoRowsMin(aSource.mGridAutoRowsMin) , mGridAutoRowsMax(aSource.mGridAutoRowsMax) + , mAspectRatio(aSource.mAspectRatio) , mGridAutoFlow(aSource.mGridAutoFlow) , mBoxSizing(aSource.mBoxSizing) , mAlignContent(aSource.mAlignContent) @@ -1636,6 +1638,11 @@ nsStylePosition::CalcDifference(const nsStylePosition& aNewData, if (isVertical ? heightChanged : widthChanged) { hint |= nsChangeHint_ReflowHintsForISizeChange; } + + if (mAspectRatio != aNewData.mAspectRatio) { + hint |= nsChangeHint_ReflowHintsForISizeChange | + nsChangeHint_ReflowHintsForBSizeChange; + } } else { if (widthChanged || heightChanged) { hint |= nsChangeHint_NeutralChange; diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h index b257c6bb5..4bda817dd 100644 --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -1815,6 +1815,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStylePosition nsStyleCoord mGridAutoColumnsMax; // [reset] coord, percent, enum, calc, flex nsStyleCoord mGridAutoRowsMin; // [reset] coord, percent, enum, calc, flex nsStyleCoord mGridAutoRowsMax; // [reset] coord, percent, enum, calc, flex + float mAspectRatio; // [reset] float uint8_t mGridAutoFlow; // [reset] enumerated. See nsStyleConsts.h mozilla::StyleBoxSizing mBoxSizing; // [reset] see nsStyleConsts.h diff --git a/layout/style/test/ListCSSProperties.cpp b/layout/style/test/ListCSSProperties.cpp index 718032f61..9f727104b 100644 --- a/layout/style/test/ListCSSProperties.cpp +++ b/layout/style/test/ListCSSProperties.cpp @@ -106,6 +106,7 @@ const char *gInaccessibleProperties[] = { "-x-span", "-x-system-font", "-x-text-zoom", + "aspect-ratio", // for now. "-moz-control-character-visibility", "-moz-script-level", // parsed by UA sheets only "-moz-script-size-multiplier", diff --git a/layout/svg/nsSVGOuterSVGFrame.cpp b/layout/svg/nsSVGOuterSVGFrame.cpp index d8cd2cb77..3f68245e2 100644 --- a/layout/svg/nsSVGOuterSVGFrame.cpp +++ b/layout/svg/nsSVGOuterSVGFrame.cpp @@ -234,22 +234,14 @@ nsSVGOuterSVGFrame::GetIntrinsicSize() /* virtual */ AspectRatio nsSVGOuterSVGFrame::GetIntrinsicRatio() -{ - // 2020-07-14 (RealityRipple) Firefox Uses a new IsReplacedAndContainSize(this) - // function call [Line 96-99 on trunk]. +{ + // When 'contain: size' is implemented, make sure to check for it. /* - static inline bool IsReplacedAndContainSize(const nsSVGOuterSVGFrame* aFrame) { - return aFrame->GetContent->GetParent() && - aFrame->StyleDisplay()->IsContainSize(); - } - */ - // but since contain: size doesn't exist in Pale Moon yet... -/* - if (IsReplacedAndContainSize(this)) { + if (this->GetContent->GetParent() && this->StyleDisplay()->IsContainSize()) { return AspectRatio(); } */ - + // We only have an intrinsic size/ratio if our width and height attributes // are both specified and set to non-percentage values, or we have a viewBox // rect: http://www.w3.org/TR/SVGMobile12/coords.html#IntrinsicSizing diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 813f609e4..5515f9916 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -4824,6 +4824,12 @@ pref("media.ondevicechange.fakeDeviceChangeEvent.enabled", false); // those platforms we don't handle touch events anyway so it's conceptually // a no-op. pref("layout.css.touch_action.enabled", true); + +// WHATWG computed intrinsic aspect ratio for an img element +// https://html.spec.whatwg.org/multipage/rendering.html#attributes-for-embedded-content-and-images +// Are the width and height attributes on image-like elements mapped to the +// internal-for-now aspect-ratio property? +pref("layout.css.intrinsic-aspect-ratio.enabled", true); // Enables some assertions in nsStyleContext that are too expensive // for general use, but might be useful to enable for specific tests. diff --git a/testing/web-platform/tests/css-backgrounds/background-size-cover-003-ref.html b/testing/web-platform/tests/css-backgrounds/background-size-cover-003-ref.html new file mode 100644 index 000000000..bd965cfec --- /dev/null +++ b/testing/web-platform/tests/css-backgrounds/background-size-cover-003-ref.html @@ -0,0 +1,21 @@ + +CSS Test Reference + +
+
+
diff --git a/testing/web-platform/tests/css-backgrounds/background-size-cover-003.html b/testing/web-platform/tests/css-backgrounds/background-size-cover-003.html new file mode 100644 index 000000000..4d2b6b125 --- /dev/null +++ b/testing/web-platform/tests/css-backgrounds/background-size-cover-003.html @@ -0,0 +1,38 @@ + +CSS Test: background-size: cover with zero-sized background positioning area. + + + + + + + +
+
+
diff --git a/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-aspect-ratio.html b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-aspect-ratio.html new file mode 100644 index 000000000..9ae87be9c --- /dev/null +++ b/testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-aspect-ratio.html @@ -0,0 +1,60 @@ + +Image width and height attributes are used to infer aspect-ratio + + + + + + + + +