Bug 1274962 - Part 6: Make preserve-3d frames only contribute to the overflow area of the preserve-3d root frame. r=dbaron

This commit is contained in:
Matt Woodrow 2016-06-03 14:26:26 +12:00
parent 421203143a
commit 7fb4a7ee36
2 changed files with 66 additions and 109 deletions

View File

@ -2249,7 +2249,7 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
this)) {
dirtyRect = overflow;
} else {
if (overflow.IsEmpty()) {
if (overflow.IsEmpty() && !Extend3DContext()) {
return;
}
@ -8118,30 +8118,49 @@ nsIFrame::FinishAndStoreOverflow(nsOverflowAreas& aOverflowAreas,
bool hasTransform = IsTransformed();
nsSize oldSize = aOldSize ? *aOldSize : mRect.Size();
bool sizeChanged = (oldSize != aNewSize);
if (ChildrenHavePerspective() && sizeChanged) {
nsRect newBounds(nsPoint(0, 0), aNewSize);
RecomputePerspectiveChildrenOverflow(this, &newBounds);
}
if (hasTransform) {
Properties().Set(nsIFrame::PreTransformOverflowAreasProperty(),
new nsOverflowAreas(aOverflowAreas));
/* Since our size might not actually have been computed yet, we need to make sure that we use the
* correct dimensions by overriding the stored bounding rectangle with the value the caller has
* ensured us we'll use.
*/
nsRect newBounds(nsPoint(0, 0), aNewSize);
// Transform affects both overflow areas.
NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
nsRect& o = aOverflowAreas.Overflow(otype);
o = nsDisplayTransform::TransformRect(o, this, &newBounds);
}
if (Extend3DContext()) {
ComputePreserve3DChildrenOverflow(aOverflowAreas, newBounds);
} else if (sizeChanged && ChildrenHavePerspective()) {
RecomputePerspectiveChildrenOverflow(this, &newBounds);
if (Combines3DTransformWithAncestors()) {
/* If we're a preserve-3d leaf frame, then our pre-transform overflow should be correct. Our
* post-transform overflow is empty though, because we only contribute to the overflow area
* of the preserve-3d root frame.
* If we're an intermediate frame then the pre-transform overflow should contain all our
* non-preserve-3d children, which is what we want. Again we have no post-transform overflow.
*/
aOverflowAreas.SetAllTo(nsRect());
} else {
/* Since our size might not actually have been computed yet, we need to make sure that we use the
* correct dimensions by overriding the stored bounding rectangle with the value the caller has
* ensured us we'll use.
*/
SetSize(aNewSize);
NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
nsRect& o = aOverflowAreas.Overflow(otype);
o = nsDisplayTransform::TransformRect(o, this);
}
/* If we're the root of the 3d context, then we want to include the overflow areas of all
* the participants. This won't have happened yet as the code above set their overflow
* area to empty. Manually collect these overflow areas now.
*/
if (Extend3DContext()) {
ComputePreserve3DChildrenOverflow(aOverflowAreas);
}
/* Revert the size change in case some caller is depending on this. */
SetSize(oldSize);
}
} else {
Properties().Delete(nsIFrame::PreTransformOverflowAreasProperty());
if (ChildrenHavePerspective() && sizeChanged) {
nsRect newBounds(nsPoint(0, 0), aNewSize);
RecomputePerspectiveChildrenOverflow(this, &newBounds);
}
}
bool anyOverflowChanged;
@ -8199,79 +8218,13 @@ nsIFrame::RecomputePerspectiveChildrenOverflow(const nsIFrame* aStartFrame, cons
SetSize(oldSize);
}
/* The overflow rects for leaf nodes in a preserve-3d hierarchy depends on
* the mRect value for their parents (since we use their transform, and transform
* depends on this for transform-origin etc). These weren't necessarily correct
* when we reflowed initially, so walk over all preserve-3d children and repeat the
* overflow calculation.
*/
static void
RecomputePreserve3DChildrenOverflow(nsIFrame* aFrame, const nsRect* aBounds)
{
// Children may check our size when getting our transform, make sure it's valid.
nsSize oldSize = aFrame->GetSize();
if (aBounds) {
aFrame->SetSize(aBounds->Size());
}
nsIFrame::ChildListIterator lists(aFrame);
for (; !lists.IsDone(); lists.Next()) {
nsFrameList::Enumerator childFrames(lists.CurrentList());
for (; !childFrames.AtEnd(); childFrames.Next()) {
nsIFrame* child = childFrames.get();
if (!child->FrameMaintainsOverflow()) {
continue; // frame does not maintain overflow rects
}
if (child->Extend3DContext()) {
RecomputePreserve3DChildrenOverflow(child, nullptr);
} else if (child->Combines3DTransformWithAncestors()) {
nsOverflowAreas* overflow =
static_cast<nsOverflowAreas*>(child->Properties().Get(nsIFrame::InitialOverflowProperty()));
nsRect bounds(nsPoint(0, 0), child->GetSize());
if (overflow) {
nsOverflowAreas overflowCopy = *overflow;
child->FinishAndStoreOverflow(overflowCopy, bounds.Size());
} else {
nsOverflowAreas boundsOverflow;
boundsOverflow.SetAllTo(bounds);
child->FinishAndStoreOverflow(boundsOverflow, bounds.Size());
}
}
}
}
// Restore our old size just in case something depends on this elesewhere.
aFrame->SetSize(oldSize);
// Only repeat computing our overflow in recursive calls since the initial caller is still
// in the middle of doing this and we don't want an infinite loop.
if (!aBounds) {
nsOverflowAreas* overflow =
static_cast<nsOverflowAreas*>(aFrame->Properties().Get(nsIFrame::InitialOverflowProperty()));
nsRect bounds(nsPoint(0, 0), aFrame->GetSize());
if (overflow) {
nsOverflowAreas overflowCopy = *overflow;
overflowCopy.UnionAllWith(bounds);
aFrame->FinishAndStoreOverflow(overflowCopy, bounds.Size());
} else {
nsOverflowAreas boundsOverflow;
boundsOverflow.SetAllTo(bounds);
aFrame->FinishAndStoreOverflow(boundsOverflow, bounds.Size());
}
}
}
void
nsIFrame::ComputePreserve3DChildrenOverflow(nsOverflowAreas& aOverflowAreas, const nsRect& aBounds)
nsIFrame::ComputePreserve3DChildrenOverflow(nsOverflowAreas& aOverflowAreas)
{
// When we are preserving 3d we need to iterate over all children separately.
// If the child also preserves 3d then their overflow will already been in our
// coordinate space, otherwise we need to transform.
// If we're the top frame in a preserve 3d chain then we need to recalculate the overflow
// areas of all our children since they will have used our size/offset which was invalid at
// the time.
if (!Combines3DTransformWithAncestors()) {
RecomputePreserve3DChildrenOverflow(this, &aBounds);
}
// Find all descendants that participate in the 3d context, and include their overflow.
// These descendants have an empty overflow, so won't have been included in the normal
// overflow calculation. Any children that don't participate have normal overflow,
// so will have been included already.
nsRect childVisual;
nsRect childScrollable;
@ -8280,27 +8233,28 @@ nsIFrame::ComputePreserve3DChildrenOverflow(nsOverflowAreas& aOverflowAreas, con
nsFrameList::Enumerator childFrames(lists.CurrentList());
for (; !childFrames.AtEnd(); childFrames.Next()) {
nsIFrame* child = childFrames.get();
nsPoint offset = child->GetPosition();
nsRect visual = child->GetVisualOverflowRect();
nsRect scrollable = child->GetScrollableOverflowRect();
visual.MoveBy(offset);
scrollable.MoveBy(offset);
// If this child participates in the 3d context, then take the pre-transform
// region (which contains all descendants that aren't participating in the 3d context)
// and transform it into the 3d context root coordinate space.
if (child->Combines3DTransformWithAncestors()) {
childVisual = childVisual.Union(visual);
childScrollable = childScrollable.Union(scrollable);
} else {
childVisual =
childVisual.Union(nsDisplayTransform::TransformRect(visual,
this, &aBounds));
childScrollable =
childScrollable.Union(nsDisplayTransform::TransformRect(scrollable,
this, &aBounds));
nsOverflowAreas childOverflow = child->GetOverflowAreasRelativeToSelf();
NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
nsRect& o = childOverflow.Overflow(otype);
o = nsDisplayTransform::TransformRect(o, child);
}
aOverflowAreas.UnionWith(childOverflow);
// If this child also extends the 3d context, then recurse into it
// looking for more participants.
if (child->Extend3DContext()) {
child->ComputePreserve3DChildrenOverflow(aOverflowAreas);
}
}
}
}
aOverflowAreas.Overflow(eVisualOverflow) = aOverflowAreas.Overflow(eVisualOverflow).Union(childVisual);
aOverflowAreas.Overflow(eScrollableOverflow) = aOverflowAreas.Overflow(eScrollableOverflow).Union(childScrollable);
}
uint32_t

View File

@ -1431,8 +1431,11 @@ public:
bool ChildrenHavePerspective() const;
// Calculate the overflow size of all child frames, taking preserve-3d into account
void ComputePreserve3DChildrenOverflow(nsOverflowAreas& aOverflowAreas, const nsRect& aBounds);
/**
* Includes the overflow area of all descendants that participate in the current
* 3d context into aOverflowAreas.
*/
void ComputePreserve3DChildrenOverflow(nsOverflowAreas& aOverflowAreas);
void RecomputePerspectiveChildrenOverflow(const nsIFrame* aStartFrame, const nsRect* aBounds);