Bug 1226904 - Fix boundary checking for leaves collecting. r=roc
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -1890,21 +1890,19 @@ void FlushFramesArray(nsTArray<FramesWit
void nsDisplayList::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
nsDisplayItem::HitTestState* aState,
nsTArray<nsIFrame*> *aOutFrames) const {
nsDisplayItem* item;
if (aState->mInPreserves3D) {
// Collect leaves of the current 3D rendering context.
for (item = GetBottom(); item; item = item->GetAbove()) {
- MOZ_ASSERT(item->GetType() == nsDisplayTransform::TYPE_TRANSFORM ||
- item->GetType() == nsDisplayTransform::TYPE_PERSPECTIVE);
- if (item->Frame()->Extend3DContext() &&
- (item->GetType() == nsDisplayTransform::TYPE_PERSPECTIVE ||
- !static_cast<nsDisplayTransform*>(item)->IsTransformSeparator())) {
+ auto itemType = item->GetType();
+ if (itemType != nsDisplayItem::TYPE_TRANSFORM ||
+ !static_cast<nsDisplayTransform*>(item)->IsLeafOf3DContext()) {
item->HitTest(aBuilder, aRect, aState, aOutFrames);
} else {
// One of leaves in the current 3D rendering context.
aState->mItemBuffer.AppendElement(item);
}
}
return;
}
@@ -1918,51 +1916,52 @@ void nsDisplayList::HitTest(nsDisplayLis
for (int32_t i = aState->mItemBuffer.Length() - 1; i >= itemBufferStart; --i) {
// Pop element off the end of the buffer. We want to shorten the buffer
// so that recursive calls to HitTest have more buffer space.
item = aState->mItemBuffer[i];
aState->mItemBuffer.SetLength(i);
bool snap;
nsRect r = item->GetBounds(aBuilder, &snap).Intersect(aRect);
+ auto itemType = item->GetType();
bool alwaysIntersect =
- item->GetType() == nsDisplayItem::TYPE_TRANSFORM &&
- (item->Frame()->Combines3DTransformWithAncestors() ||
- item->Frame()->Extend3DContext() ||
- static_cast<nsDisplayTransform*>(item)->IsTransformSeparator());
+ (itemType == nsDisplayItem::TYPE_TRANSFORM &&
+ static_cast<nsDisplayTransform*>(item)->IsParticipating3DContext()) ||
+ (itemType == nsDisplayItem::TYPE_PERSPECTIVE &&
+ static_cast<nsDisplayPerspective*>(item)->Frame()->Extend3DContext());
+ if (alwaysIntersect &&
+ !static_cast<nsDisplayTransform*>(item)->IsLeafOf3DContext()) {
+ nsAutoTArray<nsIFrame*, 1> neverUsed;
+ // Start gethering leaves of the 3D rendering context, and
+ // append leaves at the end of mItemBuffer. Leaves are
+ // processed at following iterations.
+ aState->mInPreserves3D = true;
+ item->HitTest(aBuilder, aRect, aState, &neverUsed);
+ aState->mInPreserves3D = false;
+ i = aState->mItemBuffer.Length();
+ continue;
+ }
if (alwaysIntersect || item->GetClip().MayIntersect(r)) {
nsAutoTArray<nsIFrame*, 16> outFrames;
- if (item->Frame()->Extend3DContext() &&
- item->GetType() == nsDisplayItem::TYPE_TRANSFORM &&
- !static_cast<nsDisplayTransform*>(item)->IsTransformSeparator()) {
- // Start gethering leaves of the 3D rendering context, and
- // append leaves at the end of mItemBuffer. Leaves are
- // processed at following iterations.
- aState->mInPreserves3D = true;
- item->HitTest(aBuilder, aRect, aState, &outFrames);
- aState->mInPreserves3D = false;
- i = aState->mItemBuffer.Length();
- continue;
- } else {
- item->HitTest(aBuilder, aRect, aState, &outFrames);
- }
+ item->HitTest(aBuilder, aRect, aState, &outFrames);
// For 3d transforms with preserve-3d we add hit frames into the temp list
// so we can sort them later, otherwise we add them directly to the output list.
nsTArray<nsIFrame*> *writeFrames = aOutFrames;
if (item->GetType() == nsDisplayItem::TYPE_TRANSFORM &&
- item->Frame()->Combines3DTransformWithAncestors()) {
+ static_cast<nsDisplayTransform*>(item)->IsLeafOf3DContext()) {
if (outFrames.Length()) {
nsDisplayTransform *transform = static_cast<nsDisplayTransform*>(item);
nsPoint point = aRect.TopLeft();
// A 1x1 rect means a point, otherwise use the center of the rect
if (aRect.width != 1 || aRect.height != 1) {
point = aRect.Center();
}
temp.AppendElement(FramesWithDepth(transform->GetHitDepthAtPoint(aBuilder, point)));
+ printf("depth %p %f\n", transform, transform->GetHitDepthAtPoint(aBuilder, point));
writeFrames = &temp[temp.Length() - 1].mFrames;
}
} else {
// We may have just finished a run of consecutive preserve-3d transforms,
// so flush these into the destination array before processing our frame list.
FlushFramesArray(temp, aOutFrames);
}
@@ -5565,24 +5564,24 @@ nsDisplayTransform::GetTransformForRende
// Don't include perspective transform, or the offset to origin, since
// nsDisplayPerspective will handle both of those.
return GetResultingTransformMatrix(mFrame, ToReferenceFrame(), scale, 0);
}
const Matrix4x4&
nsDisplayTransform::GetAccumulatedPreserved3DTransform(nsDisplayListBuilder* aBuilder)
{
+ MOZ_ASSERT(!mFrame->Extend3DContext() || IsLeafOf3DContext());
// XXX: should go back to fix mTransformGetter.
if (!mTransformPreserves3DInited) {
mTransformPreserves3DInited = true;
- if (!mFrame->Combines3DTransformWithAncestors()) {
+ if (!IsLeafOf3DContext()) {
mTransformPreserves3D = GetTransform();
return mTransformPreserves3D;
}
- MOZ_ASSERT(!mFrame->Extend3DContext() || IsTransformSeparator());
const nsIFrame* establisher; // Establisher of the 3D rendering context.
for (establisher = nsLayoutUtils::GetCrossDocParentFrame(mFrame);
establisher && establisher->Combines3DTransformWithAncestors();
establisher = nsLayoutUtils::GetCrossDocParentFrame(establisher)) {
}
establisher = nsLayoutUtils::GetCrossDocParentFrame(establisher);
const nsIFrame* establisherReference =
@@ -5809,17 +5808,16 @@ nsDisplayTransform::GetHitDepthAtPoint(n
NS_ASSERTION(IsFrameVisible(mFrame, matrix),
"We can't have hit a frame that isn't visible!");
Matrix4x4 inverse = matrix;
inverse.Invert();
Point4D point = inverse.ProjectPoint(Point(NSAppUnitsToFloatPixels(aPoint.x, factor),
NSAppUnitsToFloatPixels(aPoint.y, factor)));
- NS_ASSERTION(point.HasPositiveWCoord(), "Why are we trying to get the depth for a point we didn't hit?");
Point point2d = point.As2DPoint();
Point3D transformed = matrix * Point3D(point2d.x, point2d.y, 0);
return transformed.z;
}
/* The bounding rectangle for the object is the overflow rectangle translated
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -4080,16 +4080,24 @@ public:
* This item is the boundary between parent and child 3D rendering
* context.
*/
bool IsLeafOf3DContext() {
return (IsTransformSeparator() ||
(!mFrame->Extend3DContext() &&
mFrame->Combines3DTransformWithAncestors()));
}
+ /**
+ * The backing frame of this item participates a 3D rendering
+ * context.
+ */
+ bool IsParticipating3DContext() {
+ return mFrame->Extend3DContext() ||
+ mFrame->Combines3DTransformWithAncestors();
+ }
private:
void ComputeBounds(nsDisplayListBuilder* aBuilder);
void SetReferenceFrameToAncestor(nsDisplayListBuilder* aBuilder);
void Init(nsDisplayListBuilder* aBuilder);
static Matrix4x4 GetResultingTransformMatrixInternal(const FrameTransformProperties& aProperties,
const nsPoint& aOrigin,
@@ -4194,16 +4202,21 @@ public:
virtual nsDisplayList* GetChildren() override { return mList.GetChildren(); }
virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) override
{
return mList.GetComponentAlphaBounds(aBuilder);
}
nsIFrame* TransformFrame() { return mTransformFrame; }
+ virtual void
+ DoUpdateBoundsPreserves3D(nsDisplayListBuilder* aBuilder) override {
+ static_cast<nsDisplayTransform*>(mList.GetChildren()->GetTop())->DoUpdateBoundsPreserves3D(aBuilder);
+ }
+
private:
nsDisplayWrapList mList;
nsIFrame* mTransformFrame;
uint32_t mIndex;
};
/**
* This class adds basic support for limiting the rendering (in the inline axis
new file mode 100644
--- /dev/null
+++ b/layout/base/tests/bug1226904.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=684759
+-->
+<head>
+ <title>Test for Bug 684759</title>
+</head>
+<style>
+ #container {
+ transform-style: preserve-3d;
+ transform: translate3d(400px, 0px, 10px);
+ background-color: black;
+ }
+ #separator {
+ width: 400px;
+ height: 400px;
+ background-color: green;
+ }
+ #transformed {
+ width: 400px;
+ height: 400px;
+ background-color: blue;
+ transform: translate3d(-400px, 0px, 10px);
+ }
+</style>
+<body onload="run()">
+<div>
+ <div id="container">
+ <div id="separator"></div>
+ <div id="transformed"></div>
+ </div>
+</div>
+</body>
+</html>
--- a/layout/base/tests/mochitest.ini
+++ b/layout/base/tests/mochitest.ini
@@ -258,8 +258,10 @@ support-files = bug1093686_inner.html
[test_bug1120705.html]
skip-if = buildapp == 'android' || buildapp == 'b2g' || buildapp == 'b2g-debug' || os == 'mac' || toolkit == 'gtk2' || toolkit == 'gtk3' # android and b2g do not have clickable scrollbars, mac does not have scrollbar down and up buttons, gtk may or may not have scrollbar buttons depending on theme
[test_bug1153130.html]
support-files = bug1153130_inner.html
[test_bug1162990.html]
support-files =
bug1162990_inner_1.html
bug1162990_inner_2.html
+[test_bug1226904.html]
+support-files = bug1226904.html
--- a/layout/base/tests/preserve3d_sorting_hit_testing_iframe.html
+++ b/layout/base/tests/preserve3d_sorting_hit_testing_iframe.html
@@ -7,24 +7,24 @@
#parent {
-moz-transform-style: preserve-3d;
}
#big {
width: 1000px;
height: 1000px;
background-color: #995C7F;
- -moz-transform: rotatey(45deg);
+ -moz-transform: translate3d(0px, 0px, 350px) rotatey(45deg);
}
#small {
width: 100px;
height: 100px;
background-color: #835A99;
- -moz-transform: translate3d(600px, 200px, 0px);
+ -moz-transform: translate3d(600px, 200px, 350px);
}
</style>
<body>
<div id="stage">
<div id="parent">
<div id="small"></div>
<div id="big"></div>
new file mode 100644
--- /dev/null
+++ b/layout/base/tests/test_bug1226904.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=684759
+-->
+<head>
+ <title>Test for Bug 684759</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="run()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1226904">Mozilla Bug 1226904</a>
+<iframe src="bug1226904.html" id="iframe" height="1000" width="1000" style="border:none"></iframe>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 1226904 **/
+
+SimpleTest.waitForExplicitFinish();
+
+function run() {
+ var iframe = document.getElementById("iframe");
+ var doc = iframe.contentDocument;
+ var container = doc.getElementById("container");
+ var separator = doc.getElementById("separator");
+ var transformed = doc.getElementById("transformed");
+
+ function check(x, y, expected_element, description)
+ {
+ is(doc.elementFromPoint(x, y), expected_element,
+ "point (" + x + ", " + y + "): " + description);
+ }
+
+ check(600, 200, separator, "Separator object should be at right side");
+ check(900, 200, container, "Check bounds of separator object");
+ check(200, 600, transformed, "Transformed object should be at left side");
+
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
--- a/layout/base/tests/test_preserve3d_sorting_hit_testing.html
+++ b/layout/base/tests/test_preserve3d_sorting_hit_testing.html
@@ -24,17 +24,17 @@ function run() {
var doc = iframe.contentDocument;
var big= doc.getElementById("big");
var small = doc.getElementById("small");
function check(x, y, expected_element, description)
{
- is(doc.elementFromPoint(x, y), expected_element,
+ is(doc.elementFromPoint(x, y).id, expected_element.id,
"point (" + x + ", " + y + "): " + description);
}
check(650, 250, small, "Small object should be infront of big");
check(650, 308, big, "Check bounds of small object");
check(650, 207, big, "Check bounds of small object");
check(607, 250, big, "Check bounds of small object");
check(708, 250, big, "Check bounds of small object");
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -1918,16 +1918,19 @@ ItemParticipatesIn3DContext(nsIFrame* aA
nsIFrame* transformFrame;
if (aItem->GetType() == nsDisplayItem::TYPE_TRANSFORM) {
transformFrame = aItem->Frame();
} else if (aItem->GetType() == nsDisplayItem::TYPE_PERSPECTIVE) {
transformFrame = static_cast<nsDisplayPerspective*>(aItem)->TransformFrame();
} else {
return false;
}
+ if (aAncestor == transformFrame) {
+ return true;
+ }
return FrameParticipatesIn3DContext(aAncestor, transformFrame);
}
static void
WrapSeparatorTransform(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
nsRect& aDirtyRect,
nsDisplayList* aSource, nsDisplayList* aTarget,
int aIndex) {