Bug 1283827 - Flatten transform-style:preserve-3d when combined with opacity. r=dbaron

This matches the behaviour required by the latest editor's draft spec and matches what blink is planning on shipping soon.
It may not be an entirely web compatible change, but we expect only a small number of websites to be affected. See
the bug for more details.
This commit is contained in:
Matt Woodrow 2016-07-02 14:47:12 +02:00
parent 008be81639
commit b4b0f124bf
8 changed files with 76 additions and 94 deletions

View File

@ -2086,8 +2086,7 @@ void nsDisplayList::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
bool same3DContext =
(itemType == nsDisplayItem::TYPE_TRANSFORM &&
static_cast<nsDisplayTransform*>(item)->IsParticipating3DContext()) ||
((itemType == nsDisplayItem::TYPE_PERSPECTIVE ||
itemType == nsDisplayItem::TYPE_OPACITY) &&
(itemType == nsDisplayItem::TYPE_PERSPECTIVE &&
item->Frame()->Extend3DContext());
if (same3DContext &&
(itemType != nsDisplayItem::TYPE_TRANSFORM ||
@ -4325,7 +4324,6 @@ nsDisplayOpacity::nsDisplayOpacity(nsDisplayListBuilder* aBuilder,
: nsDisplayWrapList(aBuilder, aFrame, aList, aScrollClip)
, mOpacity(aFrame->StyleEffects()->mOpacity)
, mForEventsOnly(aForEventsOnly)
, mParticipatesInPreserve3D(false)
{
MOZ_COUNT_CTOR(nsDisplayOpacity);
}
@ -4363,12 +4361,6 @@ nsDisplayOpacity::BuildLayer(nsDisplayListBuilder* aBuilder,
nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(container, aBuilder,
this, mFrame,
eCSSProperty_opacity);
if (mParticipatesInPreserve3D) {
container->SetContentFlags(container->GetContentFlags() | Layer::CONTENT_EXTEND_3D_CONTEXT);
} else {
container->SetContentFlags(container->GetContentFlags() & ~Layer::CONTENT_EXTEND_3D_CONTEXT);
}
return container.forget();
}

View File

@ -3396,19 +3396,9 @@ public:
bool CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) override;
void SetParticipatesInPreserve3D(bool aParticipatesInPreserve3D)
{
mParticipatesInPreserve3D = aParticipatesInPreserve3D;
}
virtual bool ShouldBuildLayerEvenIfInvisible(nsDisplayListBuilder* aBuilder) override
{
return mParticipatesInPreserve3D;
}
private:
float mOpacity;
bool mForEventsOnly;
bool mParticipatesInPreserve3D;
};
class nsDisplayBlendMode : public nsDisplayWrapList {

View File

@ -1189,6 +1189,10 @@ nsIFrame::Extend3DContext() const
return false;
}
if (HasOpacity()) {
return false;
}
const nsStyleEffects* effects = StyleEffects();
nsRect temp;
return !nsFrame::ShouldApplyOverflowClipping(this, disp) &&
@ -2132,11 +2136,6 @@ ItemParticipatesIn3DContext(nsIFrame* aAncestor, nsDisplayItem* aItem)
nsIFrame* transformFrame;
if (aItem->GetType() == nsDisplayItem::TYPE_TRANSFORM) {
transformFrame = aItem->Frame();
} else if (aItem->GetType() == nsDisplayItem::TYPE_OPACITY) {
transformFrame = aItem->Frame();
if (!transformFrame->IsTransformed()) {
return false;
}
} else if (aItem->GetType() == nsDisplayItem::TYPE_PERSPECTIVE) {
transformFrame = static_cast<nsDisplayPerspective*>(aItem)->TransformFrame();
} else {
@ -2162,26 +2161,6 @@ WrapSeparatorTransform(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
}
}
static void
CreateOpacityItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
nsDisplayList& aList, bool aItemForEventsOnly,
const DisplayItemScrollClip* aScrollClip,
bool aParticipatesInPreserve3D)
{
// Don't clip nsDisplayOpacity items. We clip their descendants instead.
// The clip we would set on an element with opacity would clip
// all descendant content, but some should not be clipped.
DisplayListClipState::AutoSaveRestore opacityClipState(aBuilder);
opacityClipState.Clear();
nsDisplayOpacity* opacity =
new (aBuilder) nsDisplayOpacity(aBuilder, aFrame, &aList,
aScrollClip, aItemForEventsOnly);
if (opacity) {
opacity->SetParticipatesInPreserve3D(aParticipatesInPreserve3D);
aList.AppendToTop(opacity);
}
}
void
nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
const nsRect& aDirtyRect,
@ -2464,6 +2443,19 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
aBuilder->ExitSVGEffectsContents();
resultList.AppendToTop(&hoistedScrollInfoItemsStorage);
}
/* Else, if the list is non-empty and there is CSS group opacity without SVG
* effects, wrap it up in an opacity item.
*/
else if (useOpacity && !resultList.IsEmpty()) {
// Don't clip nsDisplayOpacity items. We clip their descendants instead.
// The clip we would set on an element with opacity would clip
// all descendant content, but some should not be clipped.
DisplayListClipState::AutoSaveRestore opacityClipState(aBuilder);
opacityClipState.Clear();
resultList.AppendNewToTop(
new (aBuilder) nsDisplayOpacity(aBuilder, this, &resultList,
containerItemScrollClip, opacityItemForEventsOnly));
}
/* If we're going to apply a transformation and don't have preserve-3d set, wrap
* everything in an nsDisplayTransform. If there's nothing in the list, don't add
@ -2473,10 +2465,9 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
* a separate nsDisplayTransform instead. When the child is already an nsDisplayTransform,
* we can skip this step, as the computed transform will already include our own.
*
* We also traverse into sublists created by nsDisplayWrapList or nsDisplayOpacity, so that
* we find all the correct children.
* We also traverse into sublists created by nsDisplayWrapList, so that we find all the
* correct children.
*/
bool hasPreserve3DChildren = false;
if (isTransformed && !resultList.IsEmpty() && Extend3DContext()) {
// Install dummy nsDisplayTransform as a leaf containing
// descendants not participating this 3D rendering context.
@ -2490,7 +2481,6 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
WrapSeparatorTransform(aBuilder, this, dirtyRect,
&nonparticipants, &participants, index++);
participants.AppendToTop(item);
hasPreserve3DChildren = true;
} else {
// The frame of the item doesn't participate the current
// context, or has no transform.
@ -2507,17 +2497,6 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
resultList.AppendToTop(&participants);
}
/* We create the opacity outside any transform separators we created,
* so that the opacity will be applied to them as groups. When we have
* opacity and preserve-3d we break the 'group' nature of opacity in order
* to maintain preserve-3d. This matches the behaviour of blink and WebKit,
* see bug 1250718.
*/
if (useOpacity && !resultList.IsEmpty()) {
CreateOpacityItem(aBuilder, this, resultList,
opacityItemForEventsOnly, containerItemScrollClip, hasPreserve3DChildren);
}
/* If we're creating an nsDisplayTransform item that is going to combine its transform
* with its children (preserve-3d or perspective), then we can't have an intermediate
* surface. Mask layers force an intermediate surface, so if we're going to need both

View File

@ -3,22 +3,25 @@
<style>
.first {
transform: translateZ(10px);
background-color: red;
opacity: 0.5;
background-color: blue;
top: 60px;
}
.second {
transform: translateZ(5px);
background-color: yellow;
top: 28px;
background-color: green;
top: 40px;
}
.third {
background-color: green;
top: 48px;
opacity: 0.5;
transform: translateZ(-5px);
background-color: yellow;
top: 20px;
}
.fourth {
background-color: blue;
top: 68px;
transform: translateZ(-10px);
background-color: red;
}
.preserve {
transform-style: preserve-3d;
}
.leaf {
width: 100px;
@ -28,10 +31,14 @@
</style>
</head><body>
<div class="leaf first"></div>
<div class="leaf second"></div>
<div class="leaf third"></div>
<div class="leaf fourth"></div>
<div class="preserve">
<div class="leaf first"></div>
<div style="opacity:0.5">
<div class="leaf second"></div>
<div class="leaf fourth"></div>
</div>
<div class="leaf third"></div>
</div>
</body>
</html>

View File

@ -2,23 +2,26 @@
<html><head>
<style>
.first {
background-color: green;
transform: translateZ(10px);
background-color: blue;
top: 50px;
}
.second {
background-color: blue;
top: 58px;
transform: translateZ(5px);
background-color: green;
}
.leaf {
width: 100px;
height: 100px;
position:absolute;
opacity: 0.5;
}
</style>
</head><body>
<div class="leaf first"></div>
<div class="leaf second"></div>
<div style="opacity:0.5; transform:translateX(0px)">
<div class="leaf first"></div>
<div class="leaf second"></div>
</div>
</body>
</html>

View File

@ -2,18 +2,19 @@
<html><head>
<style>
.first {
background-color: green;
top: 48px;
transform: translateZ(10px);
background-color: blue;
top: 0px;
}
.second {
top: 88px;
background-color: green;
top: 40px;
}
.third {
background-color: blue;
opacity: 0.5;
top: 80px;
}
.group {
opacity: 0.5;
.preserve {
transform-style: preserve-3d;
}
.leaf {
width: 100px;
@ -23,11 +24,13 @@
</style>
</head><body>
<div class="group">
<div class="leaf first"></div>
<canvas class="leaf second" width="100px" height="100px" id="canvas"></canvas>
<div class="preserve">
<div style="opacity:0.5; transform:translateX(0px)">
<div class="leaf first"></div>
<div class="leaf second"></div>
<canvas class="leaf third" width="100px" height="100px" id="canvas"></canvas>
</div>
</div>
<div class="leaf third"></div>
<script>
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

View File

@ -2,23 +2,31 @@
<html><head>
<style>
.first {
transform: translateZ(10px);
background-color: blue;
top: 0px;
}
.second {
background-color: green;
top: 48px;
top: 40px;
}
.preserve {
transform-style: preserve-3d;
}
.leaf {
width: 100px;
height: 100px;
position:absolute;
opacity: 0.5;
}
</style>
</head><body>
<canvas class="leaf second" width="100px" height="100px" id="canvas"></canvas>
<div class="leaf first"></div>
<div class="preserve">
<div style="opacity:0.5">
<div class="leaf first"></div>
<canvas class="leaf second" width="100px" height="100px" id="canvas"></canvas>
</div>
</div>
<script>
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

View File

@ -19,7 +19,7 @@ fuzzy-if(gtkWidget||winWidget,8,376) fuzzy-if(Android,8,441) fuzzy-if(cocoaWidge
== preserve3d-2c.html preserve3d-2-ref.html
== preserve3d-2d.html preserve3d-2-ref.html
== preserve3d-3a.html preserve3d-3-ref.html
skip-if(B2G||Mulet) == preserve3d-4a.html green-rect.html # Initial mulet triage: parity with B2G/B2G Desktop
== preserve3d-4a.html about:blank
fuzzy-if(gtkWidget,4,200) fuzzy-if(Android,4,300) fuzzy-if(winWidget&&!layersGPUAccelerated,2,100) fuzzy-if(skiaContent,16,100) == preserve3d-5a.html preserve3d-5-ref.html
== preserve3d-6a.html preserve3d-6-ref.html
== scale3d-z.html scalez-1-ref.html