Bug 735857 - Treat background-attachment:fixed as background-attachment:scroll if it's on a non-root element affected by a transform. r=mstange

MozReview-Commit-ID: 1lnQoD98xv3

--HG--
extra : rebase_source : 0cfd2c2a7926210775b6a1beff69ba32f2a10d6c
This commit is contained in:
Botond Ballo 2016-04-25 17:20:13 -04:00
Родитель 76d7859745
Коммит 992cbd5b93
11 изменённых файлов: 150 добавлений и 33 удалений

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

@ -3052,7 +3052,7 @@ nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext,
nsBackgroundLayerState state = nsBackgroundLayerState state =
PrepareImageLayer(aPresContext, aForFrame, PrepareImageLayer(aPresContext, aForFrame,
aFlags, paintBorderArea, clipState.mBGClipArea, aFlags, paintBorderArea, clipState.mBGClipArea,
layer, co); layer, nullptr, co);
result &= state.mImageRenderer.PrepareResult(); result &= state.mImageRenderer.PrepareResult();
if (!state.mFillArea.IsEmpty()) { if (!state.mFillArea.IsEmpty()) {
// Always using OP_OVER mode while drawing the bottom mask layer. // Always using OP_OVER mode while drawing the bottom mask layer.
@ -3086,7 +3086,8 @@ nsCSSRendering::ComputeImageLayerPositioningArea(nsPresContext* aPresContext,
nsIFrame* aForFrame, nsIFrame* aForFrame,
const nsRect& aBorderArea, const nsRect& aBorderArea,
const nsStyleImageLayers::Layer& aLayer, const nsStyleImageLayers::Layer& aLayer,
nsIFrame** aAttachedToFrame) nsIFrame** aAttachedToFrame,
bool* aOutIsTransformedFixed)
{ {
// Compute background origin area relative to aBorderArea now as we may need // Compute background origin area relative to aBorderArea now as we may need
// it to compute the effective image size for a CSS gradient. // it to compute the effective image size for a CSS gradient.
@ -3163,6 +3164,12 @@ nsCSSRendering::ComputeImageLayerPositioningArea(nsPresContext* aPresContext,
// else this is an embedded shell and its root frame is what we want // else this is an embedded shell and its root frame is what we want
} }
// If the background is affected by a transform, treat is as if it
// wasn't fixed.
if (nsLayoutUtils::IsTransformed(aForFrame, attachedToFrame)) {
attachedToFrame = aForFrame;
*aOutIsTransformedFixed = true;
} else {
// Set the background positioning area to the viewport's area // Set the background positioning area to the viewport's area
// (relative to aForFrame) // (relative to aForFrame)
bgPositioningArea = bgPositioningArea =
@ -3178,6 +3185,7 @@ nsCSSRendering::ComputeImageLayerPositioningArea(nsPresContext* aPresContext,
} }
} }
} }
}
*aAttachedToFrame = attachedToFrame; *aAttachedToFrame = attachedToFrame;
return bgPositioningArea; return bgPositioningArea;
@ -3227,6 +3235,7 @@ nsCSSRendering::PrepareImageLayer(nsPresContext* aPresContext,
const nsRect& aBorderArea, const nsRect& aBorderArea,
const nsRect& aBGClipRect, const nsRect& aBGClipRect,
const nsStyleImageLayers::Layer& aLayer, const nsStyleImageLayers::Layer& aLayer,
bool* aOutIsTransformedFixed,
CompositionOp aCompositonOp) CompositionOp aCompositonOp)
{ {
/* /*
@ -3308,11 +3317,16 @@ nsCSSRendering::PrepareImageLayer(nsPresContext* aPresContext,
// The frame to which the background is attached // The frame to which the background is attached
nsIFrame* attachedToFrame = aForFrame; nsIFrame* attachedToFrame = aForFrame;
// Is the background marked 'fixed', but affected by a transform?
bool transformedFixed = false;
// Compute background origin area relative to aBorderArea now as we may need // Compute background origin area relative to aBorderArea now as we may need
// it to compute the effective image size for a CSS gradient. // it to compute the effective image size for a CSS gradient.
nsRect bgPositioningArea = nsRect bgPositioningArea =
ComputeImageLayerPositioningArea(aPresContext, aForFrame, aBorderArea, ComputeImageLayerPositioningArea(aPresContext, aForFrame, aBorderArea,
aLayer, &attachedToFrame); aLayer, &attachedToFrame, &transformedFixed);
if (aOutIsTransformedFixed) {
*aOutIsTransformedFixed = transformedFixed;
}
// For background-attachment:fixed backgrounds, we'll limit the area // For background-attachment:fixed backgrounds, we'll limit the area
// where the background can be drawn to the viewport. // where the background can be drawn to the viewport.
@ -3323,9 +3337,8 @@ nsCSSRendering::PrepareImageLayer(nsPresContext* aPresContext,
// relative to aBorderArea.TopLeft() (which is where the top-left // relative to aBorderArea.TopLeft() (which is where the top-left
// of aForFrame's border-box will be rendered) // of aForFrame's border-box will be rendered)
nsPoint imageTopLeft; nsPoint imageTopLeft;
if (NS_STYLE_IMAGELAYER_ATTACHMENT_FIXED == aLayer.mAttachment) { if (NS_STYLE_IMAGELAYER_ATTACHMENT_FIXED == aLayer.mAttachment && !transformedFixed) {
if ((aFlags & nsCSSRendering::PAINTBG_TO_WINDOW) && if (aFlags & nsCSSRendering::PAINTBG_TO_WINDOW) {
!IsTransformed(aForFrame, attachedToFrame)) {
// Clip background-attachment:fixed backgrounds to the viewport, if we're // Clip background-attachment:fixed backgrounds to the viewport, if we're
// painting to the screen and not transformed. This avoids triggering // painting to the screen and not transformed. This avoids triggering
// tiling in common cases, without affecting output since drawing is // tiling in common cases, without affecting output since drawing is

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

@ -528,7 +528,8 @@ struct nsCSSRendering {
nsIFrame* aForFrame, nsIFrame* aForFrame,
const nsRect& aBorderArea, const nsRect& aBorderArea,
const nsStyleImageLayers::Layer& aLayer, const nsStyleImageLayers::Layer& aLayer,
nsIFrame** aAttachedToFrame); nsIFrame** aAttachedToFrame,
bool* aOutTransformedFixed);
static nsBackgroundLayerState static nsBackgroundLayerState
PrepareImageLayer(nsPresContext* aPresContext, PrepareImageLayer(nsPresContext* aPresContext,
@ -537,6 +538,7 @@ struct nsCSSRendering {
const nsRect& aBorderArea, const nsRect& aBorderArea,
const nsRect& aBGClipRect, const nsRect& aBGClipRect,
const nsStyleImageLayers::Layer& aLayer, const nsStyleImageLayers::Layer& aLayer,
bool* aOutIsTransformedFixed = nullptr,
CompositionOp aCompositionOp = CompositionOp::OP_OVER); CompositionOp aCompositionOp = CompositionOp::OP_OVER);
struct ImageLayerClipState { struct ImageLayerClipState {

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

@ -2337,19 +2337,22 @@ nsDisplayBackgroundImage::nsDisplayBackgroundImage(nsDisplayListBuilder* aBuilde
{ {
MOZ_COUNT_CTOR(nsDisplayBackgroundImage); MOZ_COUNT_CTOR(nsDisplayBackgroundImage);
mBounds = GetBoundsInternal(aBuilder);
if (ShouldFixToViewport(aBuilder)) {
mAnimatedGeometryRoot = aBuilder->FindAnimatedGeometryRootFor(this);
}
nsPresContext* presContext = mFrame->PresContext(); nsPresContext* presContext = mFrame->PresContext();
uint32_t flags = aBuilder->GetBackgroundPaintFlags(); uint32_t flags = aBuilder->GetBackgroundPaintFlags();
nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize()); nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
const nsStyleImageLayers::Layer &layer = mBackgroundStyle->mImage.mLayers[mLayer]; const nsStyleImageLayers::Layer &layer = mBackgroundStyle->mImage.mLayers[mLayer];
bool isTransformedFixed;
nsBackgroundLayerState state = nsBackgroundLayerState state =
nsCSSRendering::PrepareImageLayer(presContext, mFrame, flags, nsCSSRendering::PrepareImageLayer(presContext, mFrame, flags,
borderArea, borderArea, layer); borderArea, borderArea, layer,
&isTransformedFixed);
mShouldTreatAsFixed = ComputeShouldTreatAsFixed(isTransformedFixed);
mBounds = GetBoundsInternal(aBuilder);
if (ShouldFixToViewport(aBuilder)) {
mAnimatedGeometryRoot = aBuilder->FindAnimatedGeometryRootFor(this);
}
mFillRect = state.mFillArea; mFillRect = state.mFillArea;
mDestRect = state.mDestArea; mDestRect = state.mDestArea;
@ -2581,6 +2584,28 @@ static bool RoundedRectContainsRect(const nsRect& aRoundedRect,
return rgn.Contains(aContainedRect); return rgn.Contains(aContainedRect);
} }
bool
nsDisplayBackgroundImage::ShouldTreatAsFixed() const
{
return mShouldTreatAsFixed;
}
bool
nsDisplayBackgroundImage::ComputeShouldTreatAsFixed(bool isTransformedFixed) const
{
if (!mBackgroundStyle)
return false;
const nsStyleImageLayers::Layer &layer = mBackgroundStyle->mImage.mLayers[mLayer];
if (layer.mAttachment != NS_STYLE_IMAGELAYER_ATTACHMENT_FIXED)
return false;
// background-attachment:fixed is treated as background-attachment:scroll
// if it's affected by a transform.
// See https://www.w3.org/Bugs/Public/show_bug.cgi?id=17521.
return !isTransformedFixed;
}
bool bool
nsDisplayBackgroundImage::IsSingleFixedPositionImage(nsDisplayListBuilder* aBuilder, nsDisplayBackgroundImage::IsSingleFixedPositionImage(nsDisplayListBuilder* aBuilder,
const nsRect& aClipRect, const nsRect& aClipRect,
@ -2592,8 +2617,7 @@ nsDisplayBackgroundImage::IsSingleFixedPositionImage(nsDisplayListBuilder* aBuil
if (mBackgroundStyle->mImage.mLayers.Length() != 1) if (mBackgroundStyle->mImage.mLayers.Length() != 1)
return false; return false;
const nsStyleImageLayers::Layer &layer = mBackgroundStyle->mImage.mLayers[mLayer]; if (!ShouldTreatAsFixed())
if (layer.mAttachment != NS_STYLE_IMAGELAYER_ATTACHMENT_FIXED)
return false; return false;
// We only care about images here, not gradients. // We only care about images here, not gradients.
@ -2609,7 +2633,7 @@ nsDisplayBackgroundImage::IsSingleFixedPositionImage(nsDisplayListBuilder* aBuil
bool bool
nsDisplayBackgroundImage::IsNonEmptyFixedImage() const nsDisplayBackgroundImage::IsNonEmptyFixedImage() const
{ {
return mBackgroundStyle->mImage.mLayers[mLayer].mAttachment == NS_STYLE_IMAGELAYER_ATTACHMENT_FIXED && return ShouldTreatAsFixed() &&
!mBackgroundStyle->mImage.mLayers[mLayer].mImage.IsEmpty(); !mBackgroundStyle->mImage.mLayers[mLayer].mImage.IsEmpty();
} }
@ -2871,11 +2895,13 @@ nsDisplayBackgroundImage::GetPositioningArea()
return nsRect(); return nsRect();
} }
nsIFrame* attachedToFrame; nsIFrame* attachedToFrame;
bool transformedFixed;
return nsCSSRendering::ComputeImageLayerPositioningArea( return nsCSSRendering::ComputeImageLayerPositioningArea(
mFrame->PresContext(), mFrame, mFrame->PresContext(), mFrame,
nsRect(ToReferenceFrame(), mFrame->GetSize()), nsRect(ToReferenceFrame(), mFrame->GetSize()),
mBackgroundStyle->mImage.mLayers[mLayer], mBackgroundStyle->mImage.mLayers[mLayer],
&attachedToFrame) + ToReferenceFrame(); &attachedToFrame,
&transformedFixed) + ToReferenceFrame();
} }
bool bool

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

@ -2721,6 +2721,8 @@ protected:
gfxRect* aDestRect); gfxRect* aDestRect);
bool IsNonEmptyFixedImage() const; bool IsNonEmptyFixedImage() const;
nsRect GetBoundsInternal(nsDisplayListBuilder* aBuilder); nsRect GetBoundsInternal(nsDisplayListBuilder* aBuilder);
bool ShouldTreatAsFixed() const;
bool ComputeShouldTreatAsFixed(bool isTransformedFixed) const;
void PaintInternal(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx, void PaintInternal(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx,
const nsRect& aBounds, nsRect* aClipRect); const nsRect& aBounds, nsRect* aClipRect);
@ -2745,6 +2747,8 @@ protected:
nsRect mBounds; nsRect mBounds;
uint32_t mLayer; uint32_t mLayer;
bool mIsRasterImage; bool mIsRasterImage;
/* Whether the image should be treated as fixed to the viewport. */
bool mShouldTreatAsFixed;
}; };

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

@ -220,7 +220,7 @@ public:
// compositing layer. Since we know their background painting area can't // compositing layer. Since we know their background painting area can't
// change (unless the viewport size itself changes), async scrolling // change (unless the viewport size itself changes), async scrolling
// will work well. // will work well.
return mBackgroundStyle->mImage.mLayers[mLayer].mAttachment == NS_STYLE_IMAGELAYER_ATTACHMENT_FIXED && return ShouldTreatAsFixed() &&
!mBackgroundStyle->mImage.mLayers[mLayer].mImage.IsEmpty(); !mBackgroundStyle->mImage.mLayers[mLayer].mImage.IsEmpty();
} }

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

@ -0,0 +1,35 @@
<html>
<head>
<style>
body {
height: 4000px;
margin: 0;
}
#outer {
margin: 200px;
height: 700px;
width: 300px;
transform: rotate(45deg);
-webkit-transform: rotate(45deg);
overflow: hidden;
}
#inner {
height: 700px;
background-image: radial-gradient(farthest-corner at center, blue, black);
background-size: 300px 300px;
background-position: 200px 200px;
background-color: lime;
background-repeat: no-repeat;
background-attachment: scroll;
}
</style>
</head>
<body>
<div id="outer">
<div id="inner">
</div>
</div>
</body>
</html>

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

@ -0,0 +1,35 @@
<html>
<head>
<style>
body {
height: 4000px;
margin: 0;
}
#outer {
margin: 200px;
height: 700px;
width: 300px;
transform: rotate(45deg);
-webkit-transform: rotate(45deg);
overflow: hidden;
}
#inner {
height: 700px;
background-image: radial-gradient(farthest-corner at center, blue, black);
background-size: 300px 300px;
background-position: 200px 200px;
background-color: lime;
background-repeat: no-repeat;
background-attachment: fixed;
}
</style>
</head>
<body>
<div id="outer">
<div id="inner">
</div>
</div>
</body>
</html>

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

@ -123,6 +123,7 @@ fails == background-size-zoom-repeat.html background-size-zoom-repeat-ref.html
random-if(B2G||Mulet) == fixed-bg-with-transform-outside-viewport-1.html fixed-bg-with-transform-outside-viewport-ref.html # Initial mulet triage: parity with B2G/B2G Desktop random-if(B2G||Mulet) == fixed-bg-with-transform-outside-viewport-1.html fixed-bg-with-transform-outside-viewport-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
fuzzy(2,83) == fixed-bg-border-radius.html fixed-bg-border-radius-ref.html fuzzy(2,83) == fixed-bg-border-radius.html fixed-bg-border-radius-ref.html
== fixed-bg-inside-transform.html fixed-bg-inside-transform-ref.html
HTTP == root-background-1.html root-background-ref.html HTTP == root-background-1.html root-background-ref.html
HTTP != root-background-1.html about:blank HTTP != root-background-1.html about:blank

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

@ -2684,12 +2684,13 @@ nsChangeHint nsStyleBackground::CalcDifference(const nsStyleBackground& aOther)
return hint; return hint;
} }
bool nsStyleBackground::HasFixedBackground() const bool nsStyleBackground::HasFixedBackground(nsIFrame* aFrame) const
{ {
NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, mImage) { NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, mImage) {
const nsStyleImageLayers::Layer &layer = mImage.mLayers[i]; const nsStyleImageLayers::Layer &layer = mImage.mLayers[i];
if (layer.mAttachment == NS_STYLE_IMAGELAYER_ATTACHMENT_FIXED && if (layer.mAttachment == NS_STYLE_IMAGELAYER_ATTACHMENT_FIXED &&
!layer.mImage.IsEmpty()) { !layer.mImage.IsEmpty() &&
!nsLayoutUtils::IsTransformed(aFrame)) {
return true; return true;
} }
} }

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

@ -815,7 +815,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleBackground {
// but we don't want to do that when there's no image. // but we don't want to do that when there's no image.
// Not inline because it uses an nsCOMPtr<imgIRequest> // Not inline because it uses an nsCOMPtr<imgIRequest>
// FIXME: Should be in nsStyleStructInlines.h. // FIXME: Should be in nsStyleStructInlines.h.
bool HasFixedBackground() const; bool HasFixedBackground(nsIFrame* aFrame) const;
const nsStyleImageLayers::Layer& BottomLayer() const { return mImage.BottomLayer(); } const nsStyleImageLayers::Layer& BottomLayer() const { return mImage.BottomLayer(); }

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

@ -1095,7 +1095,7 @@ nsDisplayTableItem::UpdateForFrameBackground(nsIFrame* aFrame)
nsStyleContext *bgSC; nsStyleContext *bgSC;
if (!nsCSSRendering::FindBackground(aFrame, &bgSC)) if (!nsCSSRendering::FindBackground(aFrame, &bgSC))
return; return;
if (!bgSC->StyleBackground()->HasFixedBackground()) if (!bgSC->StyleBackground()->HasFixedBackground(aFrame))
return; return;
mPartHasFixedBackground = true; mPartHasFixedBackground = true;