Bug 1705877 - image-set() should influence intrinsic size of the image. r=dholbert,layout-reviewers

https://drafts.csswg.org/css-images-4/#image-set-notation has:

> [...] it also specifies the image’s natural resolution, overriding any other
> source of data that might supply a natural resolution.

Astounding that there was literally no WPT for this at all. I added three: one
for backgrounds, one for list-style-image, and one for `content`. Cursor is not
handled on this patch because that one requires a fair amount of extra work.

Differential Revision: https://phabricator.services.mozilla.com/D112474
This commit is contained in:
Emilio Cobos Álvarez 2021-04-19 19:55:27 +00:00
Родитель ca0d2c502e
Коммит 8e47e5abc2
18 изменённых файлов: 237 добавлений и 75 удалений

Просмотреть файл

@ -6322,13 +6322,13 @@ ImgDrawResult nsLayoutUtils::DrawSingleUnscaledImage(
/* static */ /* static */
ImgDrawResult nsLayoutUtils::DrawSingleImage( ImgDrawResult nsLayoutUtils::DrawSingleImage(
gfxContext& aContext, nsPresContext* aPresContext, imgIContainer* aImage, gfxContext& aContext, nsPresContext* aPresContext, imgIContainer* aImage,
const SamplingFilter aSamplingFilter, const nsRect& aDest, float aResolution, SamplingFilter aSamplingFilter, const nsRect& aDest,
const nsRect& aDirty, const Maybe<SVGImageContext>& aSVGContext, const nsRect& aDirty, const Maybe<SVGImageContext>& aSVGContext,
uint32_t aImageFlags, const nsPoint* aAnchorPoint, uint32_t aImageFlags, const nsPoint* aAnchorPoint,
const nsRect* aSourceArea) { const nsRect* aSourceArea) {
nscoord appUnitsPerCSSPixel = AppUnitsPerCSSPixel(); nscoord appUnitsPerCSSPixel = AppUnitsPerCSSPixel();
CSSIntSize pixelImageSize( CSSIntSize pixelImageSize(
ComputeSizeForDrawingWithFallback(aImage, aDest.Size())); ComputeSizeForDrawingWithFallback(aImage, aResolution, aDest.Size()));
if (pixelImageSize.width < 1 || pixelImageSize.height < 1) { if (pixelImageSize.width < 1 || pixelImageSize.height < 1) {
NS_ASSERTION(pixelImageSize.width >= 0 && pixelImageSize.height >= 0, NS_ASSERTION(pixelImageSize.width >= 0 && pixelImageSize.height >= 0,
"Image width or height is negative"); "Image width or height is negative");
@ -6369,7 +6369,8 @@ ImgDrawResult nsLayoutUtils::DrawSingleImage(
/* static */ /* static */
void nsLayoutUtils::ComputeSizeForDrawing( void nsLayoutUtils::ComputeSizeForDrawing(
imgIContainer* aImage, /* outparam */ CSSIntSize& aImageSize, imgIContainer* aImage, float aResolution,
/* outparam */ CSSIntSize& aImageSize,
/* outparam */ AspectRatio& aIntrinsicRatio, /* outparam */ AspectRatio& aIntrinsicRatio,
/* outparam */ bool& aGotWidth, /* outparam */ bool& aGotWidth,
/* outparam */ bool& aGotHeight) { /* outparam */ bool& aGotHeight) {
@ -6378,6 +6379,15 @@ void nsLayoutUtils::ComputeSizeForDrawing(
Maybe<AspectRatio> intrinsicRatio = aImage->GetIntrinsicRatio(); Maybe<AspectRatio> intrinsicRatio = aImage->GetIntrinsicRatio();
aIntrinsicRatio = intrinsicRatio.valueOr(AspectRatio()); aIntrinsicRatio = intrinsicRatio.valueOr(AspectRatio());
if (aResolution != 0.0f && aResolution != 1.0f) {
if (aGotWidth) {
aImageSize.width = std::round(float(aImageSize.width) / aResolution);
}
if (aGotHeight) {
aImageSize.height = std::round(float(aImageSize.height) / aResolution);
}
}
if (!(aGotWidth && aGotHeight) && intrinsicRatio.isNothing()) { if (!(aGotWidth && aGotHeight) && intrinsicRatio.isNothing()) {
// We hit an error (say, because the image failed to load or couldn't be // We hit an error (say, because the image failed to load or couldn't be
// decoded) and should return zero size. // decoded) and should return zero size.
@ -6388,11 +6398,12 @@ void nsLayoutUtils::ComputeSizeForDrawing(
/* static */ /* static */
CSSIntSize nsLayoutUtils::ComputeSizeForDrawingWithFallback( CSSIntSize nsLayoutUtils::ComputeSizeForDrawingWithFallback(
imgIContainer* aImage, const nsSize& aFallbackSize) { imgIContainer* aImage, float aResolution, const nsSize& aFallbackSize) {
CSSIntSize imageSize; CSSIntSize imageSize;
AspectRatio imageRatio; AspectRatio imageRatio;
bool gotHeight, gotWidth; bool gotHeight, gotWidth;
ComputeSizeForDrawing(aImage, imageSize, imageRatio, gotWidth, gotHeight); ComputeSizeForDrawing(aImage, aResolution, imageSize, imageRatio, gotWidth,
gotHeight);
// If we didn't get both width and height, try to compute them using the // If we didn't get both width and height, try to compute them using the
// intrinsic ratio of the image. // intrinsic ratio of the image.

Просмотреть файл

@ -1901,6 +1901,11 @@ class nsLayoutUtils {
* appropriate scale and transform for drawing in * appropriate scale and transform for drawing in
* app units. * app units.
* @param aImage The image. * @param aImage The image.
* @param aResolution The resolution specified by the author for the
* image, in dppx. This will affect the intrinsic
* size of the image (so e.g., if resolution is 2,
* and the image is 100x100, the intrinsic size of
* the image will be 50x50).
* @param aDest The area that the image should fill. * @param aDest The area that the image should fill.
* @param aDirty Pixels outside this area may be skipped. * @param aDirty Pixels outside this area may be skipped.
* @param aSVGContext Optionally provides an SVGImageContext. * @param aSVGContext Optionally provides an SVGImageContext.
@ -1922,7 +1927,7 @@ class nsLayoutUtils {
*/ */
static ImgDrawResult DrawSingleImage( static ImgDrawResult DrawSingleImage(
gfxContext& aContext, nsPresContext* aPresContext, imgIContainer* aImage, gfxContext& aContext, nsPresContext* aPresContext, imgIContainer* aImage,
const SamplingFilter aSamplingFilter, const nsRect& aDest, float aResolution, SamplingFilter aSamplingFilter, const nsRect& aDest,
const nsRect& aDirty, const mozilla::Maybe<SVGImageContext>& aSVGContext, const nsRect& aDirty, const mozilla::Maybe<SVGImageContext>& aSVGContext,
uint32_t aImageFlags, const nsPoint* aAnchorPoint = nullptr, uint32_t aImageFlags, const nsPoint* aAnchorPoint = nullptr,
const nsRect* aSourceArea = nullptr); const nsRect* aSourceArea = nullptr);
@ -1943,7 +1948,7 @@ class nsLayoutUtils {
* difference is that this one is simpler and is suited to places where we * difference is that this one is simpler and is suited to places where we
* have less information about the frame tree. * have less information about the frame tree.
*/ */
static void ComputeSizeForDrawing(imgIContainer* aImage, static void ComputeSizeForDrawing(imgIContainer* aImage, float aResolution,
CSSIntSize& aImageSize, CSSIntSize& aImageSize,
AspectRatio& aIntrinsicRatio, AspectRatio& aIntrinsicRatio,
bool& aGotWidth, bool& aGotHeight); bool& aGotWidth, bool& aGotHeight);
@ -1957,7 +1962,7 @@ class nsLayoutUtils {
* dimensions, the corresponding dimension of aFallbackSize is used instead. * dimensions, the corresponding dimension of aFallbackSize is used instead.
*/ */
static CSSIntSize ComputeSizeForDrawingWithFallback( static CSSIntSize ComputeSizeForDrawingWithFallback(
imgIContainer* aImage, const nsSize& aFallbackSize); imgIContainer* aImage, float aResolution, const nsSize& aFallbackSize);
/** /**
* Given the image container, frame, and dest rect, determine the best fitting * Given the image container, frame, and dest rect, determine the best fitting

Просмотреть файл

@ -781,12 +781,9 @@ void nsBulletFrame::GetDesiredSize(nsPresContext* aCX,
RemoveStateBits(BULLET_FRAME_IMAGE_LOADING); RemoveStateBits(BULLET_FRAME_IMAGE_LOADING);
if (RefPtr<imgIContainer> image = GetImage()) { if (Maybe<CSSIntSize> intrinsicSize =
int32_t w = 0; StyleList()->mListStyleImage.GetIntrinsicSize()) {
int32_t h = 0; LogicalSize size(GetWritingMode(), CSSPixel::ToAppUnits(*intrinsicSize));
image->GetWidth(&w);
image->GetHeight(&h);
LogicalSize size(GetWritingMode(), CSSPixel::ToAppUnits(CSSIntSize(w, h)));
// auto size the image // auto size the image
finalSize.ISize(wm) = size.ISize(wm); finalSize.ISize(wm) = size.ISize(wm);
finalSize.BSize(wm) = size.BSize(wm); finalSize.BSize(wm) = size.BSize(wm);

Просмотреть файл

@ -398,11 +398,13 @@ void nsImageFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
} }
MOZ_RELEASE_ASSERT(contentIndex < styleContent->ContentCount()); MOZ_RELEASE_ASSERT(contentIndex < styleContent->ContentCount());
MOZ_RELEASE_ASSERT(styleContent->ContentAt(contentIndex).IsImage()); MOZ_RELEASE_ASSERT(styleContent->ContentAt(contentIndex).IsImage());
auto& imageUrl = styleContent->ContentAt(contentIndex).AsImage(); const StyleImage& image = styleContent->ContentAt(contentIndex).AsImage();
MOZ_ASSERT(imageUrl.IsImageRequestType(), MOZ_ASSERT(image.IsImageRequestType(),
"Content image should only parse url() type"); "Content image should only parse url() type");
auto [finalImage, resolution] = image.FinalImageAndResolution();
Document* doc = PresContext()->Document(); Document* doc = PresContext()->Document();
if (imgRequestProxy* proxy = imageUrl.GetImageRequest()) { if (imgRequestProxy* proxy = finalImage->GetImageRequest()) {
mContentURLRequestResolution = resolution;
proxy->Clone(mListener, doc, getter_AddRefs(mContentURLRequest)); proxy->Clone(mListener, doc, getter_AddRefs(mContentURLRequest));
SetupForContentURLRequest(); SetupForContentURLRequest();
} }
@ -454,6 +456,20 @@ void nsImageFrame::SetupForContentURLRequest() {
} }
} }
static void ScaleIntrinsicSizeForDensity(IntrinsicSize& aSize,
double aDensity) {
if (aDensity == 1.0) {
return;
}
if (aSize.width) {
aSize.width = Some(NSToCoordRound(double(*aSize.width) / aDensity));
}
if (aSize.height) {
aSize.height = Some(NSToCoordRound(double(*aSize.height) / aDensity));
}
}
static void ScaleIntrinsicSizeForDensity(nsIContent& aContent, static void ScaleIntrinsicSizeForDensity(nsIContent& aContent,
IntrinsicSize& aSize) { IntrinsicSize& aSize) {
auto* image = HTMLImageElement::FromNode(aContent); auto* image = HTMLImageElement::FromNode(aContent);
@ -468,16 +484,7 @@ static void ScaleIntrinsicSizeForDensity(nsIContent& aContent,
double density = selector->GetSelectedImageDensity(); double density = selector->GetSelectedImageDensity();
MOZ_ASSERT(density >= 0.0); MOZ_ASSERT(density >= 0.0);
if (density == 1.0) { ScaleIntrinsicSizeForDensity(aSize, density);
return;
}
if (aSize.width) {
aSize.width = Some(NSToCoordRound(double(*aSize.width) / density));
}
if (aSize.height) {
aSize.height = Some(NSToCoordRound(double(*aSize.height) / density));
}
} }
static IntrinsicSize ComputeIntrinsicSize(imgIContainer* aImage, static IntrinsicSize ComputeIntrinsicSize(imgIContainer* aImage,
@ -496,6 +503,9 @@ static IntrinsicSize ComputeIntrinsicSize(imgIContainer* aImage,
intrinsicSize.height = size.height == -1 ? Nothing() : Some(size.height); intrinsicSize.height = size.height == -1 ? Nothing() : Some(size.height);
if (aKind == nsImageFrame::Kind::ImageElement) { if (aKind == nsImageFrame::Kind::ImageElement) {
ScaleIntrinsicSizeForDensity(*aFrame.GetContent(), intrinsicSize); ScaleIntrinsicSizeForDensity(*aFrame.GetContent(), intrinsicSize);
} else {
ScaleIntrinsicSizeForDensity(intrinsicSize,
aFrame.GetContentURLRequestResolution());
} }
return intrinsicSize; return intrinsicSize;
} }
@ -1476,7 +1486,7 @@ ImgDrawResult nsImageFrame::DisplayAltFeedback(gfxContext& aRenderingContext,
nsRect dest(flushRight ? inner.XMost() - size : inner.x, inner.y, size, nsRect dest(flushRight ? inner.XMost() - size : inner.x, inner.y, size,
size); size);
result = nsLayoutUtils::DrawSingleImage( result = nsLayoutUtils::DrawSingleImage(
aRenderingContext, PresContext(), imgCon, aRenderingContext, PresContext(), imgCon, /* aResolution = */ 1.0f,
nsLayoutUtils::GetSamplingFilterForFrame(this), dest, aDirtyRect, nsLayoutUtils::GetSamplingFilterForFrame(this), dest, aDirtyRect,
/* no SVGImageContext */ Nothing(), aFlags); /* no SVGImageContext */ Nothing(), aFlags);
} }
@ -2080,8 +2090,10 @@ ImgDrawResult nsImageFrame::PaintImage(gfxContext& aRenderingContext,
Maybe<SVGImageContext> svgContext; Maybe<SVGImageContext> svgContext;
SVGImageContext::MaybeStoreContextPaint(svgContext, this, aImage); SVGImageContext::MaybeStoreContextPaint(svgContext, this, aImage);
// We've already accounted for resolution via mIntrinsicSize, which influences
// the dest rect, so we don't need to worry about it here..
ImgDrawResult result = nsLayoutUtils::DrawSingleImage( ImgDrawResult result = nsLayoutUtils::DrawSingleImage(
aRenderingContext, PresContext(), aImage, aRenderingContext, PresContext(), aImage, /* aResolution = */ 1.0f,
nsLayoutUtils::GetSamplingFilterForFrame(this), dest, aDirtyRect, nsLayoutUtils::GetSamplingFilterForFrame(this), dest, aDirtyRect,
svgContext, flags, &anchorPoint); svgContext, flags, &anchorPoint);

Просмотреть файл

@ -103,6 +103,12 @@ class nsImageFrame : public nsAtomicContainerFrame, public nsIReflowCallback {
void SetupForContentURLRequest(); void SetupForContentURLRequest();
bool ShouldShowBrokenImageIcon() const; bool ShouldShowBrokenImageIcon() const;
// Get the resolution, in dppx, for the image that mContentURLRequest
// represents.
float GetContentURLRequestResolution() const {
return mContentURLRequestResolution;
}
#ifdef ACCESSIBILITY #ifdef ACCESSIBILITY
mozilla::a11y::AccType AccessibleType() override; mozilla::a11y::AccType AccessibleType() override;
#endif #endif
@ -353,6 +359,7 @@ class nsImageFrame : public nsAtomicContainerFrame, public nsIReflowCallback {
// An image request created for content: url(..). // An image request created for content: url(..).
RefPtr<imgRequestProxy> mContentURLRequest; RefPtr<imgRequestProxy> mContentURLRequest;
float mContentURLRequestResolution = 1.0f;
nsCOMPtr<imgIContainer> mImage; nsCOMPtr<imgIContainer> mImage;
nsCOMPtr<imgIContainer> mPrevImage; nsCOMPtr<imgIContainer> mPrevImage;

Просмотреть файл

@ -50,8 +50,6 @@ nsSize CSSSizeOrRatio::ComputeConcreteSize() const {
nsImageRenderer::nsImageRenderer(nsIFrame* aForFrame, const StyleImage* aImage, nsImageRenderer::nsImageRenderer(nsIFrame* aForFrame, const StyleImage* aImage,
uint32_t aFlags) uint32_t aFlags)
: mForFrame(aForFrame), : mForFrame(aForFrame),
mImage(&aImage->FinalImage()),
mType(mImage->tag),
mImageContainer(nullptr), mImageContainer(nullptr),
mGradientData(nullptr), mGradientData(nullptr),
mPaintServerFrame(nullptr), mPaintServerFrame(nullptr),
@ -59,7 +57,12 @@ nsImageRenderer::nsImageRenderer(nsIFrame* aForFrame, const StyleImage* aImage,
mSize(0, 0), mSize(0, 0),
mFlags(aFlags), mFlags(aFlags),
mExtendMode(ExtendMode::CLAMP), mExtendMode(ExtendMode::CLAMP),
mMaskOp(StyleMaskMode::MatchSource) {} mMaskOp(StyleMaskMode::MatchSource) {
auto pair = aImage->FinalImageAndResolution();
mImage = pair.first;
mType = mImage->tag;
mImageResolution = pair.second;
}
bool nsImageRenderer::PrepareImage() { bool nsImageRenderer::PrepareImage() {
if (mImage->IsNone()) { if (mImage->IsNone()) {
@ -198,14 +201,14 @@ CSSSizeOrRatio nsImageRenderer::ComputeIntrinsicSize() {
case StyleImage::Tag::Url: { case StyleImage::Tag::Url: {
bool haveWidth, haveHeight; bool haveWidth, haveHeight;
CSSIntSize imageIntSize; CSSIntSize imageIntSize;
nsLayoutUtils::ComputeSizeForDrawing( nsLayoutUtils::ComputeSizeForDrawing(mImageContainer, mImageResolution,
mImageContainer, imageIntSize, result.mRatio, haveWidth, haveHeight); imageIntSize, result.mRatio,
haveWidth, haveHeight);
if (haveWidth) { if (haveWidth) {
result.SetWidth(nsPresContext::CSSPixelsToAppUnits(imageIntSize.width)); result.SetWidth(CSSPixel::ToAppUnits(imageIntSize.width));
} }
if (haveHeight) { if (haveHeight) {
result.SetHeight( result.SetHeight(CSSPixel::ToAppUnits(imageIntSize.height));
nsPresContext::CSSPixelsToAppUnits(imageIntSize.height));
} }
// If we know the aspect ratio and one of the dimensions, // If we know the aspect ratio and one of the dimensions,
@ -937,8 +940,8 @@ ImgDrawResult nsImageRenderer::DrawBorderImageComponent(
if (!RequiresScaling(aFill, aHFill, aVFill, aUnitSize)) { if (!RequiresScaling(aFill, aHFill, aVFill, aUnitSize)) {
ImgDrawResult result = nsLayoutUtils::DrawSingleImage( ImgDrawResult result = nsLayoutUtils::DrawSingleImage(
aRenderingContext, aPresContext, subImage, samplingFilter, aFill, aRenderingContext, aPresContext, subImage, mImageResolution,
aDirtyRect, samplingFilter, aFill, aDirtyRect,
/* no SVGImageContext */ Nothing(), drawFlags); /* no SVGImageContext */ Nothing(), drawFlags);
if (!mImage->IsComplete()) { if (!mImage->IsComplete()) {
@ -1007,8 +1010,9 @@ ImgDrawResult nsImageRenderer::DrawShapeImage(nsPresContext* aPresContext,
// rendered pixel has an alpha that precisely matches the alpha of the // rendered pixel has an alpha that precisely matches the alpha of the
// closest pixel in the image. // closest pixel in the image.
return nsLayoutUtils::DrawSingleImage( return nsLayoutUtils::DrawSingleImage(
aRenderingContext, aPresContext, mImageContainer, SamplingFilter::POINT, aRenderingContext, aPresContext, mImageContainer, mImageResolution,
dest, dest, Nothing(), drawFlags, nullptr, nullptr); SamplingFilter::POINT, dest, dest, Nothing(), drawFlags, nullptr,
nullptr);
} }
if (mImage->IsGradient()) { if (mImage->IsGradient()) {

Просмотреть файл

@ -299,6 +299,7 @@ class nsImageRenderer {
nsIFrame* mForFrame; nsIFrame* mForFrame;
const mozilla::StyleImage* mImage; const mozilla::StyleImage* mImage;
float mImageResolution;
mozilla::StyleImage::Tag mType; mozilla::StyleImage::Tag mType;
nsCOMPtr<imgIContainer> mImageContainer; nsCOMPtr<imgIContainer> mImageContainer;
const mozilla::StyleGradient* mGradientData; const mozilla::StyleGradient* mGradientData;

Просмотреть файл

@ -15,6 +15,7 @@
#include "mozilla/ServoBindingTypes.h" #include "mozilla/ServoBindingTypes.h"
#include "mozilla/css/DocumentMatchingFunction.h" #include "mozilla/css/DocumentMatchingFunction.h"
#include "mozilla/css/SheetLoadData.h" #include "mozilla/css/SheetLoadData.h"
#include "mozilla/dom/Document.h"
#include "mozilla/EffectCompositor.h" #include "mozilla/EffectCompositor.h"
#include "mozilla/ComputedTimingFunction.h" #include "mozilla/ComputedTimingFunction.h"
#include "mozilla/PreferenceSheet.h" #include "mozilla/PreferenceSheet.h"

Просмотреть файл

@ -942,14 +942,25 @@ inline bool RestyleHint::DefinitelyRecascadesAllSubtree() const {
} }
template <> template <>
inline const StyleImage& StyleImage::FinalImage() const { inline std::pair<const StyleImage*, float> StyleImage::FinalImageAndResolution()
const {
if (!IsImageSet()) { if (!IsImageSet()) {
return *this; return {this, 1.0f};
} }
auto& set = AsImageSet(); auto& set = AsImageSet();
return set->items.AsSpan()[set->selected_index].image.FinalImage(); auto& selectedItem = set->items.AsSpan()[set->selected_index];
return {selectedItem.image.FinalImageAndResolution().first,
selectedItem.resolution._0};
} }
template <>
inline const StyleImage& StyleImage::FinalImage() const {
return *FinalImageAndResolution().first;
}
template <>
Maybe<CSSIntSize> StyleImage::GetIntrinsicSize() const;
template <> template <>
inline bool StyleImage::IsImageRequestType() const { inline bool StyleImage::IsImageRequestType() const {
auto& finalImage = FinalImage(); auto& finalImage = FinalImage();

Просмотреть файл

@ -1629,6 +1629,31 @@ void StyleImage::ResolveImage(Document& aDoc, const StyleImage* aOld) {
const_cast<StyleComputedImageUrl*>(url)->ResolveImage(aDoc, old); const_cast<StyleComputedImageUrl*>(url)->ResolveImage(aDoc, old);
} }
template <>
Maybe<CSSIntSize> StyleImage::GetIntrinsicSize() const {
auto [finalImage, resolution] = FinalImageAndResolution();
imgRequestProxy* request = finalImage->GetImageRequest();
if (!request) {
return Nothing();
}
RefPtr<imgIContainer> image;
request->GetImage(getter_AddRefs(image));
if (!image) {
return Nothing();
}
// FIXME(emilio): Seems like this should be smarter about unspecified width /
// height, aspect ratio, etc, but this preserves the current behavior of our
// only caller for now...
int32_t w = 0, h = 0;
image->GetWidth(&w);
image->GetHeight(&h);
if (resolution != 0.0f && resolution != 1.0f) {
w = std::round(float(w) / resolution);
h = std::round(float(h) / resolution);
}
return Some(CSSIntSize{w, h});
}
// -------------------- // --------------------
// nsStyleImageLayers // nsStyleImageLayers
// //
@ -1882,8 +1907,11 @@ static bool SizeDependsOnPositioningAreaSize(const StyleBackgroundSize& aSize,
CSSIntSize imageSize; CSSIntSize imageSize;
AspectRatio imageRatio; AspectRatio imageRatio;
bool hasWidth, hasHeight; bool hasWidth, hasHeight;
nsLayoutUtils::ComputeSizeForDrawing(imgContainer, imageSize, imageRatio, // We could bother getting the right resolution here but it doesn't matter
hasWidth, hasHeight); // since we ignore `imageSize`.
nsLayoutUtils::ComputeSizeForDrawing(imgContainer,
/* aResolution = */ 1.0f, imageSize,
imageRatio, hasWidth, hasHeight);
// If the image has a fixed width and height, rendering never depends on // If the image has a fixed width and height, rendering never depends on
// the frame size. // the frame size.

Просмотреть файл

@ -405,7 +405,7 @@ void SVGImageFrame::PaintSVG(gfxContext& aContext, const gfxMatrix& aTransform,
// That method needs our image to have a fixed native width & height, // That method needs our image to have a fixed native width & height,
// and that's not always true for TYPE_VECTOR images. // and that's not always true for TYPE_VECTOR images.
aImgParams.result &= nsLayoutUtils::DrawSingleImage( aImgParams.result &= nsLayoutUtils::DrawSingleImage(
aContext, PresContext(), mImageContainer, aContext, PresContext(), mImageContainer, /* aResolution = */ 1.0f,
nsLayoutUtils::GetSamplingFilterForFrame(this), destRect, nsLayoutUtils::GetSamplingFilterForFrame(this), destRect,
aDirtyRect ? dirtyRect : destRect, context, flags); aDirtyRect ? dirtyRect : destRect, context, flags);
} else { // mImageContainer->GetType() == TYPE_RASTER } else { // mImageContainer->GetType() == TYPE_RASTER

Просмотреть файл

@ -18,6 +18,7 @@
#include "nsStyleConsts.h" #include "nsStyleConsts.h"
#include "nsStyleUtil.h" #include "nsStyleUtil.h"
#include "nsCOMPtr.h" #include "nsCOMPtr.h"
#include "nsLayoutUtils.h"
#include "nsPresContext.h" #include "nsPresContext.h"
#include "nsBoxLayoutState.h" #include "nsBoxLayoutState.h"
@ -239,6 +240,7 @@ void nsImageBoxFrame::UpdateImage() {
mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::src, src); mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::src, src);
mUseSrcAttr = !src.IsEmpty(); mUseSrcAttr = !src.IsEmpty();
if (mUseSrcAttr) { if (mUseSrcAttr) {
mImageResolution = 1.0f;
nsContentPolicyType contentPolicyType; nsContentPolicyType contentPolicyType;
nsCOMPtr<nsIPrincipal> triggeringPrincipal; nsCOMPtr<nsIPrincipal> triggeringPrincipal;
uint64_t requestContextID = 0; uint64_t requestContextID = 0;
@ -267,10 +269,14 @@ void nsImageBoxFrame::UpdateImage() {
doc->ImageTracker()->Add(mImageRequest); doc->ImageTracker()->Add(mImageRequest);
} }
} }
} else if (auto* styleRequest = GetRequestFromStyle()) { } else if (auto* styleImage = GetImageFromStyle()) {
auto [finalImage, resolution] = styleImage->FinalImageAndResolution();
mImageResolution = resolution;
if (auto* styleRequest = finalImage->GetImageRequest()) {
styleRequest->SyncClone(mListener, mContent->GetComposedDoc(), styleRequest->SyncClone(mListener, mContent->GetComposedDoc(),
getter_AddRefs(mImageRequest)); getter_AddRefs(mImageRequest));
} }
}
if (!mImageRequest) { if (!mImageRequest) {
// We have no image, so size to 0 // We have no image, so size to 0
@ -388,7 +394,7 @@ ImgDrawResult nsImageBoxFrame::PaintImage(gfxContext& aRenderingContext,
Maybe<SVGImageContext> svgContext; Maybe<SVGImageContext> svgContext;
SVGImageContext::MaybeStoreContextPaint(svgContext, this, imgCon); SVGImageContext::MaybeStoreContextPaint(svgContext, this, imgCon);
return nsLayoutUtils::DrawSingleImage( return nsLayoutUtils::DrawSingleImage(
aRenderingContext, PresContext(), imgCon, aRenderingContext, PresContext(), imgCon, mImageResolution,
nsLayoutUtils::GetSamplingFilterForFrame(this), dest, dirty, svgContext, nsLayoutUtils::GetSamplingFilterForFrame(this), dest, dirty, svgContext,
aFlags, anchorPoint.ptrOr(nullptr), hasSubRect ? &mSubRect : nullptr); aFlags, anchorPoint.ptrOr(nullptr), hasSubRect ? &mSubRect : nullptr);
} }
@ -603,8 +609,9 @@ bool nsImageBoxFrame::CanOptimizeToImageLayer() {
return true; return true;
} }
imgRequestProxy* nsImageBoxFrame::GetRequestFromStyle() { const mozilla::StyleImage* nsImageBoxFrame::GetImageFromStyle(
const nsStyleDisplay* disp = StyleDisplay(); const ComputedStyle& aStyle) {
const nsStyleDisplay* disp = aStyle.StyleDisplay();
if (disp->HasAppearance()) { if (disp->HasAppearance()) {
nsPresContext* pc = PresContext(); nsPresContext* pc = PresContext();
if (pc->Theme()->ThemeSupportsWidget(pc, this, if (pc->Theme()->ThemeSupportsWidget(pc, this,
@ -612,7 +619,11 @@ imgRequestProxy* nsImageBoxFrame::GetRequestFromStyle() {
return nullptr; return nullptr;
} }
} }
return StyleList()->mListStyleImage.GetImageRequest(); auto& image = aStyle.StyleList()->mListStyleImage;
if (!image.IsImageRequestType()) {
return nullptr;
}
return &image;
} }
/* virtual */ /* virtual */
@ -623,26 +634,18 @@ void nsImageBoxFrame::DidSetComputedStyle(ComputedStyle* aOldStyle) {
const nsStyleList* myList = StyleList(); const nsStyleList* myList = StyleList();
mSubRect = myList->GetImageRegion(); // before |mSuppressStyleCheck| test! mSubRect = myList->GetImageRegion(); // before |mSuppressStyleCheck| test!
if (mUseSrcAttr || mSuppressStyleCheck) if (mUseSrcAttr || mSuppressStyleCheck) {
return; // No more work required, since the image isn't specified by style. return; // No more work required, since the image isn't specified by style.
}
// If the image to use changes, we have a new image. auto* oldImage = aOldStyle ? GetImageFromStyle(*aOldStyle) : nullptr;
nsCOMPtr<nsIURI> oldURI, newURI; auto* newImage = GetImageFromStyle();
if (mImageRequest) { if (newImage == oldImage ||
mImageRequest->GetURI(getter_AddRefs(oldURI)); (newImage && oldImage && *oldImage == *newImage)) {
}
if (auto* newImage = GetRequestFromStyle()) {
newImage->GetURI(getter_AddRefs(newURI));
}
bool equal;
if (newURI == oldURI || // handles null==null
(newURI && oldURI && NS_SUCCEEDED(newURI->Equals(oldURI, &equal)) &&
equal)) {
return; return;
} }
UpdateImage(); UpdateImage();
} // DidSetComputedStyle }
void nsImageBoxFrame::GetImageSize() { void nsImageBoxFrame::GetImageSize() {
if (mIntrinsicSize.width > 0 && mIntrinsicSize.height > 0) { if (mIntrinsicSize.width > 0 && mIntrinsicSize.height > 0) {
@ -796,12 +799,16 @@ void nsImageBoxFrame::OnSizeAvailable(imgIRequest* aRequest,
aImage->SetAnimationMode(PresContext()->ImageAnimationMode()); aImage->SetAnimationMode(PresContext()->ImageAnimationMode());
nscoord w, h; int32_t w = 0, h = 0;
aImage->GetWidth(&w); aImage->GetWidth(&w);
aImage->GetHeight(&h); aImage->GetHeight(&h);
mIntrinsicSize.SizeTo(nsPresContext::CSSPixelsToAppUnits(w), if (mImageResolution != 0.0f && mImageResolution != 1.0f) {
nsPresContext::CSSPixelsToAppUnits(h)); w = std::round(w / mImageResolution);
h = std::round(h / mImageResolution);
}
mIntrinsicSize.SizeTo(CSSPixel::ToAppUnits(w), CSSPixel::ToAppUnits(h));
if (!HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) { if (!HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
PresShell()->FrameNeedsReflow(this, IntrinsicDirty::StyleChange, PresShell()->FrameNeedsReflow(this, IntrinsicDirty::StyleChange,

Просмотреть файл

@ -73,11 +73,15 @@ class nsImageBoxFrame final : public nsLeafBoxFrame {
#endif #endif
/** /**
* Gets the image request to be loaded from the current style. * Gets the image to be loaded from the current style. May be null if themed,
* or if not an url image.
* *
* May be null if themed. * TODO(emilio): Maybe support list-style-image: linear-gradient() etc?
*/ */
imgRequestProxy* GetRequestFromStyle(); const mozilla::StyleImage* GetImageFromStyle(const ComputedStyle&);
const mozilla::StyleImage* GetImageFromStyle() {
return GetImageFromStyle(*Style());
}
/** /**
* Update mUseSrcAttr from appropriate content attributes or from * Update mUseSrcAttr from appropriate content attributes or from
@ -137,6 +141,7 @@ class nsImageBoxFrame final : public nsLeafBoxFrame {
nsSize mImageSize; nsSize mImageSize;
RefPtr<imgRequestProxy> mImageRequest; RefPtr<imgRequestProxy> mImageRequest;
float mImageResolution = 1.0f;
nsCOMPtr<imgINotificationObserver> mListener; nsCOMPtr<imgINotificationObserver> mListener;
int32_t mLoadFlags; int32_t mLoadFlags;

Просмотреть файл

@ -790,6 +790,17 @@ renaming_overrides_prefixing = true
"GenericImage" = """ "GenericImage" = """
public: public:
// Returns the final image we've selected taken from the image-set, along with
// its resolution, or `this` and `1.0` if this image is not an image-set.
//
// The resolution is in dppx, and should be used to impact the intrinsic size
// of the image.
std::pair<const StyleGenericImage*, float> FinalImageAndResolution() const;
// Returns the intrinsic size of the image, if there's one, accounting for
// resolution as needed.
Maybe<CSSIntSize> GetIntrinsicSize() const;
// If this is an image-set(), the final image we've selected, otherwise it // If this is an image-set(), the final image we've selected, otherwise it
// returns *this. // returns *this.
const StyleGenericImage& FinalImage() const; const StyleGenericImage& FinalImage() const;

Просмотреть файл

@ -0,0 +1,6 @@
<!doctype html>
<title>CSS Test Reference</title>
<style>
body { margin: 0 }
</style>
<img srcset="/images/green.png 0.5x">

Просмотреть файл

@ -0,0 +1,16 @@
<!doctype html>
<title>Image set resolution affects intrinsic size of the image</title>
<link rel="match" href="image-set-resolution-001-ref.html">
<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez">
<link rel="author" href="https://mozilla.org" title="Mozilla">
<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1705877">
<link rel="help" href="https://drafts.csswg.org/css-images-4/#image-set-notation">
<style>
body { margin: 0 }
div {
/* green.png is 100x50, should be 200x100 instead */
content: -webkit-image-set(url('/images/green.png') 0.5x);
content: image-set(url('/images/green.png') 0.5x);
}
</style>
<div></div>

Просмотреть файл

@ -0,0 +1,20 @@
<!doctype html>
<title>Image set resolution affects intrinsic size of the image</title>
<link rel="match" href="image-set-resolution-001-ref.html">
<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez">
<link rel="author" href="https://mozilla.org" title="Mozilla">
<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1705877">
<link rel="help" href="https://drafts.csswg.org/css-images-4/#image-set-notation">
<style>
body { margin: 0 }
div {
/* green.png is 100x50, should be 200x100 instead */
background-image: -webkit-image-set(url('/images/green.png') 0.5x);
background-image: image-set(url('/images/green.png') 0.5x);
background-repeat: no-repeat;
background-origin: 0 0;
width: 100vw;
height: 100vh;
}
</style>
<div></div>

Просмотреть файл

@ -0,0 +1,20 @@
<!doctype html>
<title>Image set resolution affects intrinsic size of the image</title>
<link rel="match" href="image-set-resolution-001-ref.html">
<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez">
<link rel="author" href="https://mozilla.org" title="Mozilla">
<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1705877">
<link rel="help" href="https://drafts.csswg.org/css-images-4/#image-set-notation">
<style>
body { margin: 0 }
ul, li { margin: 0; padding: 0 }
li {
list-style-position: inside;
/* green.png is 100x50, should be 200x100 instead */
list-style-image: -webkit-image-set(url('/images/green.png') 0.5x);
list-style-image: image-set(url('/images/green.png') 0.5x);
}
</style>
<ul>
<li></li>
</ul>