diff options
author | Simon Hausmann <simon.hausmann@nokia.com> | 2012-06-20 13:01:08 +0200 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2012-06-20 13:01:08 +0200 |
commit | 49233e234e5c787396cadb2cea33b31ae0cd65c1 (patch) | |
tree | 5410cb9a8fd53168bb60d62c54b654d86f03c38d /Source/WebKit/chromium/tests/CCLayerTreeHostCommonTest.cpp | |
parent | b211c645d8ab690f713515dfdc84d80b11c27d2c (diff) | |
download | qtwebkit-49233e234e5c787396cadb2cea33b31ae0cd65c1.tar.gz |
Imported WebKit commit 3a8c29f35d00659d2ce7a0ccdfa8304f14e82327 (http://svn.webkit.org/repository/webkit/trunk@120813)
New snapshot with Windows build fixes
Diffstat (limited to 'Source/WebKit/chromium/tests/CCLayerTreeHostCommonTest.cpp')
-rw-r--r-- | Source/WebKit/chromium/tests/CCLayerTreeHostCommonTest.cpp | 2025 |
1 files changed, 2008 insertions, 17 deletions
diff --git a/Source/WebKit/chromium/tests/CCLayerTreeHostCommonTest.cpp b/Source/WebKit/chromium/tests/CCLayerTreeHostCommonTest.cpp index fce9046c7..89d211e81 100644 --- a/Source/WebKit/chromium/tests/CCLayerTreeHostCommonTest.cpp +++ b/Source/WebKit/chromium/tests/CCLayerTreeHostCommonTest.cpp @@ -31,7 +31,12 @@ #include "LayerChromium.h" #include "TranslateTransformOperation.h" #include "cc/CCLayerAnimationController.h" +#include "cc/CCLayerImpl.h" +#include "cc/CCLayerSorter.h" #include "cc/CCMathUtil.h" +#include "cc/CCProxy.h" +#include "cc/CCSingleThreadProxy.h" +#include "cc/CCThread.h" #include <gmock/gmock.h> #include <gtest/gtest.h> @@ -43,7 +48,8 @@ using WebKit::WebTransformationMatrix; namespace { -void setLayerPropertiesForTesting(LayerChromium* layer, const WebTransformationMatrix& transform, const WebTransformationMatrix& sublayerTransform, const FloatPoint& anchor, const FloatPoint& position, const IntSize& bounds, bool preserves3D) +template<typename LayerType> +void setLayerPropertiesForTesting(LayerType* layer, const WebTransformationMatrix& transform, const WebTransformationMatrix& sublayerTransform, const FloatPoint& anchor, const FloatPoint& position, const IntSize& bounds, bool preserves3D) { layer->setTransform(transform); layer->setSublayerTransform(sublayerTransform); @@ -53,13 +59,36 @@ void setLayerPropertiesForTesting(LayerChromium* layer, const WebTransformationM layer->setPreserves3D(preserves3D); } +void setLayerPropertiesForTesting(LayerChromium* layer, const WebTransformationMatrix& transform, const WebTransformationMatrix& sublayerTransform, const FloatPoint& anchor, const FloatPoint& position, const IntSize& bounds, bool preserves3D) +{ + setLayerPropertiesForTesting<LayerChromium>(layer, transform, sublayerTransform, anchor, position, bounds, preserves3D); +} + +void setLayerPropertiesForTesting(CCLayerImpl* layer, const WebTransformationMatrix& transform, const WebTransformationMatrix& sublayerTransform, const FloatPoint& anchor, const FloatPoint& position, const IntSize& bounds, bool preserves3D) +{ + setLayerPropertiesForTesting<CCLayerImpl>(layer, transform, sublayerTransform, anchor, position, bounds, preserves3D); + layer->setContentBounds(bounds); +} + void executeCalculateDrawTransformsAndVisibility(LayerChromium* rootLayer) { WebTransformationMatrix identityMatrix; Vector<RefPtr<LayerChromium> > dummyRenderSurfaceLayerList; Vector<RefPtr<LayerChromium> > dummyLayerList; int dummyMaxTextureSize = 512; - CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(rootLayer, rootLayer, identityMatrix, identityMatrix, dummyRenderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); + CCLayerTreeHostCommon::calculateDrawTransforms(rootLayer, rootLayer, identityMatrix, identityMatrix, dummyRenderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); + CCLayerTreeHostCommon::calculateVisibleAndScissorRects(dummyRenderSurfaceLayerList, rootLayer->renderSurface()->contentRect()); +} + +void executeCalculateDrawTransformsAndVisibility(CCLayerImpl* rootLayer) +{ + // Note: this version skips layer sorting. + WebTransformationMatrix identityMatrix; + Vector<CCLayerImpl*> dummyRenderSurfaceLayerList; + Vector<CCLayerImpl*> dummyLayerList; + int dummyMaxTextureSize = 512; + CCLayerTreeHostCommon::calculateDrawTransforms(rootLayer, rootLayer, identityMatrix, identityMatrix, dummyRenderSurfaceLayerList, dummyLayerList, 0, dummyMaxTextureSize); + CCLayerTreeHostCommon::calculateVisibleAndScissorRects(dummyRenderSurfaceLayerList, rootLayer->renderSurface()->contentRect()); } WebTransformationMatrix remove3DComponentOfMatrix(const WebTransformationMatrix& mat) @@ -75,6 +104,30 @@ WebTransformationMatrix remove3DComponentOfMatrix(const WebTransformationMatrix& return ret; } +PassOwnPtr<CCLayerImpl> createTreeForFixedPositionTests() +{ + OwnPtr<CCLayerImpl> root = CCLayerImpl::create(1); + OwnPtr<CCLayerImpl> child = CCLayerImpl::create(2); + OwnPtr<CCLayerImpl> grandChild = CCLayerImpl::create(3); + OwnPtr<CCLayerImpl> greatGrandChild = CCLayerImpl::create(4); + + WebTransformationMatrix IdentityMatrix; + FloatPoint anchor(0, 0); + FloatPoint position(0, 0); + IntSize bounds(100, 100); + setLayerPropertiesForTesting(root.get(), IdentityMatrix, IdentityMatrix, anchor, position, bounds, false); + setLayerPropertiesForTesting(child.get(), IdentityMatrix, IdentityMatrix, anchor, position, bounds, false); + setLayerPropertiesForTesting(grandChild.get(), IdentityMatrix, IdentityMatrix, anchor, position, bounds, false); + setLayerPropertiesForTesting(greatGrandChild.get(), IdentityMatrix, IdentityMatrix, anchor, position, bounds, false); + + grandChild->addChild(greatGrandChild.release()); + child->addChild(grandChild.release()); + root->addChild(child.release()); + root->createRenderSurface(); + + return root.release(); +} + class LayerChromiumWithForcedDrawsContent : public LayerChromium { public: LayerChromiumWithForcedDrawsContent() @@ -175,7 +228,7 @@ TEST(CCLayerTreeHostCommonTest, verifyTransformsForSingleLayer) EXPECT_TRANSFORMATION_MATRIX_EQ(expectedResult, layer->screenSpaceTransform()); // Case 7: Verify that position pre-multiplies the layer transform. - // The current implementation of calculateDrawTransformsAndVisibility does this implicitly, but it is + // The current implementation of calculateDrawTransforms does this implicitly, but it is // still worth testing to detect accidental regressions. expectedResult = positionTransform * translationToAnchor * layerTransform * translationToAnchor.inverse(); setLayerPropertiesForTesting(layer.get(), layerTransform, identityMatrix, FloatPoint(0.5f, 0.0f), FloatPoint(5.0f, 1.2f), IntSize(10, 12), false); @@ -318,6 +371,558 @@ TEST(CCLayerTreeHostCommonTest, verifyTransformsForSingleRenderSurface) EXPECT_TRANSFORMATION_MATRIX_EQ(parentCompositeTransform, child->targetRenderSurface()->screenSpaceTransform()); } +TEST(CCLayerTreeHostCommonTest, scissorRectNoClip) +{ + DebugScopedSetImplThread thisScopeIsOnImplThread; + + /* + Layers are created as follows: + + +--------------------+ + | 1 | + | +-----------+ | + | | 2 | | + | | +-------------------+ + | | | 3 | + | | +-------------------+ + | | | | + | +-----------+ | + | | + | | + +--------------------+ + + Layers 1, 2 have render surfaces + */ + OwnPtr<CCLayerImpl> root = CCLayerImpl::create(1); + OwnPtr<CCLayerImpl> child = CCLayerImpl::create(2); + OwnPtr<CCLayerImpl> grandChild = CCLayerImpl::create(3); + + IntRect rootRect(0, 0, 100, 100); + IntRect childRect(10, 10, 50, 50); + IntRect grandChildRect(5, 5, 150, 150); + + root->createRenderSurface(); + root->setAnchorPoint(FloatPoint(0, 0)); + root->setPosition(FloatPoint(rootRect.x(), rootRect.y())); + root->setBounds(IntSize(rootRect.width(), rootRect.height())); + root->setDrawsContent(true); + root->renderSurface()->setContentRect(IntRect(IntPoint(), IntSize(rootRect.width(), rootRect.height()))); + + child->setAnchorPoint(FloatPoint(0, 0)); + child->setPosition(FloatPoint(childRect.x(), childRect.y())); + child->setOpacity(0.5f); + child->setBounds(IntSize(childRect.width(), childRect.height())); + child->setDrawsContent(true); + + grandChild->setAnchorPoint(FloatPoint(0, 0)); + grandChild->setPosition(IntPoint(grandChildRect.x(), grandChildRect.y())); + grandChild->setBounds(IntSize(grandChildRect.width(), grandChildRect.height())); + grandChild->setDrawsContent(true); + + CCLayerImpl* childPtr = child.get(); + CCLayerImpl* grandChildPtr = grandChild.get(); + + child->addChild(grandChild.release()); + root->addChild(child.release()); + + Vector<CCLayerImpl*> renderSurfaceLayerList; + { + WebTransformationMatrix identityMatrix; + Vector<CCLayerImpl*> layerList; + int dummyMaxTextureSize = 512; + CCLayerSorter layerSorter; + + renderSurfaceLayerList.append(root.get()); + + CCLayerTreeHostCommon::calculateDrawTransforms(root.get(), root.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, layerList, &layerSorter, dummyMaxTextureSize); + + FloatRect dummyDamageRect; + CCLayerTreeHostCommon::calculateVisibleAndScissorRects(renderSurfaceLayerList, dummyDamageRect); + } + + ASSERT_TRUE(childPtr->renderSurface()); + ASSERT_TRUE(root->renderSurface()); + ASSERT_FALSE(grandChildPtr->renderSurface()); + + EXPECT_EQ(renderSurfaceLayerList.size(), 2U); + + ASSERT_EQ(root->clipRect(), IntRect(0, 0, 0, 0)); + + // Layer's clipRect is a union of all its children's bounds + ASSERT_EQ(childPtr->clipRect(), IntRect(0, 0, grandChildRect.x() + grandChildRect.width(), grandChildRect.y() + grandChildRect.height())); + ASSERT_EQ(grandChildPtr->clipRect(), IntRect(0, 0, 0, 0)); + + ASSERT_EQ(root->renderSurface()->clipRect(), IntRect(0, 0, 0, 0)); + ASSERT_EQ(childPtr->renderSurface()->clipRect(), IntRect(0, 0, 0, 0)); + + ASSERT_FALSE(root->usesLayerClipping()); + ASSERT_FALSE(childPtr->usesLayerClipping()); + ASSERT_FALSE(grandChildPtr->usesLayerClipping()); + + // Damage the entire screen + IntRect rootDamage(rootRect); + CCLayerTreeHostCommon::calculateVisibleAndScissorRects(renderSurfaceLayerList, rootDamage); + + EXPECT_EQ(root->targetRenderSurface()->scissorRect(), IntRect(0, 0, 0, 0)); + + // child surface doesn't have a clip rect, therefore it will be computed as intersection + // between root surface's contentrect and child surface's drawable content rect. + EXPECT_EQ(childPtr->targetRenderSurface()->scissorRect(), IntRect(childRect.x(), childRect.y(), rootRect.width() - childRect.x(), rootRect.height() - childRect.y())); + + EXPECT_EQ(root->scissorRect(), IntRect(rootRect)); + + // The damage is the entire rootRect, but child layer starts at an offset. + // Even though it has bounds, it is not clipping to bounds so its children + // (which extend beyond the bounds) extend the scissor rect + EXPECT_EQ(childPtr->scissorRect(), IntRect(0, 0, rootRect.width() - childRect.x(), rootRect.height() - childRect.y())); + + // Grand child will have the same scissor rect as it doesn't have a surface + // of its own + EXPECT_EQ(grandChildPtr->scissorRect(), IntRect(0, 0, rootRect.width() - childRect.x(), rootRect.height() - childRect.y())); + + // Empty damage + rootDamage = IntRect(0, 0, 0, 0); + CCLayerTreeHostCommon::calculateVisibleAndScissorRects(renderSurfaceLayerList, rootDamage); + + // Empty damage == empty scissor + EXPECT_EQ(root->targetRenderSurface()->scissorRect(), IntRect(0, 0, 0, 0)); + EXPECT_EQ(childPtr->targetRenderSurface()->scissorRect(), IntRect(0, 0, 0, 0)); + + EXPECT_EQ(root->scissorRect(), IntRect(0, 0, 0, 0)); + EXPECT_EQ(childPtr->scissorRect(), IntRect(0, 0, 0, 0)); + EXPECT_EQ(grandChildPtr->scissorRect(), IntRect(0, 0, 0, 0)); + + // Partial damage within child + rootDamage = IntRect(10, 10, 20, 20); + CCLayerTreeHostCommon::calculateVisibleAndScissorRects(renderSurfaceLayerList, rootDamage); + + // Scissors are not computed for root + EXPECT_EQ(root->targetRenderSurface()->scissorRect(), IntRect(0, 0, 0, 0)); + + // Entire damage rect is within the root surface + EXPECT_EQ(childPtr->targetRenderSurface()->scissorRect(), rootDamage); + + // Entire damage rect is within the layer + EXPECT_EQ(root->scissorRect(), rootDamage); + + // Entire damage rect is within the layer, but with different offset + EXPECT_EQ(childPtr->scissorRect(), IntRect(rootDamage.x() - childRect.x(), rootDamage.y() - childRect.y(), rootDamage.width(), rootDamage.height())); + + // Grand child does not have its own surface, so its scissor rect is identical to child's + EXPECT_EQ(grandChildPtr->scissorRect(), IntRect(rootDamage.x() - childRect.x(), rootDamage.y() - childRect.y(), rootDamage.width(), rootDamage.height())); + + // Partial damage beyond child + rootDamage = IntRect(10, 10, 80, 80); + CCLayerTreeHostCommon::calculateVisibleAndScissorRects(renderSurfaceLayerList, rootDamage); + + // Scissors are not computed for root + EXPECT_EQ(root->targetRenderSurface()->scissorRect(), IntRect(0, 0, 0, 0)); + + // Entire damage rect is within the root surface + EXPECT_EQ(childPtr->targetRenderSurface()->scissorRect(), rootDamage); + + // Entire damage rect is within the layer + EXPECT_EQ(root->scissorRect(), rootDamage); + + // Entire damage rect is within the layer, but with different offset + EXPECT_EQ(childPtr->scissorRect(), IntRect(rootDamage.x() - childRect.x(), rootDamage.y() - childRect.y(), rootDamage.width(), rootDamage.height())); + + // Grand child does not have its own surface, so its scissor rect is identical to child's + EXPECT_EQ(grandChildPtr->scissorRect(), IntRect(rootDamage.x() - childRect.x(), rootDamage.y() - childRect.y(), rootDamage.width(), rootDamage.height())); + + // Partial damage beyond root + rootDamage = IntRect(10, 10, 110, 110); + CCLayerTreeHostCommon::calculateVisibleAndScissorRects(renderSurfaceLayerList, rootDamage); + + // Scissors are not computed for root + EXPECT_EQ(root->targetRenderSurface()->scissorRect(), IntRect(0, 0, 0, 0)); + + // Root surface does not have a clipRect, so its contentRect will be used to intersect with damage. + // Result is that root damage rect is clipped at root layer boundary + EXPECT_EQ(childPtr->targetRenderSurface()->scissorRect(), IntRect(rootDamage.x(), rootDamage.y(), rootRect.width() - rootDamage.x(), rootRect.height() - rootDamage.y())); + + // Root does not use layer clipping, so its content rect will be used to intersect with damage + // Result is that root damage rect is clipped at root layer boundary + EXPECT_EQ(root->scissorRect(), IntRect(rootDamage.x(), rootDamage.y(), rootRect.width() - rootDamage.x(), rootRect.height() - rootDamage.y())); + + // Children's content rects are bigger than the root's so they don't clip the damage rect, but change its offset. + EXPECT_EQ(childPtr->scissorRect(), IntRect(rootDamage.x() - childRect.x(), rootDamage.y() - childRect.y(), rootDamage.width(), rootDamage.height())); + EXPECT_EQ(grandChildPtr->scissorRect(), IntRect(rootDamage.x() - childRect.x(), rootDamage.y() - childRect.y(), rootDamage.width(), rootDamage.height())); +} + +TEST(CCLayerTreeHostCommonTest, scissorRectWithClip) +{ + DebugScopedSetImplThread thisScopeIsOnImplThread; + + /* + Layers are created as follows: + + +--------------------+ + | 1 | + | +-----------+ | + | | 2 | | + | | +-------------------+ + | | | 3 | + | | +-------------------+ + | | | | + | +-----------+ | + | | + | | + +--------------------+ + + Layers 1, 2 have render surfaces + */ + OwnPtr<CCLayerImpl> root = CCLayerImpl::create(1); + OwnPtr<CCLayerImpl> child = CCLayerImpl::create(2); + OwnPtr<CCLayerImpl> grandChild = CCLayerImpl::create(3); + + IntRect rootRect(0, 0, 100, 100); + IntRect childRect(10, 10, 50, 50); + IntRect grandChildRect(5, 5, 150, 150); + + root->createRenderSurface(); + root->setAnchorPoint(FloatPoint(0, 0)); + root->setPosition(FloatPoint(rootRect.x(), rootRect.y())); + root->setBounds(IntSize(rootRect.width(), rootRect.height())); + root->setDrawsContent(true); + root->renderSurface()->setContentRect(IntRect(IntPoint(), IntSize(rootRect.width(), rootRect.height()))); + + child->setAnchorPoint(FloatPoint(0, 0)); + child->setPosition(FloatPoint(childRect.x(), childRect.y())); + child->setOpacity(0.5f); + child->setBounds(IntSize(childRect.width(), childRect.height())); + child->setDrawsContent(true); + + grandChild->setAnchorPoint(FloatPoint(0, 0)); + grandChild->setPosition(IntPoint(grandChildRect.x(), grandChildRect.y())); + grandChild->setBounds(IntSize(grandChildRect.width(), grandChildRect.height())); + grandChild->setDrawsContent(true); + + CCLayerImpl* childPtr = child.get(); + CCLayerImpl* grandChildPtr = grandChild.get(); + + child->addChild(grandChild.release()); + root->addChild(child.release()); + + root->setMasksToBounds(true); + + Vector<CCLayerImpl*> renderSurfaceLayerList; + { + WebTransformationMatrix identityMatrix; + Vector<CCLayerImpl*> layerList; + int dummyMaxTextureSize = 512; + CCLayerSorter layerSorter; + + renderSurfaceLayerList.append(root.get()); + + CCLayerTreeHostCommon::calculateDrawTransforms(root.get(), root.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, layerList, &layerSorter, dummyMaxTextureSize); + + FloatRect dummyDamageRect; + CCLayerTreeHostCommon::calculateVisibleAndScissorRects(renderSurfaceLayerList, dummyDamageRect); + } + + ASSERT_TRUE(childPtr->renderSurface()); + ASSERT_TRUE(root->renderSurface()); + ASSERT_FALSE(grandChildPtr->renderSurface()); + + EXPECT_EQ(renderSurfaceLayerList.size(), 2U); + + // Now root is clipping to its bounds + ASSERT_EQ(root->clipRect(), rootRect); + + // Layer's clipRect is a union of all its children's bounds + ASSERT_EQ(childPtr->clipRect(), IntRect(0, 0, grandChildRect.x() + grandChildRect.width(), grandChildRect.y() + grandChildRect.height())); + ASSERT_EQ(grandChildPtr->clipRect(), IntRect(0, 0, 0, 0)); + + ASSERT_EQ(root->renderSurface()->clipRect(), IntRect(0, 0, 0, 0)); + + // Child surface's clipping rect is now set to root's + ASSERT_EQ(childPtr->renderSurface()->clipRect(), rootRect); + + ASSERT_TRUE(root->usesLayerClipping()); + ASSERT_FALSE(childPtr->usesLayerClipping()); + ASSERT_FALSE(grandChildPtr->usesLayerClipping()); + + // Damage the entire screen + IntRect rootDamage(rootRect); + CCLayerTreeHostCommon::calculateVisibleAndScissorRects(renderSurfaceLayerList, rootDamage); + + EXPECT_EQ(root->targetRenderSurface()->scissorRect(), IntRect(0, 0, 0, 0)); + EXPECT_EQ(childPtr->targetRenderSurface()->scissorRect(), IntRect(rootRect)); + + EXPECT_EQ(root->scissorRect(), IntRect(rootRect)); + + // The damage is the entire rootRect, but child layer starts at an offset. + // Even though it has bounds, it is not clipping to bounds so its children + // (which extend beyond the bounds) extend the scissor rect + EXPECT_EQ(childPtr->scissorRect(), IntRect(0, 0, rootRect.width() - childRect.x(), rootRect.height() - childRect.y())); + + // Grand child will have the same scissor rect as it doesn't have a surface + // of its own + EXPECT_EQ(grandChildPtr->scissorRect(), IntRect(0, 0, rootRect.width() - childRect.x(), rootRect.height() - childRect.y())); + + // Empty damage + rootDamage = IntRect(0, 0, 0, 0); + CCLayerTreeHostCommon::calculateVisibleAndScissorRects(renderSurfaceLayerList, rootDamage); + + // Empty damage == empty scissor + EXPECT_EQ(root->targetRenderSurface()->scissorRect(), IntRect(0, 0, 0, 0)); + EXPECT_EQ(childPtr->targetRenderSurface()->scissorRect(), IntRect(0, 0, 0, 0)); + + EXPECT_EQ(root->scissorRect(), IntRect(0, 0, 0, 0)); + EXPECT_EQ(childPtr->scissorRect(), IntRect(0, 0, 0, 0)); + EXPECT_EQ(grandChildPtr->scissorRect(), IntRect(0, 0, 0, 0)); + + // Partial damage within child + rootDamage = IntRect(10, 10, 20, 20); + CCLayerTreeHostCommon::calculateVisibleAndScissorRects(renderSurfaceLayerList, rootDamage); + + // Scissors are not computed for root + EXPECT_EQ(root->targetRenderSurface()->scissorRect(), IntRect(0, 0, 0, 0)); + + // Entire damage rect is within the root surface + EXPECT_EQ(childPtr->targetRenderSurface()->scissorRect(), rootDamage); + + // Entire damage rect is within the layer + EXPECT_EQ(root->scissorRect(), rootDamage); + + // Entire damage rect is within the layer, but with different offset + EXPECT_EQ(childPtr->scissorRect(), IntRect(rootDamage.x() - childRect.x(), rootDamage.y() - childRect.y(), rootDamage.width(), rootDamage.height())); + + // Grand child does not have its own surface, so its scissor rect is identical to child's + EXPECT_EQ(grandChildPtr->scissorRect(), IntRect(rootDamage.x() - childRect.x(), rootDamage.y() - childRect.y(), rootDamage.width(), rootDamage.height())); + + // Partial damage beyond child + rootDamage = IntRect(10, 10, 80, 80); + CCLayerTreeHostCommon::calculateVisibleAndScissorRects(renderSurfaceLayerList, rootDamage); + + // Scissors are not computed for root + EXPECT_EQ(root->targetRenderSurface()->scissorRect(), IntRect(0, 0, 0, 0)); + + // Entire damage rect is within the root surface + EXPECT_EQ(childPtr->targetRenderSurface()->scissorRect(), rootDamage); + + // Entire damage rect is within the layer + EXPECT_EQ(root->scissorRect(), rootDamage); + + // Entire damage rect is within the layer, but with different offset + EXPECT_EQ(childPtr->scissorRect(), IntRect(rootDamage.x() - childRect.x(), rootDamage.y() - childRect.y(), rootDamage.width(), rootDamage.height())); + + // Grand child does not have its own surface, so its scissor rect is identical to child's + EXPECT_EQ(grandChildPtr->scissorRect(), IntRect(rootDamage.x() - childRect.x(), rootDamage.y() - childRect.y(), rootDamage.width(), rootDamage.height())); + + // Partial damage beyond root + rootDamage = IntRect(10, 10, 110, 110); + CCLayerTreeHostCommon::calculateVisibleAndScissorRects(renderSurfaceLayerList, rootDamage); + + // Scissors are not computed for root + EXPECT_EQ(root->targetRenderSurface()->scissorRect(), IntRect(0, 0, 0, 0)); + + // Root surface does not have a clipRect, so its contentRect will be used to intersect with damage. + // Result is that root damage rect is clipped at root layer boundary + EXPECT_EQ(childPtr->targetRenderSurface()->scissorRect(), IntRect(rootDamage.x(), rootDamage.y(), rootRect.width() - rootDamage.x(), rootRect.height() - rootDamage.y())); + + // Root does not use layer clipping, so its content rect will be used to intersect with damage + // Result is that root damage rect is clipped at root layer boundary + EXPECT_EQ(root->scissorRect(), IntRect(rootDamage.x(), rootDamage.y(), rootRect.width() - rootDamage.x(), rootRect.height() - rootDamage.y())); + + // Now the scissor rects are clipped by surfaces contentRect + EXPECT_EQ(childPtr->scissorRect(), IntRect(rootDamage.x() - childRect.x(), rootDamage.y() - childRect.y(), rootRect.width() - rootDamage.x(), rootRect.height() - rootDamage.y())); + EXPECT_EQ(grandChildPtr->scissorRect(), IntRect(rootDamage.x() - childRect.x(), rootDamage.y() - childRect.y(), rootRect.width() - rootDamage.x(), rootRect.height() - rootDamage.y())); +} + +TEST(CCLayerTreeHostCommonTest, scissorRectWithClipAndSpaceTransform) +{ + DebugScopedSetImplThread thisScopeIsOnImplThread; + + /* + Layers are created as follows: + + +--------------------+ + | 1 | + | +-----------+ | + | | 2 | | + | | +-------------------+ + | | | 3,4 | + | | +-------------------+ + | | | | + | +-----------+ | + | | + | | + +--------------------+ + + Layers 1, 2 and 3 have render surfaces + */ + OwnPtr<CCLayerImpl> root = CCLayerImpl::create(1); + OwnPtr<CCLayerImpl> child = CCLayerImpl::create(2); + OwnPtr<CCLayerImpl> grandChild = CCLayerImpl::create(3); + OwnPtr<CCLayerImpl> grandChild2 = CCLayerImpl::create(4); + + IntRect rootRect(0, 0, 100, 100); + IntRect childRect(10, 10, 50, 50); + IntRect grandChildRect(5, 5, 150, 150); + + root->createRenderSurface(); + root->setAnchorPoint(FloatPoint(0, 0)); + root->setPosition(FloatPoint(rootRect.x(), rootRect.y())); + root->setBounds(IntSize(rootRect.width(), rootRect.height())); + root->setDrawsContent(true); + root->renderSurface()->setContentRect(IntRect(IntPoint(), IntSize(rootRect.width(), rootRect.height()))); + + child->setAnchorPoint(FloatPoint(0, 0)); + child->setPosition(FloatPoint(childRect.x(), childRect.y())); + child->setOpacity(0.5f); + child->setBounds(IntSize(childRect.width(), childRect.height())); + child->setDrawsContent(true); + + grandChild->setAnchorPoint(FloatPoint(0, 0)); + grandChild->setPosition(IntPoint(grandChildRect.x(), grandChildRect.y())); + grandChild->setOpacity(0.5f); + grandChild->setBounds(IntSize(grandChildRect.width(), grandChildRect.height())); + grandChild->setDrawsContent(true); + + grandChild2->setAnchorPoint(FloatPoint(0, 0)); + grandChild2->setPosition(IntPoint(grandChildRect.x(), grandChildRect.y())); + grandChild2->setOpacity(0.5f); + grandChild2->setBounds(IntSize(grandChildRect.width(), grandChildRect.height())); + grandChild2->setDrawsContent(true); + + CCLayerImpl* childPtr = child.get(); + CCLayerImpl* grandChildPtr = grandChild.get(); + + grandChild->addChild(grandChild2.release()); + child->addChild(grandChild.release()); + root->addChild(child.release()); + + root->setMasksToBounds(true); + + Vector<CCLayerImpl*> renderSurfaceLayerList; + { + WebTransformationMatrix identityMatrix; + Vector<CCLayerImpl*> layerList; + int dummyMaxTextureSize = 512; + CCLayerSorter layerSorter; + + renderSurfaceLayerList.append(root.get()); + + CCLayerTreeHostCommon::calculateDrawTransforms(root.get(), root.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, layerList, &layerSorter, dummyMaxTextureSize); + + FloatRect dummyDamageRect; + CCLayerTreeHostCommon::calculateVisibleAndScissorRects(renderSurfaceLayerList, dummyDamageRect); + } + + ASSERT_TRUE(childPtr->renderSurface()); + ASSERT_TRUE(root->renderSurface()); + ASSERT_TRUE(grandChildPtr->renderSurface()); + + EXPECT_EQ(renderSurfaceLayerList.size(), 3U); + + // Now root is clipping to its bounds + ASSERT_EQ(root->clipRect(), rootRect); + + ASSERT_EQ(childPtr->clipRect(), IntRect(0, 0, childRect.x() + grandChildRect.width() , childRect.y() + grandChildRect.height())); + + // Grandchild now clips + ASSERT_EQ(grandChildPtr->clipRect(), IntRect(0, 0, grandChildRect.x() + grandChildRect.width(), grandChildRect.y() + grandChildRect.height())); + + ASSERT_EQ(root->renderSurface()->clipRect(), IntRect(0, 0, 0, 0)); + + // Child surface's clipping rect is now set to root's + ASSERT_EQ(childPtr->renderSurface()->clipRect(), rootRect); + + ASSERT_TRUE(root->usesLayerClipping()); + ASSERT_FALSE(childPtr->usesLayerClipping()); + ASSERT_FALSE(grandChildPtr->usesLayerClipping()); + + // Damage the entire screen + IntRect rootDamage(rootRect); + CCLayerTreeHostCommon::calculateVisibleAndScissorRects(renderSurfaceLayerList, rootDamage); + + EXPECT_EQ(root->targetRenderSurface()->scissorRect(), IntRect(0, 0, 0, 0)); + EXPECT_EQ(childPtr->targetRenderSurface()->scissorRect(), IntRect(rootRect)); + + EXPECT_EQ(root->scissorRect(), IntRect(rootRect)); + + // The damage is the entire rootRect, but child layer starts at an offset. + // Even though it has bounds, it is not clipping to bounds so its children + // (which extend beyond the bounds) extend the scissor rect + EXPECT_EQ(childPtr->scissorRect(), IntRect(0, 0, rootRect.width() - childRect.x(), rootRect.height() - childRect.y())); + + // Grand child is now scissored by the render surface + EXPECT_EQ(grandChildPtr->scissorRect(), IntRect(0, 0, rootRect.width() - childRect.x() - grandChildRect.x(), rootRect.height() - childRect.y() - grandChildRect.y())); + + // Empty damage + rootDamage = IntRect(0, 0, 0, 0); + CCLayerTreeHostCommon::calculateVisibleAndScissorRects(renderSurfaceLayerList, rootDamage); + + // Empty damage == empty scissor + EXPECT_EQ(root->targetRenderSurface()->scissorRect(), IntRect(0, 0, 0, 0)); + EXPECT_EQ(childPtr->targetRenderSurface()->scissorRect(), IntRect(0, 0, 0, 0)); + + EXPECT_EQ(root->scissorRect(), IntRect(0, 0, 0, 0)); + EXPECT_EQ(childPtr->scissorRect(), IntRect(0, 0, 0, 0)); + EXPECT_EQ(grandChildPtr->scissorRect(), IntRect(0, 0, 0, 0)); + + // Partial damage within child + rootDamage = IntRect(10, 10, 20, 20); + CCLayerTreeHostCommon::calculateVisibleAndScissorRects(renderSurfaceLayerList, rootDamage); + + // Scissors are not computed for root + EXPECT_EQ(root->targetRenderSurface()->scissorRect(), IntRect(0, 0, 0, 0)); + + // Entire damage rect is within the root surface + EXPECT_EQ(childPtr->targetRenderSurface()->scissorRect(), rootDamage); + + // Entire damage rect is within the layer + EXPECT_EQ(root->scissorRect(), rootDamage); + + // Entire damage rect is within the layer, but with different offset + EXPECT_EQ(childPtr->scissorRect(), IntRect(rootDamage.x() - childRect.x(), rootDamage.y() - childRect.y(), rootDamage.width(), rootDamage.height())); + + // Grand child now gets scissored by its target surface as well as root + EXPECT_EQ(grandChildPtr->scissorRect(), IntRect(rootDamage.x() - childRect.x(), rootDamage.y() - childRect.y(), rootDamage.width() - grandChildRect.x(), rootDamage.height() - grandChildRect.y())); + + // Partial damage beyond child + rootDamage = IntRect(10, 10, 80, 80); + CCLayerTreeHostCommon::calculateVisibleAndScissorRects(renderSurfaceLayerList, rootDamage); + + // Scissors are not computed for root + EXPECT_EQ(root->targetRenderSurface()->scissorRect(), IntRect(0, 0, 0, 0)); + + // Entire damage rect is within the root surface + EXPECT_EQ(childPtr->targetRenderSurface()->scissorRect(), rootDamage); + + // Entire damage rect is within the layer + EXPECT_EQ(root->scissorRect(), rootDamage); + + // Entire damage rect is within the layer, but with different offset + EXPECT_EQ(childPtr->scissorRect(), IntRect(rootDamage.x() - childRect.x(), rootDamage.y() - childRect.y(), rootDamage.width(), rootDamage.height())); + + // Grand child now gets scissored by its target surface as well as root + EXPECT_EQ(grandChildPtr->scissorRect(), IntRect(rootDamage.x() - childRect.x(), rootDamage.y() - childRect.y(), rootDamage.width() - grandChildRect.x(), rootDamage.height() - grandChildRect.y())); + + // Partial damage beyond root + rootDamage = IntRect(10, 10, 110, 110); + CCLayerTreeHostCommon::calculateVisibleAndScissorRects(renderSurfaceLayerList, rootDamage); + + // Scissors are not computed for root + EXPECT_EQ(root->targetRenderSurface()->scissorRect(), IntRect(0, 0, 0, 0)); + + // Root surface does not have a clipRect, so its contentRect will be used to intersect with damage. + // Result is that root damage rect is clipped at root layer boundary + EXPECT_EQ(childPtr->targetRenderSurface()->scissorRect(), IntRect(rootDamage.x(), rootDamage.y(), rootRect.width() - rootDamage.x(), rootRect.height() - rootDamage.y())); + + // Root does not use layer clipping, so its content rect will be used to intersect with damage + // Result is that root damage rect is clipped at root layer boundary + EXPECT_EQ(root->scissorRect(), IntRect(rootDamage.x(), rootDamage.y(), rootRect.width() - rootDamage.x(), rootRect.height() - rootDamage.y())); + + // Now the scissor rects are clipped by surfaces contentRect + EXPECT_EQ(childPtr->scissorRect(), IntRect(rootDamage.x() - childRect.x(), rootDamage.y() - childRect.y(), rootRect.width() - rootDamage.x(), rootRect.height() - rootDamage.y())); + + // Grandchild's scissor rect is clipped by its target surface + EXPECT_EQ(grandChildPtr->scissorRect(), IntRect(rootDamage.x() - childRect.x(), rootDamage.y() - childRect.y(), rootDamage.width() - grandChildRect.x(), rootDamage.height() - grandChildRect.y())); +} + TEST(CCLayerTreeHostCommonTest, verifyTransformsForReplica) { RefPtr<LayerChromium> parent = LayerChromium::create(); @@ -545,7 +1150,10 @@ TEST(CCLayerTreeHostCommonTest, verifyRenderSurfaceListForClipLayer) Vector<RefPtr<LayerChromium> > renderSurfaceLayerList; Vector<RefPtr<LayerChromium> > dummyLayerList; int dummyMaxTextureSize = 512; - CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); + CCLayerTreeHostCommon::calculateDrawTransforms(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); + + FloatRect dummyDamageRect; + CCLayerTreeHostCommon::calculateVisibleAndScissorRects(renderSurfaceLayerList, parent->renderSurface()->contentRect()); // The child layer's content is entirely outside the parent's clip rect, so the intermediate // render surface should have been removed. Render surfaces without children or visible @@ -573,7 +1181,9 @@ TEST(CCLayerTreeHostCommonTest, verifyRenderSurfaceListForTransparentChild) Vector<RefPtr<LayerChromium> > renderSurfaceLayerList; Vector<RefPtr<LayerChromium> > dummyLayerList; int dummyMaxTextureSize = 512; - CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); + CCLayerTreeHostCommon::calculateDrawTransforms(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); + + CCLayerTreeHostCommon::calculateVisibleAndScissorRects(renderSurfaceLayerList, parent->renderSurface()->contentRect()); // Since the layer is transparent, renderSurface1->renderSurface() should not have gotten added anywhere. // Also, the drawable content rect should not have been extended by the children. @@ -601,18 +1211,612 @@ TEST(CCLayerTreeHostCommonTest, verifyForceRenderSurface) Vector<RefPtr<LayerChromium> > renderSurfaceLayerList; Vector<RefPtr<LayerChromium> > dummyLayerList; int dummyMaxTextureSize = 512; - CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); + CCLayerTreeHostCommon::calculateDrawTransforms(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); EXPECT_TRUE(renderSurface1->renderSurface()); EXPECT_EQ(renderSurfaceLayerList.size(), 1U); renderSurfaceLayerList.clear(); renderSurface1->setForceRenderSurface(false); - CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); + CCLayerTreeHostCommon::calculateDrawTransforms(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); EXPECT_FALSE(renderSurface1->renderSurface()); EXPECT_EQ(renderSurfaceLayerList.size(), 0U); } +TEST(CCLayerTreeHostCommonTest, verifyScrollCompensationForFixedPositionLayerWithDirectContainer) +{ + // This test checks for correct scroll compensation when the fixed-position container + // is the direct parent of the fixed-position layer. + + DebugScopedSetImplThread scopedImplThread; + OwnPtr<CCLayerImpl> root = createTreeForFixedPositionTests(); + CCLayerImpl* child = root->children()[0].get(); + CCLayerImpl* grandChild = child->children()[0].get(); + + child->setIsContainerForFixedPositionLayers(true); + grandChild->setFixedToContainerLayer(true); + + // Case 1: scrollDelta of 0, 0 + child->setScrollDelta(IntSize(0, 0)); + executeCalculateDrawTransformsAndVisibility(root.get()); + + // The expected drawTransforms without any scroll should still include a translation to the center of the layer (i.e. translation by 50, 50). + WebTransformationMatrix expectedChildTransform; + expectedChildTransform.translate(50, 50); + + WebTransformationMatrix expectedGrandChildTransform = expectedChildTransform; + + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildTransform, child->drawTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGrandChildTransform, grandChild->drawTransform()); + + // Case 2: scrollDelta of 10, 10 + child->setScrollDelta(IntSize(10, 10)); + executeCalculateDrawTransformsAndVisibility(root.get()); + + // Here the child is affected by scrollDelta, but the fixed position grandChild should not be affected. + expectedChildTransform.makeIdentity(); + expectedChildTransform.translate(40, 40); + + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildTransform, child->drawTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGrandChildTransform, grandChild->drawTransform()); +} + +TEST(CCLayerTreeHostCommonTest, verifyScrollCompensationForFixedPositionLayerWithTransformedDirectContainer) +{ + // This test checks for correct scroll compensation when the fixed-position container + // is the direct parent of the fixed-position layer, but that container is transformed. + // In this case, the fixed position element inherits the container's transform, + // but the scrollDelta that has to be undone should not be affected by that transform. + // + // Transforms are in general non-commutative; using something like a non-uniform scale + // helps to verify that translations and non-uniform scales are applied in the correct + // order. + + DebugScopedSetImplThread scopedImplThread; + OwnPtr<CCLayerImpl> root = createTreeForFixedPositionTests(); + CCLayerImpl* child = root->children()[0].get(); + CCLayerImpl* grandChild = child->children()[0].get(); + + // This scale will cause child and grandChild to be effectively 200 x 800 with respect to the targetRenderSurface. + WebTransformationMatrix nonUniformScale; + nonUniformScale.scaleNonUniform(2, 8); + child->setTransform(nonUniformScale); + + child->setIsContainerForFixedPositionLayers(true); + grandChild->setFixedToContainerLayer(true); + + // Case 1: scrollDelta of 0, 0 + child->setScrollDelta(IntSize(0, 0)); + executeCalculateDrawTransformsAndVisibility(root.get()); + + // The expected drawTransforms without any scroll should still include a translation to the center of the layer (i.e. translation by 50, 50). + WebTransformationMatrix expectedChildTransform; + expectedChildTransform.multiply(nonUniformScale); + expectedChildTransform.translate(50, 50); + + WebTransformationMatrix expectedGrandChildTransform = expectedChildTransform; + + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildTransform, child->drawTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGrandChildTransform, grandChild->drawTransform()); + + // Case 2: scrollDelta of 10, 20 + child->setScrollDelta(IntSize(10, 20)); + executeCalculateDrawTransformsAndVisibility(root.get()); + + // The child should be affected by scrollDelta, but the fixed position grandChild should not be affected. + expectedChildTransform.makeIdentity(); + expectedChildTransform.translate(-10, -20); // scrollDelta + expectedChildTransform.multiply(nonUniformScale); + expectedChildTransform.translate(50, 50); + + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildTransform, child->drawTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGrandChildTransform, grandChild->drawTransform()); +} + +TEST(CCLayerTreeHostCommonTest, verifyScrollCompensationForFixedPositionLayerWithDistantContainer) +{ + // This test checks for correct scroll compensation when the fixed-position container + // is NOT the direct parent of the fixed-position layer. + DebugScopedSetImplThread scopedImplThread; + + OwnPtr<CCLayerImpl> root = createTreeForFixedPositionTests(); + CCLayerImpl* child = root->children()[0].get(); + CCLayerImpl* grandChild = child->children()[0].get(); + CCLayerImpl* greatGrandChild = grandChild->children()[0].get(); + + child->setIsContainerForFixedPositionLayers(true); + grandChild->setPosition(FloatPoint(8, 6)); + greatGrandChild->setFixedToContainerLayer(true); + + // Case 1: scrollDelta of 0, 0 + child->setScrollDelta(IntSize(0, 0)); + executeCalculateDrawTransformsAndVisibility(root.get()); + + WebTransformationMatrix expectedChildTransform; + expectedChildTransform.translate(50, 50); + + WebTransformationMatrix expectedGrandChildTransform; + expectedGrandChildTransform.translate(58, 56); + + WebTransformationMatrix expectedGreatGrandChildTransform = expectedGrandChildTransform; + + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildTransform, child->drawTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGrandChildTransform, grandChild->drawTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGreatGrandChildTransform, greatGrandChild->drawTransform()); + + // Case 2: scrollDelta of 10, 10 + child->setScrollDelta(IntSize(10, 10)); + executeCalculateDrawTransformsAndVisibility(root.get()); + + // Here the child and grandChild are affected by scrollDelta, but the fixed position greatGrandChild should not be affected. + expectedChildTransform.makeIdentity(); + expectedChildTransform.translate(40, 40); + expectedGrandChildTransform.makeIdentity(); + expectedGrandChildTransform.translate(48, 46); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildTransform, child->drawTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGrandChildTransform, grandChild->drawTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGreatGrandChildTransform, greatGrandChild->drawTransform()); +} + +TEST(CCLayerTreeHostCommonTest, verifyScrollCompensationForFixedPositionLayerWithDistantContainerAndTransforms) +{ + // This test checks for correct scroll compensation when the fixed-position container + // is NOT the direct parent of the fixed-position layer, and the hierarchy has various + // transforms that have to be processed in the correct order. + DebugScopedSetImplThread scopedImplThread; + + OwnPtr<CCLayerImpl> root = createTreeForFixedPositionTests(); + CCLayerImpl* child = root->children()[0].get(); + CCLayerImpl* grandChild = child->children()[0].get(); + CCLayerImpl* greatGrandChild = grandChild->children()[0].get(); + + WebTransformationMatrix rotationAboutZ; + rotationAboutZ.rotate3d(0, 0, 90); + + child->setIsContainerForFixedPositionLayers(true); + child->setTransform(rotationAboutZ); + grandChild->setPosition(FloatPoint(8, 6)); + grandChild->setTransform(rotationAboutZ); + greatGrandChild->setFixedToContainerLayer(true); // greatGrandChild is positioned upside-down with respect to the targetRenderSurface + + // Case 1: scrollDelta of 0, 0 + child->setScrollDelta(IntSize(0, 0)); + executeCalculateDrawTransformsAndVisibility(root.get()); + + WebTransformationMatrix expectedChildTransform; + expectedChildTransform.multiply(rotationAboutZ); + expectedChildTransform.translate(50, 50); + + WebTransformationMatrix expectedGrandChildTransform; + expectedGrandChildTransform.multiply(rotationAboutZ); // child's local transform is inherited + expectedGrandChildTransform.translate(8, 6); // translation because of position occurs before layer's local transform. + expectedGrandChildTransform.multiply(rotationAboutZ); // grandChild's local transform + expectedGrandChildTransform.translate(50, 50); // translation because of half-width half-height occurs after layer's local transform + + WebTransformationMatrix expectedGreatGrandChildTransform = expectedGrandChildTransform; + + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildTransform, child->drawTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGrandChildTransform, grandChild->drawTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGreatGrandChildTransform, greatGrandChild->drawTransform()); + + // Case 2: scrollDelta of 10, 20 + child->setScrollDelta(IntSize(10, 20)); + executeCalculateDrawTransformsAndVisibility(root.get()); + + // Here the child and grandChild are affected by scrollDelta, but the fixed position greatGrandChild should not be affected. + expectedChildTransform.makeIdentity(); + expectedChildTransform.translate(-10, -20); // scrollDelta + expectedChildTransform.multiply(rotationAboutZ); + expectedChildTransform.translate(50, 50); + + expectedGrandChildTransform.makeIdentity(); + expectedGrandChildTransform.translate(-10, -20); // child's scrollDelta is inherited + expectedGrandChildTransform.multiply(rotationAboutZ); // child's local transform is inherited + expectedGrandChildTransform.translate(8, 6); // translation because of position occurs before layer's local transform. + expectedGrandChildTransform.multiply(rotationAboutZ); // grandChild's local transform + expectedGrandChildTransform.translate(50, 50); // translation because of half-width half-height occurs after layer's local transform + + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildTransform, child->drawTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGrandChildTransform, grandChild->drawTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGreatGrandChildTransform, greatGrandChild->drawTransform()); +} + +TEST(CCLayerTreeHostCommonTest, verifyScrollCompensationForFixedPositionLayerWithMultipleScrollDeltas) +{ + // This test checks for correct scroll compensation when the fixed-position container + // has multiple ancestors that have nonzero scrollDelta before reaching the space where the layer is fixed. + // In this test, each scrollDelta occurs in a different space because of each layer's local transform. + // This test checks for correct scroll compensation when the fixed-position container + // is NOT the direct parent of the fixed-position layer, and the hierarchy has various + // transforms that have to be processed in the correct order. + DebugScopedSetImplThread scopedImplThread; + + OwnPtr<CCLayerImpl> root = createTreeForFixedPositionTests(); + CCLayerImpl* child = root->children()[0].get(); + CCLayerImpl* grandChild = child->children()[0].get(); + CCLayerImpl* greatGrandChild = grandChild->children()[0].get(); + + WebTransformationMatrix rotationAboutZ; + rotationAboutZ.rotate3d(0, 0, 90); + + child->setIsContainerForFixedPositionLayers(true); + child->setTransform(rotationAboutZ); + grandChild->setPosition(FloatPoint(8, 6)); + grandChild->setTransform(rotationAboutZ); + greatGrandChild->setFixedToContainerLayer(true); // greatGrandChild is positioned upside-down with respect to the targetRenderSurface + + // Case 1: scrollDelta of 0, 0 + child->setScrollDelta(IntSize(0, 0)); + executeCalculateDrawTransformsAndVisibility(root.get()); + + WebTransformationMatrix expectedChildTransform; + expectedChildTransform.multiply(rotationAboutZ); + expectedChildTransform.translate(50, 50); + + WebTransformationMatrix expectedGrandChildTransform; + expectedGrandChildTransform.multiply(rotationAboutZ); // child's local transform is inherited + expectedGrandChildTransform.translate(8, 6); // translation because of position occurs before layer's local transform. + expectedGrandChildTransform.multiply(rotationAboutZ); // grandChild's local transform + expectedGrandChildTransform.translate(50, 50); // translation because of half-width half-height occurs after layer's local transform + + WebTransformationMatrix expectedGreatGrandChildTransform = expectedGrandChildTransform; + + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildTransform, child->drawTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGrandChildTransform, grandChild->drawTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGreatGrandChildTransform, greatGrandChild->drawTransform()); + + // Case 2: scrollDelta of 10, 20 + child->setScrollDelta(IntSize(10, 0)); + grandChild->setScrollDelta(IntSize(5, 0)); + executeCalculateDrawTransformsAndVisibility(root.get()); + + // Here the child and grandChild are affected by scrollDelta, but the fixed position greatGrandChild should not be affected. + expectedChildTransform.makeIdentity(); + expectedChildTransform.translate(-10, 0); // scrollDelta + expectedChildTransform.multiply(rotationAboutZ); + expectedChildTransform.translate(50, 50); + + expectedGrandChildTransform.makeIdentity(); + expectedGrandChildTransform.translate(-10, 0); // child's scrollDelta is inherited + expectedGrandChildTransform.multiply(rotationAboutZ); // child's local transform is inherited + expectedGrandChildTransform.translate(-5, 0); // grandChild's scrollDelta + expectedGrandChildTransform.translate(8, 6); // translation because of position occurs before layer's local transform. + expectedGrandChildTransform.multiply(rotationAboutZ); // grandChild's local transform + expectedGrandChildTransform.translate(50, 50); // translation because of half-width half-height occurs after layer's local transform + + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildTransform, child->drawTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGrandChildTransform, grandChild->drawTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGreatGrandChildTransform, greatGrandChild->drawTransform()); +} + +TEST(CCLayerTreeHostCommonTest, verifyScrollCompensationForFixedPositionLayerWithIntermediateSurfaceAndTransforms) +{ + // This test checks for correct scroll compensation when the fixed-position container + // contributes to a different renderSurface than the fixed-position layer. In this + // case, the surface originTransforms also have to be accounted for when checking the + // scrollDelta. + DebugScopedSetImplThread scopedImplThread; + + OwnPtr<CCLayerImpl> root = createTreeForFixedPositionTests(); + CCLayerImpl* child = root->children()[0].get(); + CCLayerImpl* grandChild = child->children()[0].get(); + CCLayerImpl* greatGrandChild = grandChild->children()[0].get(); + + child->setIsContainerForFixedPositionLayers(true); + grandChild->setPosition(FloatPoint(8, 6)); + grandChild->setForceRenderSurface(true); + greatGrandChild->setFixedToContainerLayer(true); + greatGrandChild->setDrawsContent(true); + + WebTransformationMatrix rotationAboutZ; + rotationAboutZ.rotate3d(0, 0, 90); + grandChild->setTransform(rotationAboutZ); + + // Case 1: scrollDelta of 0, 0 + child->setScrollDelta(IntSize(0, 0)); + executeCalculateDrawTransformsAndVisibility(root.get()); + + WebTransformationMatrix expectedChildTransform; + expectedChildTransform.translate(50, 50); + WebTransformationMatrix expectedSurfaceOriginTransform; + expectedSurfaceOriginTransform.translate(8, 6); + expectedSurfaceOriginTransform.multiply(rotationAboutZ); + WebTransformationMatrix expectedGrandChildTransform; + expectedGrandChildTransform.translate(50, 50); + WebTransformationMatrix expectedGreatGrandChildTransform; + expectedGreatGrandChildTransform.translate(50, 50); + ASSERT_TRUE(grandChild->renderSurface()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildTransform, child->drawTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedSurfaceOriginTransform, grandChild->renderSurface()->originTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGrandChildTransform, grandChild->drawTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGreatGrandChildTransform, greatGrandChild->drawTransform()); + + // Case 2: scrollDelta of 10, 30 + child->setScrollDelta(IntSize(10, 30)); + executeCalculateDrawTransformsAndVisibility(root.get()); + + // Here the grandChild remains unchanged, because it scrolls along with the + // renderSurface, and the translation is actually in the renderSurface. But, the fixed + // position greatGrandChild is more awkward: its actually being drawn with respect to + // the renderSurface, but it needs to remain fixed with resepct to a container beyond + // that surface. So, the net result is that, unlike previous tests where the fixed + // position layer's transform remains unchanged, here the fixed position layer's + // transform explicitly contains the translation that cancels out the scroll. + expectedChildTransform.makeIdentity(); + expectedChildTransform.translate(-10, -30); // scrollDelta + expectedChildTransform.translate(50, 50); + + expectedSurfaceOriginTransform.makeIdentity(); + expectedSurfaceOriginTransform.translate(-10, -30); // scrollDelta + expectedSurfaceOriginTransform.translate(8, 6); + expectedSurfaceOriginTransform.multiply(rotationAboutZ); + + // The rotation and its inverse are needed to place the scrollDelta compensation in + // the correct space. This test will fail if the rotation/inverse are backwards, too, + // so it requires perfect order of operations. + expectedGreatGrandChildTransform.makeIdentity(); + expectedGreatGrandChildTransform.multiply(rotationAboutZ.inverse()); + expectedGreatGrandChildTransform.translate(10, 30); // explicit canceling out the scrollDelta that gets embedded in the fixed position layer's surface. + expectedGreatGrandChildTransform.multiply(rotationAboutZ); + expectedGreatGrandChildTransform.translate(50, 50); + + ASSERT_TRUE(grandChild->renderSurface()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildTransform, child->drawTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedSurfaceOriginTransform, grandChild->renderSurface()->originTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGrandChildTransform, grandChild->drawTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGreatGrandChildTransform, greatGrandChild->drawTransform()); +} + +TEST(CCLayerTreeHostCommonTest, verifyScrollCompensationForFixedPositionLayerWithMultipleIntermediateSurfaces) +{ + // This test checks for correct scroll compensation when the fixed-position container + // contributes to a different renderSurface than the fixed-position layer, with + // additional renderSurfaces in-between. This checks that the conversion to ancestor + // surfaces is accumulated properly in the final matrix transform. + DebugScopedSetImplThread scopedImplThread; + + OwnPtr<CCLayerImpl> root = createTreeForFixedPositionTests(); + CCLayerImpl* child = root->children()[0].get(); + CCLayerImpl* grandChild = child->children()[0].get(); + CCLayerImpl* greatGrandChild = grandChild->children()[0].get(); + + // Add one more layer to the test tree for this scenario. + { + WebTransformationMatrix identity; + OwnPtr<CCLayerImpl> fixedPositionChild = CCLayerImpl::create(5); + setLayerPropertiesForTesting(fixedPositionChild.get(), identity, identity, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false); + greatGrandChild->addChild(fixedPositionChild.release()); + } + CCLayerImpl* fixedPositionChild = greatGrandChild->children()[0].get(); + + // Actually set up the scenario here. + child->setIsContainerForFixedPositionLayers(true); + grandChild->setPosition(FloatPoint(8, 6)); + grandChild->setForceRenderSurface(true); + greatGrandChild->setPosition(FloatPoint(140, 120)); + greatGrandChild->setForceRenderSurface(true); + fixedPositionChild->setFixedToContainerLayer(true); + fixedPositionChild->setDrawsContent(true); + + // The additional rotations, which are non-commutative with translations, help to + // verify that we have correct order-of-operations in the final scroll compensation. + WebTransformationMatrix rotationAboutZ; + rotationAboutZ.rotate3d(0, 0, 90); + grandChild->setTransform(rotationAboutZ); + greatGrandChild->setTransform(rotationAboutZ); + + // Case 1: scrollDelta of 0, 0 + child->setScrollDelta(IntSize(0, 0)); + executeCalculateDrawTransformsAndVisibility(root.get()); + + WebTransformationMatrix expectedChildTransform; + expectedChildTransform.translate(50, 50); + + WebTransformationMatrix expectedGrandChildSurfaceOriginTransform; + expectedGrandChildSurfaceOriginTransform.translate(8, 6); + expectedGrandChildSurfaceOriginTransform.multiply(rotationAboutZ); + + WebTransformationMatrix expectedGrandChildTransform; + expectedGrandChildTransform.translate(50, 50); + + WebTransformationMatrix expectedGreatGrandChildSurfaceOriginTransform; + expectedGreatGrandChildSurfaceOriginTransform.translate(140, 120); + expectedGreatGrandChildSurfaceOriginTransform.multiply(rotationAboutZ); + + WebTransformationMatrix expectedGreatGrandChildTransform; + expectedGreatGrandChildTransform.translate(50, 50); + + WebTransformationMatrix expectedFixedPositionChildTransform; + expectedFixedPositionChildTransform.translate(50, 50); + + ASSERT_TRUE(grandChild->renderSurface()); + ASSERT_TRUE(greatGrandChild->renderSurface()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildTransform, child->drawTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGrandChildSurfaceOriginTransform, grandChild->renderSurface()->originTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGrandChildTransform, grandChild->drawTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGreatGrandChildSurfaceOriginTransform, greatGrandChild->renderSurface()->originTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGreatGrandChildTransform, greatGrandChild->drawTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedFixedPositionChildTransform, fixedPositionChild->drawTransform()); + + // Case 2: scrollDelta of 10, 30 + child->setScrollDelta(IntSize(10, 30)); + executeCalculateDrawTransformsAndVisibility(root.get()); + + expectedChildTransform.makeIdentity(); + expectedChildTransform.translate(-10, -30); // scrollDelta + expectedChildTransform.translate(50, 50); + + expectedGrandChildSurfaceOriginTransform.makeIdentity(); + expectedGrandChildSurfaceOriginTransform.translate(-10, -30); // scrollDelta + expectedGrandChildSurfaceOriginTransform.translate(8, 6); + expectedGrandChildSurfaceOriginTransform.multiply(rotationAboutZ); + + // grandChild, greatGrandChild, and greatGrandChild's surface are not expected to + // change, since they are all not fixed, and they are all drawn with respect to + // grandChild's surface that already has the scrollDelta accounted for. + + // But the great-great grandchild, "fixedPositionChild", should have a transform that explicitly cancels out the scrollDelta. + // The expected transform is: + // compoundOriginTransform.inverse() * translate(positive scrollDelta) * compoundOriginTransform * half-width-half-height translation + WebTransformationMatrix compoundOriginTransform; // transform from greatGrandChildSurface's origin to the root surface. + compoundOriginTransform.translate(8, 6); // origin translation of grandChild + compoundOriginTransform.multiply(rotationAboutZ); // rotation of grandChild + compoundOriginTransform.translate(140, 120); // origin translation of greatGrandChild + compoundOriginTransform.multiply(rotationAboutZ); // rotation of greatGrandChild + + expectedFixedPositionChildTransform.makeIdentity(); + expectedFixedPositionChildTransform.multiply(compoundOriginTransform.inverse()); + expectedFixedPositionChildTransform.translate(10, 30); // explicit canceling out the scrollDelta that gets embedded in the fixed position layer's surface. + expectedFixedPositionChildTransform.multiply(compoundOriginTransform); + expectedFixedPositionChildTransform.translate(50, 50); + + ASSERT_TRUE(grandChild->renderSurface()); + ASSERT_TRUE(greatGrandChild->renderSurface()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildTransform, child->drawTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGrandChildSurfaceOriginTransform, grandChild->renderSurface()->originTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGrandChildTransform, grandChild->drawTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGreatGrandChildSurfaceOriginTransform, greatGrandChild->renderSurface()->originTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGreatGrandChildTransform, greatGrandChild->drawTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedFixedPositionChildTransform, fixedPositionChild->drawTransform()); +} + +TEST(CCLayerTreeHostCommonTest, verifyScrollCompensationForFixedPositionLayerWithContainerLayerThatHasSurface) +{ + // This test checks for correct scroll compensation when the fixed-position container + // itself has a renderSurface. In this case, the container layer should be treated + // like a layer that contributes to a targetRenderSurface, and that targetRenderSurface + // is completely irrelevant; it should not affect the scroll compensation. + DebugScopedSetImplThread scopedImplThread; + + OwnPtr<CCLayerImpl> root = createTreeForFixedPositionTests(); + CCLayerImpl* child = root->children()[0].get(); + CCLayerImpl* grandChild = child->children()[0].get(); + + child->setIsContainerForFixedPositionLayers(true); + child->setForceRenderSurface(true); + grandChild->setFixedToContainerLayer(true); + grandChild->setDrawsContent(true); + + // Case 1: scrollDelta of 0, 0 + child->setScrollDelta(IntSize(0, 0)); + executeCalculateDrawTransformsAndVisibility(root.get()); + + // The expected draw transforms without any scroll should still include a translation to the center of the layer (i.e. translation by 50, 50). + WebTransformationMatrix expectedSurfaceOriginTransform; + expectedSurfaceOriginTransform.translate(0, 0); + WebTransformationMatrix expectedChildTransform; + expectedChildTransform.translate(50, 50); + WebTransformationMatrix expectedGrandChildTransform; + expectedGrandChildTransform.translate(50, 50); + ASSERT_TRUE(child->renderSurface()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedSurfaceOriginTransform, child->renderSurface()->originTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildTransform, child->drawTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGrandChildTransform, grandChild->drawTransform()); + + // Case 2: scrollDelta of 10, 10 + child->setScrollDelta(IntSize(10, 10)); + executeCalculateDrawTransformsAndVisibility(root.get()); + + // The surface is translated by scrollDelta, the child transform doesn't change + // because it scrolls along with the surface, but the fixed position grandChild + // needs to compensate for the scroll translation. + expectedSurfaceOriginTransform.makeIdentity(); + expectedSurfaceOriginTransform.translate(-10, -10); + expectedGrandChildTransform.makeIdentity(); + expectedGrandChildTransform.translate(60, 60); + ASSERT_TRUE(child->renderSurface()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedSurfaceOriginTransform, child->renderSurface()->originTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildTransform, child->drawTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGrandChildTransform, grandChild->drawTransform()); +} + +TEST(CCLayerTreeHostCommonTest, verifyScrollCompensationForFixedPositionLayerThatIsAlsoFixedPositionContainer) +{ + // This test checks the scenario where a fixed-position layer also happens to be a + // container itself for a descendant fixed position layer. In particular, the layer + // should not accidentally be fixed to itself. + DebugScopedSetImplThread scopedImplThread; + + OwnPtr<CCLayerImpl> root = createTreeForFixedPositionTests(); + CCLayerImpl* child = root->children()[0].get(); + CCLayerImpl* grandChild = child->children()[0].get(); + + child->setIsContainerForFixedPositionLayers(true); + grandChild->setFixedToContainerLayer(true); + + // This should not confuse the grandChild. If correct, the grandChild would still be considered fixed to its container (i.e. "child"). + grandChild->setIsContainerForFixedPositionLayers(true); + + // Case 1: scrollDelta of 0, 0 + child->setScrollDelta(IntSize(0, 0)); + executeCalculateDrawTransformsAndVisibility(root.get()); + + // The expected draw transforms without any scroll should still include a translation to the center of the layer (i.e. translation by 50, 50). + WebTransformationMatrix expectedChildTransform; + expectedChildTransform.translate(50, 50); + WebTransformationMatrix expectedGrandChildTransform; + expectedGrandChildTransform.translate(50, 50); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildTransform, child->drawTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGrandChildTransform, grandChild->drawTransform()); + + // Case 2: scrollDelta of 10, 10 + child->setScrollDelta(IntSize(10, 10)); + executeCalculateDrawTransformsAndVisibility(root.get()); + + // Here the child is affected by scrollDelta, but the fixed position grandChild should not be affected. + expectedChildTransform.makeIdentity(); + expectedChildTransform.translate(40, 40); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildTransform, child->drawTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGrandChildTransform, grandChild->drawTransform()); +} + +TEST(CCLayerTreeHostCommonTest, verifyScrollCompensationForFixedPositionLayerThatHasNoContainer) +{ + // This test checks scroll compensation when a fixed-position layer does not find any + // ancestor that is a "containerForFixedPositionLayers". In this situation, the layer should + // be fixed to the viewport -- not the rootLayer, which may have transforms of its own. + DebugScopedSetImplThread scopedImplThread; + + OwnPtr<CCLayerImpl> root = createTreeForFixedPositionTests(); + CCLayerImpl* child = root->children()[0].get(); + CCLayerImpl* grandChild = child->children()[0].get(); + + WebTransformationMatrix rotationByZ; + rotationByZ.rotate3d(0, 0, 90); + + root->setTransform(rotationByZ); + grandChild->setFixedToContainerLayer(true); + + // Case 1: root scrollDelta of 0, 0 + root->setScrollDelta(IntSize(0, 0)); + executeCalculateDrawTransformsAndVisibility(root.get()); + + // The expected draw transforms without any scroll should still include a translation to the center of the layer (i.e. translation by 50, 50). + WebTransformationMatrix expectedChildTransform; + expectedChildTransform.multiply(rotationByZ); + expectedChildTransform.translate(50, 50); + + WebTransformationMatrix expectedGrandChildTransform; + expectedGrandChildTransform.multiply(rotationByZ); + expectedGrandChildTransform.translate(50, 50); + + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildTransform, child->drawTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGrandChildTransform, grandChild->drawTransform()); + + // Case 2: root scrollDelta of 10, 10 + root->setScrollDelta(IntSize(10, 10)); + executeCalculateDrawTransformsAndVisibility(root.get()); + + // Here the child is affected by scrollDelta, but the fixed position grandChild should not be affected. + expectedChildTransform.makeIdentity(); + expectedChildTransform.translate(-10, -10); // the scrollDelta + expectedChildTransform.multiply(rotationByZ); + expectedChildTransform.translate(50, 50); + + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedChildTransform, child->drawTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(expectedGrandChildTransform, grandChild->drawTransform()); +} + TEST(CCLayerTreeHostCommonTest, verifyClipRectCullsRenderSurfaces) { // The entire subtree of layers that are outside the clipRect should be culled away, @@ -666,7 +1870,11 @@ TEST(CCLayerTreeHostCommonTest, verifyClipRectCullsRenderSurfaces) parent->setClipRect(IntRect(IntPoint::zero(), parent->bounds())); renderSurfaceLayerList.append(parent.get()); - CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); + CCLayerTreeHostCommon::calculateDrawTransforms(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); + + FloatRect dummyDamageRect; + CCLayerTreeHostCommon::calculateVisibleAndScissorRects(renderSurfaceLayerList, parent->renderSurface()->contentRect()); + ASSERT_EQ(2U, renderSurfaceLayerList.size()); EXPECT_EQ(parent->id(), renderSurfaceLayerList[0]->id()); @@ -725,7 +1933,9 @@ TEST(CCLayerTreeHostCommonTest, verifyClipRectCullsRenderSurfacesCrashRepro) parent->setClipRect(IntRect(IntPoint::zero(), parent->bounds())); renderSurfaceLayerList.append(parent.get()); - CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); + CCLayerTreeHostCommon::calculateDrawTransforms(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); + + CCLayerTreeHostCommon::calculateVisibleAndScissorRects(renderSurfaceLayerList, parent->renderSurface()->contentRect()); ASSERT_EQ(2U, renderSurfaceLayerList.size()); EXPECT_EQ(parent->id(), renderSurfaceLayerList[0]->id()); @@ -776,7 +1986,7 @@ TEST(CCLayerTreeHostCommonTest, verifyClipRectCullsSurfaceWithoutVisibleContent) parent->createRenderSurface(); renderSurfaceLayerList.append(parent.get()); - CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); + CCLayerTreeHostCommon::calculateDrawTransforms(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); // Without an animation, we should cull child and grandChild from the renderSurfaceLayerList. ASSERT_EQ(1U, renderSurfaceLayerList.size()); @@ -795,7 +2005,7 @@ TEST(CCLayerTreeHostCommonTest, verifyClipRectCullsSurfaceWithoutVisibleContent) parent->createRenderSurface(); renderSurfaceLayerList.append(parent.get()); - CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); + CCLayerTreeHostCommon::calculateDrawTransforms(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); // With an animating transform, we should keep child and grandChild in the renderSurfaceLayerList. ASSERT_EQ(3U, renderSurfaceLayerList.size()); @@ -855,7 +2065,10 @@ TEST(CCLayerTreeHostCommonTest, verifyClipRectIsPropagatedCorrectlyToLayers) parent->setClipRect(IntRect(IntPoint::zero(), parent->bounds())); renderSurfaceLayerList.append(parent.get()); - CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); + CCLayerTreeHostCommon::calculateDrawTransforms(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); + + CCLayerTreeHostCommon::calculateVisibleAndScissorRects(renderSurfaceLayerList, parent->renderSurface()->contentRect()); + EXPECT_INT_RECT_EQ(IntRect(IntPoint::zero(), IntSize(20, 20)), grandChild1->clipRect()); EXPECT_INT_RECT_EQ(IntRect(IntPoint::zero(), IntSize(20, 20)), grandChild2->clipRect()); @@ -927,7 +2140,9 @@ TEST(CCLayerTreeHostCommonTest, verifyClipRectIsPropagatedCorrectlyToSurfaces) parent->setClipRect(IntRect(IntPoint::zero(), parent->bounds())); renderSurfaceLayerList.append(parent.get()); - CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); + CCLayerTreeHostCommon::calculateDrawTransforms(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); + + CCLayerTreeHostCommon::calculateVisibleAndScissorRects(renderSurfaceLayerList, parent->renderSurface()->contentRect()); ASSERT_TRUE(grandChild1->renderSurface()); ASSERT_TRUE(grandChild2->renderSurface()); @@ -1408,7 +2623,7 @@ TEST(CCLayerTreeHostCommonTest, verifyBackFaceCullingWithoutPreserves3d) parent->setClipRect(IntRect(IntPoint::zero(), parent->bounds())); renderSurfaceLayerList.append(parent.get()); - CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); + CCLayerTreeHostCommon::calculateDrawTransforms(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); // Verify which renderSurfaces were created. EXPECT_FALSE(frontFacingChild->renderSurface()); @@ -1513,7 +2728,7 @@ TEST(CCLayerTreeHostCommonTest, verifyBackFaceCullingWithPreserves3d) parent->setClipRect(IntRect(IntPoint::zero(), parent->bounds())); renderSurfaceLayerList.append(parent.get()); - CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); + CCLayerTreeHostCommon::calculateDrawTransforms(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); // Verify which renderSurfaces were created. EXPECT_FALSE(frontFacingChild->renderSurface()); @@ -1598,7 +2813,9 @@ TEST(CCLayerTreeHostCommonTest, verifyBackFaceCullingWithAnimatingTransforms) parent->setClipRect(IntRect(IntPoint::zero(), parent->bounds())); renderSurfaceLayerList.append(parent.get()); - CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); + CCLayerTreeHostCommon::calculateDrawTransforms(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); + + CCLayerTreeHostCommon::calculateVisibleAndScissorRects(renderSurfaceLayerList, parent->renderSurface()->contentRect()); EXPECT_FALSE(child->renderSurface()); EXPECT_TRUE(animatingSurface->renderSurface()); @@ -1670,7 +2887,7 @@ TEST(CCLayerTreeHostCommonTest, verifyBackFaceCullingWithPreserves3dForFlattenin parent->setClipRect(IntRect(IntPoint::zero(), parent->bounds())); renderSurfaceLayerList.append(parent.get()); - CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); + CCLayerTreeHostCommon::calculateDrawTransforms(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); // Verify which renderSurfaces were created. EXPECT_TRUE(frontFacingSurface->renderSurface()); @@ -1693,6 +2910,780 @@ TEST(CCLayerTreeHostCommonTest, verifyBackFaceCullingWithPreserves3dForFlattenin EXPECT_EQ(child1->id(), renderSurfaceLayerList[1]->renderSurface()->layerList()[1]->id()); } +TEST(CCLayerTreeHostCommonTest, verifyHitTestingForEmptyLayerList) +{ + // Hit testing on an empty renderSurfaceLayerList should return a null pointer. + DebugScopedSetImplThread thisScopeIsOnImplThread; + + Vector<CCLayerImpl*> renderSurfaceLayerList; + + IntPoint testPoint(0, 0); + CCLayerImpl* resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + EXPECT_FALSE(resultLayer); + + testPoint = IntPoint(10, 20); + resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + EXPECT_FALSE(resultLayer); +} + +TEST(CCLayerTreeHostCommonTest, verifyHitTestingForSingleLayer) +{ + DebugScopedSetImplThread thisScopeIsOnImplThread; + + OwnPtr<CCLayerImpl> root = CCLayerImpl::create(12345); + root->createRenderSurface(); + root->renderSurface()->setContentRect(IntRect(IntPoint::zero(), IntSize(100, 100))); + + WebTransformationMatrix identityMatrix; + FloatPoint anchor(0, 0); + FloatPoint position(0, 0); + IntSize bounds(100, 100); + setLayerPropertiesForTesting(root.get(), identityMatrix, identityMatrix, anchor, position, bounds, false); + root->setDrawsContent(true); + + Vector<CCLayerImpl*> renderSurfaceLayerList; + Vector<CCLayerImpl*> dummyLayerList; + int dummyMaxTextureSize = 512; + renderSurfaceLayerList.append(root.get()); + CCLayerTreeHostCommon::calculateDrawTransforms(root.get(), root.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, 0, dummyMaxTextureSize); + CCLayerTreeHostCommon::calculateVisibleAndScissorRects(renderSurfaceLayerList, FloatRect()); // empty scissorRect will help ensure we're hit testing the correct rect. + + // Sanity check the scenario we just created. + ASSERT_EQ(1u, renderSurfaceLayerList.size()); + ASSERT_EQ(1u, root->renderSurface()->layerList().size()); + + // Hit testing for a point outside the layer should return a null pointer. + IntPoint testPoint(101, 101); + CCLayerImpl* resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + EXPECT_FALSE(resultLayer); + + testPoint = IntPoint(-1, -1); + resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + EXPECT_FALSE(resultLayer); + + // Hit testing for a point inside should return the root layer. + testPoint = IntPoint(1, 1); + resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + ASSERT_TRUE(resultLayer); + EXPECT_EQ(12345, resultLayer->id()); + + testPoint = IntPoint(99, 99); + resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + ASSERT_TRUE(resultLayer); + EXPECT_EQ(12345, resultLayer->id()); +} + +TEST(CCLayerTreeHostCommonTest, verifyHitTestingForUninvertibleTransform) +{ + DebugScopedSetImplThread thisScopeIsOnImplThread; + + OwnPtr<CCLayerImpl> root = CCLayerImpl::create(12345); + root->createRenderSurface(); + root->renderSurface()->setContentRect(IntRect(IntPoint::zero(), IntSize(100, 100))); + + WebTransformationMatrix uninvertibleTransform; + uninvertibleTransform.setM11(0); + uninvertibleTransform.setM22(0); + uninvertibleTransform.setM33(0); + uninvertibleTransform.setM44(0); + ASSERT_FALSE(uninvertibleTransform.isInvertible()); + + WebTransformationMatrix identityMatrix; + FloatPoint anchor(0, 0); + FloatPoint position(0, 0); + IntSize bounds(100, 100); + setLayerPropertiesForTesting(root.get(), uninvertibleTransform, identityMatrix, anchor, position, bounds, false); + root->setDrawsContent(true); + + Vector<CCLayerImpl*> renderSurfaceLayerList; + Vector<CCLayerImpl*> dummyLayerList; + int dummyMaxTextureSize = 512; + renderSurfaceLayerList.append(root.get()); + CCLayerTreeHostCommon::calculateDrawTransforms(root.get(), root.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, 0, dummyMaxTextureSize); + CCLayerTreeHostCommon::calculateVisibleAndScissorRects(renderSurfaceLayerList, FloatRect()); // empty scissorRect will help ensure we're hit testing the correct rect. + + // Sanity check the scenario we just created. + ASSERT_EQ(1u, renderSurfaceLayerList.size()); + ASSERT_EQ(1u, root->renderSurface()->layerList().size()); + ASSERT_FALSE(root->screenSpaceTransform().isInvertible()); + + // Hit testing any point should not hit the layer. If the invertible matrix is + // accidentally ignored and treated like an identity, then the hit testing will + // incorrectly hit the layer when it shouldn't. + IntPoint testPoint(1, 1); + CCLayerImpl* resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + EXPECT_FALSE(resultLayer); + + testPoint = IntPoint(10, 10); + resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + EXPECT_FALSE(resultLayer); + + testPoint = IntPoint(10, 30); + resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + EXPECT_FALSE(resultLayer); + + testPoint = IntPoint(50, 50); + resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + EXPECT_FALSE(resultLayer); + + testPoint = IntPoint(67, 48); + resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + EXPECT_FALSE(resultLayer); + + testPoint = IntPoint(99, 99); + resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + EXPECT_FALSE(resultLayer); + + testPoint = IntPoint(-1, -1); + resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + EXPECT_FALSE(resultLayer); +} + +TEST(CCLayerTreeHostCommonTest, verifyHitTestingForSinglePositionedLayer) +{ + DebugScopedSetImplThread thisScopeIsOnImplThread; + + OwnPtr<CCLayerImpl> root = CCLayerImpl::create(12345); + root->createRenderSurface(); + root->renderSurface()->setContentRect(IntRect(IntPoint::zero(), IntSize(100, 100))); + + WebTransformationMatrix identityMatrix; + FloatPoint anchor(0, 0); + FloatPoint position(50, 50); // this layer is positioned, and hit testing should correctly know where the layer is located. + IntSize bounds(100, 100); + setLayerPropertiesForTesting(root.get(), identityMatrix, identityMatrix, anchor, position, bounds, false); + root->setDrawsContent(true); + + Vector<CCLayerImpl*> renderSurfaceLayerList; + Vector<CCLayerImpl*> dummyLayerList; + int dummyMaxTextureSize = 512; + renderSurfaceLayerList.append(root.get()); + CCLayerTreeHostCommon::calculateDrawTransforms(root.get(), root.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, 0, dummyMaxTextureSize); + CCLayerTreeHostCommon::calculateVisibleAndScissorRects(renderSurfaceLayerList, FloatRect()); // empty scissorRect will help ensure we're hit testing the correct rect. + + // Sanity check the scenario we just created. + ASSERT_EQ(1u, renderSurfaceLayerList.size()); + ASSERT_EQ(1u, root->renderSurface()->layerList().size()); + + // Hit testing for a point outside the layer should return a null pointer. + IntPoint testPoint(49, 49); + CCLayerImpl* resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + EXPECT_FALSE(resultLayer); + + // Even though the layer exists at (101, 101), it should not be visible there since the root renderSurface would clamp it. + testPoint = IntPoint(101, 101); + resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + EXPECT_FALSE(resultLayer); + + // Hit testing for a point inside should return the root layer. + testPoint = IntPoint(51, 51); + resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + ASSERT_TRUE(resultLayer); + EXPECT_EQ(12345, resultLayer->id()); + + testPoint = IntPoint(99, 99); + resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + ASSERT_TRUE(resultLayer); + EXPECT_EQ(12345, resultLayer->id()); +} + +TEST(CCLayerTreeHostCommonTest, verifyHitTestingForSingleRotatedLayer) +{ + DebugScopedSetImplThread thisScopeIsOnImplThread; + + OwnPtr<CCLayerImpl> root = CCLayerImpl::create(12345); + root->createRenderSurface(); + root->renderSurface()->setContentRect(IntRect(IntPoint::zero(), IntSize(100, 100))); + + WebTransformationMatrix identityMatrix; + WebTransformationMatrix rotation45DegreesAboutCenter; + rotation45DegreesAboutCenter.translate(50, 50); + rotation45DegreesAboutCenter.rotate3d(0, 0, 45); + rotation45DegreesAboutCenter.translate(-50, -50); + FloatPoint anchor(0, 0); + FloatPoint position(0, 0); + IntSize bounds(100, 100); + setLayerPropertiesForTesting(root.get(), rotation45DegreesAboutCenter, identityMatrix, anchor, position, bounds, false); + root->setDrawsContent(true); + + Vector<CCLayerImpl*> renderSurfaceLayerList; + Vector<CCLayerImpl*> dummyLayerList; + int dummyMaxTextureSize = 512; + renderSurfaceLayerList.append(root.get()); + CCLayerTreeHostCommon::calculateDrawTransforms(root.get(), root.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, 0, dummyMaxTextureSize); + CCLayerTreeHostCommon::calculateVisibleAndScissorRects(renderSurfaceLayerList, FloatRect()); // empty scissorRect will help ensure we're hit testing the correct rect. + + // Sanity check the scenario we just created. + ASSERT_EQ(1u, renderSurfaceLayerList.size()); + ASSERT_EQ(1u, root->renderSurface()->layerList().size()); + + // Hit testing for points outside the layer. + // These corners would have been inside the un-transformed layer, but they should not hit the correctly transformed layer. + IntPoint testPoint(99, 99); + CCLayerImpl* resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + EXPECT_FALSE(resultLayer); + + testPoint = IntPoint(1, 1); + resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + EXPECT_FALSE(resultLayer); + + // Hit testing for a point inside should return the root layer. + testPoint = IntPoint(1, 50); + resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + ASSERT_TRUE(resultLayer); + EXPECT_EQ(12345, resultLayer->id()); + + // Hit testing the corners that would overlap the unclipped layer, but are outside the clipped region. + testPoint = IntPoint(50, -1); + resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + ASSERT_FALSE(resultLayer); + + testPoint = IntPoint(-1, 50); + resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + ASSERT_FALSE(resultLayer); +} + +TEST(CCLayerTreeHostCommonTest, verifyHitTestingForSinglePerspectiveLayer) +{ + DebugScopedSetImplThread thisScopeIsOnImplThread; + + OwnPtr<CCLayerImpl> root = CCLayerImpl::create(12345); + root->createRenderSurface(); + root->renderSurface()->setContentRect(IntRect(IntPoint::zero(), IntSize(100, 100))); + + WebTransformationMatrix identityMatrix; + + // perspectiveProjectionAboutCenter * translationByZ is designed so that the 100 x 100 layer becomes 50 x 50, and remains centered at (50, 50). + WebTransformationMatrix perspectiveProjectionAboutCenter; + perspectiveProjectionAboutCenter.translate(50, 50); + perspectiveProjectionAboutCenter.applyPerspective(1); + perspectiveProjectionAboutCenter.translate(-50, -50); + WebTransformationMatrix translationByZ; + translationByZ.translate3d(0, 0, -1); + + FloatPoint anchor(0, 0); + FloatPoint position(0, 0); + IntSize bounds(100, 100); + setLayerPropertiesForTesting(root.get(), perspectiveProjectionAboutCenter * translationByZ, identityMatrix, anchor, position, bounds, false); + root->setDrawsContent(true); + + Vector<CCLayerImpl*> renderSurfaceLayerList; + Vector<CCLayerImpl*> dummyLayerList; + int dummyMaxTextureSize = 512; + renderSurfaceLayerList.append(root.get()); + CCLayerTreeHostCommon::calculateDrawTransforms(root.get(), root.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, 0, dummyMaxTextureSize); + CCLayerTreeHostCommon::calculateVisibleAndScissorRects(renderSurfaceLayerList, FloatRect()); // empty scissorRect will help ensure we're hit testing the correct rect. + + // Sanity check the scenario we just created. + ASSERT_EQ(1u, renderSurfaceLayerList.size()); + ASSERT_EQ(1u, root->renderSurface()->layerList().size()); + + // Hit testing for points outside the layer. + // These corners would have been inside the un-transformed layer, but they should not hit the correctly transformed layer. + IntPoint testPoint(24, 24); + CCLayerImpl* resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + EXPECT_FALSE(resultLayer); + + testPoint = IntPoint(76, 76); + resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + EXPECT_FALSE(resultLayer); + + // Hit testing for a point inside should return the root layer. + testPoint = IntPoint(26, 26); + resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + ASSERT_TRUE(resultLayer); + EXPECT_EQ(12345, resultLayer->id()); + + testPoint = IntPoint(74, 74); + resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + ASSERT_TRUE(resultLayer); + EXPECT_EQ(12345, resultLayer->id()); +} + +TEST(CCLayerTreeHostCommonTest, verifyHitTestingForSingleLayerWithScaledContents) +{ + // A layer's visibleLayerRect is actually in the layer's content space. The + // screenSpaceTransform converts from the layer's origin space to screen space. This + // test makes sure that hit testing works correctly accounts for the contents scale. + // A contentsScale that is not 1 effectively forces a non-identity transform between + // layer's content space and layer's origin space, which is not included in the + // screenSpaceTransformn. The hit testing code must take this into account. + // + // To test this, the layer is positioned at (25, 25), and is size (50, 50). If + // contentsScale is ignored, then hit testing will mis-interpret the visibleLayerRect + // as being larger than the actual bounds of the layer. + // + DebugScopedSetImplThread thisScopeIsOnImplThread; + + OwnPtr<CCLayerImpl> root = CCLayerImpl::create(12345); + root->createRenderSurface(); + root->renderSurface()->setContentRect(IntRect(IntPoint::zero(), IntSize(100, 100))); + + WebTransformationMatrix identityMatrix; + FloatPoint anchor(0, 0); + FloatPoint position(25, 25); + IntSize bounds(50, 50); + setLayerPropertiesForTesting(root.get(), identityMatrix, identityMatrix, anchor, position, bounds, false); + root->setDrawsContent(true); + + root->setContentBounds(IntSize(100, 100)); + + Vector<CCLayerImpl*> renderSurfaceLayerList; + Vector<CCLayerImpl*> dummyLayerList; + int dummyMaxTextureSize = 512; + renderSurfaceLayerList.append(root.get()); + CCLayerTreeHostCommon::calculateDrawTransforms(root.get(), root.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, 0, dummyMaxTextureSize); + CCLayerTreeHostCommon::calculateVisibleAndScissorRects(renderSurfaceLayerList, FloatRect()); // empty scissorRect will help ensure we're hit testing the correct rect. + + // Sanity check the scenario we just created. + // The visibleLayerRect is actually 100x100, even though the layout size of the layer is 50x50, positioned at 25x25. + EXPECT_INT_RECT_EQ(IntRect(IntPoint::zero(), IntSize(100, 100)), root->visibleLayerRect()); + ASSERT_EQ(1u, renderSurfaceLayerList.size()); + ASSERT_EQ(1u, root->renderSurface()->layerList().size()); + + // Hit testing for a point outside the layer should return a null pointer. + IntPoint testPoint(24, 24); + CCLayerImpl* resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + EXPECT_FALSE(resultLayer); + + // Even though the layer exists at (101, 101), it should not be visible there since the root renderSurface would clamp it. + // This case in particular is likely to fail if contents scale is not correctly accounted for. + testPoint = IntPoint(76, 76); + resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + EXPECT_FALSE(resultLayer); + + // Hit testing for a point inside should return the root layer. + testPoint = IntPoint(26, 26); + resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + ASSERT_TRUE(resultLayer); + EXPECT_EQ(12345, resultLayer->id()); + + testPoint = IntPoint(74, 74); + resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + ASSERT_TRUE(resultLayer); + EXPECT_EQ(12345, resultLayer->id()); +} + +TEST(CCLayerTreeHostCommonTest, verifyHitTestingForSimpleClippedLayer) +{ + // Test that hit-testing will only work for the visible portion of a layer, and not + // the entire layer bounds. Here we just test the simple axis-aligned case. + DebugScopedSetImplThread thisScopeIsOnImplThread; + + OwnPtr<CCLayerImpl> root = CCLayerImpl::create(123); + root->createRenderSurface(); + root->renderSurface()->setContentRect(IntRect(IntPoint::zero(), IntSize(100, 100))); + + WebTransformationMatrix identityMatrix; + FloatPoint anchor(0, 0); + FloatPoint position(25, 25); // this layer is positioned, and hit testing should correctly know where the layer is located. + IntSize bounds(50, 50); + setLayerPropertiesForTesting(root.get(), identityMatrix, identityMatrix, anchor, position, bounds, false); + root->setMasksToBounds(true); + + { + OwnPtr<CCLayerImpl> child = CCLayerImpl::create(456); + position = FloatPoint(-50, -50); + bounds = IntSize(300, 300); + setLayerPropertiesForTesting(child.get(), identityMatrix, identityMatrix, anchor, position, bounds, false); + child->setDrawsContent(true); + root->addChild(child.release()); + } + + Vector<CCLayerImpl*> renderSurfaceLayerList; + Vector<CCLayerImpl*> dummyLayerList; + int dummyMaxTextureSize = 512; + renderSurfaceLayerList.append(root.get()); + CCLayerTreeHostCommon::calculateDrawTransforms(root.get(), root.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, 0, dummyMaxTextureSize); + CCLayerTreeHostCommon::calculateVisibleAndScissorRects(renderSurfaceLayerList, FloatRect()); // empty scissorRect will help ensure we're hit testing the correct rect. + + // Sanity check the scenario we just created. + ASSERT_EQ(1u, renderSurfaceLayerList.size()); + ASSERT_EQ(1u, root->renderSurface()->layerList().size()); + + // Hit testing for a point outside the layer should return a null pointer. + // Despite the child layer being very large, it should be clipped to the root layer's bounds. + IntPoint testPoint(24, 24); + CCLayerImpl* resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + EXPECT_FALSE(resultLayer); + + // Even though the layer exists at (101, 101), it should not be visible there since the root renderSurface would clamp it. + testPoint = IntPoint(76, 76); + resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + EXPECT_FALSE(resultLayer); + + // Hit testing for a point inside should return the child layer. + testPoint = IntPoint(26, 26); + resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + ASSERT_TRUE(resultLayer); + EXPECT_EQ(456, resultLayer->id()); + + testPoint = IntPoint(74, 74); + resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + ASSERT_TRUE(resultLayer); + EXPECT_EQ(456, resultLayer->id()); +} + +TEST(CCLayerTreeHostCommonTest, verifyHitTestingForMultiClippedRotatedLayer) +{ + // This test checks whether hit testing correctly avoids hit testing with multiple + // ancestors that clip in non axis-aligned ways. To pass this test, the hit testing + // algorithm needs to recognize that multiple parent layers may clip the layer, and + // should not actually hit those clipped areas. + // + // The child and grandChild layers are both initialized to clip the rotatedLeaf. The + // child layer is rotated about the top-left corner, so that the root + child clips + // combined create a triangle. The rotatedLeaf will only be visible where it overlaps + // this triangle. + // + DebugScopedSetImplThread thisScopeIsOnImplThread; + + OwnPtr<CCLayerImpl> root = CCLayerImpl::create(123); + root->createRenderSurface(); + root->renderSurface()->setContentRect(IntRect(IntPoint::zero(), IntSize(100, 100))); + + WebTransformationMatrix identityMatrix; + FloatPoint anchor(0, 0); + FloatPoint position(0, 0); + IntSize bounds(100, 100); + setLayerPropertiesForTesting(root.get(), identityMatrix, identityMatrix, anchor, position, bounds, false); + root->setMasksToBounds(true); + + { + OwnPtr<CCLayerImpl> child = CCLayerImpl::create(456); + OwnPtr<CCLayerImpl> grandChild = CCLayerImpl::create(789); + OwnPtr<CCLayerImpl> rotatedLeaf = CCLayerImpl::create(2468); + + position = FloatPoint(10, 10); + bounds = IntSize(80, 80); + setLayerPropertiesForTesting(child.get(), identityMatrix, identityMatrix, anchor, position, bounds, false); + child->setMasksToBounds(true); + + WebTransformationMatrix rotation45DegreesAboutCorner; + rotation45DegreesAboutCorner.rotate3d(0, 0, 45); + + position = FloatPoint(0, 0); // remember, positioned with respect to its parent which is already at 10, 10 + bounds = IntSize(200, 200); // to ensure it covers at least sqrt(2) * 100. + setLayerPropertiesForTesting(grandChild.get(), rotation45DegreesAboutCorner, identityMatrix, anchor, position, bounds, false); + grandChild->setMasksToBounds(true); + + // Rotates about the center of the layer + WebTransformationMatrix rotatedLeafTransform; + rotatedLeafTransform.translate(-10, -10); // cancel out the grandParent's position + rotatedLeafTransform.rotate3d(0, 0, -45); // cancel out the corner 45-degree rotation of the parent. + rotatedLeafTransform.translate(50, 50); + rotatedLeafTransform.rotate3d(0, 0, 45); + rotatedLeafTransform.translate(-50, -50); + position = FloatPoint(0, 0); + bounds = IntSize(100, 100); + setLayerPropertiesForTesting(rotatedLeaf.get(), rotatedLeafTransform, identityMatrix, anchor, position, bounds, false); + rotatedLeaf->setDrawsContent(true); + + grandChild->addChild(rotatedLeaf.release()); + child->addChild(grandChild.release()); + root->addChild(child.release()); + } + + Vector<CCLayerImpl*> renderSurfaceLayerList; + Vector<CCLayerImpl*> dummyLayerList; + int dummyMaxTextureSize = 512; + renderSurfaceLayerList.append(root.get()); + CCLayerTreeHostCommon::calculateDrawTransforms(root.get(), root.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, 0, dummyMaxTextureSize); + CCLayerTreeHostCommon::calculateVisibleAndScissorRects(renderSurfaceLayerList, FloatRect()); // empty scissorRect will help ensure we're hit testing the correct rect. + + // Sanity check the scenario we just created. + // The grandChild is expected to create a renderSurface because it masksToBounds and is not axis aligned. + ASSERT_EQ(2u, renderSurfaceLayerList.size()); + ASSERT_EQ(1u, renderSurfaceLayerList[0]->renderSurface()->layerList().size()); + ASSERT_EQ(789, renderSurfaceLayerList[0]->renderSurface()->layerList()[0]->id()); // grandChild's surface. + ASSERT_EQ(1u, renderSurfaceLayerList[1]->renderSurface()->layerList().size()); + ASSERT_EQ(2468, renderSurfaceLayerList[1]->renderSurface()->layerList()[0]->id()); + + // (11, 89) is close to the the bottom left corner within the clip, but it is not inside the layer. + IntPoint testPoint(11, 89); + CCLayerImpl* resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + EXPECT_FALSE(resultLayer); + + // Closer inwards from the bottom left will overlap the layer. + testPoint = IntPoint(25, 75); + resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + ASSERT_TRUE(resultLayer); + EXPECT_EQ(2468, resultLayer->id()); + + // (4, 50) is inside the unclipped layer, but that corner of the layer should be + // clipped away by the grandParent and should not get hit. If hit testing blindly uses + // visibleLayerRect without considering how parent may clip the layer, then hit + // testing would accidentally think that the point successfully hits the layer. + testPoint = IntPoint(4, 50); + resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + EXPECT_FALSE(resultLayer); + + // (11, 50) is inside the layer and within the clipped area. + testPoint = IntPoint(11, 50); + resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + ASSERT_TRUE(resultLayer); + EXPECT_EQ(2468, resultLayer->id()); + + // Around the middle, just to the right and up, would have hit the layer except that + // that area should be clipped away by the parent. + testPoint = IntPoint(51, 51); + resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + EXPECT_FALSE(resultLayer); + + // Around the middle, just to the left and down, should successfully hit the layer. + testPoint = IntPoint(49, 51); + resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + ASSERT_TRUE(resultLayer); + EXPECT_EQ(2468, resultLayer->id()); +} + +TEST(CCLayerTreeHostCommonTest, verifyHitTestingForMultipleLayers) +{ + DebugScopedSetImplThread thisScopeIsOnImplThread; + + OwnPtr<CCLayerImpl> root = CCLayerImpl::create(1); + root->createRenderSurface(); + root->renderSurface()->setContentRect(IntRect(IntPoint::zero(), IntSize(100, 100))); + + WebTransformationMatrix identityMatrix; + FloatPoint anchor(0, 0); + FloatPoint position(0, 0); + IntSize bounds(100, 100); + setLayerPropertiesForTesting(root.get(), identityMatrix, identityMatrix, anchor, position, bounds, false); + root->setDrawsContent(true); + + { + // child 1 and child2 are initialized to overlap between x=50 and x=60. + // grandChild is set to overlap both child1 and child2 between y=50 and y=60. + // The expected stacking order is: + // (front) child2, (second) grandChild, (third) child1, and (back) the root layer behind all other layers. + + OwnPtr<CCLayerImpl> child1 = CCLayerImpl::create(2); + OwnPtr<CCLayerImpl> child2 = CCLayerImpl::create(3); + OwnPtr<CCLayerImpl> grandChild1 = CCLayerImpl::create(4); + + position = FloatPoint(10, 10); + bounds = IntSize(50, 50); + setLayerPropertiesForTesting(child1.get(), identityMatrix, identityMatrix, anchor, position, bounds, false); + child1->setDrawsContent(true); + + position = FloatPoint(50, 10); + bounds = IntSize(50, 50); + setLayerPropertiesForTesting(child2.get(), identityMatrix, identityMatrix, anchor, position, bounds, false); + child2->setDrawsContent(true); + + // Remember that grandChild is positioned with respect to its parent (i.e. child1). + // In screen space, the intended position is (10, 50), with size 100 x 50. + position = FloatPoint(0, 40); + bounds = IntSize(100, 50); + setLayerPropertiesForTesting(grandChild1.get(), identityMatrix, identityMatrix, anchor, position, bounds, false); + grandChild1->setDrawsContent(true); + + child1->addChild(grandChild1.release()); + root->addChild(child1.release()); + root->addChild(child2.release()); + } + + CCLayerImpl* child1 = root->children()[0].get(); + CCLayerImpl* child2 = root->children()[1].get(); + CCLayerImpl* grandChild1 = child1->children()[0].get(); + + Vector<CCLayerImpl*> renderSurfaceLayerList; + Vector<CCLayerImpl*> dummyLayerList; + int dummyMaxTextureSize = 512; + renderSurfaceLayerList.append(root.get()); + CCLayerTreeHostCommon::calculateDrawTransforms(root.get(), root.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, 0, dummyMaxTextureSize); + CCLayerTreeHostCommon::calculateVisibleAndScissorRects(renderSurfaceLayerList, FloatRect()); // empty scissorRect will help ensure we're hit testing the correct rect. + + // Sanity check the scenario we just created. + ASSERT_TRUE(child1); + ASSERT_TRUE(child2); + ASSERT_TRUE(grandChild1); + ASSERT_EQ(1u, renderSurfaceLayerList.size()); + ASSERT_EQ(4u, root->renderSurface()->layerList().size()); + ASSERT_EQ(1, root->renderSurface()->layerList()[0]->id()); // root layer + ASSERT_EQ(2, root->renderSurface()->layerList()[1]->id()); // child1 + ASSERT_EQ(4, root->renderSurface()->layerList()[2]->id()); // grandChild1 + ASSERT_EQ(3, root->renderSurface()->layerList()[3]->id()); // child2 + + // Nothing overlaps the rootLayer at (1, 1), so hit testing there should find the root layer. + IntPoint testPoint = IntPoint(1, 1); + CCLayerImpl* resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + ASSERT_TRUE(resultLayer); + EXPECT_EQ(1, resultLayer->id()); + + // At (15, 15), child1 and root are the only layers. child1 is expected to be on top. + testPoint = IntPoint(15, 15); + resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + ASSERT_TRUE(resultLayer); + EXPECT_EQ(2, resultLayer->id()); + + // At (51, 20), child1 and child2 overlap. child2 is expected to be on top. + testPoint = IntPoint(51, 20); + resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + ASSERT_TRUE(resultLayer); + EXPECT_EQ(3, resultLayer->id()); + + // At (80, 51), child2 and grandChild1 overlap. child2 is expected to be on top. + testPoint = IntPoint(80, 51); + resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + ASSERT_TRUE(resultLayer); + EXPECT_EQ(3, resultLayer->id()); + + // At (51, 51), all layers overlap each other. child2 is expected to be on top of all other layers. + testPoint = IntPoint(51, 51); + resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + ASSERT_TRUE(resultLayer); + EXPECT_EQ(3, resultLayer->id()); + + // At (20, 51), child1 and grandChild1 overlap. grandChild1 is expected to be on top. + testPoint = IntPoint(20, 51); + resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + ASSERT_TRUE(resultLayer); + EXPECT_EQ(4, resultLayer->id()); +} + +TEST(CCLayerTreeHostCommonTest, verifyHitTestingForMultipleLayerLists) +{ + // + // The geometry is set up similarly to the previous case, but + // all layers are forced to be renderSurfaces now. + // + DebugScopedSetImplThread thisScopeIsOnImplThread; + + OwnPtr<CCLayerImpl> root = CCLayerImpl::create(1); + root->createRenderSurface(); + root->renderSurface()->setContentRect(IntRect(IntPoint::zero(), IntSize(100, 100))); + + WebTransformationMatrix identityMatrix; + FloatPoint anchor(0, 0); + FloatPoint position(0, 0); + IntSize bounds(100, 100); + setLayerPropertiesForTesting(root.get(), identityMatrix, identityMatrix, anchor, position, bounds, false); + root->setDrawsContent(true); + + { + // child 1 and child2 are initialized to overlap between x=50 and x=60. + // grandChild is set to overlap both child1 and child2 between y=50 and y=60. + // The expected stacking order is: + // (front) child2, (second) grandChild, (third) child1, and (back) the root layer behind all other layers. + + OwnPtr<CCLayerImpl> child1 = CCLayerImpl::create(2); + OwnPtr<CCLayerImpl> child2 = CCLayerImpl::create(3); + OwnPtr<CCLayerImpl> grandChild1 = CCLayerImpl::create(4); + + position = FloatPoint(10, 10); + bounds = IntSize(50, 50); + setLayerPropertiesForTesting(child1.get(), identityMatrix, identityMatrix, anchor, position, bounds, false); + child1->setDrawsContent(true); + child1->setForceRenderSurface(true); + + position = FloatPoint(50, 10); + bounds = IntSize(50, 50); + setLayerPropertiesForTesting(child2.get(), identityMatrix, identityMatrix, anchor, position, bounds, false); + child2->setDrawsContent(true); + child2->setForceRenderSurface(true); + + // Remember that grandChild is positioned with respect to its parent (i.e. child1). + // In screen space, the intended position is (10, 50), with size 100 x 50. + position = FloatPoint(0, 40); + bounds = IntSize(100, 50); + setLayerPropertiesForTesting(grandChild1.get(), identityMatrix, identityMatrix, anchor, position, bounds, false); + grandChild1->setDrawsContent(true); + grandChild1->setForceRenderSurface(true); + + child1->addChild(grandChild1.release()); + root->addChild(child1.release()); + root->addChild(child2.release()); + } + + CCLayerImpl* child1 = root->children()[0].get(); + CCLayerImpl* child2 = root->children()[1].get(); + CCLayerImpl* grandChild1 = child1->children()[0].get(); + + Vector<CCLayerImpl*> renderSurfaceLayerList; + Vector<CCLayerImpl*> dummyLayerList; + int dummyMaxTextureSize = 512; + renderSurfaceLayerList.append(root.get()); + CCLayerTreeHostCommon::calculateDrawTransforms(root.get(), root.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, 0, dummyMaxTextureSize); + CCLayerTreeHostCommon::calculateVisibleAndScissorRects(renderSurfaceLayerList, FloatRect()); // empty scissorRect will help ensure we're hit testing the correct rect. + + // Sanity check the scenario we just created. + ASSERT_TRUE(child1); + ASSERT_TRUE(child2); + ASSERT_TRUE(grandChild1); + ASSERT_TRUE(child1->renderSurface()); + ASSERT_TRUE(child2->renderSurface()); + ASSERT_TRUE(grandChild1->renderSurface()); + ASSERT_EQ(4u, renderSurfaceLayerList.size()); + ASSERT_EQ(3u, root->renderSurface()->layerList().size()); // The root surface has the root layer, and child1's and child2's renderSurfaces. + ASSERT_EQ(2u, child1->renderSurface()->layerList().size()); // The child1 surface has the child1 layer and grandChild1's renderSurface. + ASSERT_EQ(1u, child2->renderSurface()->layerList().size()); + ASSERT_EQ(1u, grandChild1->renderSurface()->layerList().size()); + ASSERT_EQ(1, renderSurfaceLayerList[0]->id()); // root layer + ASSERT_EQ(2, renderSurfaceLayerList[1]->id()); // child1 + ASSERT_EQ(4, renderSurfaceLayerList[2]->id()); // grandChild1 + ASSERT_EQ(3, renderSurfaceLayerList[3]->id()); // child2 + + // Nothing overlaps the rootLayer at (1, 1), so hit testing there should find the root layer. + IntPoint testPoint = IntPoint(1, 1); + CCLayerImpl* resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + ASSERT_TRUE(resultLayer); + EXPECT_EQ(1, resultLayer->id()); + + // At (15, 15), child1 and root are the only layers. child1 is expected to be on top. + testPoint = IntPoint(15, 15); + resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + ASSERT_TRUE(resultLayer); + EXPECT_EQ(2, resultLayer->id()); + + // At (51, 20), child1 and child2 overlap. child2 is expected to be on top. + testPoint = IntPoint(51, 20); + resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + ASSERT_TRUE(resultLayer); + EXPECT_EQ(3, resultLayer->id()); + + // At (80, 51), child2 and grandChild1 overlap. child2 is expected to be on top. + testPoint = IntPoint(80, 51); + resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + ASSERT_TRUE(resultLayer); + EXPECT_EQ(3, resultLayer->id()); + + // At (51, 51), all layers overlap each other. child2 is expected to be on top of all other layers. + testPoint = IntPoint(51, 51); + resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + ASSERT_TRUE(resultLayer); + EXPECT_EQ(3, resultLayer->id()); + + // At (20, 51), child1 and grandChild1 overlap. grandChild1 is expected to be on top. + testPoint = IntPoint(20, 51); + resultLayer = CCLayerTreeHostCommon::findLayerThatIsHitByPoint(testPoint, renderSurfaceLayerList); + ASSERT_TRUE(resultLayer); + EXPECT_EQ(4, resultLayer->id()); +} + +TEST(CCLayerTreeHostCommonTest, verifySubtreeSearch) +{ + RefPtr<LayerChromium> root = LayerChromium::create(); + RefPtr<LayerChromium> child = LayerChromium::create(); + RefPtr<LayerChromium> grandChild = LayerChromium::create(); + RefPtr<LayerChromium> maskLayer = LayerChromium::create(); + RefPtr<LayerChromium> replicaLayer = LayerChromium::create(); + + grandChild->setReplicaLayer(replicaLayer.get()); + child->addChild(grandChild.get()); + child->setMaskLayer(maskLayer.get()); + root->addChild(child.get()); + + int nonexistentId = -1; + EXPECT_EQ(root, CCLayerTreeHostCommon::findLayerInSubtree(root.get(), root->id())); + EXPECT_EQ(child, CCLayerTreeHostCommon::findLayerInSubtree(root.get(), child->id())); + EXPECT_EQ(grandChild, CCLayerTreeHostCommon::findLayerInSubtree(root.get(), grandChild->id())); + EXPECT_EQ(maskLayer, CCLayerTreeHostCommon::findLayerInSubtree(root.get(), maskLayer->id())); + EXPECT_EQ(replicaLayer, CCLayerTreeHostCommon::findLayerInSubtree(root.get(), replicaLayer->id())); + EXPECT_EQ(0, CCLayerTreeHostCommon::findLayerInSubtree(root.get(), nonexistentId)); +} + // FIXME: // continue working on https://bugs.webkit.org/show_bug.cgi?id=68942 // - add a test to verify clipping that changes the "center point" |