summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--dom/html/HTMLImageElement.cpp2
-rw-r--r--dom/html/HTMLImageElement.h5
-rw-r--r--dom/html/nsGenericHTMLElement.cpp60
-rw-r--r--dom/html/nsGenericHTMLElement.h4
-rw-r--r--layout/generic/crashtests/1633434.html15
-rw-r--r--layout/generic/crashtests/crashtests.list1
-rw-r--r--layout/generic/nsImageFrame.cpp266
-rw-r--r--layout/generic/nsImageFrame.h20
-rw-r--r--layout/style/nsCSSPropList.h11
-rw-r--r--layout/style/nsRuleNode.cpp6
-rw-r--r--layout/style/nsStyleStruct.cpp7
-rw-r--r--layout/style/nsStyleStruct.h1
-rw-r--r--layout/style/test/ListCSSProperties.cpp1
-rw-r--r--modules/libpref/init/all.js6
-rw-r--r--testing/web-platform/tests/css-backgrounds/background-size-cover-003-ref.html21
-rw-r--r--testing/web-platform/tests/css-backgrounds/background-size-cover-003.html38
-rw-r--r--testing/web-platform/tests/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-aspect-ratio.html60
17 files changed, 381 insertions, 143 deletions
diff --git a/dom/html/HTMLImageElement.cpp b/dom/html/HTMLImageElement.cpp
index fab1cdef4..08f2404ce 100644
--- a/dom/html/HTMLImageElement.cpp
+++ b/dom/html/HTMLImageElement.cpp
@@ -324,7 +324,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 62323e801..1e63cd79c 100644
--- a/dom/html/HTMLImageElement.h
+++ b/dom/html/HTMLImageElement.h
@@ -206,6 +206,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 ef077cfb2..922ba1d29 100644
--- a/dom/html/nsGenericHTMLElement.cpp
+++ b/dom/html/nsGenericHTMLElement.cpp
@@ -1449,29 +1449,59 @@ 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());
+ }
+ }
+
+ // 2020-07-15 (RealityRipple) Much of this is a guess based on a few sources.
+ // Please go over this with a fine-tooth comb before production.
+ if (Preferences::GetBool("layout.css.width-and-height-map-to-aspect-ratio.enabled") &&
+ aMapAspectRatio && aWidth && aHeight) {
+ Maybe<double> w;
+ if (aWidth->Type() == nsAttrValue::eInteger) {
+ w.emplace(aWidth->GetIntegerValue());
+ } else if (aWidth->Type() == nsAttrValue::eDoubleValue) {
+ w.emplace(aWidth->GetDoubleValue());
+ }
+
+ Maybe<double> 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 23fabc4e8..6d7dc0cef 100644
--- a/dom/html/nsGenericHTMLElement.h
+++ b/dom/html/nsGenericHTMLElement.h
@@ -712,10 +712,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 @@
+<!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/nsImageFrame.cpp b/layout/generic/nsImageFrame.cpp
index 68076ce1e..029f82e45 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<imgIContainer> 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,114 @@ nsImageFrame::Init(nsIContent* aContent,
p->AdjustPriority(-1);
}
-bool
-nsImageFrame::UpdateIntrinsicSize(imgIContainer* aImage)
+// 2020-07-14 (RealityRipple) Firefox is doing this completely differently
+// because of loading="lazy" support and the StyleDisplay()->IsContainSize()
+// property. Double-check all of this for problems.
+
+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;
+}
- AspectRatio 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 = AspectRatio();
+bool
+nsImageFrame::UpdateIntrinsicRatio()
+{
+ AspectRatio oldIntrinsicRatio = mIntrinsicRatio;
+ mIntrinsicRatio =
+ ComputeAspectRatio(mImage, ShouldUseMappedAspectRatio(), *this);
return mIntrinsicRatio != oldIntrinsicRatio;
}
@@ -541,30 +606,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;
+ }
+
+ // We're going to need to repaint now either way.
+ InvalidateFrame();
- if (intrinsicSizeChanged && (mState & IMAGE_GOTINITIALREFLOW)) {
+ 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 +651,6 @@ nsImageFrame::OnSizeAvailable(imgIRequest* aRequest, imgIContainer* aImage)
mPrevImage = nullptr;
}
-
- return NS_OK;
}
nsresult
@@ -654,45 +725,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 = 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 +821,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
@@ -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.
*/
diff --git a/layout/style/nsCSSPropList.h b/layout/style/nsCSSPropList.h
index 411f982a4..44bd44cef 100644
--- a/layout/style/nsCSSPropList.h
+++ b/layout/style/nsCSSPropList.h
@@ -470,6 +470,17 @@ CSS_PROP_DISPLAY(
kAppearanceKTable,
CSS_PROP_NO_OFFSET,
eStyleAnimType_Discrete)
+CSS_PROP_POSITION(
+ aspect-ratio,
+ aspect_ratio,
+ AspectRatio,
+ CSS_PROPERTY_INTERNAL |
+ CSS_PROPERTY_PARSE_INACCESSIBLE,
+ "",
+ VARIANT_NUMBER,
+ nullptr,
+ offsetof(nsStylePosition, mAspectRatio),
+ eStyleAnimType_None)
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/modules/libpref/init/all.js b/modules/libpref/init/all.js
index 8cfa63032..21203552b 100644
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -4814,6 +4814,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.width-and-height-map-to-aspect-ratio.enabled", false);
// 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 @@
+<!doctype html>
+<title>CSS Test Reference</title>
+<style>
+body { margin: 0 }
+.first {
+ width: 100px;
+ height: 50px;
+ background: lime;
+}
+.space {
+ height: 50px;
+}
+.second {
+ width: 100px;
+ height: 100px;
+ background: lime;
+}
+</style>
+<div class="first"></div>
+<div class="space"></div>
+<div class="second"></div>
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 @@
+<!DOCTYPE html>
+<title>CSS Test: background-size: cover with zero-sized background positioning area.</title>
+<link rel="help" href="https://drafts.csswg.org/css-backgrounds/#valdef-background-size-cover">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/4049">
+<link rel="help" href=" https://bugzilla.mozilla.org/show_bug.cgi?id=1559094">
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez">
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<link rel="match" href="background-size-cover-003-ref.html">
+<style>
+body { margin: 0 }
+div {
+ background-size: cover;
+ background-repeat: no-repeat;
+ background-position: top left;
+ background-origin: content-box;
+ background-image: url(/images/green-100x50.png);
+}
+#test1 {
+ height: 0;
+ width: 100px;
+ padding-bottom: 100px;
+}
+
+#test2 {
+ height: 100px;
+ width: 0;
+ padding-right: 100px;
+}
+#test3 {
+ height: 0;
+ width: 0;
+ padding-right: 100px;
+ padding-bottom: 100px;
+}
+</style>
+<div id="test1"></div>
+<div id="test2"></div>
+<div id="test3"></div>
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 @@
+<!doctype html>
+<title>Image width and height attributes are used to infer aspect-ratio</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ img {
+ width: 100%;
+ max-width: 100px;
+ height: auto;
+ }
+</style>
+<img src="/images/green.png">
+<img src="/images/green.png" width=100 height=125>
+<img src="" width=100 height=125>
+<img src="error.png" width=100 height=125>
+<img src="error.png">
+<script>
+let t = async_test("Image width and height attributes are used to infer aspect-ratio");
+function assert_ratio(img, expected) {
+ let epsilon = 0.001;
+ assert_approx_equals(parseFloat(getComputedStyle(img).width, 10) / parseFloat(getComputedStyle(img).height, 10), expected, epsilon);
+}
+// Create and append a new image and immediately check the ratio.
+// This is not racy because the spec requires the user agent to queue a task:
+// https://html.spec.whatwg.org/multipage/images.html#updating-the-image-data
+t.step(function() {
+ var img = new Image();
+ img.width = 250;
+ img.height = 100;
+ img.src = "/images/blue.png";
+ document.body.appendChild(img);
+ assert_ratio(img, 2.5);
+
+ img = new Image();
+ img.setAttribute("width", "0.8");
+ img.setAttribute("height", "0.2");
+ img.src = "/images/blue.png";
+ document.body.appendChild(img);
+ // Decimals are apparently ignored?
+ assert_equals(getComputedStyle(img).height, "0px");
+
+ img = new Image();
+ img.setAttribute("width", "50%");
+ img.setAttribute("height", "25%");
+ img.src = "/images/blue.png";
+ document.body.appendChild(img);
+ // Percentages should be ignored.
+ assert_equals(getComputedStyle(img).height, "0px");
+});
+
+onload = t.step_func_done(function() {
+ let images = document.querySelectorAll("img");
+ assert_ratio(images[0], 2.0); // Loaded image's aspect ratio, at least by default, overrides width / height ratio.
+ assert_ratio(images[1], 2.0); // 2.0 is the original aspect ratio of green.png
+ assert_equals(getComputedStyle(images[2]).height, "0px"); // aspect-ratio doesn't override intrinsic size of images that don't have any src.
+ assert_equals(getComputedStyle(images[3]).height, "125px"); // what intrinsic size?
+ assert_equals(getComputedStyle(images[4]).height, "100px"); // what aspect ratio?
+ assert_ratio(images[5], 133/106); // The original aspect ratio of blue.png
+});
+</script>