Use Intrinsic Aspect Ratio for Images.
parent
5306579682
commit
1e9e9ef0cb
|
@ -325,7 +325,7 @@ HTMLImageElement::MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
|
||||||
nsGenericHTMLElement::MapImageAlignAttributeInto(aAttributes, aData);
|
nsGenericHTMLElement::MapImageAlignAttributeInto(aAttributes, aData);
|
||||||
nsGenericHTMLElement::MapImageBorderAttributeInto(aAttributes, aData);
|
nsGenericHTMLElement::MapImageBorderAttributeInto(aAttributes, aData);
|
||||||
nsGenericHTMLElement::MapImageMarginAttributeInto(aAttributes, aData);
|
nsGenericHTMLElement::MapImageMarginAttributeInto(aAttributes, aData);
|
||||||
nsGenericHTMLElement::MapImageSizeAttributesInto(aAttributes, aData);
|
nsGenericHTMLElement::MapImageSizeAttributesInto(aAttributes, aData, true);
|
||||||
nsGenericHTMLElement::MapCommonAttributesInto(aAttributes, aData);
|
nsGenericHTMLElement::MapCommonAttributesInto(aAttributes, aData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -196,6 +196,11 @@ public:
|
||||||
return GetReferrerPolicyAsEnum();
|
return GetReferrerPolicyAsEnum();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsAwaitingLoad() const
|
||||||
|
{
|
||||||
|
return !!mPendingImageLoadTask;
|
||||||
|
}
|
||||||
|
|
||||||
int32_t X();
|
int32_t X();
|
||||||
int32_t Y();
|
int32_t Y();
|
||||||
// Uses XPCOM GetLowsrc.
|
// Uses XPCOM GetLowsrc.
|
||||||
|
|
|
@ -1449,29 +1449,57 @@ nsGenericHTMLElement::MapImageMarginAttributeInto(const nsMappedAttributes* aAtt
|
||||||
|
|
||||||
void
|
void
|
||||||
nsGenericHTMLElement::MapImageSizeAttributesInto(const nsMappedAttributes* aAttributes,
|
nsGenericHTMLElement::MapImageSizeAttributesInto(const nsMappedAttributes* aAttributes,
|
||||||
nsRuleData* aData)
|
nsRuleData* aData,
|
||||||
|
bool aMapAspectRatio)
|
||||||
{
|
{
|
||||||
if (!(aData->mSIDs & NS_STYLE_INHERIT_BIT(Position)))
|
if (!(aData->mSIDs & NS_STYLE_INHERIT_BIT(Position)))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
auto* aWidth = aAttributes->GetAttr(nsGkAtoms::width);
|
||||||
|
auto* aHeight = aAttributes->GetAttr(nsGkAtoms::height);
|
||||||
|
|
||||||
// width: value
|
// width: value
|
||||||
nsCSSValue* width = aData->ValueForWidth();
|
if (aWidth) {
|
||||||
if (width->GetUnit() == eCSSUnit_Null) {
|
nsCSSValue* cWidth = aData->ValueForWidth();
|
||||||
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::width);
|
if (cWidth->GetUnit() == eCSSUnit_Null) {
|
||||||
if (value && value->Type() == nsAttrValue::eInteger)
|
if (aWidth->Type() == nsAttrValue::eInteger)
|
||||||
width->SetFloatValue((float)value->GetIntegerValue(), eCSSUnit_Pixel);
|
cWidth->SetFloatValue((float)aWidth->GetIntegerValue(), eCSSUnit_Pixel);
|
||||||
else if (value && value->Type() == nsAttrValue::ePercent)
|
else if (aWidth->Type() == nsAttrValue::ePercent)
|
||||||
width->SetPercentValue(value->GetPercentValue());
|
cWidth->SetPercentValue(aWidth->GetPercentValue());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// height: value
|
// height: value
|
||||||
nsCSSValue* height = aData->ValueForHeight();
|
if (aHeight) {
|
||||||
if (height->GetUnit() == eCSSUnit_Null) {
|
nsCSSValue* cHeight = aData->ValueForHeight();
|
||||||
const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::height);
|
if (cHeight->GetUnit() == eCSSUnit_Null) {
|
||||||
if (value && value->Type() == nsAttrValue::eInteger)
|
if (aHeight->Type() == nsAttrValue::eInteger)
|
||||||
height->SetFloatValue((float)value->GetIntegerValue(), eCSSUnit_Pixel);
|
cHeight->SetFloatValue((float)aHeight->GetIntegerValue(), eCSSUnit_Pixel);
|
||||||
else if (value && value->Type() == nsAttrValue::ePercent)
|
else if (aHeight->Type() == nsAttrValue::ePercent)
|
||||||
height->SetPercentValue(value->GetPercentValue());
|
cHeight->SetPercentValue(aHeight->GetPercentValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Preferences::GetBool("layout.css.intrinsic-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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -702,10 +702,12 @@ public:
|
||||||
*
|
*
|
||||||
* @param aAttributes the list of attributes to map
|
* @param aAttributes the list of attributes to map
|
||||||
* @param aData the returned rule data [INOUT]
|
* @param aData the returned rule data [INOUT]
|
||||||
|
* @param aMapAspectRatio map width and height attributes on aspect-ratio
|
||||||
* @see GetAttributeMappingFunction
|
* @see GetAttributeMappingFunction
|
||||||
*/
|
*/
|
||||||
static void MapImageSizeAttributesInto(const nsMappedAttributes* aAttributes,
|
static void MapImageSizeAttributesInto(const nsMappedAttributes* aAttributes,
|
||||||
nsRuleData* aData);
|
nsRuleData* aData,
|
||||||
|
bool = false);
|
||||||
/**
|
/**
|
||||||
* Helper to map the background attribute
|
* Helper to map the background attribute
|
||||||
* into a style struct.
|
* into a style struct.
|
||||||
|
|
|
@ -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>
|
|
@ -644,3 +644,4 @@ load 1304441.html
|
||||||
load 1316649.html
|
load 1316649.html
|
||||||
load 1381134.html
|
load 1381134.html
|
||||||
load 1381134-2.html
|
load 1381134-2.html
|
||||||
|
load 1633434.html
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include "mozilla/gfx/2D.h"
|
#include "mozilla/gfx/2D.h"
|
||||||
#include "mozilla/gfx/Helpers.h"
|
#include "mozilla/gfx/Helpers.h"
|
||||||
#include "mozilla/gfx/PathHelpers.h"
|
#include "mozilla/gfx/PathHelpers.h"
|
||||||
|
#include "mozilla/dom/HTMLImageElement.h"
|
||||||
#include "mozilla/MouseEvents.h"
|
#include "mozilla/MouseEvents.h"
|
||||||
#include "mozilla/Unused.h"
|
#include "mozilla/Unused.h"
|
||||||
|
|
||||||
|
@ -229,26 +230,26 @@ nsImageFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
|
||||||
{
|
{
|
||||||
nsAtomicContainerFrame::DidSetStyleContext(aOldStyleContext);
|
nsAtomicContainerFrame::DidSetStyleContext(aOldStyleContext);
|
||||||
|
|
||||||
if (!mImage) {
|
|
||||||
// We'll pick this change up whenever we do get an image.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsStyleImageOrientation newOrientation = StyleVisibility()->mImageOrientation;
|
nsStyleImageOrientation newOrientation = StyleVisibility()->mImageOrientation;
|
||||||
|
|
||||||
// We need to update our orientation either if we had no style context before
|
// 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
|
// because this is the first time it's been set, or if the image-orientation
|
||||||
// property changed from its previous value.
|
// property changed from its previous value.
|
||||||
bool shouldUpdateOrientation =
|
bool shouldUpdateOrientation =
|
||||||
!aOldStyleContext ||
|
mImage &&
|
||||||
aOldStyleContext->StyleVisibility()->mImageOrientation != newOrientation;
|
(!aOldStyleContext ||
|
||||||
|
aOldStyleContext->StyleVisibility()->mImageOrientation != newOrientation);
|
||||||
|
|
||||||
if (shouldUpdateOrientation) {
|
if (shouldUpdateOrientation) {
|
||||||
nsCOMPtr<imgIContainer> image(mImage->Unwrap());
|
nsCOMPtr<imgIContainer> image(mImage->Unwrap());
|
||||||
mImage = nsLayoutUtils::OrientImage(image, newOrientation);
|
mImage = nsLayoutUtils::OrientImage(image, newOrientation);
|
||||||
|
|
||||||
UpdateIntrinsicSize(mImage);
|
UpdateIntrinsicSize();
|
||||||
UpdateIntrinsicRatio(mImage);
|
UpdateIntrinsicRatio();
|
||||||
|
} else if (!aOldStyleContext ||
|
||||||
|
aOldStyleContext->StylePosition()->mAspectRatio !=
|
||||||
|
StylePosition()->mAspectRatio) {
|
||||||
|
UpdateIntrinsicRatio();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -286,50 +287,110 @@ nsImageFrame::Init(nsIContent* aContent,
|
||||||
p->AdjustPriority(-1);
|
p->AdjustPriority(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
static IntrinsicSize
|
||||||
nsImageFrame::UpdateIntrinsicSize(imgIContainer* aImage)
|
ComputeIntrinsicSize(imgIContainer* aImage,
|
||||||
|
bool aUseMappedRatio,
|
||||||
|
const nsImageFrame& aFrame)
|
||||||
{
|
{
|
||||||
NS_PRECONDITION(aImage, "null image");
|
// When 'contain: size' is implemented, make sure to check for it.
|
||||||
if (!aImage)
|
/*
|
||||||
return false;
|
const ComputedStyle& style = *aFrame.Style();
|
||||||
|
if (style.StyleDisplay()->IsContainSize()) {
|
||||||
IntrinsicSize oldIntrinsicSize = mIntrinsicSize;
|
return AspectRatio();
|
||||||
mIntrinsicSize = IntrinsicSize();
|
}
|
||||||
|
*/
|
||||||
// Set intrinsic size to match aImage's reported intrinsic width & height.
|
nsSize size;
|
||||||
nsSize intrinsicSize;
|
IntrinsicSize intrinsicSize;
|
||||||
if (NS_SUCCEEDED(aImage->GetIntrinsicSize(&intrinsicSize))) {
|
if (aImage && NS_SUCCEEDED(aImage->GetIntrinsicSize(&size))) {
|
||||||
// If the image has no intrinsic width, intrinsicSize.width will be -1, and
|
if (size.width != -1)
|
||||||
// we can leave mIntrinsicSize.width at its default value of eStyleUnit_None.
|
intrinsicSize.width.SetCoordValue(size.width);
|
||||||
// Otherwise we use intrinsicSize.width. Height works the same way.
|
if (size.height != -1)
|
||||||
if (intrinsicSize.width != -1)
|
intrinsicSize.height.SetCoordValue(size.height);
|
||||||
mIntrinsicSize.width.SetCoordValue(intrinsicSize.width);
|
return intrinsicSize;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 <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
|
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)
|
static AspectRatio
|
||||||
return false;
|
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;
|
AspectRatio oldIntrinsicRatio = mIntrinsicRatio;
|
||||||
|
mIntrinsicRatio =
|
||||||
// Set intrinsic ratio to match aImage's reported intrinsic ratio.
|
ComputeAspectRatio(mImage, ShouldUseMappedAspectRatio(), *this);
|
||||||
if (NS_FAILED(aImage->GetIntrinsicRatio(&mIntrinsicRatio)))
|
|
||||||
mIntrinsicRatio = AspectRatio();
|
|
||||||
|
|
||||||
return mIntrinsicRatio != oldIntrinsicRatio;
|
return mIntrinsicRatio != oldIntrinsicRatio;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -541,30 +602,38 @@ nsImageFrame::OnSizeAvailable(imgIRequest* aRequest, imgIContainer* aImage)
|
||||||
return NS_OK;
|
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)) {
|
if (SizeIsAvailable(aRequest)) {
|
||||||
// This is valid and for the current request, so update our stored image
|
// This is valid and for the current request, so update our stored image
|
||||||
// container, orienting according to our style.
|
// container, orienting according to our style.
|
||||||
mImage = nsLayoutUtils::OrientImage(aImage, StyleVisibility()->mImageOrientation);
|
mImage = nsLayoutUtils::OrientImage(aImage,
|
||||||
|
StyleVisibility()->mImageOrientation);
|
||||||
intrinsicSizeChanged = UpdateIntrinsicSize(mImage);
|
MOZ_ASSERT(mImage);
|
||||||
intrinsicSizeChanged = UpdateIntrinsicRatio(mImage) || intrinsicSizeChanged;
|
|
||||||
} else {
|
} else {
|
||||||
// We no longer have a valid image, so release our stored image container.
|
// We no longer have a valid image, so release our stored image container.
|
||||||
mImage = mPrevImage = nullptr;
|
mImage = mPrevImage = nullptr;
|
||||||
|
}
|
||||||
// Have to size to 0,0 so that GetDesiredSize recalculates the size.
|
// NOTE(emilio): Intentionally using `|` instead of `||` to avoid
|
||||||
mIntrinsicSize.width.SetCoordValue(0);
|
// short-circuiting.
|
||||||
mIntrinsicSize.height.SetCoordValue(0);
|
bool intrinsicSizeChanged =
|
||||||
mIntrinsicRatio = AspectRatio();
|
UpdateIntrinsicSize() | UpdateIntrinsicRatio();
|
||||||
intrinsicSizeChanged = true;
|
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
|
// 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)) {
|
if (!(mState & IMAGE_SIZECONSTRAINED)) {
|
||||||
nsIPresShell *presShell = presContext->GetPresShell();
|
nsIPresShell *presShell = PresContext()->GetPresShell();
|
||||||
NS_ASSERTION(presShell, "No PresShell.");
|
NS_ASSERTION(presShell, "No PresShell.");
|
||||||
if (presShell) {
|
if (presShell) {
|
||||||
presShell->FrameNeedsReflow(this, nsIPresShell::eStyleChange,
|
presShell->FrameNeedsReflow(this, nsIPresShell::eStyleChange,
|
||||||
|
@ -578,8 +647,6 @@ nsImageFrame::OnSizeAvailable(imgIRequest* aRequest, imgIContainer* aImage)
|
||||||
|
|
||||||
mPrevImage = nullptr;
|
mPrevImage = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return NS_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
|
@ -654,45 +721,9 @@ nsImageFrame::NotifyNewCurrentRequest(imgIRequest *aRequest,
|
||||||
{
|
{
|
||||||
nsCOMPtr<imgIContainer> image;
|
nsCOMPtr<imgIContainer> image;
|
||||||
aRequest->GetImage(getter_AddRefs(image));
|
aRequest->GetImage(getter_AddRefs(image));
|
||||||
NS_ASSERTION(image || NS_FAILED(aStatus), "Successful load with no container?");
|
NS_ASSERTION(image || NS_FAILED(aStatus),
|
||||||
|
"Successful load with no container?");
|
||||||
// May have to switch sizes here!
|
UpdateImage(aRequest, image);
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -786,32 +817,27 @@ bool nsImageFrame::ShouldShowBrokenImageIcon() const
|
||||||
void
|
void
|
||||||
nsImageFrame::EnsureIntrinsicSizeAndRatio()
|
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
|
// If mIntrinsicSize.width and height are 0, then we need to update from the
|
||||||
// image container.
|
// image container.
|
||||||
if (mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord &&
|
if (!(mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord &&
|
||||||
mIntrinsicSize.width.GetCoordValue() == 0 &&
|
mIntrinsicSize.width.GetCoordValue() == 0 &&
|
||||||
mIntrinsicSize.height.GetUnit() == eStyleUnit_Coord &&
|
mIntrinsicSize.height.GetUnit() == eStyleUnit_Coord &&
|
||||||
mIntrinsicSize.height.GetCoordValue() == 0) {
|
mIntrinsicSize.height.GetCoordValue() == 0)) {
|
||||||
|
return;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
UpdateIntrinsicSize();
|
||||||
|
UpdateIntrinsicRatio();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* virtual */
|
/* virtual */
|
||||||
|
|
|
@ -273,21 +273,19 @@ private:
|
||||||
void GetDocumentCharacterSet(nsACString& aCharset) const;
|
void GetDocumentCharacterSet(nsACString& aCharset) const;
|
||||||
bool ShouldDisplaySelection();
|
bool ShouldDisplaySelection();
|
||||||
|
|
||||||
|
// Whether the image frame should use the mapped aspect ratio from width=""
|
||||||
|
// and height="".
|
||||||
|
bool ShouldUseMappedAspectRatio() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recalculate mIntrinsicSize from the image.
|
* 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.
|
* 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
|
* This function calculates the transform for converting between
|
||||||
|
@ -307,6 +305,12 @@ private:
|
||||||
*/
|
*/
|
||||||
bool IsPendingLoad(imgIRequest* aRequest) const;
|
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
|
* Function to convert a dirty rect in the source image to a dirty
|
||||||
* rect for the image frame.
|
* rect for the image frame.
|
||||||
|
|
|
@ -470,6 +470,19 @@ CSS_PROP_DISPLAY(
|
||||||
kAppearanceKTable,
|
kAppearanceKTable,
|
||||||
CSS_PROP_NO_OFFSET,
|
CSS_PROP_NO_OFFSET,
|
||||||
eStyleAnimType_Discrete)
|
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(
|
CSS_PROP_DISPLAY(
|
||||||
backface-visibility,
|
backface-visibility,
|
||||||
backface_visibility,
|
backface_visibility,
|
||||||
|
|
|
@ -8544,6 +8544,12 @@ nsRuleNode::ComputePositionData(void* aStartStruct,
|
||||||
SETCOORD_UNSET_INITIAL,
|
SETCOORD_UNSET_INITIAL,
|
||||||
aContext, mPresContext, conditions);
|
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
|
// box-sizing: enum, inherit, initial
|
||||||
SetValue(*aRuleData->ValueForBoxSizing(),
|
SetValue(*aRuleData->ValueForBoxSizing(),
|
||||||
pos->mBoxSizing, conditions,
|
pos->mBoxSizing, conditions,
|
||||||
|
|
|
@ -1408,6 +1408,7 @@ nsStylePosition::nsStylePosition(StyleStructContext aContext)
|
||||||
, mGridAutoColumnsMax(eStyleUnit_Auto)
|
, mGridAutoColumnsMax(eStyleUnit_Auto)
|
||||||
, mGridAutoRowsMin(eStyleUnit_Auto)
|
, mGridAutoRowsMin(eStyleUnit_Auto)
|
||||||
, mGridAutoRowsMax(eStyleUnit_Auto)
|
, mGridAutoRowsMax(eStyleUnit_Auto)
|
||||||
|
, mAspectRatio(0.0f)
|
||||||
, mGridAutoFlow(NS_STYLE_GRID_AUTO_FLOW_ROW)
|
, mGridAutoFlow(NS_STYLE_GRID_AUTO_FLOW_ROW)
|
||||||
, mBoxSizing(StyleBoxSizing::Content)
|
, mBoxSizing(StyleBoxSizing::Content)
|
||||||
, mAlignContent(NS_STYLE_ALIGN_NORMAL)
|
, mAlignContent(NS_STYLE_ALIGN_NORMAL)
|
||||||
|
@ -1466,6 +1467,7 @@ nsStylePosition::nsStylePosition(const nsStylePosition& aSource)
|
||||||
, mGridAutoColumnsMax(aSource.mGridAutoColumnsMax)
|
, mGridAutoColumnsMax(aSource.mGridAutoColumnsMax)
|
||||||
, mGridAutoRowsMin(aSource.mGridAutoRowsMin)
|
, mGridAutoRowsMin(aSource.mGridAutoRowsMin)
|
||||||
, mGridAutoRowsMax(aSource.mGridAutoRowsMax)
|
, mGridAutoRowsMax(aSource.mGridAutoRowsMax)
|
||||||
|
, mAspectRatio(aSource.mAspectRatio)
|
||||||
, mGridAutoFlow(aSource.mGridAutoFlow)
|
, mGridAutoFlow(aSource.mGridAutoFlow)
|
||||||
, mBoxSizing(aSource.mBoxSizing)
|
, mBoxSizing(aSource.mBoxSizing)
|
||||||
, mAlignContent(aSource.mAlignContent)
|
, mAlignContent(aSource.mAlignContent)
|
||||||
|
@ -1636,6 +1638,11 @@ nsStylePosition::CalcDifference(const nsStylePosition& aNewData,
|
||||||
if (isVertical ? heightChanged : widthChanged) {
|
if (isVertical ? heightChanged : widthChanged) {
|
||||||
hint |= nsChangeHint_ReflowHintsForISizeChange;
|
hint |= nsChangeHint_ReflowHintsForISizeChange;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mAspectRatio != aNewData.mAspectRatio) {
|
||||||
|
hint |= nsChangeHint_ReflowHintsForISizeChange |
|
||||||
|
nsChangeHint_ReflowHintsForBSizeChange;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (widthChanged || heightChanged) {
|
if (widthChanged || heightChanged) {
|
||||||
hint |= nsChangeHint_NeutralChange;
|
hint |= nsChangeHint_NeutralChange;
|
||||||
|
|
|
@ -1815,6 +1815,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStylePosition
|
||||||
nsStyleCoord mGridAutoColumnsMax; // [reset] coord, percent, enum, calc, flex
|
nsStyleCoord mGridAutoColumnsMax; // [reset] coord, percent, enum, calc, flex
|
||||||
nsStyleCoord mGridAutoRowsMin; // [reset] coord, percent, enum, calc, flex
|
nsStyleCoord mGridAutoRowsMin; // [reset] coord, percent, enum, calc, flex
|
||||||
nsStyleCoord mGridAutoRowsMax; // [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
|
uint8_t mGridAutoFlow; // [reset] enumerated. See nsStyleConsts.h
|
||||||
mozilla::StyleBoxSizing mBoxSizing; // [reset] see nsStyleConsts.h
|
mozilla::StyleBoxSizing mBoxSizing; // [reset] see nsStyleConsts.h
|
||||||
|
|
||||||
|
|
|
@ -106,6 +106,7 @@ const char *gInaccessibleProperties[] = {
|
||||||
"-x-span",
|
"-x-span",
|
||||||
"-x-system-font",
|
"-x-system-font",
|
||||||
"-x-text-zoom",
|
"-x-text-zoom",
|
||||||
|
"aspect-ratio", // for now.
|
||||||
"-moz-control-character-visibility",
|
"-moz-control-character-visibility",
|
||||||
"-moz-script-level", // parsed by UA sheets only
|
"-moz-script-level", // parsed by UA sheets only
|
||||||
"-moz-script-size-multiplier",
|
"-moz-script-size-multiplier",
|
||||||
|
|
|
@ -235,17 +235,9 @@ nsSVGOuterSVGFrame::GetIntrinsicSize()
|
||||||
/* virtual */ AspectRatio
|
/* virtual */ AspectRatio
|
||||||
nsSVGOuterSVGFrame::GetIntrinsicRatio()
|
nsSVGOuterSVGFrame::GetIntrinsicRatio()
|
||||||
{
|
{
|
||||||
// 2020-07-14 (RealityRipple) Firefox Uses a new IsReplacedAndContainSize(this)
|
// When 'contain: size' is implemented, make sure to check for it.
|
||||||
// function call [Line 96-99 on trunk].
|
|
||||||
/*
|
/*
|
||||||
static inline bool IsReplacedAndContainSize(const nsSVGOuterSVGFrame* aFrame) {
|
if (this->GetContent->GetParent() && this->StyleDisplay()->IsContainSize()) {
|
||||||
return aFrame->GetContent->GetParent() &&
|
|
||||||
aFrame->StyleDisplay()->IsContainSize();
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
// but since contain: size doesn't exist in Pale Moon yet...
|
|
||||||
/*
|
|
||||||
if (IsReplacedAndContainSize(this)) {
|
|
||||||
return AspectRatio();
|
return AspectRatio();
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -4825,6 +4825,12 @@ pref("media.ondevicechange.fakeDeviceChangeEvent.enabled", false);
|
||||||
// a no-op.
|
// a no-op.
|
||||||
pref("layout.css.touch_action.enabled", true);
|
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
|
// Enables some assertions in nsStyleContext that are too expensive
|
||||||
// for general use, but might be useful to enable for specific tests.
|
// for general use, but might be useful to enable for specific tests.
|
||||||
// This only has an effect in DEBUG-builds.
|
// This only has an effect in DEBUG-builds.
|
||||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
Loading…
Reference in New Issue