diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp
index 9469ac64170a..8d07a8522562 100644
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -1604,7 +1604,7 @@ bool
nsIFrame::Combines3DTransformWithAncestors(const nsStyleDisplay* aStyleDisplay) const
{
MOZ_ASSERT(aStyleDisplay == StyleDisplay());
- nsIFrame* parent = GetInFlowParent();
+ nsIFrame* parent = GetClosestFlattenedTreeAncestorPrimaryFrame();
if (!parent || !parent->Extend3DContext()) {
return false;
}
@@ -2625,9 +2625,9 @@ FrameParticipatesIn3DContext(nsIFrame* aAncestor, nsIFrame* aDescendant) {
MOZ_ASSERT(aAncestor != aDescendant);
MOZ_ASSERT(aAncestor->Extend3DContext());
nsIFrame* frame;
- for (frame = aDescendant->GetInFlowParent();
+ for (frame = aDescendant->GetClosestFlattenedTreeAncestorPrimaryFrame();
frame && aAncestor != frame;
- frame = frame->GetInFlowParent()) {
+ frame = frame->GetClosestFlattenedTreeAncestorPrimaryFrame()) {
if (!frame->Extend3DContext()) {
return false;
}
diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h
index 27d70cd771be..4e9641f042cf 100644
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -867,13 +867,16 @@ public:
/**
* Gets the parent of a frame, using the parent of the placeholder for
* out-of-flow frames.
- *
- * This is effectively the primary frame (or one of the continuations) of the
- * closest flattened tree ancestor that has a frame (flattened tree ancestors
- * may not have frames in presence of display: contents).
*/
inline nsContainerFrame* GetInFlowParent() const;
+ /**
+ * Gets the primary frame of the closest flattened tree ancestor that has a
+ * frame (flattened tree ancestors may not have frames in presence of display:
+ * contents).
+ */
+ inline nsIFrame* GetClosestFlattenedTreeAncestorPrimaryFrame() const;
+
/**
* Return the placeholder for this frame (which must be out-of-flow).
* @note this will only return non-null if |this| is the first-in-flow
diff --git a/layout/generic/nsIFrameInlines.h b/layout/generic/nsIFrameInlines.h
index 5a00479f759b..566e04b20173 100644
--- a/layout/generic/nsIFrameInlines.h
+++ b/layout/generic/nsIFrameInlines.h
@@ -7,6 +7,7 @@
#ifndef nsIFrameInlines_h___
#define nsIFrameInlines_h___
+#include "mozilla/dom/ElementInlines.h"
#include "nsContainerFrame.h"
#include "nsPlaceholderFrame.h"
#include "nsStyleStructInlines.h"
@@ -180,4 +181,42 @@ nsIFrame::GetInFlowParent() const
return GetParent();
}
+// We generally want to follow the style tree for preserve-3d, jumping through
+// display: contents.
+//
+// There are various fun mismatches between the flattened tree and the frame
+// tree which makes this non-trivial to do looking at the frame tree state:
+//
+// - Anon boxes. You'd have to step through them, because you generally want to
+// ignore them.
+//
+// - IB-splits, which produce a frame tree where frames for the block inside
+// the inline are not children of any frame from the inline.
+//
+// - display: contents, which makes DOM ancestors not have frames even when a
+// descendant does.
+//
+// See GetFlattenedTreeParentElementForStyle for the difference between it and
+// plain GetFlattenedTreeParentElement.
+nsIFrame*
+nsIFrame::GetClosestFlattenedTreeAncestorPrimaryFrame() const
+{
+ if (!mContent) {
+ return nullptr;
+ }
+ Element* parent = mContent->GetFlattenedTreeParentElementForStyle();
+ while (parent) {
+ if (nsIFrame* frame = parent->GetPrimaryFrame()) {
+ return frame;
+ }
+ // NOTE(emilio): This should be an assert except we have code in tree which
+ // violates invariants like the