Created attachment 563888 [details]
###!!! ASSERTION: Cannot get transform matrix for a null frame!: 'aFrame', file layout/base/nsDisplayList.cpp, line 2448
Followed by a crash (debug-only?)
Created attachment 563889 [details]
stack trace for assertion and crash
So it appears that at least one of the child nsDisplayItems that we are trying to wrap in an nsDisplayTransform is not actually a descendent of the current frame.
In BuildDisplayListForStackingContext, the first item in the display list doesn't even return the current frame when looping over GetParent().
This is bad because with preserve-3d, we create an nsDisplayTransform with the child's frame pointer, and walk up through the parents later to find the actual frame with a transform set.
In this case we're never finding a transformed parent and crashing when we hit the root.
What's the display list?
The current frame is an nsQueryFrame (with preserve-3d set).
The first child is an nsDisplayWrapList, around another nsQueryFrame.
Sorry, I take that back. Apparently the looking at the vtable pointer isn't useful.
The current frame is a TableFrame (with preserve-3d set).
The first child is an nsDisplayWrapList, around a BlockFrame.
Walking up the parent chain of this child we have a CanvasFrame, ScrollFrame, ViewportFrame, NULL
Currently WrapPreserve3DList is operating under the assumption that all items in the display list belong to frames that are descendents of the current frame. This doesn't appear to be true in this case.
We could possibly add special handling for TableFrames, but that's only useful if this is the only possible case that breaks my assumption.
A more general alternative would be to store a second frame pointer on nsDisplayTransform that is the frame with the actual transform. We need to keep mFrame as is, because otherwise we end up with multiple nsDisplayTransforms for a single frame.
In this example theres only a preserve-3d parent, and no child transforms to actually pass the transform down onto.
Is it possible to construct a test case where there is a transformed child, but the frame pointer still isn't a descendent of the preserve-3d parent? This would break (though not crash) the preserve-3d logic and stop the child from being able to find the parent transform.
Not sure if this case is even somewhere we'd want to attempt to preserve-3d.
Sorry, I should have looked at the testcase before asking you questions.
In this testcase, the abs-pos DIV's frame will be a child of the nearest abs-pos containing block, which is the nsCanvasFrame (in CSS terms, the initial containing block associated with the viewport). And when painting, we dig through placeholders so indeed painting the table will include display items from the abs-pos frame.
But normally this wouldn't be an issue for transformed content because applying a CSS transform to an element forces it to be an abs-pos containing block! So building display lists for a transformed element, we shouldn't see display items for frames from outside our frame subtree.
In this testcase, nothing is transformed, which is why the abs-pos containing block is the ICB. So in this testcase, we don't really need to run WrapPreserve3DList at all. I think we just need to ensure that WrapPreserve3DList is only called if there's some chain of preserve-3d ancestor frames leading up to a transformed frame with a 3D transform. If there isn't, there's no 3D to preserve.
BTW, aFrame is unused in WrapPreserve3DList.
Should we instead be forcing frames with preserve-3d set to be a containing block?
We often get patterns like:
<div style="-moz-transform: <transform>;">
Usually because an animation/toggle has reset the preserve-3d frame back to an identity transform. We still want to treat this frame as 'transformed' though, so that the perspective being passed down to it is applied to the child.
This is currently posed as an open question on the 3d transforms spec:
"Does transform-style: preserve-3d need to establish a stacking context and containing block like transform does?"
(In reply to Matt Woodrow (:mattwoodrow) from comment #9)
> Usually because an animation/toggle has reset the preserve-3d frame back to
> an identity transform. We still want to treat this frame as 'transformed'
> though, so that the perspective being passed down to it is applied to the
But nothing is actually transformed except for the innermost <div>, right?
I don't think we should be applying WrapPreserve3DList to any frame except for the innermost block frame in that case.
(In reply to Robert O'Callahan (:roc) (Mozilla Corporation) from comment #10)
> But nothing is actually transformed except for the innermost <div>, right?
> I don't think we should be applying WrapPreserve3DList to any frame except
> for the innermost block frame in that case.
This makes sense.
This crash is actually fixed (and I believe correctly) by my patch for bug 691106. Since we are now creating the new nsDisplayTransforms with the current frame pointer, we don't need to be able to walk back up to the transformed frame.
As long as all hierarchies of preserve-3d are descendants of each other, then we should be fine.
This should be fixed by bug 691106, which I just landed on inbound.