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)) { this)) {
dirtyRect = overflow; dirtyRect = overflow;
} else { } else {
if (overflow.IsEmpty()) { if (overflow.IsEmpty() && !Extend3DContext()) {
return; return;
} }
@ -8118,30 +8118,49 @@ nsIFrame::FinishAndStoreOverflow(nsOverflowAreas& aOverflowAreas,
bool hasTransform = IsTransformed(); bool hasTransform = IsTransformed();
nsSize oldSize = aOldSize ? *aOldSize : mRect.Size(); nsSize oldSize = aOldSize ? *aOldSize : mRect.Size();
bool sizeChanged = (oldSize != aNewSize); bool sizeChanged = (oldSize != aNewSize);
if (ChildrenHavePerspective() && sizeChanged) {
nsRect newBounds(nsPoint(0, 0), aNewSize);
RecomputePerspectiveChildrenOverflow(this, &newBounds);
}
if (hasTransform) { if (hasTransform) {
Properties().Set(nsIFrame::PreTransformOverflowAreasProperty(), Properties().Set(nsIFrame::PreTransformOverflowAreasProperty(),
new nsOverflowAreas(aOverflowAreas)); 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 if (Combines3DTransformWithAncestors()) {
* ensured us we'll use. /* 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
nsRect newBounds(nsPoint(0, 0), aNewSize); * of the preserve-3d root frame.
// Transform affects both overflow areas. * If we're an intermediate frame then the pre-transform overflow should contain all our
NS_FOR_FRAME_OVERFLOW_TYPES(otype) { * non-preserve-3d children, which is what we want. Again we have no post-transform overflow.
nsRect& o = aOverflowAreas.Overflow(otype); */
o = nsDisplayTransform::TransformRect(o, this, &newBounds); aOverflowAreas.SetAllTo(nsRect());
} } else {
if (Extend3DContext()) { /* Since our size might not actually have been computed yet, we need to make sure that we use the
ComputePreserve3DChildrenOverflow(aOverflowAreas, newBounds); * correct dimensions by overriding the stored bounding rectangle with the value the caller has
} else if (sizeChanged && ChildrenHavePerspective()) { * ensured us we'll use.
RecomputePerspectiveChildrenOverflow(this, &newBounds); */
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 { } else {
Properties().Delete(nsIFrame::PreTransformOverflowAreasProperty()); Properties().Delete(nsIFrame::PreTransformOverflowAreasProperty());
if (ChildrenHavePerspective() && sizeChanged) {
nsRect newBounds(nsPoint(0, 0), aNewSize);
RecomputePerspectiveChildrenOverflow(this, &newBounds);
}
} }
bool anyOverflowChanged; bool anyOverflowChanged;
@ -8199,79 +8218,13 @@ nsIFrame::RecomputePerspectiveChildrenOverflow(const nsIFrame* aStartFrame, cons
SetSize(oldSize); 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 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. // Find all descendants that participate in the 3d context, and include their overflow.
// If the child also preserves 3d then their overflow will already been in our // These descendants have an empty overflow, so won't have been included in the normal
// coordinate space, otherwise we need to transform. // overflow calculation. Any children that don't participate have normal overflow,
// so will have been included already.
// 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);
}
nsRect childVisual; nsRect childVisual;
nsRect childScrollable; nsRect childScrollable;
@ -8280,27 +8233,28 @@ nsIFrame::ComputePreserve3DChildrenOverflow(nsOverflowAreas& aOverflowAreas, con
nsFrameList::Enumerator childFrames(lists.CurrentList()); nsFrameList::Enumerator childFrames(lists.CurrentList());
for (; !childFrames.AtEnd(); childFrames.Next()) { for (; !childFrames.AtEnd(); childFrames.Next()) {
nsIFrame* child = childFrames.get(); nsIFrame* child = childFrames.get();
nsPoint offset = child->GetPosition();
nsRect visual = child->GetVisualOverflowRect(); // If this child participates in the 3d context, then take the pre-transform
nsRect scrollable = child->GetScrollableOverflowRect(); // region (which contains all descendants that aren't participating in the 3d context)
visual.MoveBy(offset); // and transform it into the 3d context root coordinate space.
scrollable.MoveBy(offset);
if (child->Combines3DTransformWithAncestors()) { if (child->Combines3DTransformWithAncestors()) {
childVisual = childVisual.Union(visual); nsOverflowAreas childOverflow = child->GetOverflowAreasRelativeToSelf();
childScrollable = childScrollable.Union(scrollable);
} else { NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
childVisual = nsRect& o = childOverflow.Overflow(otype);
childVisual.Union(nsDisplayTransform::TransformRect(visual, o = nsDisplayTransform::TransformRect(o, child);
this, &aBounds)); }
childScrollable =
childScrollable.Union(nsDisplayTransform::TransformRect(scrollable, aOverflowAreas.UnionWith(childOverflow);
this, &aBounds));
// 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 uint32_t

View File

@ -1431,8 +1431,11 @@ public:
bool ChildrenHavePerspective() const; 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); void RecomputePerspectiveChildrenOverflow(const nsIFrame* aStartFrame, const nsRect* aBounds);