Last Comment Bug 690965 - "ASSERTION: Cannot get transform matrix for a null frame!"
: "ASSERTION: Cannot get transform matrix for a null frame!"
Status: RESOLVED FIXED
: assertion, crash, testcase
Product: Core
Classification: Components
Component: Layout (show other bugs)
: Other Branch
: x86 Mac OS X
: -- critical (vote)
: ---
Assigned To: Nobody; OK to take it and work on it
:
Mentors:
Depends on: 691106
Blocks: randomstyles
  Show dependency treegraph
 
Reported: 2011-09-30 16:39 PDT by Jesse Ruderman
Modified: 2012-02-16 14:57 PST (History)
3 users (show)
See Also:
Crash Signature:
(edit)
QA Whiteboard:
Iteration: ---
Points: ---
Has Regression Range: ---
Has STR: ---


Attachments
testcase (178 bytes, application/xhtml+xml)
2011-09-30 16:39 PDT, Jesse Ruderman
no flags Details
stack trace for assertion and crash (12.89 KB, text/plain)
2011-09-30 16:40 PDT, Jesse Ruderman
no flags Details

Description Jesse Ruderman 2011-09-30 16:39:11 PDT
Created attachment 563888 [details]
testcase

###!!! ASSERTION: Cannot get transform matrix for a null frame!: 'aFrame', file layout/base/nsDisplayList.cpp, line 2448

Followed by a crash (debug-only?)
Comment 1 Jesse Ruderman 2011-09-30 16:40:04 PDT
Created attachment 563889 [details]
stack trace for assertion and crash
Comment 2 Matt Woodrow (:mattwoodrow) (PTO until 27 June) 2011-09-30 21:16:23 PDT
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.
Comment 3 Robert O'Callahan (:roc) (Exited; email my personal email if necessary) 2011-09-30 21:23:38 PDT
What's the display list?
Comment 4 Matt Woodrow (:mattwoodrow) (PTO until 27 June) 2011-10-01 15:44:33 PDT
The current frame is an nsQueryFrame (with preserve-3d set).

The first child is an nsDisplayWrapList, around another nsQueryFrame.
Comment 5 Matt Woodrow (:mattwoodrow) (PTO until 27 June) 2011-10-01 15:52:20 PDT
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
Comment 6 Matt Woodrow (:mattwoodrow) (PTO until 27 June) 2011-10-02 00:46:13 PDT
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.
Comment 7 Matt Woodrow (:mattwoodrow) (PTO until 27 June) 2011-10-02 00:53:32 PDT
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.
Comment 8 Robert O'Callahan (:roc) (Exited; email my personal email if necessary) 2011-10-02 02:11:29 PDT
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.
Comment 9 Matt Woodrow (:mattwoodrow) (PTO until 27 June) 2011-10-02 16:47:33 PDT
Should we instead be forcing frames with preserve-3d set to be a containing block?

We often get patterns like:

<div style="-moz-perspective:1000px;">
  <div style="-moz-transform-style:preserve-3d;">
    <div style="-moz-transform: <transform>;">
      //Content
    </div>
  </div>
</div>

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?"

http://dev.w3.org/csswg/css3-3d-transforms/#transform-style-property
Comment 10 Robert O'Callahan (:roc) (Exited; email my personal email if necessary) 2011-10-02 16:55:26 PDT
(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
> child.

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.
Comment 11 Matt Woodrow (:mattwoodrow) (PTO until 27 June) 2011-10-02 19:37:43 PDT
(In reply to Robert O'Callahan (:roc) (Mozilla Corporation) from comment #10)
> 
> But nothing is actually transformed except for the innermost <div>, right?

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.
Comment 12 Matt Woodrow (:mattwoodrow) (PTO until 27 June) 2011-10-02 21:06:07 PDT
This should be fixed by bug 691106, which I just landed on inbound.
Comment 13 Jesse Ruderman 2012-02-16 14:57:29 PST
WFM

Note You need to log in before you can comment on or make changes to this bug.