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 | |
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')
42 files changed, 6381 insertions, 1791 deletions
diff --git a/Source/WebKit/chromium/tests/AssociatedURLLoaderTest.cpp b/Source/WebKit/chromium/tests/AssociatedURLLoaderTest.cpp index bdf3a3ab4..03559f7f1 100644 --- a/Source/WebKit/chromium/tests/AssociatedURLLoaderTest.cpp +++ b/Source/WebKit/chromium/tests/AssociatedURLLoaderTest.cpp @@ -585,4 +585,34 @@ TEST_F(AssociatedURLLoaderTest, CrossOriginHeaderWhitelisting) EXPECT_FALSE(CheckAccessControlHeaders("Set-Cookie", true)); } +// Test that the loader can allow non-whitelisted response headers for trusted CORS loads. +TEST_F(AssociatedURLLoaderTest, CrossOriginHeaderAllowResponseHeaders) +{ + WebURLRequest request; + request.initialize(); + GURL url = GURL("http://www.other.com/CrossOriginHeaderAllowResponseHeaders.html"); + request.setURL(url); + + WebString headerNameString(WebString::fromUTF8("non-whitelisted")); + m_expectedResponse = WebURLResponse(); + m_expectedResponse.initialize(); + m_expectedResponse.setMIMEType("text/html"); + m_expectedResponse.addHTTPHeaderField("Access-Control-Allow-Origin", "*"); + m_expectedResponse.addHTTPHeaderField(headerNameString, "foo"); + webkit_support::RegisterMockedURL(url, m_expectedResponse, m_frameFilePath); + + WebURLLoaderOptions options; + options.exposeAllResponseHeaders = true; // This turns off response whitelisting. + options.crossOriginRequestPolicy = WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl; + m_expectedLoader = createAssociatedURLLoader(options); + EXPECT_TRUE(m_expectedLoader); + m_expectedLoader->loadAsynchronously(request, this); + serveRequests(); + EXPECT_TRUE(m_didReceiveResponse); + EXPECT_TRUE(m_didReceiveData); + EXPECT_TRUE(m_didFinishLoading); + + EXPECT_FALSE(m_actualResponse.httpHeaderField(headerNameString).isEmpty()); +} + } diff --git a/Source/WebKit/chromium/tests/CCAnimationTestCommon.cpp b/Source/WebKit/chromium/tests/CCAnimationTestCommon.cpp index 0e8c1a401..854595380 100644 --- a/Source/WebKit/chromium/tests/CCAnimationTestCommon.cpp +++ b/Source/WebKit/chromium/tests/CCAnimationTestCommon.cpp @@ -29,9 +29,12 @@ #include "GraphicsLayer.h" #include "LayerChromium.h" #include "TranslateTransformOperation.h" +#include "cc/CCKeyframedAnimationCurve.h" #include "cc/CCLayerAnimationController.h" #include "cc/CCLayerImpl.h" +#include <public/WebTransformOperations.h> + using namespace WebCore; namespace { @@ -39,38 +42,38 @@ namespace { template <class Target> void addOpacityTransition(Target& target, double duration, float startOpacity, float endOpacity, bool useTimingFunction) { - WebCore::KeyframeValueList values(AnimatedPropertyOpacity); - if (duration > 0) - values.insert(new FloatAnimationValue(0, startOpacity)); - values.insert(new FloatAnimationValue(duration, endOpacity)); + OwnPtr<CCKeyframedFloatAnimationCurve> curve(CCKeyframedFloatAnimationCurve::create()); - RefPtr<Animation> animation = Animation::create(); - animation->setDuration(duration); - - if (useTimingFunction) - animation->setTimingFunction(LinearTimingFunction::create()); + if (duration > 0) + curve->addKeyframe(CCFloatKeyframe::create(0, startOpacity, useTimingFunction ? nullptr : CCEaseTimingFunction::create())); + curve->addKeyframe(CCFloatKeyframe::create(duration, endOpacity, nullptr)); - IntSize boxSize; + OwnPtr<CCActiveAnimation> animation(CCActiveAnimation::create(curve.release(), 0, 0, CCActiveAnimation::Opacity)); + animation->setNeedsSynchronizedStartTime(true); - target.addAnimation(values, boxSize, animation.get(), 0, 0, 0); + target.addAnimation(animation.release()); } template <class Target> void addAnimatedTransform(Target& target, double duration, int deltaX, int deltaY) { static int id = 0; - WebCore::KeyframeValueList values(AnimatedPropertyWebkitTransform); + OwnPtr<CCKeyframedTransformAnimationCurve> curve(CCKeyframedTransformAnimationCurve::create()); - TransformOperations operations; - operations.operations().append(TranslateTransformOperation::create(Length(deltaX, WebCore::Fixed), Length(deltaY, WebCore::Fixed), TransformOperation::TRANSLATE_X)); - values.insert(new TransformAnimationValue(0, &operations)); + if (duration > 0) { + WebKit::WebTransformOperations startOperations; + startOperations.appendTranslate(deltaX, deltaY, 0); + curve->addKeyframe(CCTransformKeyframe::create(0, startOperations, nullptr)); + } - RefPtr<Animation> animation = Animation::create(); - animation->setDuration(duration); + WebKit::WebTransformOperations operations; + operations.appendTranslate(deltaX, deltaY, 0); + curve->addKeyframe(CCTransformKeyframe::create(duration, operations, nullptr)); - IntSize boxSize; + OwnPtr<CCActiveAnimation> animation(CCActiveAnimation::create(curve.release(), id++, 0, CCActiveAnimation::Transform)); + animation->setNeedsSynchronizedStartTime(true); - target.addAnimation(values, boxSize, animation.get(), ++id, 0, 0); + target.addAnimation(animation.release()); } } // namespace @@ -99,7 +102,7 @@ FakeTransformTransition::~FakeTransformTransition() { } -WebKit::WebTransformationMatrix FakeTransformTransition::getValue(double time, const WebCore::IntSize& size) const +WebKit::WebTransformationMatrix FakeTransformTransition::getValue(double time) const { return WebKit::WebTransformationMatrix(); } diff --git a/Source/WebKit/chromium/tests/CCAnimationTestCommon.h b/Source/WebKit/chromium/tests/CCAnimationTestCommon.h index 4384117d3..4bbd60112 100644 --- a/Source/WebKit/chromium/tests/CCAnimationTestCommon.h +++ b/Source/WebKit/chromium/tests/CCAnimationTestCommon.h @@ -54,7 +54,7 @@ public: virtual ~FakeTransformTransition(); virtual double duration() const OVERRIDE { return m_duration; } - virtual WebKit::WebTransformationMatrix getValue(double time, const WebCore::IntSize&) const OVERRIDE; + virtual WebKit::WebTransformationMatrix getValue(double time) const OVERRIDE; virtual PassOwnPtr<WebCore::CCAnimationCurve> clone() const OVERRIDE; @@ -89,12 +89,10 @@ public: virtual float opacity() const OVERRIDE { return m_opacity; } virtual void setTransformFromAnimation(const WebKit::WebTransformationMatrix& transform) OVERRIDE { m_transform = transform; } virtual const WebKit::WebTransformationMatrix& transform() const OVERRIDE { return m_transform; } - virtual const WebCore::IntSize& bounds() const OVERRIDE { return m_bounds; } private: float m_opacity; WebKit::WebTransformationMatrix m_transform; - WebCore::IntSize m_bounds; }; void addOpacityTransitionToController(WebCore::CCLayerAnimationController&, double duration, float startOpacity, float endOpacity, bool useTimingFunction); diff --git a/Source/WebKit/chromium/tests/CCDamageTrackerTest.cpp b/Source/WebKit/chromium/tests/CCDamageTrackerTest.cpp index 56d85fafa..f498069f0 100644 --- a/Source/WebKit/chromium/tests/CCDamageTrackerTest.cpp +++ b/Source/WebKit/chromium/tests/CCDamageTrackerTest.cpp @@ -58,7 +58,18 @@ void executeCalculateDrawTransformsAndVisibility(CCLayerImpl* root, Vector<CCLay root->renderSurface()->clearLayerList(); renderSurfaceLayerList.append(root); - CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(root, root, identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, &layerSorter, dummyMaxTextureSize); + CCLayerTreeHostCommon::calculateDrawTransforms(root, root, identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, &layerSorter, dummyMaxTextureSize); + CCLayerTreeHostCommon::calculateVisibleAndScissorRects(renderSurfaceLayerList, root->renderSurface()->contentRect()); +} + +void clearDamageForAllSurfaces(CCLayerImpl* layer) +{ + if (layer->renderSurface()) + layer->renderSurface()->damageTracker()->didDrawDamagedArea(); + + // Recursively clear damage for any existing surface. + for (size_t i = 0; i < layer->children().size(); ++i) + clearDamageForAllSurfaces(layer->children()[i].get()); } void emulateDrawingOneFrame(CCLayerImpl* root) @@ -221,6 +232,7 @@ TEST_F(CCDamageTrackerTest, verifyDamageForUpdateRects) // CASE 1: Setting the update rect should cause the corresponding damage to the surface. // + clearDamageForAllSurfaces(root.get()); child->setUpdateRect(FloatRect(10, 11, 12, 13)); emulateDrawingOneFrame(root.get()); @@ -230,6 +242,7 @@ TEST_F(CCDamageTrackerTest, verifyDamageForUpdateRects) // CASE 2: The same update rect twice in a row still produces the same damage. // + clearDamageForAllSurfaces(root.get()); child->setUpdateRect(FloatRect(10, 11, 12, 13)); emulateDrawingOneFrame(root.get()); rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect(); @@ -237,6 +250,7 @@ TEST_F(CCDamageTrackerTest, verifyDamageForUpdateRects) // CASE 3: Setting a different update rect should cause damage on the new update region, but no additional exposed old region. // + clearDamageForAllSurfaces(root.get()); child->setUpdateRect(FloatRect(20, 25, 1, 2)); emulateDrawingOneFrame(root.get()); @@ -252,6 +266,7 @@ TEST_F(CCDamageTrackerTest, verifyDamageForPropertyChanges) // CASE 1: The layer's property changed flag takes priority over update rect. // + clearDamageForAllSurfaces(root.get()); child->setUpdateRect(FloatRect(10, 11, 12, 13)); child->setOpacity(0.5); emulateDrawingOneFrame(root.get()); @@ -270,10 +285,12 @@ TEST_F(CCDamageTrackerTest, verifyDamageForPropertyChanges) // not just the updateRect. // Cycle one frame of no change, just to sanity check that the next rect is not because of the old damage state. + clearDamageForAllSurfaces(root.get()); emulateDrawingOneFrame(root.get()); EXPECT_TRUE(root->renderSurface()->damageTracker()->currentDamageRect().isEmpty()); // Then, test the actual layer movement. + clearDamageForAllSurfaces(root.get()); child->setPosition(FloatPoint(200, 230)); emulateDrawingOneFrame(root.get()); @@ -297,6 +314,7 @@ TEST_F(CCDamageTrackerTest, verifyDamageForTransformedLayer) // Note carefully, the anchor is actually part of layer->position(). By setting anchor // to (0.5, 0.5), the layer's position (100, 100) now refers to the center of the // layer, not the corner. This means the layer has actually changed position. + clearDamageForAllSurfaces(root.get()); child->setAnchorPoint(FloatPoint(0.5, 0.5)); emulateDrawingOneFrame(root.get()); @@ -306,6 +324,7 @@ TEST_F(CCDamageTrackerTest, verifyDamageForTransformedLayer) // With the anchor on the layer's center, now we can test the rotation more // intuitively, since it applies about the layer's anchor. + clearDamageForAllSurfaces(root.get()); child->setTransform(rotation); emulateDrawingOneFrame(root.get()); @@ -326,15 +345,17 @@ TEST_F(CCDamageTrackerTest, verifyDamageForPerspectiveClippedLayer) // tracked properly. // // The transform is constructed so that if w < 0 clipping is not performed, the - // incorrect rect will be very small, specifically: position (-3.153448, -2.750628) and size 8.548689 x 5.661383. - // Instead, the correctly transformed rect should actually be very huge (i.e. in theory, infinite) + // incorrect rect will be very small, specifically: position (500.972504, 498.544617) and size 0.056610 x 2.910767. + // Instead, the correctly transformed rect should actually be very huge (i.e. in theory, -infinity on the left), + // and positioned so that the right-most bound rect will be approximately 501 units in root surface space. + // OwnPtr<CCLayerImpl> root = createAndSetUpTestTreeWithOneSurface(); CCLayerImpl* child = root->children()[0].get(); WebTransformationMatrix transform; + transform.translate3d(500, 500, 0); transform.applyPerspective(1); - transform.translate3d(-150, -50, 0); transform.rotate3d(0, 45, 0); transform.translate3d(-50, -50, 0); @@ -352,14 +373,15 @@ TEST_F(CCDamageTrackerTest, verifyDamageForPerspectiveClippedLayer) EXPECT_TRUE(clipped); // Damage the child without moving it. + clearDamageForAllSurfaces(root.get()); child->setOpacity(0.5); emulateDrawingOneFrame(root.get()); // The expected damage should cover the entire root surface (500x500), but we don't // care whether the damage rect was clamped or is larger than the surface for this test. FloatRect rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect(); - EXPECT_GE(rootDamageRect.width(), 500); - EXPECT_GE(rootDamageRect.height(), 500); + FloatRect damageWeCareAbout = FloatRect(FloatPoint::zero(), FloatSize(500, 500)); + EXPECT_TRUE(rootDamageRect.contains(damageWeCareAbout)); } TEST_F(CCDamageTrackerTest, verifyDamageForBlurredSurface) @@ -371,12 +393,14 @@ TEST_F(CCDamageTrackerTest, verifyDamageForBlurredSurface) filters.append(WebFilterOperation::createBlurFilter(5)); int outsetTop, outsetRight, outsetBottom, outsetLeft; filters.getOutsets(outsetTop, outsetRight, outsetBottom, outsetLeft); - root->setFilters(filters); // Setting the filter will damage the whole surface. + clearDamageForAllSurfaces(root.get()); + root->setFilters(filters); emulateDrawingOneFrame(root.get()); // Setting the update rect should cause the corresponding damage to the surface, blurred based on the size of the blur filter. + clearDamageForAllSurfaces(root.get()); child->setUpdateRect(FloatRect(10, 11, 12, 13)); emulateDrawingOneFrame(root.get()); @@ -401,16 +425,17 @@ TEST_F(CCDamageTrackerTest, verifyDamageForBackgroundBlurredChild) filters.append(WebFilterOperation::createBlurFilter(2)); int outsetTop, outsetRight, outsetBottom, outsetLeft; filters.getOutsets(outsetTop, outsetRight, outsetBottom, outsetLeft); - child1->setBackgroundFilters(filters); // Setting the filter will damage the whole surface. + clearDamageForAllSurfaces(root.get()); + child1->setBackgroundFilters(filters); emulateDrawingOneFrame(root.get()); // CASE 1: Setting the update rect should cause the corresponding damage to // the surface, blurred based on the size of the child's background blur // filter. + clearDamageForAllSurfaces(root.get()); root->setUpdateRect(FloatRect(297, 297, 2, 2)); - emulateDrawingOneFrame(root.get()); FloatRect rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect(); @@ -425,8 +450,8 @@ TEST_F(CCDamageTrackerTest, verifyDamageForBackgroundBlurredChild) // the surface, blurred based on the size of the child's background blur // filter. Since the damage extends to the right/bottom outside of the // blurred layer, only the left/top should end up expanded. + clearDamageForAllSurfaces(root.get()); root->setUpdateRect(FloatRect(297, 297, 30, 30)); - emulateDrawingOneFrame(root.get()); rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect(); @@ -440,8 +465,8 @@ TEST_F(CCDamageTrackerTest, verifyDamageForBackgroundBlurredChild) // CASE 3: Setting this update rect outside the blurred contentBounds of the blurred // child1 will not cause it to be expanded. + clearDamageForAllSurfaces(root.get()); root->setUpdateRect(FloatRect(30, 30, 2, 2)); - emulateDrawingOneFrame(root.get()); rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect(); @@ -452,8 +477,8 @@ TEST_F(CCDamageTrackerTest, verifyDamageForBackgroundBlurredChild) // CASE 4: Setting this update rect inside the blurred contentBounds but outside the // original contentBounds of the blurred child1 will cause it to be expanded. + clearDamageForAllSurfaces(root.get()); root->setUpdateRect(FloatRect(99, 99, 1, 1)); - emulateDrawingOneFrame(root.get()); rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect(); @@ -467,8 +492,8 @@ TEST_F(CCDamageTrackerTest, verifyDamageForBackgroundBlurredChild) // CASE 5: Setting the update rect on child2, which is above child1, will // not get blurred by child1, so it does not need to get expanded. + clearDamageForAllSurfaces(root.get()); child2->setUpdateRect(FloatRect(0, 0, 1, 1)); - emulateDrawingOneFrame(root.get()); rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect(); @@ -478,8 +503,8 @@ TEST_F(CCDamageTrackerTest, verifyDamageForBackgroundBlurredChild) // CASE 6: Setting the update rect on child1 will also blur the damage, so // that any pixels needed for the blur are redrawn in the current frame. + clearDamageForAllSurfaces(root.get()); child1->setUpdateRect(FloatRect(0, 0, 1, 1)); - emulateDrawingOneFrame(root.get()); rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect(); @@ -495,6 +520,9 @@ TEST_F(CCDamageTrackerTest, verifyDamageForAddingAndRemovingLayer) OwnPtr<CCLayerImpl> root = createAndSetUpTestTreeWithOneSurface(); CCLayerImpl* child1 = root->children()[0].get(); + // CASE 1: Adding a new layer should cause the appropriate damage. + // + clearDamageForAllSurfaces(root.get()); { OwnPtr<CCLayerImpl> child2 = CCLayerImpl::create(3); child2->setPosition(FloatPoint(400, 380)); @@ -503,9 +531,6 @@ TEST_F(CCDamageTrackerTest, verifyDamageForAddingAndRemovingLayer) child2->setDrawsContent(true); root->addChild(child2.release()); } - - // CASE 1: Adding a new layer should cause the appropriate damage. - // emulateDrawingOneFrame(root.get()); // Sanity check - all 3 layers should be on the same render surface; render surfaces are tested elsewhere. @@ -518,6 +543,7 @@ TEST_F(CCDamageTrackerTest, verifyDamageForAddingAndRemovingLayer) // last update rect. // Advance one frame without damage so that we know the damage rect is not leftover from the previous case. + clearDamageForAllSurfaces(root.get()); emulateDrawingOneFrame(root.get()); EXPECT_TRUE(root->renderSurface()->damageTracker()->currentDamageRect().isEmpty()); @@ -535,6 +561,7 @@ TEST_F(CCDamageTrackerTest, verifyDamageForNewUnchangedLayer) OwnPtr<CCLayerImpl> root = createAndSetUpTestTreeWithOneSurface(); + clearDamageForAllSurfaces(root.get()); { OwnPtr<CCLayerImpl> child2 = CCLayerImpl::create(3); child2->setPosition(FloatPoint(400, 380)); @@ -548,7 +575,6 @@ TEST_F(CCDamageTrackerTest, verifyDamageForNewUnchangedLayer) ASSERT_TRUE(child2->updateRect().isEmpty()); root->addChild(child2.release()); } - emulateDrawingOneFrame(root.get()); // Sanity check - all 3 layers should be on the same render surface; render surfaces are tested elsewhere. @@ -563,6 +589,8 @@ TEST_F(CCDamageTrackerTest, verifyDamageForMultipleLayers) OwnPtr<CCLayerImpl> root = createAndSetUpTestTreeWithOneSurface(); CCLayerImpl* child1 = root->children()[0].get(); + // In this test we don't want the above tree manipulation to be considered part of the same frame. + clearDamageForAllSurfaces(root.get()); { OwnPtr<CCLayerImpl> child2 = CCLayerImpl::create(3); child2->setPosition(FloatPoint(400, 380)); @@ -572,13 +600,12 @@ TEST_F(CCDamageTrackerTest, verifyDamageForMultipleLayers) root->addChild(child2.release()); } CCLayerImpl* child2 = root->children()[1].get(); - - // In this test we don't want the above tree manipulation to be considered part of the same frame. emulateDrawingOneFrame(root.get()); // Damaging two layers simultaneously should cause combined damage. // - child1 update rect in surface space: FloatRect(100, 100, 1, 2); // - child2 update rect in surface space: FloatRect(400, 380, 3, 4); + clearDamageForAllSurfaces(root.get()); child1->setUpdateRect(FloatRect(0, 0, 1, 2)); child2->setUpdateRect(FloatRect(0, 0, 3, 4)); emulateDrawingOneFrame(root.get()); @@ -597,6 +624,7 @@ TEST_F(CCDamageTrackerTest, verifyDamageForNestedSurfaces) // CASE 1: Damage to a descendant surface should propagate properly to ancestor surface. // + clearDamageForAllSurfaces(root.get()); grandChild1->setOpacity(0.5); emulateDrawingOneFrame(root.get()); childDamageRect = child1->renderSurface()->damageTracker()->currentDamageRect(); @@ -607,6 +635,7 @@ TEST_F(CCDamageTrackerTest, verifyDamageForNestedSurfaces) // CASE 2: Same as previous case, but with additional damage elsewhere that should be properly unioned. // - child1 surface damage in root surface space: FloatRect(300, 300, 6, 8); // - child2 damage in root surface space: FloatRect(11, 11, 18, 18); + clearDamageForAllSurfaces(root.get()); grandChild1->setOpacity(0.7f); child2->setOpacity(0.7f); emulateDrawingOneFrame(root.get()); @@ -631,6 +660,7 @@ TEST_F(CCDamageTrackerTest, verifyDamageForSurfaceChangeFromDescendantLayer) FloatRect childDamageRect; FloatRect rootDamageRect; + clearDamageForAllSurfaces(root.get()); grandChild1->setPosition(FloatPoint(195, 205)); emulateDrawingOneFrame(root.get()); childDamageRect = child1->renderSurface()->damageTracker()->currentDamageRect(); @@ -662,6 +692,7 @@ TEST_F(CCDamageTrackerTest, verifyDamageForSurfaceChangeFromAncestorLayer) FloatRect childDamageRect; FloatRect rootDamageRect; + clearDamageForAllSurfaces(root.get()); child1->setPosition(FloatPoint(50, 50)); emulateDrawingOneFrame(root.get()); childDamageRect = child1->renderSurface()->damageTracker()->currentDamageRect(); @@ -685,6 +716,7 @@ TEST_F(CCDamageTrackerTest, verifyDamageForAddingAndRemovingRenderSurfaces) // CASE 1: If a descendant surface disappears, its entire old area becomes exposed. // + clearDamageForAllSurfaces(root.get()); child1->setOpacity(1); emulateDrawingOneFrame(root.get()); @@ -698,11 +730,13 @@ TEST_F(CCDamageTrackerTest, verifyDamageForAddingAndRemovingRenderSurfaces) // CASE 2: If a descendant surface appears, its entire old area becomes exposed. // Cycle one frame of no change, just to sanity check that the next rect is not because of the old damage state. + clearDamageForAllSurfaces(root.get()); emulateDrawingOneFrame(root.get()); rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect(); EXPECT_TRUE(rootDamageRect.isEmpty()); // Then change the tree so that the render surface is added back. + clearDamageForAllSurfaces(root.get()); child1->setOpacity(0.5); emulateDrawingOneFrame(root.get()); @@ -726,6 +760,7 @@ TEST_F(CCDamageTrackerTest, verifyNoDamageWhenNothingChanged) // CASE 1: If nothing changes, the damage rect should be empty. // + clearDamageForAllSurfaces(root.get()); emulateDrawingOneFrame(root.get()); childDamageRect = child1->renderSurface()->damageTracker()->currentDamageRect(); rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect(); @@ -734,6 +769,7 @@ TEST_F(CCDamageTrackerTest, verifyNoDamageWhenNothingChanged) // CASE 2: If nothing changes twice in a row, the damage rect should still be empty. // + clearDamageForAllSurfaces(root.get()); emulateDrawingOneFrame(root.get()); childDamageRect = child1->renderSurface()->damageTracker()->currentDamageRect(); rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect(); @@ -750,6 +786,7 @@ TEST_F(CCDamageTrackerTest, verifyNoDamageForUpdateRectThatDoesNotDrawContent) // In our specific tree, the update rect of child1 should not cause any damage to any // surface because it does not actually draw content. + clearDamageForAllSurfaces(root.get()); child1->setUpdateRect(FloatRect(0, 0, 1, 2)); emulateDrawingOneFrame(root.get()); childDamageRect = child1->renderSurface()->damageTracker()->currentDamageRect(); @@ -785,6 +822,7 @@ TEST_F(CCDamageTrackerTest, verifyDamageForReplica) // CASE 1: adding a reflection about the left edge of grandChild1. // + clearDamageForAllSurfaces(root.get()); { OwnPtr<CCLayerImpl> grandChild1Replica = CCLayerImpl::create(7); grandChild1Replica->setPosition(FloatPoint::zero()); @@ -808,6 +846,7 @@ TEST_F(CCDamageTrackerTest, verifyDamageForReplica) // CASE 2: moving the descendant surface should cause both the original and reflected // areas to be damaged on the target. + clearDamageForAllSurfaces(root.get()); IntRect oldContentRect = child1->renderSurface()->contentRect(); grandChild1->setPosition(FloatPoint(195.0, 205.0)); emulateDrawingOneFrame(root.get()); @@ -827,6 +866,7 @@ TEST_F(CCDamageTrackerTest, verifyDamageForReplica) // CASE 3: removing the reflection should cause the entire region including reflection // to damage the target surface. + clearDamageForAllSurfaces(root.get()); grandChild1->setReplicaLayer(nullptr); emulateDrawingOneFrame(root.get()); ASSERT_EQ(oldContentRect.width(), child1->renderSurface()->contentRect().width()); @@ -848,6 +888,8 @@ TEST_F(CCDamageTrackerTest, verifyDamageForMask) // In the current implementation of the damage tracker, changes to mask layers should // damage the entire corresponding surface. + clearDamageForAllSurfaces(root.get()); + // Set up the mask layer. { OwnPtr<CCLayerImpl> maskLayer = CCLayerImpl::create(3); @@ -875,6 +917,7 @@ TEST_F(CCDamageTrackerTest, verifyDamageForMask) // CASE 1: the updateRect on a mask layer should damage the entire target surface. // + clearDamageForAllSurfaces(root.get()); maskLayer->setUpdateRect(FloatRect(1, 2, 3, 4)); emulateDrawingOneFrame(root.get()); FloatRect childDamageRect = child->renderSurface()->damageTracker()->currentDamageRect(); @@ -884,12 +927,15 @@ TEST_F(CCDamageTrackerTest, verifyDamageForMask) // // Advance one frame without damage so that we know the damage rect is not leftover from the previous case. + clearDamageForAllSurfaces(root.get()); emulateDrawingOneFrame(root.get()); childDamageRect = child->renderSurface()->damageTracker()->currentDamageRect(); EXPECT_TRUE(childDamageRect.isEmpty()); // Then test the property change. - maskLayer->setOpacity(0.5); + clearDamageForAllSurfaces(root.get()); + maskLayer->setStackingOrderChanged(true); + emulateDrawingOneFrame(root.get()); childDamageRect = child->renderSurface()->damageTracker()->currentDamageRect(); EXPECT_FLOAT_RECT_EQ(FloatRect(0, 0, 30, 30), childDamageRect); @@ -898,11 +944,13 @@ TEST_F(CCDamageTrackerTest, verifyDamageForMask) // // Advance one frame without damage so that we know the damage rect is not leftover from the previous case. + clearDamageForAllSurfaces(root.get()); emulateDrawingOneFrame(root.get()); childDamageRect = child->renderSurface()->damageTracker()->currentDamageRect(); EXPECT_TRUE(childDamageRect.isEmpty()); // Then test mask removal. + clearDamageForAllSurfaces(root.get()); child->setMaskLayer(nullptr); ASSERT_TRUE(child->layerPropertyChanged()); emulateDrawingOneFrame(root.get()); @@ -923,6 +971,8 @@ TEST_F(CCDamageTrackerTest, verifyDamageForReplicaMask) // Changes to a replica's mask should not damage the original surface, because it is // not masked. But it does damage the ancestor target surface. + clearDamageForAllSurfaces(root.get()); + // Create a reflection about the left edge of grandChild1. { OwnPtr<CCLayerImpl> grandChild1Replica = CCLayerImpl::create(6); @@ -951,7 +1001,8 @@ TEST_F(CCDamageTrackerTest, verifyDamageForReplicaMask) ASSERT_TRUE(grandChild1->renderSurface()); // CASE 1: a property change on the mask should damage only the reflected region on the target surface. - replicaMaskLayer->setOpacity(0.6f); + clearDamageForAllSurfaces(root.get()); + replicaMaskLayer->setStackingOrderChanged(true); emulateDrawingOneFrame(root.get()); FloatRect grandChildDamageRect = grandChild1->renderSurface()->damageTracker()->currentDamageRect(); @@ -962,6 +1013,7 @@ TEST_F(CCDamageTrackerTest, verifyDamageForReplicaMask) // CASE 2: removing the replica mask damages only the reflected region on the target surface. // + clearDamageForAllSurfaces(root.get()); grandChild1Replica->setMaskLayer(nullptr); emulateDrawingOneFrame(root.get()); @@ -982,6 +1034,8 @@ TEST_F(CCDamageTrackerTest, verifyDamageForReplicaMaskWithAnchor) // incorrect old code incorrectly accounted for the anchor for the replica. A // non-zero anchor point should not affect the replica reflection. + clearDamageForAllSurfaces(root.get()); + grandChild1->setAnchorPoint(FloatPoint(1.0, 0.0)); // This is the anchor being tested. { @@ -1011,7 +1065,9 @@ TEST_F(CCDamageTrackerTest, verifyDamageForReplicaMaskWithAnchor) ASSERT_TRUE(grandChild1->renderSurface()); // A property change on the replicaMask should damage the reflected region on the target surface. - replicaMaskLayer->setOpacity(0.6f); + clearDamageForAllSurfaces(root.get()); + replicaMaskLayer->setStackingOrderChanged(true); + emulateDrawingOneFrame(root.get()); FloatRect childDamageRect = child1->renderSurface()->damageTracker()->currentDamageRect(); @@ -1026,6 +1082,7 @@ TEST_F(CCDamageTrackerTest, verifyDamageWhenForcedFullDamage) // Case 1: This test ensures that when the tracker is forced to have full damage, that // it takes priority over any other partial damage. // + clearDamageForAllSurfaces(root.get()); child->setUpdateRect(FloatRect(10, 11, 12, 13)); root->renderSurface()->damageTracker()->forceFullDamageNextUpdate(); emulateDrawingOneFrame(root.get()); @@ -1035,6 +1092,7 @@ TEST_F(CCDamageTrackerTest, verifyDamageWhenForcedFullDamage) // Case 2: An additional sanity check that forcing full damage works even when nothing // on the layer tree changed. // + clearDamageForAllSurfaces(root.get()); root->renderSurface()->damageTracker()->forceFullDamageNextUpdate(); emulateDrawingOneFrame(root.get()); rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect(); @@ -1058,4 +1116,36 @@ TEST_F(CCDamageTrackerTest, verifyDamageForEmptyLayerList) EXPECT_TRUE(damageRect.isEmpty()); } +TEST_F(CCDamageTrackerTest, verifyDamageAccumulatesUntilReset) +{ + // If damage is not cleared, it should accumulate. + + OwnPtr<CCLayerImpl> root = createAndSetUpTestTreeWithOneSurface(); + CCLayerImpl* child = root->children()[0].get(); + + clearDamageForAllSurfaces(root.get()); + child->setUpdateRect(FloatRect(10, 11, 1, 2)); + emulateDrawingOneFrame(root.get()); + + // Sanity check damage after the first frame; this isnt the actual test yet. + FloatRect rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect(); + EXPECT_FLOAT_RECT_EQ(FloatRect(110, 111, 1, 2), rootDamageRect); + + // New damage, without having cleared the previous damage, should be unioned to the previous one. + child->setUpdateRect(FloatRect(20, 25, 1, 2)); + emulateDrawingOneFrame(root.get()); + rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect(); + EXPECT_FLOAT_RECT_EQ(FloatRect(110, 111, 11, 16), rootDamageRect); + + // If we notify the damage tracker that we drew the damaged area, then damage should be emptied. + root->renderSurface()->damageTracker()->didDrawDamagedArea(); + rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect(); + EXPECT_TRUE(rootDamageRect.isEmpty()); + + // Damage should remain empty even after one frame, since there's yet no new damage + emulateDrawingOneFrame(root.get()); + rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect(); + EXPECT_TRUE(rootDamageRect.isEmpty()); +} + } // namespace diff --git a/Source/WebKit/chromium/tests/CCKeyframedAnimationCurveTest.cpp b/Source/WebKit/chromium/tests/CCKeyframedAnimationCurveTest.cpp index ad1ae74ca..eb6bb0363 100644 --- a/Source/WebKit/chromium/tests/CCKeyframedAnimationCurveTest.cpp +++ b/Source/WebKit/chromium/tests/CCKeyframedAnimationCurveTest.cpp @@ -31,6 +31,7 @@ #include <gmock/gmock.h> #include <gtest/gtest.h> +#include <public/WebTransformOperations.h> #include <public/WebTransformationMatrix.h> #include <wtf/OwnPtr.h> #include <wtf/Vector.h> @@ -113,57 +114,55 @@ TEST(CCKeyframedAnimationCurveTest, RepeatedFloatKeyTimes) TEST(CCKeyframedAnimationCurveTest, OneTransformKeyframe) { OwnPtr<CCKeyframedTransformAnimationCurve> curve(CCKeyframedTransformAnimationCurve::create()); - TransformOperations operations; - operations.operations().append(TranslateTransformOperation::create(Length(2, Fixed), Length(0, Fixed), TransformOperation::TRANSLATE_X)); + WebKit::WebTransformOperations operations; + operations.appendTranslate(2, 0, 0); curve->addKeyframe(CCTransformKeyframe::create(0, operations, nullptr)); - IntSize layerSize; // ignored - expectTranslateX(2, curve->getValue(-1, layerSize)); - expectTranslateX(2, curve->getValue(0, layerSize)); - expectTranslateX(2, curve->getValue(0.5, layerSize)); - expectTranslateX(2, curve->getValue(1, layerSize)); - expectTranslateX(2, curve->getValue(2, layerSize)); + expectTranslateX(2, curve->getValue(-1)); + expectTranslateX(2, curve->getValue(0)); + expectTranslateX(2, curve->getValue(0.5)); + expectTranslateX(2, curve->getValue(1)); + expectTranslateX(2, curve->getValue(2)); } // Tests that a transform animation with two keyframes works as expected. TEST(CCKeyframedAnimationCurveTest, TwoTransformKeyframe) { OwnPtr<CCKeyframedTransformAnimationCurve> curve(CCKeyframedTransformAnimationCurve::create()); - TransformOperations operations1; - operations1.operations().append(TranslateTransformOperation::create(Length(2, Fixed), Length(0, Fixed), TransformOperation::TRANSLATE_X)); - TransformOperations operations2; - operations2.operations().append(TranslateTransformOperation::create(Length(4, Fixed), Length(0, Fixed), TransformOperation::TRANSLATE_X)); + WebKit::WebTransformOperations operations1; + operations1.appendTranslate(2, 0, 0); + WebKit::WebTransformOperations operations2; + operations2.appendTranslate(4, 0, 0); + curve->addKeyframe(CCTransformKeyframe::create(0, operations1, nullptr)); curve->addKeyframe(CCTransformKeyframe::create(1, operations2, nullptr)); - IntSize layerSize; // ignored - expectTranslateX(2, curve->getValue(-1, layerSize)); - expectTranslateX(2, curve->getValue(0, layerSize)); - expectTranslateX(3, curve->getValue(0.5, layerSize)); - expectTranslateX(4, curve->getValue(1, layerSize)); - expectTranslateX(4, curve->getValue(2, layerSize)); + expectTranslateX(2, curve->getValue(-1)); + expectTranslateX(2, curve->getValue(0)); + expectTranslateX(3, curve->getValue(0.5)); + expectTranslateX(4, curve->getValue(1)); + expectTranslateX(4, curve->getValue(2)); } // Tests that a transform animation with three keyframes works as expected. TEST(CCKeyframedAnimationCurveTest, ThreeTransformKeyframe) { OwnPtr<CCKeyframedTransformAnimationCurve> curve(CCKeyframedTransformAnimationCurve::create()); - TransformOperations operations1; - operations1.operations().append(TranslateTransformOperation::create(Length(2, Fixed), Length(0, Fixed), TransformOperation::TRANSLATE_X)); - TransformOperations operations2; - operations2.operations().append(TranslateTransformOperation::create(Length(4, Fixed), Length(0, Fixed), TransformOperation::TRANSLATE_X)); - TransformOperations operations3; - operations3.operations().append(TranslateTransformOperation::create(Length(8, Fixed), Length(0, Fixed), TransformOperation::TRANSLATE_X)); + WebKit::WebTransformOperations operations1; + operations1.appendTranslate(2, 0, 0); + WebKit::WebTransformOperations operations2; + operations2.appendTranslate(4, 0, 0); + WebKit::WebTransformOperations operations3; + operations3.appendTranslate(8, 0, 0); curve->addKeyframe(CCTransformKeyframe::create(0, operations1, nullptr)); curve->addKeyframe(CCTransformKeyframe::create(1, operations2, nullptr)); curve->addKeyframe(CCTransformKeyframe::create(2, operations3, nullptr)); - IntSize layerSize; // ignored - expectTranslateX(2, curve->getValue(-1, layerSize)); - expectTranslateX(2, curve->getValue(0, layerSize)); - expectTranslateX(3, curve->getValue(0.5, layerSize)); - expectTranslateX(4, curve->getValue(1, layerSize)); - expectTranslateX(6, curve->getValue(1.5, layerSize)); - expectTranslateX(8, curve->getValue(2, layerSize)); - expectTranslateX(8, curve->getValue(3, layerSize)); + expectTranslateX(2, curve->getValue(-1)); + expectTranslateX(2, curve->getValue(0)); + expectTranslateX(3, curve->getValue(0.5)); + expectTranslateX(4, curve->getValue(1)); + expectTranslateX(6, curve->getValue(1.5)); + expectTranslateX(8, curve->getValue(2)); + expectTranslateX(8, curve->getValue(3)); } // Tests that a transform animation with multiple keys at a given time works sanely. @@ -171,32 +170,30 @@ TEST(CCKeyframedAnimationCurveTest, RepeatedTransformKeyTimes) { OwnPtr<CCKeyframedTransformAnimationCurve> curve(CCKeyframedTransformAnimationCurve::create()); // A step function. - TransformOperations operations1; - operations1.operations().append(TranslateTransformOperation::create(Length(4, Fixed), Length(0, Fixed), TransformOperation::TRANSLATE_X)); - TransformOperations operations2; - operations2.operations().append(TranslateTransformOperation::create(Length(4, Fixed), Length(0, Fixed), TransformOperation::TRANSLATE_X)); - TransformOperations operations3; - operations3.operations().append(TranslateTransformOperation::create(Length(6, Fixed), Length(0, Fixed), TransformOperation::TRANSLATE_X)); - TransformOperations operations4; - operations4.operations().append(TranslateTransformOperation::create(Length(6, Fixed), Length(0, Fixed), TransformOperation::TRANSLATE_X)); + WebKit::WebTransformOperations operations1; + operations1.appendTranslate(4, 0, 0); + WebKit::WebTransformOperations operations2; + operations2.appendTranslate(4, 0, 0); + WebKit::WebTransformOperations operations3; + operations3.appendTranslate(6, 0, 0); + WebKit::WebTransformOperations operations4; + operations4.appendTranslate(6, 0, 0); curve->addKeyframe(CCTransformKeyframe::create(0, operations1, nullptr)); curve->addKeyframe(CCTransformKeyframe::create(1, operations2, nullptr)); curve->addKeyframe(CCTransformKeyframe::create(1, operations3, nullptr)); curve->addKeyframe(CCTransformKeyframe::create(2, operations4, nullptr)); - IntSize layerSize; // ignored - - expectTranslateX(4, curve->getValue(-1, layerSize)); - expectTranslateX(4, curve->getValue(0, layerSize)); - expectTranslateX(4, curve->getValue(0.5, layerSize)); + expectTranslateX(4, curve->getValue(-1)); + expectTranslateX(4, curve->getValue(0)); + expectTranslateX(4, curve->getValue(0.5)); // There is a discontinuity at 1. Any value between 4 and 6 is valid. - WebTransformationMatrix value = curve->getValue(1, layerSize); + WebTransformationMatrix value = curve->getValue(1); EXPECT_TRUE(value.m41() >= 4 && value.m41() <= 6); - expectTranslateX(6, curve->getValue(1.5, layerSize)); - expectTranslateX(6, curve->getValue(2, layerSize)); - expectTranslateX(6, curve->getValue(3, layerSize)); + expectTranslateX(6, curve->getValue(1.5)); + expectTranslateX(6, curve->getValue(2)); + expectTranslateX(6, curve->getValue(3)); } // Tests that the keyframes may be added out of order. diff --git a/Source/WebKit/chromium/tests/CCLayerAnimationControllerTest.cpp b/Source/WebKit/chromium/tests/CCLayerAnimationControllerTest.cpp index dbed02c74..72361092a 100644 --- a/Source/WebKit/chromium/tests/CCLayerAnimationControllerTest.cpp +++ b/Source/WebKit/chromium/tests/CCLayerAnimationControllerTest.cpp @@ -29,7 +29,6 @@ #include "CCAnimationTestCommon.h" #include "GraphicsLayer.h" #include "Length.h" -#include "TranslateTransformOperation.h" #include "cc/CCActiveAnimation.h" #include "cc/CCAnimationCurve.h" @@ -54,197 +53,6 @@ PassOwnPtr<CCActiveAnimation> createActiveAnimation(PassOwnPtr<CCAnimationCurve> return CCActiveAnimation::create(curve, 0, id, property); } -TEST(CCLayerAnimationControllerTest, createOpacityAnimation) -{ - FakeLayerAnimationControllerClient dummy; - OwnPtr<CCLayerAnimationController> controller(CCLayerAnimationController::create(&dummy)); - const double duration = 1; - WebCore::KeyframeValueList values(AnimatedPropertyOpacity); - values.insert(new FloatAnimationValue(0, 0)); - values.insert(new FloatAnimationValue(duration, 1)); - - RefPtr<Animation> animation = Animation::create(); - animation->setDuration(duration); - - IntSize boxSize; - controller->addAnimation(values, boxSize, animation.get(), 0, 0, 0); - - EXPECT_TRUE(controller->hasActiveAnimation()); - - CCActiveAnimation* activeAnimation = controller->getActiveAnimation(0, CCActiveAnimation::Opacity); - EXPECT_TRUE(activeAnimation); - - EXPECT_EQ(1, activeAnimation->iterations()); - EXPECT_EQ(CCActiveAnimation::Opacity, activeAnimation->targetProperty()); - - EXPECT_EQ(CCAnimationCurve::Float, activeAnimation->curve()->type()); - - const CCFloatAnimationCurve* curve = activeAnimation->curve()->toFloatAnimationCurve(); - EXPECT_TRUE(curve); - - EXPECT_EQ(0, curve->getValue(0)); - EXPECT_EQ(1, curve->getValue(duration)); -} - -TEST(CCLayerAnimationControllerTest, createTransformAnimation) -{ - FakeLayerAnimationControllerClient dummy; - OwnPtr<CCLayerAnimationController> controller(CCLayerAnimationController::create(&dummy)); - const double duration = 1; - WebCore::KeyframeValueList values(AnimatedPropertyWebkitTransform); - - TransformOperations operations1; - operations1.operations().append(TranslateTransformOperation::create(Length(2, Fixed), Length(0, Fixed), TransformOperation::TRANSLATE_X)); - values.insert(new TransformAnimationValue(0, &operations1)); - - TransformOperations operations2; - operations2.operations().append(TranslateTransformOperation::create(Length(4, Fixed), Length(0, Fixed), TransformOperation::TRANSLATE_X)); - values.insert(new TransformAnimationValue(duration, &operations2)); - - RefPtr<Animation> animation = Animation::create(); - animation->setDuration(duration); - - IntSize boxSize; - controller->addAnimation(values, boxSize, animation.get(), 0, 0, 0); - - EXPECT_TRUE(controller->hasActiveAnimation()); - - CCActiveAnimation* activeAnimation = controller->getActiveAnimation(0, CCActiveAnimation::Transform); - EXPECT_TRUE(activeAnimation); - - EXPECT_EQ(1, activeAnimation->iterations()); - EXPECT_EQ(CCActiveAnimation::Transform, activeAnimation->targetProperty()); - - EXPECT_EQ(CCAnimationCurve::Transform, activeAnimation->curve()->type()); - - const CCTransformAnimationCurve* curve = activeAnimation->curve()->toTransformAnimationCurve(); - EXPECT_TRUE(curve); - - expectTranslateX(2, curve->getValue(0, boxSize)); - expectTranslateX(4, curve->getValue(duration, boxSize)); -} - -TEST(CCLayerAnimationControllerTest, createReversedAnimation) -{ - FakeLayerAnimationControllerClient dummy; - OwnPtr<CCLayerAnimationController> controller(CCLayerAnimationController::create(&dummy)); - const double duration = 1; - WebCore::KeyframeValueList values(AnimatedPropertyWebkitTransform); - - TransformOperations operations1; - operations1.operations().append(TranslateTransformOperation::create(Length(2, Fixed), Length(0, Fixed), TransformOperation::TRANSLATE_X)); - values.insert(new TransformAnimationValue(0, &operations1)); - - TransformOperations operations2; - operations2.operations().append(TranslateTransformOperation::create(Length(4, Fixed), Length(0, Fixed), TransformOperation::TRANSLATE_X)); - values.insert(new TransformAnimationValue(duration, &operations2)); - - RefPtr<Animation> animation = Animation::create(); - animation->setDuration(duration); - animation->setDirection(Animation::AnimationDirectionReverse); - - IntSize boxSize; - controller->addAnimation(values, boxSize, animation.get(), 0, 0, 0); - - EXPECT_TRUE(controller->hasActiveAnimation()); - - CCActiveAnimation* activeAnimation = controller->getActiveAnimation(0, CCActiveAnimation::Transform); - EXPECT_TRUE(activeAnimation); - - EXPECT_EQ(1, activeAnimation->iterations()); - EXPECT_EQ(CCActiveAnimation::Transform, activeAnimation->targetProperty()); - - EXPECT_EQ(CCAnimationCurve::Transform, activeAnimation->curve()->type()); - - const CCTransformAnimationCurve* curve = activeAnimation->curve()->toTransformAnimationCurve(); - EXPECT_TRUE(curve); - - expectTranslateX(4, curve->getValue(0, boxSize)); - expectTranslateX(2, curve->getValue(duration, boxSize)); -} - -TEST(CCLayerAnimationControllerTest, createAlternatingAnimation) -{ - FakeLayerAnimationControllerClient dummy; - OwnPtr<CCLayerAnimationController> controller(CCLayerAnimationController::create(&dummy)); - const double duration = 1; - WebCore::KeyframeValueList values(AnimatedPropertyWebkitTransform); - - TransformOperations operations1; - operations1.operations().append(TranslateTransformOperation::create(Length(2, Fixed), Length(0, Fixed), TransformOperation::TRANSLATE_X)); - values.insert(new TransformAnimationValue(0, &operations1)); - - TransformOperations operations2; - operations2.operations().append(TranslateTransformOperation::create(Length(4, Fixed), Length(0, Fixed), TransformOperation::TRANSLATE_X)); - values.insert(new TransformAnimationValue(duration, &operations2)); - - RefPtr<Animation> animation = Animation::create(); - animation->setDuration(duration); - animation->setDirection(Animation::AnimationDirectionAlternate); - animation->setIterationCount(2); - - IntSize boxSize; - controller->addAnimation(values, boxSize, animation.get(), 0, 0, 0); - - EXPECT_TRUE(controller->hasActiveAnimation()); - - CCActiveAnimation* activeAnimation = controller->getActiveAnimation(0, CCActiveAnimation::Transform); - EXPECT_TRUE(activeAnimation); - EXPECT_TRUE(activeAnimation->alternatesDirection()); - - EXPECT_EQ(2, activeAnimation->iterations()); - EXPECT_EQ(CCActiveAnimation::Transform, activeAnimation->targetProperty()); - - EXPECT_EQ(CCAnimationCurve::Transform, activeAnimation->curve()->type()); - - const CCTransformAnimationCurve* curve = activeAnimation->curve()->toTransformAnimationCurve(); - EXPECT_TRUE(curve); - - expectTranslateX(2, curve->getValue(0, boxSize)); - expectTranslateX(4, curve->getValue(duration, boxSize)); -} - -TEST(CCLayerAnimationControllerTest, createReversedAlternatingAnimation) -{ - FakeLayerAnimationControllerClient dummy; - OwnPtr<CCLayerAnimationController> controller(CCLayerAnimationController::create(&dummy)); - const double duration = 1; - WebCore::KeyframeValueList values(AnimatedPropertyWebkitTransform); - - TransformOperations operations1; - operations1.operations().append(TranslateTransformOperation::create(Length(2, Fixed), Length(0, Fixed), TransformOperation::TRANSLATE_X)); - values.insert(new TransformAnimationValue(0, &operations1)); - - TransformOperations operations2; - operations2.operations().append(TranslateTransformOperation::create(Length(4, Fixed), Length(0, Fixed), TransformOperation::TRANSLATE_X)); - values.insert(new TransformAnimationValue(duration, &operations2)); - - RefPtr<Animation> animation = Animation::create(); - animation->setDuration(duration); - animation->setDirection(Animation::AnimationDirectionAlternateReverse); - animation->setIterationCount(2); - - IntSize boxSize; - controller->addAnimation(values, boxSize, animation.get(), 0, 0, 0); - - EXPECT_TRUE(controller->hasActiveAnimation()); - - CCActiveAnimation* activeAnimation = controller->getActiveAnimation(0, CCActiveAnimation::Transform); - EXPECT_TRUE(activeAnimation); - EXPECT_TRUE(activeAnimation->alternatesDirection()); - - EXPECT_EQ(2, activeAnimation->iterations()); - EXPECT_EQ(CCActiveAnimation::Transform, activeAnimation->targetProperty()); - - EXPECT_EQ(CCAnimationCurve::Transform, activeAnimation->curve()->type()); - - const CCTransformAnimationCurve* curve = activeAnimation->curve()->toTransformAnimationCurve(); - EXPECT_TRUE(curve); - - expectTranslateX(4, curve->getValue(0, boxSize)); - expectTranslateX(2, curve->getValue(duration, boxSize)); -} - TEST(CCLayerAnimationControllerTest, syncNewAnimation) { FakeLayerAnimationControllerClient dummyImpl; @@ -333,7 +141,6 @@ TEST(CCLayerAnimationControllerTest, syncPauseAndResume) EXPECT_EQ(CCActiveAnimation::Running, controllerImpl->getActiveAnimation(0, CCActiveAnimation::Opacity)->runState()); } - TEST(CCLayerAnimationControllerTest, doNotSyncFinishedAnimation) { FakeLayerAnimationControllerClient dummyImpl; @@ -375,7 +182,7 @@ TEST(CCLayerAnimationControllerTest, TrivialTransition) OwnPtr<CCActiveAnimation> toAdd(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 1)), 1, CCActiveAnimation::Opacity)); - controller->add(toAdd.release()); + controller->addAnimation(toAdd.release()); controller->animate(0, events.get()); EXPECT_TRUE(controller->hasActiveAnimation()); EXPECT_EQ(0, dummy.opacity()); @@ -396,7 +203,7 @@ TEST(CCLayerAnimationControllerTest, AnimationsWaitingForStartTimeDoNotFinishIfT toAdd->setNeedsSynchronizedStartTime(true); // We should pause at the first keyframe indefinitely waiting for that animation to start. - controller->add(toAdd.release()); + controller->addAnimation(toAdd.release()); controller->animate(0, events.get()); EXPECT_TRUE(controller->hasActiveAnimation()); EXPECT_EQ(0, dummy.opacity()); @@ -422,8 +229,8 @@ TEST(CCLayerAnimationControllerTest, TrivialQueuing) OwnPtr<CCLayerAnimationController> controller( CCLayerAnimationController::create(&dummy)); - controller->add(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 1)), 1, CCActiveAnimation::Opacity)); - controller->add(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 1, 0.5)), 2, CCActiveAnimation::Opacity)); + controller->addAnimation(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 1)), 1, CCActiveAnimation::Opacity)); + controller->addAnimation(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 1, 0.5)), 2, CCActiveAnimation::Opacity)); controller->animate(0, events.get()); EXPECT_TRUE(controller->hasActiveAnimation()); @@ -443,14 +250,14 @@ TEST(CCLayerAnimationControllerTest, Interrupt) FakeLayerAnimationControllerClient dummy; OwnPtr<CCLayerAnimationController> controller( CCLayerAnimationController::create(&dummy)); - controller->add(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 1)), 1, CCActiveAnimation::Opacity)); + controller->addAnimation(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 1)), 1, CCActiveAnimation::Opacity)); controller->animate(0, events.get()); EXPECT_TRUE(controller->hasActiveAnimation()); EXPECT_EQ(0, dummy.opacity()); OwnPtr<CCActiveAnimation> toAdd(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 1, 0.5)), 2, CCActiveAnimation::Opacity)); toAdd->setRunState(CCActiveAnimation::WaitingForNextTick, 0); - controller->add(toAdd.release()); + controller->addAnimation(toAdd.release()); // Since the animation was in the WaitingForNextTick state, it should start right in // this call to animate. @@ -470,9 +277,9 @@ TEST(CCLayerAnimationControllerTest, ScheduleTogetherWhenAPropertyIsBlocked) OwnPtr<CCLayerAnimationController> controller( CCLayerAnimationController::create(&dummy)); - controller->add(createActiveAnimation(adoptPtr(new FakeTransformTransition(1)), 1, CCActiveAnimation::Transform)); - controller->add(createActiveAnimation(adoptPtr(new FakeTransformTransition(1)), 2, CCActiveAnimation::Transform)); - controller->add(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 1)), 2, CCActiveAnimation::Opacity)); + controller->addAnimation(createActiveAnimation(adoptPtr(new FakeTransformTransition(1)), 1, CCActiveAnimation::Transform)); + controller->addAnimation(createActiveAnimation(adoptPtr(new FakeTransformTransition(1)), 2, CCActiveAnimation::Transform)); + controller->addAnimation(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 1)), 2, CCActiveAnimation::Opacity)); controller->animate(0, events.get()); EXPECT_EQ(0, dummy.opacity()); @@ -497,9 +304,9 @@ TEST(CCLayerAnimationControllerTest, ScheduleTogetherWithAnAnimWaiting) OwnPtr<CCLayerAnimationController> controller( CCLayerAnimationController::create(&dummy)); - controller->add(createActiveAnimation(adoptPtr(new FakeTransformTransition(2)), 1, CCActiveAnimation::Transform)); - controller->add(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 1)), 1, CCActiveAnimation::Opacity)); - controller->add(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 1, 0.5)), 2, CCActiveAnimation::Opacity)); + controller->addAnimation(createActiveAnimation(adoptPtr(new FakeTransformTransition(2)), 1, CCActiveAnimation::Transform)); + controller->addAnimation(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 1)), 1, CCActiveAnimation::Opacity)); + controller->addAnimation(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 1, 0.5)), 2, CCActiveAnimation::Opacity)); // Animations with id 1 should both start now. controller->animate(0, events.get()); @@ -530,7 +337,7 @@ TEST(CCLayerAnimationControllerTest, ScheduleAnimation) OwnPtr<CCActiveAnimation> toAdd(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 1)), 1, CCActiveAnimation::Opacity)); toAdd->setRunState(CCActiveAnimation::WaitingForStartTime, 0); toAdd->setStartTime(1); - controller->add(toAdd.release()); + controller->addAnimation(toAdd.release()); controller->animate(0, events.get()); EXPECT_TRUE(controller->hasActiveAnimation()); @@ -551,12 +358,12 @@ TEST(CCLayerAnimationControllerTest, ScheduledAnimationInterruptsRunningAnimatio OwnPtr<CCLayerAnimationController> controller( CCLayerAnimationController::create(&dummy)); - controller->add(createActiveAnimation(adoptPtr(new FakeFloatTransition(2, 0, 1)), 1, CCActiveAnimation::Opacity)); + controller->addAnimation(createActiveAnimation(adoptPtr(new FakeFloatTransition(2, 0, 1)), 1, CCActiveAnimation::Opacity)); OwnPtr<CCActiveAnimation> toAdd(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0.5, 0)), 2, CCActiveAnimation::Opacity)); toAdd->setRunState(CCActiveAnimation::WaitingForStartTime, 0); toAdd->setStartTime(1); - controller->add(toAdd.release()); + controller->addAnimation(toAdd.release()); // First 2s opacity transition should start immediately. controller->animate(0, events.get()); @@ -582,14 +389,14 @@ TEST(CCLayerAnimationControllerTest, ScheduledAnimationInterruptsRunningAnimatio OwnPtr<CCLayerAnimationController> controller( CCLayerAnimationController::create(&dummy)); - controller->add(createActiveAnimation(adoptPtr(new FakeFloatTransition(2, 0, 1)), 1, CCActiveAnimation::Opacity)); + controller->addAnimation(createActiveAnimation(adoptPtr(new FakeFloatTransition(2, 0, 1)), 1, CCActiveAnimation::Opacity)); OwnPtr<CCActiveAnimation> toAdd(createActiveAnimation(adoptPtr(new FakeFloatTransition(2, 0.5, 0)), 2, CCActiveAnimation::Opacity)); toAdd->setRunState(CCActiveAnimation::WaitingForStartTime, 0); toAdd->setStartTime(1); - controller->add(toAdd.release()); + controller->addAnimation(toAdd.release()); - controller->add(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 0.75)), 3, CCActiveAnimation::Opacity)); + controller->addAnimation(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 0.75)), 3, CCActiveAnimation::Opacity)); // First 2s opacity transition should start immediately. controller->animate(0, events.get()); @@ -620,7 +427,7 @@ TEST(CCLayerAnimationControllerTest, TrivialLooping) OwnPtr<CCActiveAnimation> toAdd(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 1)), 1, CCActiveAnimation::Opacity)); toAdd->setIterations(3); - controller->add(toAdd.release()); + controller->addAnimation(toAdd.release()); controller->animate(0, events.get()); EXPECT_TRUE(controller->hasActiveAnimation()); @@ -657,7 +464,7 @@ TEST(CCLayerAnimationControllerTest, InfiniteLooping) const int id = 1; OwnPtr<CCActiveAnimation> toAdd(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 1)), id, CCActiveAnimation::Opacity)); toAdd->setIterations(-1); - controller->add(toAdd.release()); + controller->addAnimation(toAdd.release()); controller->animate(0, events.get()); EXPECT_TRUE(controller->hasActiveAnimation()); @@ -691,7 +498,7 @@ TEST(CCLayerAnimationControllerTest, PauseResume) CCLayerAnimationController::create(&dummy)); const int id = 1; - controller->add(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 1)), id, CCActiveAnimation::Opacity)); + controller->addAnimation(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 1)), id, CCActiveAnimation::Opacity)); controller->animate(0, events.get()); EXPECT_TRUE(controller->hasActiveAnimation()); @@ -726,9 +533,9 @@ TEST(CCLayerAnimationControllerTest, AbortAGroupedAnimation) CCLayerAnimationController::create(&dummy)); const int id = 1; - controller->add(createActiveAnimation(adoptPtr(new FakeTransformTransition(1)), id, CCActiveAnimation::Transform)); - controller->add(createActiveAnimation(adoptPtr(new FakeFloatTransition(2, 0, 1)), id, CCActiveAnimation::Opacity)); - controller->add(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 1, 0.75)), 2, CCActiveAnimation::Opacity)); + controller->addAnimation(createActiveAnimation(adoptPtr(new FakeTransformTransition(1)), id, CCActiveAnimation::Transform)); + controller->addAnimation(createActiveAnimation(adoptPtr(new FakeFloatTransition(2, 0, 1)), id, CCActiveAnimation::Opacity)); + controller->addAnimation(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 1, 0.75)), 2, CCActiveAnimation::Opacity)); controller->animate(0, events.get()); EXPECT_TRUE(controller->hasActiveAnimation()); @@ -758,7 +565,7 @@ TEST(CCLayerAnimationControllerTest, ForceSyncWhenSynchronizedStartTimeNeeded) OwnPtr<CCActiveAnimation> toAdd(createActiveAnimation(adoptPtr(new FakeFloatTransition(2, 0, 1)), 0, CCActiveAnimation::Opacity)); toAdd->setNeedsSynchronizedStartTime(true); - controller->add(toAdd.release()); + controller->addAnimation(toAdd.release()); controller->animate(0, 0); EXPECT_TRUE(controller->hasActiveAnimation()); diff --git a/Source/WebKit/chromium/tests/CCLayerImplTest.cpp b/Source/WebKit/chromium/tests/CCLayerImplTest.cpp index 827c9adaa..01f943a64 100644 --- a/Source/WebKit/chromium/tests/CCLayerImplTest.cpp +++ b/Source/WebKit/chromium/tests/CCLayerImplTest.cpp @@ -42,21 +42,33 @@ namespace { codeToTest; \ EXPECT_TRUE(root->layerPropertyChanged()); \ EXPECT_TRUE(child->layerPropertyChanged()); \ - EXPECT_TRUE(grandChild->layerPropertyChanged()) + EXPECT_TRUE(grandChild->layerPropertyChanged()); \ + EXPECT_FALSE(root->layerSurfacePropertyChanged()) + #define EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(codeToTest) \ root->resetAllChangeTrackingForSubtree(); \ codeToTest; \ EXPECT_FALSE(root->layerPropertyChanged()); \ EXPECT_FALSE(child->layerPropertyChanged()); \ - EXPECT_FALSE(grandChild->layerPropertyChanged()) + EXPECT_FALSE(grandChild->layerPropertyChanged()); \ + EXPECT_FALSE(root->layerSurfacePropertyChanged()) #define EXECUTE_AND_VERIFY_ONLY_LAYER_CHANGED(codeToTest) \ root->resetAllChangeTrackingForSubtree(); \ codeToTest; \ EXPECT_TRUE(root->layerPropertyChanged()); \ EXPECT_FALSE(child->layerPropertyChanged()); \ - EXPECT_FALSE(grandChild->layerPropertyChanged()) + EXPECT_FALSE(grandChild->layerPropertyChanged()); \ + EXPECT_FALSE(root->layerSurfacePropertyChanged()) + +#define EXECUTE_AND_VERIFY_ONLY_SURFACE_CHANGED(codeToTest) \ + root->resetAllChangeTrackingForSubtree(); \ + codeToTest; \ + EXPECT_FALSE(root->layerPropertyChanged()); \ + EXPECT_FALSE(child->layerPropertyChanged()); \ + EXPECT_FALSE(grandChild->layerPropertyChanged()); \ + EXPECT_TRUE(root->layerSurfacePropertyChanged()) TEST(CCLayerImplTest, verifyLayerChangesAreTrackedProperly) { @@ -98,11 +110,9 @@ TEST(CCLayerImplTest, verifyLayerChangesAreTrackedProperly) EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->setMaskLayer(CCLayerImpl::create(4))); EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->setMasksToBounds(true)); EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->setOpaque(true)); - EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->setOpacity(arbitraryNumber)); EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->setReplicaLayer(CCLayerImpl::create(5))); EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->setPosition(arbitraryFloatPoint)); EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->setPreserves3D(true)); - EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->setTransform(arbitraryTransform)); EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->setDoubleSided(false)); // constructor initializes it to "true". EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->scrollBy(arbitraryIntSize)); EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->setScrollDelta(arbitraryIntSize)); @@ -117,6 +127,10 @@ TEST(CCLayerImplTest, verifyLayerChangesAreTrackedProperly) EXECUTE_AND_VERIFY_ONLY_LAYER_CHANGED(root->setBackgroundColor(Color::gray)); EXECUTE_AND_VERIFY_ONLY_LAYER_CHANGED(root->setBackgroundFilters(arbitraryFilters)); + // Changing these properties only affects how render surface is drawn + EXECUTE_AND_VERIFY_ONLY_SURFACE_CHANGED(root->setOpacity(arbitraryNumber)); + EXECUTE_AND_VERIFY_ONLY_SURFACE_CHANGED(root->setTransform(arbitraryTransform)); + // Special case: check that sublayer transform changes all layer's descendants, but not the layer itself. root->resetAllChangeTrackingForSubtree(); root->setSublayerTransform(arbitraryTransform); diff --git a/Source/WebKit/chromium/tests/CCLayerIteratorTest.cpp b/Source/WebKit/chromium/tests/CCLayerIteratorTest.cpp index 105c7186e..86a374325 100644 --- a/Source/WebKit/chromium/tests/CCLayerIteratorTest.cpp +++ b/Source/WebKit/chromium/tests/CCLayerIteratorTest.cpp @@ -122,6 +122,14 @@ void iterateBackToFront(Vector<RefPtr<LayerChromium> >* renderSurfaceLayerList) } } +TEST(CCLayerIteratorTest, emptyTree) +{ + Vector<RefPtr<LayerChromium> > renderSurfaceLayerList; + + iterateBackToFront(&renderSurfaceLayerList); + iterateFrontToBack(&renderSurfaceLayerList); +} + TEST(CCLayerIteratorTest, simpleTree) { RefPtr<TestLayerChromium> rootLayer = TestLayerChromium::create(); @@ -140,10 +148,11 @@ TEST(CCLayerIteratorTest, simpleTree) Vector<RefPtr<LayerChromium> > renderSurfaceLayerList; Vector<RefPtr<LayerChromium> > layerList; renderSurfaceLayerList.append(rootLayer.get()); - CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(rootLayer.get(), rootLayer.get(), - WebTransformationMatrix(), WebTransformationMatrix(), - renderSurfaceLayerList, layerList, - 256); + CCLayerTreeHostCommon::calculateDrawTransforms(rootLayer.get(), rootLayer.get(), + WebTransformationMatrix(), WebTransformationMatrix(), + renderSurfaceLayerList, layerList, + 256); + CCLayerTreeHostCommon::calculateVisibleAndScissorRects(renderSurfaceLayerList, rootLayer->renderSurface()->contentRect()); iterateBackToFront(&renderSurfaceLayerList); EXPECT_COUNT(rootLayer, 0, -1, 1); @@ -187,10 +196,11 @@ TEST(CCLayerIteratorTest, complexTree) Vector<RefPtr<LayerChromium> > renderSurfaceLayerList; Vector<RefPtr<LayerChromium> > layerList; renderSurfaceLayerList.append(rootLayer.get()); - CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(rootLayer.get(), rootLayer.get(), - WebTransformationMatrix(), WebTransformationMatrix(), - renderSurfaceLayerList, layerList, - 256); + CCLayerTreeHostCommon::calculateDrawTransforms(rootLayer.get(), rootLayer.get(), + WebTransformationMatrix(), WebTransformationMatrix(), + renderSurfaceLayerList, layerList, + 256); + CCLayerTreeHostCommon::calculateVisibleAndScissorRects(renderSurfaceLayerList, rootLayer->renderSurface()->contentRect()); iterateBackToFront(&renderSurfaceLayerList); EXPECT_COUNT(rootLayer, 0, -1, 1); @@ -246,10 +256,11 @@ TEST(CCLayerIteratorTest, complexTreeMultiSurface) Vector<RefPtr<LayerChromium> > renderSurfaceLayerList; Vector<RefPtr<LayerChromium> > layerList; renderSurfaceLayerList.append(rootLayer.get()); - CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(rootLayer.get(), rootLayer.get(), - WebTransformationMatrix(), WebTransformationMatrix(), - renderSurfaceLayerList, layerList, - 256); + CCLayerTreeHostCommon::calculateDrawTransforms(rootLayer.get(), rootLayer.get(), + WebTransformationMatrix(), WebTransformationMatrix(), + renderSurfaceLayerList, layerList, + 256); + CCLayerTreeHostCommon::calculateVisibleAndScissorRects(renderSurfaceLayerList, rootLayer->renderSurface()->contentRect()); iterateBackToFront(&renderSurfaceLayerList); EXPECT_COUNT(rootLayer, 0, -1, 1); 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" diff --git a/Source/WebKit/chromium/tests/CCLayerTreeHostImplTest.cpp b/Source/WebKit/chromium/tests/CCLayerTreeHostImplTest.cpp index 91c2d1dff..738c88190 100644 --- a/Source/WebKit/chromium/tests/CCLayerTreeHostImplTest.cpp +++ b/Source/WebKit/chromium/tests/CCLayerTreeHostImplTest.cpp @@ -28,18 +28,24 @@ #include "CCAnimationTestCommon.h" #include "CCLayerTestCommon.h" +#include "CCTestCommon.h" #include "FakeWebGraphicsContext3D.h" #include "GraphicsContext3DPrivate.h" #include "LayerRendererChromium.h" +#include "cc/CCIOSurfaceLayerImpl.h" #include "cc/CCLayerImpl.h" #include "cc/CCLayerTilingData.h" #include "cc/CCQuadCuller.h" +#include "cc/CCRenderPassDrawQuad.h" #include "cc/CCScrollbarLayerImpl.h" +#include "cc/CCSettings.h" #include "cc/CCSingleThreadProxy.h" +#include "cc/CCSolidColorDrawQuad.h" #include "cc/CCTextureLayerImpl.h" #include "cc/CCTileDrawQuad.h" #include "cc/CCTiledLayerImpl.h" #include "cc/CCVideoLayerImpl.h" +#include <gmock/gmock.h> #include <gtest/gtest.h> #include <public/WebVideoFrame.h> #include <public/WebVideoFrameProvider.h> @@ -49,6 +55,12 @@ using namespace WebCore; using namespace WebKit; using namespace WebKitTests; +using ::testing::Mock; +using ::testing::Return; +using ::testing::AnyNumber; +using ::testing::AtLeast; +using ::testing::_; + namespace { class CCLayerTreeHostImplTest : public testing::Test, public CCLayerTreeHostImplClient { @@ -57,7 +69,7 @@ public: : m_didRequestCommit(false) , m_didRequestRedraw(false) { - CCSettings settings; + CCLayerTreeSettings settings; m_hostImpl = CCLayerTreeHostImpl::create(settings, this); m_hostImpl->initializeLayerRenderer(createContext(), UnthrottledUploader); m_hostImpl->setViewportSize(IntSize(10, 10)); @@ -70,6 +82,28 @@ public: virtual void postAnimationEventsToMainThreadOnImplThread(PassOwnPtr<CCAnimationEventsVector>, double wallClockTime) OVERRIDE { } virtual void postSetContentsMemoryAllocationLimitBytesToMainThreadOnImplThread(size_t) OVERRIDE { } + PassOwnPtr<CCLayerTreeHostImpl> createLayerTreeHost(bool partialSwap, PassRefPtr<CCGraphicsContext> graphicsContext, PassOwnPtr<CCLayerImpl> rootPtr) + { + CCSettings::setPartialSwapEnabled(partialSwap); + + CCLayerTreeSettings settings; + OwnPtr<CCLayerTreeHostImpl> myHostImpl = CCLayerTreeHostImpl::create(settings, this); + + myHostImpl->initializeLayerRenderer(graphicsContext, UnthrottledUploader); + myHostImpl->setViewportSize(IntSize(10, 10)); + + OwnPtr<CCLayerImpl> root = rootPtr; + + root->setAnchorPoint(FloatPoint(0, 0)); + root->setPosition(FloatPoint(0, 0)); + root->setBounds(IntSize(10, 10)); + root->setContentBounds(IntSize(10, 10)); + root->setVisibleLayerRect(IntRect(0, 0, 10, 10)); + root->setDrawsContent(true); + myHostImpl->setRootLayer(root.release()); + return myHostImpl.release(); + } + static void expectClearedScrollDeltasRecursive(CCLayerImpl* layer) { ASSERT_EQ(layer->scrollDelta(), IntSize()); @@ -106,10 +140,31 @@ public: m_hostImpl->setRootLayer(root.release()); } + static PassOwnPtr<CCLayerImpl> createScrollableLayer(int id, const FloatPoint& position, const IntSize& size) + { + OwnPtr<CCLayerImpl> layer = CCLayerImpl::create(id); + layer->setScrollable(true); + layer->setDrawsContent(true); + layer->setPosition(position); + layer->setBounds(size); + layer->setContentBounds(size); + layer->setMaxScrollPosition(IntSize(size.width() * 2, size.height() * 2)); + return layer.release(); + } + + void initializeLayerRendererAndDrawFrame() + { + m_hostImpl->initializeLayerRenderer(createContext(), UnthrottledUploader); + CCLayerTreeHostImpl::FrameData frame; + EXPECT_TRUE(m_hostImpl->prepareToDraw(frame)); + m_hostImpl->drawLayers(frame); + m_hostImpl->didDrawAllLayers(frame); + } + protected: - PassRefPtr<GraphicsContext3D> createContext() + PassRefPtr<CCGraphicsContext> createContext() { - return GraphicsContext3DPrivate::createGraphicsContextFromWebContext(adoptPtr(new FakeWebGraphicsContext3D()), GraphicsContext3D::RenderDirectlyToHostWindow); + return CCGraphicsContext::create3D(GraphicsContext3DPrivate::createGraphicsContextFromWebContext(adoptPtr(new FakeWebGraphicsContext3D()), GraphicsContext3D::RenderDirectlyToHostWindow)); } DebugScopedSetImplThread m_alwaysImplThread; @@ -118,6 +173,7 @@ protected: OwnPtr<CCLayerTreeHostImpl> m_hostImpl; bool m_didRequestCommit; bool m_didRequestRedraw; + CCScopedSettings m_scopedSettings; }; TEST_F(CCLayerTreeHostImplTest, scrollDeltaNoLayers) @@ -189,13 +245,9 @@ TEST_F(CCLayerTreeHostImplTest, scrollDeltaRepeatedScrolls) TEST_F(CCLayerTreeHostImplTest, scrollRootCallsCommitAndRedraw) { - { - OwnPtr<CCLayerImpl> root = CCLayerImpl::create(0); - root->setScrollable(true); - root->setScrollPosition(IntPoint(0, 0)); - root->setMaxScrollPosition(IntSize(100, 100)); - m_hostImpl->setRootLayer(root.release()); - } + setupScrollAndContentsLayers(IntSize(100, 100)); + m_hostImpl->setViewportSize(IntSize(50, 50)); + initializeLayerRendererAndDrawFrame(); EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(0, 0), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollStarted); m_hostImpl->scrollBy(IntSize(0, 10)); @@ -204,20 +256,57 @@ TEST_F(CCLayerTreeHostImplTest, scrollRootCallsCommitAndRedraw) EXPECT_TRUE(m_didRequestCommit); } +TEST_F(CCLayerTreeHostImplTest, scrollWithoutRootLayer) +{ + // We should not crash when trying to scroll an empty layer tree. + EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(0, 0), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollIgnored); +} + +TEST_F(CCLayerTreeHostImplTest, replaceTreeWhileScrolling) +{ + const int scrollLayerId = 0; + + setupScrollAndContentsLayers(IntSize(100, 100)); + m_hostImpl->setViewportSize(IntSize(50, 50)); + initializeLayerRendererAndDrawFrame(); + + // We should not crash if the tree is replaced while we are scrolling. + EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(0, 0), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollStarted); + m_hostImpl->detachLayerTree(); + + setupScrollAndContentsLayers(IntSize(100, 100)); + + // We should still be scrolling, because the scrolled layer also exists in the new tree. + IntSize scrollDelta(0, 10); + m_hostImpl->scrollBy(scrollDelta); + m_hostImpl->scrollEnd(); + OwnPtr<CCScrollAndScaleSet> scrollInfo = m_hostImpl->processScrollDeltas(); + expectContains(*scrollInfo, scrollLayerId, scrollDelta); +} + +TEST_F(CCLayerTreeHostImplTest, clearRootRenderSurfaceAndScroll) +{ + setupScrollAndContentsLayers(IntSize(100, 100)); + m_hostImpl->setViewportSize(IntSize(50, 50)); + initializeLayerRendererAndDrawFrame(); + + // We should be able to scroll even if the root layer loses its render surface after the most + // recent render. + m_hostImpl->rootLayer()->clearRenderSurface(); + EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(0, 0), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollStarted); +} + TEST_F(CCLayerTreeHostImplTest, wheelEventHandlers) { - { - OwnPtr<CCLayerImpl> root = CCLayerImpl::create(0); - root->setScrollable(true); - root->setScrollPosition(IntPoint(0, 0)); - root->setMaxScrollPosition(IntSize(100, 100)); - m_hostImpl->setRootLayer(root.release()); - } + setupScrollAndContentsLayers(IntSize(100, 100)); + m_hostImpl->setViewportSize(IntSize(50, 50)); + initializeLayerRendererAndDrawFrame(); CCLayerImpl* root = m_hostImpl->rootLayer(); root->setHaveWheelEventHandlers(true); + // With registered event handlers, wheel scrolls have to go to the main thread. - EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(0, 0), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollFailed); + EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(0, 0), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollOnMainThread); // But gesture scrolls can still be handled. EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(0, 0), CCInputHandlerClient::Gesture), CCInputHandlerClient::ScrollStarted); @@ -225,27 +314,29 @@ TEST_F(CCLayerTreeHostImplTest, wheelEventHandlers) TEST_F(CCLayerTreeHostImplTest, shouldScrollOnMainThread) { - OwnPtr<CCLayerImpl> root = CCLayerImpl::create(0); - root->setScrollable(true); - root->setScrollPosition(IntPoint(0, 0)); - root->setMaxScrollPosition(IntSize(100, 100)); + setupScrollAndContentsLayers(IntSize(100, 100)); + m_hostImpl->setViewportSize(IntSize(50, 50)); + initializeLayerRendererAndDrawFrame(); + CCLayerImpl* root = m_hostImpl->rootLayer(); + root->setShouldScrollOnMainThread(true); - m_hostImpl->setRootLayer(root.release()); - EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(0, 0), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollFailed); - EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(0, 0), CCInputHandlerClient::Gesture), CCInputHandlerClient::ScrollFailed); + + EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(0, 0), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollOnMainThread); + EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(0, 0), CCInputHandlerClient::Gesture), CCInputHandlerClient::ScrollOnMainThread); } TEST_F(CCLayerTreeHostImplTest, nonFastScrollableRegionBasic) { - OwnPtr<CCLayerImpl> root = CCLayerImpl::create(0); - root->setScrollable(true); - root->setScrollPosition(IntPoint(0, 0)); - root->setMaxScrollPosition(IntSize(100, 100)); + setupScrollAndContentsLayers(IntSize(200, 200)); + m_hostImpl->setViewportSize(IntSize(100, 100)); + initializeLayerRendererAndDrawFrame(); + CCLayerImpl* root = m_hostImpl->rootLayer(); + root->setNonFastScrollableRegion(IntRect(0, 0, 50, 50)); - m_hostImpl->setRootLayer(root.release()); + // All scroll types inside the non-fast scrollable region should fail. - EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(25, 25), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollFailed); - EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(25, 25), CCInputHandlerClient::Gesture), CCInputHandlerClient::ScrollFailed); + EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(25, 25), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollOnMainThread); + EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(25, 25), CCInputHandlerClient::Gesture), CCInputHandlerClient::ScrollOnMainThread); // All scroll types outside this region should succeed. EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(75, 75), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollStarted); @@ -258,16 +349,13 @@ TEST_F(CCLayerTreeHostImplTest, nonFastScrollableRegionBasic) TEST_F(CCLayerTreeHostImplTest, nonFastScrollableRegionWithOffset) { - OwnPtr<CCLayerImpl> root = CCLayerImpl::create(0); - root->setScrollable(true); - root->setScrollPosition(IntPoint(0, 0)); - root->setMaxScrollPosition(IntSize(100, 100)); + setupScrollAndContentsLayers(IntSize(200, 200)); + m_hostImpl->setViewportSize(IntSize(100, 100)); + CCLayerImpl* root = m_hostImpl->rootLayer(); + root->setNonFastScrollableRegion(IntRect(0, 0, 50, 50)); root->setPosition(FloatPoint(-25, 0)); - m_hostImpl->setRootLayer(root.release()); - CCLayerTreeHostImpl::FrameData frame; - EXPECT_TRUE(m_hostImpl->prepareToDraw(frame)); - m_hostImpl->drawLayers(frame); // Update draw transforms so we can correctly map points into layer space. + initializeLayerRendererAndDrawFrame(); // This point would fall into the non-fast scrollable region except that we've moved the layer down by 25 pixels. EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(40, 10), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollStarted); @@ -275,15 +363,16 @@ TEST_F(CCLayerTreeHostImplTest, nonFastScrollableRegionWithOffset) m_hostImpl->scrollEnd(); // This point is still inside the non-fast region. - EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(10, 10), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollFailed); + EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(10, 10), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollOnMainThread); } TEST_F(CCLayerTreeHostImplTest, pinchGesture) { setupScrollAndContentsLayers(IntSize(100, 100)); m_hostImpl->setViewportSize(IntSize(50, 50)); + initializeLayerRendererAndDrawFrame(); - CCLayerImpl* scrollLayer = m_hostImpl->scrollLayer(); + CCLayerImpl* scrollLayer = m_hostImpl->rootScrollLayer(); ASSERT(scrollLayer); const float minPageScale = 0.5, maxPageScale = 4; @@ -362,8 +451,9 @@ TEST_F(CCLayerTreeHostImplTest, pageScaleAnimation) { setupScrollAndContentsLayers(IntSize(100, 100)); m_hostImpl->setViewportSize(IntSize(50, 50)); + initializeLayerRendererAndDrawFrame(); - CCLayerImpl* scrollLayer = m_hostImpl->scrollLayer(); + CCLayerImpl* scrollLayer = m_hostImpl->rootScrollLayer(); ASSERT(scrollLayer); const float minPageScale = 0.5, maxPageScale = 4; @@ -407,6 +497,101 @@ TEST_F(CCLayerTreeHostImplTest, pageScaleAnimation) } } +TEST_F(CCLayerTreeHostImplTest, inhibitScrollAndPageScaleUpdatesWhilePinchZooming) +{ + setupScrollAndContentsLayers(IntSize(100, 100)); + m_hostImpl->setViewportSize(IntSize(50, 50)); + initializeLayerRendererAndDrawFrame(); + + CCLayerImpl* scrollLayer = m_hostImpl->rootScrollLayer(); + ASSERT(scrollLayer); + + const float minPageScale = 0.5, maxPageScale = 4; + + // Pinch zoom in. + { + // Start a pinch in gesture at the bottom right corner of the viewport. + const float zoomInDelta = 2; + m_hostImpl->setPageScaleFactorAndLimits(1, minPageScale, maxPageScale); + m_hostImpl->pinchGestureBegin(); + m_hostImpl->pinchGestureUpdate(zoomInDelta, IntPoint(50, 50)); + + // Because we are pinch zooming in, we shouldn't get any scroll or page + // scale deltas. + OwnPtr<CCScrollAndScaleSet> scrollInfo = m_hostImpl->processScrollDeltas(); + EXPECT_EQ(scrollInfo->pageScaleDelta, 1); + EXPECT_EQ(scrollInfo->scrolls.size(), 0u); + + // Once the gesture ends, we get the final scroll and page scale values. + m_hostImpl->pinchGestureEnd(); + scrollInfo = m_hostImpl->processScrollDeltas(); + EXPECT_EQ(scrollInfo->pageScaleDelta, zoomInDelta); + expectContains(*scrollInfo, scrollLayer->id(), IntSize(25, 25)); + } + + // Pinch zoom out. + { + // Start a pinch out gesture at the bottom right corner of the viewport. + const float zoomOutDelta = 0.75; + m_hostImpl->setPageScaleFactorAndLimits(1, minPageScale, maxPageScale); + m_hostImpl->pinchGestureBegin(); + m_hostImpl->pinchGestureUpdate(zoomOutDelta, IntPoint(50, 50)); + + // Since we are pinch zooming out, we should get an update to zoom all + // the way out to the minimum page scale. + OwnPtr<CCScrollAndScaleSet> scrollInfo = m_hostImpl->processScrollDeltas(); + EXPECT_EQ(scrollInfo->pageScaleDelta, minPageScale); + expectContains(*scrollInfo, scrollLayer->id(), IntSize(0, 0)); + + // Once the gesture ends, we get the final scroll and page scale values. + m_hostImpl->pinchGestureEnd(); + scrollInfo = m_hostImpl->processScrollDeltas(); + EXPECT_EQ(scrollInfo->pageScaleDelta, zoomOutDelta); + expectContains(*scrollInfo, scrollLayer->id(), IntSize(8, 8)); + } +} + +TEST_F(CCLayerTreeHostImplTest, inhibitScrollAndPageScaleUpdatesWhileAnimatingPageScale) +{ + setupScrollAndContentsLayers(IntSize(100, 100)); + m_hostImpl->setViewportSize(IntSize(50, 50)); + initializeLayerRendererAndDrawFrame(); + + CCLayerImpl* scrollLayer = m_hostImpl->rootScrollLayer(); + ASSERT(scrollLayer); + + const float minPageScale = 0.5, maxPageScale = 4; + const double startTime = 1; + const double duration = 0.1; + const double halfwayThroughAnimation = startTime + duration / 2; + const double endTime = startTime + duration; + + // Start a page scale animation. + const float pageScaleDelta = 2; + m_hostImpl->setPageScaleFactorAndLimits(1, minPageScale, maxPageScale); + m_hostImpl->startPageScaleAnimation(IntSize(50, 50), false, pageScaleDelta, startTime, duration); + + // We should immediately get the final zoom and scroll values for the + // animation. + m_hostImpl->animate(halfwayThroughAnimation, halfwayThroughAnimation); + OwnPtr<CCScrollAndScaleSet> scrollInfo = m_hostImpl->processScrollDeltas(); + EXPECT_EQ(scrollInfo->pageScaleDelta, pageScaleDelta); + expectContains(*scrollInfo, scrollLayer->id(), IntSize(25, 25)); + + // Scrolling during the animation is ignored. + const IntSize scrollDelta(0, 10); + EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(25, 25), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollStarted); + m_hostImpl->scrollBy(scrollDelta); + m_hostImpl->scrollEnd(); + + // The final page scale and scroll deltas should match what we got + // earlier. + m_hostImpl->animate(endTime, endTime); + scrollInfo = m_hostImpl->processScrollDeltas(); + EXPECT_EQ(scrollInfo->pageScaleDelta, pageScaleDelta); + expectContains(*scrollInfo, scrollLayer->id(), IntSize(25, 25)); +} + class DidDrawCheckLayer : public CCTiledLayerImpl { public: static PassOwnPtr<DidDrawCheckLayer> create(int id) { return adoptPtr(new DidDrawCheckLayer(id)); } @@ -416,7 +601,7 @@ public: m_didDrawCalled = true; } - virtual void willDraw(LayerRendererChromium*) + virtual void willDraw(CCRenderer*, CCGraphicsContext*) { m_willDrawCalled = true; } @@ -424,6 +609,12 @@ public: bool didDrawCalled() const { return m_didDrawCalled; } bool willDrawCalled() const { return m_willDrawCalled; } + void clearDidDrawCheck() + { + m_didDrawCalled = false; + m_willDrawCalled = false; + } + protected: explicit DidDrawCheckLayer(int id) : CCTiledLayerImpl(id) @@ -432,7 +623,12 @@ protected: { setAnchorPoint(FloatPoint(0, 0)); setBounds(IntSize(10, 10)); + setContentBounds(IntSize(10, 10)); setDrawsContent(true); + setSkipsDraw(false); + + OwnPtr<CCLayerTilingData> tiler = CCLayerTilingData::create(IntSize(100, 100), CCLayerTilingData::HasBorderTexels); + setTilingData(*tiler.get()); } private: @@ -485,6 +681,42 @@ TEST_F(CCLayerTreeHostImplTest, didDrawNotCalledOnHiddenLayer) EXPECT_FALSE(layer->visibleLayerRect().isEmpty()); } +TEST_F(CCLayerTreeHostImplTest, willDrawNotCalledOnOccludedLayer) +{ + // Make the viewport large so that we can have large layers that get considered for occlusion (small layers do not). + IntSize bigSize(1000, 1000); + m_hostImpl->setViewportSize(bigSize); + + m_hostImpl->setRootLayer(DidDrawCheckLayer::create(0)); + DidDrawCheckLayer* root = static_cast<DidDrawCheckLayer*>(m_hostImpl->rootLayer()); + + root->addChild(DidDrawCheckLayer::create(1)); + DidDrawCheckLayer* occludedLayer = static_cast<DidDrawCheckLayer*>(root->children()[0].get()); + + root->addChild(DidDrawCheckLayer::create(2)); + DidDrawCheckLayer* topLayer = static_cast<DidDrawCheckLayer*>(root->children()[1].get()); + // This layer covers the occludedLayer above. Make this layer large so it can occlude. + topLayer->setBounds(bigSize); + topLayer->setContentBounds(bigSize); + topLayer->setOpaque(true); + + CCLayerTreeHostImpl::FrameData frame; + + EXPECT_FALSE(occludedLayer->willDrawCalled()); + EXPECT_FALSE(occludedLayer->didDrawCalled()); + EXPECT_FALSE(topLayer->willDrawCalled()); + EXPECT_FALSE(topLayer->didDrawCalled()); + + EXPECT_TRUE(m_hostImpl->prepareToDraw(frame)); + m_hostImpl->drawLayers(frame); + m_hostImpl->didDrawAllLayers(frame); + + EXPECT_FALSE(occludedLayer->willDrawCalled()); + EXPECT_FALSE(occludedLayer->didDrawCalled()); + EXPECT_TRUE(topLayer->willDrawCalled()); + EXPECT_TRUE(topLayer->didDrawCalled()); +} + TEST_F(CCLayerTreeHostImplTest, didDrawCalledOnAllLayers) { m_hostImpl->setRootLayer(DidDrawCheckLayer::create(0)); @@ -562,9 +794,7 @@ TEST_F(CCLayerTreeHostImplTest, prepareToDrawFailsWhenAnimationUsesCheckerboard) root = static_cast<DidDrawCheckLayer*>(m_hostImpl->rootLayer()); root->addChild(MissingTextureAnimatingLayer::create(1, true, false, true)); - m_didRequestCommit = false; EXPECT_FALSE(m_hostImpl->prepareToDraw(frame)); - EXPECT_TRUE(m_didRequestCommit); m_hostImpl->drawLayers(frame); m_hostImpl->didDrawAllLayers(frame); @@ -578,6 +808,299 @@ TEST_F(CCLayerTreeHostImplTest, prepareToDrawFailsWhenAnimationUsesCheckerboard) m_hostImpl->didDrawAllLayers(frame); } +TEST_F(CCLayerTreeHostImplTest, scrollRootIgnored) +{ + OwnPtr<CCLayerImpl> root = CCLayerImpl::create(0); + root->setScrollable(false); + m_hostImpl->setRootLayer(root.release()); + initializeLayerRendererAndDrawFrame(); + + // Scroll event is ignored because layer is not scrollable. + EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(0, 0), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollIgnored); + EXPECT_FALSE(m_didRequestRedraw); + EXPECT_FALSE(m_didRequestCommit); +} + +TEST_F(CCLayerTreeHostImplTest, scrollNonCompositedRoot) +{ + // Test the configuration where a non-composited root layer is embedded in a + // scrollable outer layer. + IntSize surfaceSize(10, 10); + + OwnPtr<CCLayerImpl> contentLayer = CCLayerImpl::create(1); + contentLayer->setIsNonCompositedContent(true); + contentLayer->setDrawsContent(true); + contentLayer->setPosition(IntPoint(5, 5)); + contentLayer->setBounds(surfaceSize); + contentLayer->setContentBounds(IntSize(surfaceSize.width() * 2, surfaceSize.height() * 2)); + + OwnPtr<CCLayerImpl> scrollLayer = CCLayerImpl::create(0); + scrollLayer->setScrollable(true); + scrollLayer->setMaxScrollPosition(surfaceSize); + scrollLayer->addChild(contentLayer.release()); + + m_hostImpl->setRootLayer(scrollLayer.release()); + m_hostImpl->setViewportSize(surfaceSize); + initializeLayerRendererAndDrawFrame(); + + EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(5, 5), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollStarted); + m_hostImpl->scrollBy(IntSize(0, 10)); + m_hostImpl->scrollEnd(); + EXPECT_TRUE(m_didRequestRedraw); + EXPECT_TRUE(m_didRequestCommit); +} + +TEST_F(CCLayerTreeHostImplTest, scrollChildCallsCommitAndRedraw) +{ + IntSize surfaceSize(10, 10); + OwnPtr<CCLayerImpl> root = CCLayerImpl::create(0); + root->addChild(createScrollableLayer(1, FloatPoint(5, 5), surfaceSize)); + m_hostImpl->setRootLayer(root.release()); + m_hostImpl->setViewportSize(surfaceSize); + initializeLayerRendererAndDrawFrame(); + + EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(5, 5), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollStarted); + m_hostImpl->scrollBy(IntSize(0, 10)); + m_hostImpl->scrollEnd(); + EXPECT_TRUE(m_didRequestRedraw); + EXPECT_TRUE(m_didRequestCommit); +} + +TEST_F(CCLayerTreeHostImplTest, scrollMissesChild) +{ + IntSize surfaceSize(10, 10); + OwnPtr<CCLayerImpl> root = CCLayerImpl::create(0); + root->addChild(createScrollableLayer(1, FloatPoint(5, 5), surfaceSize)); + m_hostImpl->setRootLayer(root.release()); + m_hostImpl->setViewportSize(surfaceSize); + initializeLayerRendererAndDrawFrame(); + + // Scroll event is ignored because the input coordinate is outside the layer boundaries. + EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(15, 5), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollIgnored); + EXPECT_FALSE(m_didRequestRedraw); + EXPECT_FALSE(m_didRequestCommit); +} + +TEST_F(CCLayerTreeHostImplTest, scrollMissesBackfacingChild) +{ + IntSize surfaceSize(10, 10); + OwnPtr<CCLayerImpl> root = CCLayerImpl::create(0); + OwnPtr<CCLayerImpl> child = createScrollableLayer(1, FloatPoint(5, 5), surfaceSize); + m_hostImpl->setViewportSize(surfaceSize); + + WebTransformationMatrix matrix; + matrix.rotate3d(180, 0, 0); + child->setTransform(matrix); + child->setDoubleSided(false); + + root->addChild(child.release()); + m_hostImpl->setRootLayer(root.release()); + initializeLayerRendererAndDrawFrame(); + + // Scroll event is ignored because the scrollable layer is not facing the viewer and there is + // nothing scrollable behind it. + EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(5, 5), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollIgnored); + EXPECT_FALSE(m_didRequestRedraw); + EXPECT_FALSE(m_didRequestCommit); +} + +TEST_F(CCLayerTreeHostImplTest, scrollBlockedByContentLayer) +{ + IntSize surfaceSize(10, 10); + OwnPtr<CCLayerImpl> contentLayer = createScrollableLayer(0, FloatPoint(5, 5), surfaceSize); + contentLayer->setShouldScrollOnMainThread(true); + contentLayer->setScrollable(false); + + OwnPtr<CCLayerImpl> scrollLayer = createScrollableLayer(1, FloatPoint(5, 5), surfaceSize); + scrollLayer->addChild(contentLayer.release()); + + m_hostImpl->setRootLayer(scrollLayer.release()); + m_hostImpl->setViewportSize(surfaceSize); + initializeLayerRendererAndDrawFrame(); + + // Scrolling fails because the content layer is asking to be scrolled on the main thread. + EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(5, 5), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollOnMainThread); +} + +TEST_F(CCLayerTreeHostImplTest, scrollRootAndChangePageScaleOnMainThread) +{ + IntSize surfaceSize(10, 10); + float pageScale = 2; + OwnPtr<CCLayerImpl> root = createScrollableLayer(0, FloatPoint(5, 5), surfaceSize); + m_hostImpl->setRootLayer(root.release()); + m_hostImpl->setViewportSize(surfaceSize); + initializeLayerRendererAndDrawFrame(); + + IntSize scrollDelta(0, 10); + IntSize expectedScrollDelta(scrollDelta); + IntSize expectedMaxScroll(m_hostImpl->rootLayer()->maxScrollPosition()); + EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(5, 5), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollStarted); + m_hostImpl->scrollBy(scrollDelta); + m_hostImpl->scrollEnd(); + + // Set new page scale from main thread. + m_hostImpl->setPageScaleFactorAndLimits(pageScale, pageScale, pageScale); + + // The scale should apply to the scroll delta. + expectedScrollDelta.scale(pageScale); + OwnPtr<CCScrollAndScaleSet> scrollInfo = m_hostImpl->processScrollDeltas(); + expectContains(*scrollInfo.get(), 0, expectedScrollDelta); + + // The scroll range should also have been updated. + EXPECT_EQ(m_hostImpl->rootLayer()->maxScrollPosition(), expectedMaxScroll); + + // The page scale delta remains constant because the impl thread did not scale. + EXPECT_EQ(m_hostImpl->rootLayer()->pageScaleDelta(), 1); +} + +TEST_F(CCLayerTreeHostImplTest, scrollRootAndChangePageScaleOnImplThread) +{ + IntSize surfaceSize(10, 10); + float pageScale = 2; + OwnPtr<CCLayerImpl> root = createScrollableLayer(0, FloatPoint(5, 5), surfaceSize); + m_hostImpl->setRootLayer(root.release()); + m_hostImpl->setViewportSize(surfaceSize); + m_hostImpl->setPageScaleFactorAndLimits(1, 1, pageScale); + initializeLayerRendererAndDrawFrame(); + + IntSize scrollDelta(0, 10); + IntSize expectedScrollDelta(scrollDelta); + IntSize expectedMaxScroll(m_hostImpl->rootLayer()->maxScrollPosition()); + EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(5, 5), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollStarted); + m_hostImpl->scrollBy(scrollDelta); + m_hostImpl->scrollEnd(); + + // Set new page scale on impl thread by pinching. + m_hostImpl->pinchGestureBegin(); + m_hostImpl->pinchGestureUpdate(pageScale, IntPoint()); + m_hostImpl->pinchGestureEnd(); + + // The scroll delta is not scaled because the main thread did not scale. + OwnPtr<CCScrollAndScaleSet> scrollInfo = m_hostImpl->processScrollDeltas(); + expectContains(*scrollInfo.get(), 0, expectedScrollDelta); + + // The scroll range should also have been updated. + EXPECT_EQ(m_hostImpl->rootLayer()->maxScrollPosition(), expectedMaxScroll); + + // The page scale delta should match the new scale on the impl side. + EXPECT_EQ(m_hostImpl->rootLayer()->pageScaleDelta(), pageScale); +} + +TEST_F(CCLayerTreeHostImplTest, scrollChildAndChangePageScaleOnMainThread) +{ + IntSize surfaceSize(10, 10); + OwnPtr<CCLayerImpl> root = CCLayerImpl::create(0); + // Also mark the root scrollable so it becomes the root scroll layer. + root->setScrollable(true); + root->addChild(createScrollableLayer(1, FloatPoint(5, 5), surfaceSize)); + m_hostImpl->setRootLayer(root.release()); + m_hostImpl->setViewportSize(surfaceSize); + initializeLayerRendererAndDrawFrame(); + + CCLayerImpl* child = m_hostImpl->rootLayer()->children()[0].get(); + + IntSize scrollDelta(0, 10); + IntSize expectedScrollDelta(scrollDelta); + IntSize expectedMaxScroll(child->maxScrollPosition()); + EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(5, 5), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollStarted); + m_hostImpl->scrollBy(scrollDelta); + m_hostImpl->scrollEnd(); + + float pageScale = 2; + m_hostImpl->setPageScaleFactorAndLimits(pageScale, 1, pageScale); + + // The scale should apply to the scroll delta. + expectedScrollDelta.scale(pageScale); + OwnPtr<CCScrollAndScaleSet> scrollInfo = m_hostImpl->processScrollDeltas(); + expectContains(*scrollInfo.get(), 1, expectedScrollDelta); + + // The scroll range should not have changed. + EXPECT_EQ(child->maxScrollPosition(), expectedMaxScroll); + + // The page scale delta remains constant because the impl thread did not scale. + EXPECT_EQ(child->pageScaleDelta(), 1); +} + +TEST_F(CCLayerTreeHostImplTest, scrollChildBeyondLimit) +{ + // Scroll a child layer beyond its maximum scroll range and make sure the + // parent layer is scrolled on the axis on which the child was unable to + // scroll. + IntSize surfaceSize(10, 10); + OwnPtr<CCLayerImpl> root = createScrollableLayer(0, FloatPoint(5, 5), surfaceSize); + + OwnPtr<CCLayerImpl> grandChild = createScrollableLayer(2, FloatPoint(5, 5), surfaceSize); + grandChild->setScrollPosition(IntPoint(0, 5)); + + OwnPtr<CCLayerImpl> child = createScrollableLayer(1, FloatPoint(5, 5), surfaceSize); + child->setScrollPosition(IntPoint(3, 0)); + child->addChild(grandChild.release()); + + root->addChild(child.release()); + m_hostImpl->setRootLayer(root.release()); + m_hostImpl->setViewportSize(surfaceSize); + initializeLayerRendererAndDrawFrame(); + { + IntSize scrollDelta(-3, -7); + EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(5, 5), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollStarted); + m_hostImpl->scrollBy(scrollDelta); + m_hostImpl->scrollEnd(); + + OwnPtr<CCScrollAndScaleSet> scrollInfo = m_hostImpl->processScrollDeltas(); + + // The grand child should have scrolled up to its limit. + CCLayerImpl* child = m_hostImpl->rootLayer()->children()[0].get(); + CCLayerImpl* grandChild = child->children()[0].get(); + expectContains(*scrollInfo.get(), grandChild->id(), IntSize(0, -5)); + + // The child should have only scrolled on the other axis. + expectContains(*scrollInfo.get(), child->id(), IntSize(-3, 0)); + } +} + +TEST_F(CCLayerTreeHostImplTest, scrollEventBubbling) +{ + // When we try to scroll a non-scrollable child layer, the scroll delta + // should be applied to one of its ancestors if possible. + IntSize surfaceSize(10, 10); + OwnPtr<CCLayerImpl> root = createScrollableLayer(0, FloatPoint(5, 5), surfaceSize); + OwnPtr<CCLayerImpl> child = createScrollableLayer(1, FloatPoint(5, 5), surfaceSize); + + child->setScrollable(false); + root->addChild(child.release()); + + m_hostImpl->setRootLayer(root.release()); + m_hostImpl->setViewportSize(surfaceSize); + initializeLayerRendererAndDrawFrame(); + { + IntSize scrollDelta(0, 4); + EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(5, 5), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollStarted); + m_hostImpl->scrollBy(scrollDelta); + m_hostImpl->scrollEnd(); + + OwnPtr<CCScrollAndScaleSet> scrollInfo = m_hostImpl->processScrollDeltas(); + + // Only the root should have scrolled. + ASSERT_EQ(scrollInfo->scrolls.size(), 1u); + expectContains(*scrollInfo.get(), m_hostImpl->rootLayer()->id(), scrollDelta); + } +} + +TEST_F(CCLayerTreeHostImplTest, scrollBeforeRedraw) +{ + IntSize surfaceSize(10, 10); + m_hostImpl->setRootLayer(createScrollableLayer(0, FloatPoint(5, 5), surfaceSize)); + m_hostImpl->setViewportSize(surfaceSize); + + // Draw one frame and then immediately rebuild the layer tree to mimic a tree synchronization. + initializeLayerRendererAndDrawFrame(); + m_hostImpl->detachLayerTree(); + m_hostImpl->setRootLayer(createScrollableLayer(0, FloatPoint(5, 5), surfaceSize)); + + // Scrolling should still work even though we did not draw yet. + EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(5, 5), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollStarted); +} + class BlendStateTrackerContext: public FakeWebGraphicsContext3D { public: BlendStateTrackerContext() : m_blend(false) { } @@ -646,6 +1169,7 @@ private: { setAnchorPoint(FloatPoint(0, 0)); setBounds(IntSize(10, 10)); + setContentBounds(IntSize(10, 10)); setDrawsContent(true); } @@ -666,6 +1190,7 @@ TEST_F(CCLayerTreeHostImplTest, blendingOffWhenDrawingOpaqueLayers) OwnPtr<CCLayerImpl> root = CCLayerImpl::create(0); root->setAnchorPoint(FloatPoint(0, 0)); root->setBounds(IntSize(10, 10)); + root->setContentBounds(root->bounds()); root->setDrawsContent(false); m_hostImpl->setRootLayer(root.release()); } @@ -988,7 +1513,8 @@ TEST_F(CCLayerTreeHostImplTest, reshapeNotCalledUntilDraw) { ReshapeTrackerContext* reshapeTracker = new ReshapeTrackerContext(); RefPtr<GraphicsContext3D> context = GraphicsContext3DPrivate::createGraphicsContextFromWebContext(adoptPtr(reshapeTracker), GraphicsContext3D::RenderDirectlyToHostWindow); - m_hostImpl->initializeLayerRenderer(context, UnthrottledUploader); + RefPtr<CCGraphicsContext> ccContext = CCGraphicsContext::create3D(context); + m_hostImpl->initializeLayerRenderer(ccContext, UnthrottledUploader); CCLayerImpl* root = new FakeDrawableCCLayerImpl(1); root->setAnchorPoint(FloatPoint(0, 0)); @@ -1031,13 +1557,14 @@ TEST_F(CCLayerTreeHostImplTest, partialSwapReceivesDamageRect) { PartialSwapTrackerContext* partialSwapTracker = new PartialSwapTrackerContext(); RefPtr<GraphicsContext3D> context = GraphicsContext3DPrivate::createGraphicsContextFromWebContext(adoptPtr(partialSwapTracker), GraphicsContext3D::RenderDirectlyToHostWindow); + RefPtr<CCGraphicsContext> ccContext = CCGraphicsContext::create3D(context); // This test creates its own CCLayerTreeHostImpl, so // that we can force partial swap enabled. - CCSettings settings; - settings.partialSwapEnabled = true; + CCLayerTreeSettings settings; + CCSettings::setPartialSwapEnabled(true); OwnPtr<CCLayerTreeHostImpl> layerTreeHostImpl = CCLayerTreeHostImpl::create(settings, this); - layerTreeHostImpl->initializeLayerRenderer(context, UnthrottledUploader); + layerTreeHostImpl->initializeLayerRenderer(ccContext, UnthrottledUploader); layerTreeHostImpl->setViewportSize(IntSize(500, 500)); CCLayerImpl* root = new FakeDrawableCCLayerImpl(1); @@ -1099,6 +1626,349 @@ TEST_F(CCLayerTreeHostImplTest, partialSwapReceivesDamageRect) EXPECT_EQ(expectedSwapRect.height(), actualSwapRect.height()); } +} // namespace + +class FakeLayerWithQuads : public CCLayerImpl { +public: + static PassOwnPtr<FakeLayerWithQuads> create(int id) { return adoptPtr(new FakeLayerWithQuads(id)); } + + virtual void appendQuads(CCQuadCuller& quadList, const CCSharedQuadState* sharedQuadState, bool&) OVERRIDE + { + const Color gray(100, 100, 100); + IntRect quadRect(0, 0, 5, 5); + OwnPtr<CCDrawQuad> myQuad = CCSolidColorDrawQuad::create(sharedQuadState, quadRect, gray); + quadList.append(myQuad.release()); + } + +private: + FakeLayerWithQuads(int id) + : CCLayerImpl(id) + { + } +}; + +namespace { + +class MockContext : public FakeWebGraphicsContext3D { +public: + MOCK_METHOD1(useProgram, void(WebGLId program)); + MOCK_METHOD5(uniform4f, void(WGC3Dint location, WGC3Dfloat x, WGC3Dfloat y, WGC3Dfloat z, WGC3Dfloat w)); + MOCK_METHOD4(uniformMatrix4fv, void(WGC3Dint location, WGC3Dsizei count, WGC3Dboolean transpose, const WGC3Dfloat* value)); + MOCK_METHOD4(drawElements, void(WGC3Denum mode, WGC3Dsizei count, WGC3Denum type, WGC3Dintptr offset)); + MOCK_METHOD1(getString, WebString(WGC3Denum name)); + MOCK_METHOD0(getRequestableExtensionsCHROMIUM, WebString()); + MOCK_METHOD1(enable, void(WGC3Denum cap)); + MOCK_METHOD4(scissor, void(WGC3Dint x, WGC3Dint y, WGC3Dsizei width, WGC3Dsizei height)); +}; + +class MockContextHarness { +private: + MockContext* m_context; +public: + MockContextHarness(MockContext* context) + : m_context(context) + { + // Catch "uninteresting" calls + EXPECT_CALL(*m_context, useProgram(_)) + .Times(0); + + EXPECT_CALL(*m_context, drawElements(_, _, _, _)) + .Times(0); + + // These are not asserted + EXPECT_CALL(*m_context, uniformMatrix4fv(_, _, _, _)) + .WillRepeatedly(Return()); + + EXPECT_CALL(*m_context, uniform4f(_, _, _, _, _)) + .WillRepeatedly(Return()); + + // Any other strings are empty + EXPECT_CALL(*m_context, getString(_)) + .WillRepeatedly(Return(WebString())); + + // Support for partial swap, if needed + EXPECT_CALL(*m_context, getString(GraphicsContext3D::EXTENSIONS)) + .WillRepeatedly(Return(WebString("GL_CHROMIUM_post_sub_buffer"))); + + EXPECT_CALL(*m_context, getRequestableExtensionsCHROMIUM()) + .WillRepeatedly(Return(WebString("GL_CHROMIUM_post_sub_buffer"))); + + // Any un-sanctioned calls to enable() are OK + EXPECT_CALL(*m_context, enable(_)) + .WillRepeatedly(Return()); + } + + void mustDrawSolidQuad() + { + EXPECT_CALL(*m_context, drawElements(GraphicsContext3D::TRIANGLES, 6, GraphicsContext3D::UNSIGNED_SHORT, 0)) + .WillOnce(Return()) + .RetiresOnSaturation(); + + // 1 is hardcoded return value of fake createProgram() + EXPECT_CALL(*m_context, useProgram(1)) + .WillOnce(Return()) + .RetiresOnSaturation(); + + } + + void mustSetScissor(int x, int y, int width, int height) + { + EXPECT_CALL(*m_context, enable(GraphicsContext3D::SCISSOR_TEST)) + .WillRepeatedly(Return()); + + EXPECT_CALL(*m_context, scissor(x, y, width, height)) + .Times(AtLeast(1)) + .WillRepeatedly(Return()); + } + +}; + +TEST_F(CCLayerTreeHostImplTest, noPartialSwap) +{ + MockContext* mockContext = new MockContext(); + RefPtr<CCGraphicsContext> context = CCGraphicsContext::create3D(GraphicsContext3DPrivate::createGraphicsContextFromWebContext(adoptPtr(mockContext), GraphicsContext3D::RenderDirectlyToHostWindow)); + MockContextHarness harness(mockContext); + + harness.mustDrawSolidQuad(); + harness.mustSetScissor(0, 0, 10, 10); + + // Run test case + OwnPtr<CCLayerTreeHostImpl> myHostImpl = createLayerTreeHost(false, context, FakeLayerWithQuads::create(1)); + + CCLayerTreeHostImpl::FrameData frame; + EXPECT_TRUE(myHostImpl->prepareToDraw(frame)); + myHostImpl->drawLayers(frame); + myHostImpl->didDrawAllLayers(frame); + Mock::VerifyAndClearExpectations(&mockContext); +} + +TEST_F(CCLayerTreeHostImplTest, partialSwap) +{ + MockContext* mockContext = new MockContext(); + RefPtr<CCGraphicsContext> context = CCGraphicsContext::create3D(GraphicsContext3DPrivate::createGraphicsContextFromWebContext(adoptPtr(mockContext), GraphicsContext3D::RenderDirectlyToHostWindow)); + MockContextHarness harness(mockContext); + + harness.mustDrawSolidQuad(); + harness.mustSetScissor(0, 0, 10, 10); + + OwnPtr<CCLayerTreeHostImpl> myHostImpl = createLayerTreeHost(true, context, FakeLayerWithQuads::create(1)); + + CCLayerTreeHostImpl::FrameData frame; + EXPECT_TRUE(myHostImpl->prepareToDraw(frame)); + myHostImpl->drawLayers(frame); + myHostImpl->didDrawAllLayers(frame); + Mock::VerifyAndClearExpectations(&mockContext); +} + +TEST_F(CCLayerTreeHostImplTest, partialSwapNoUpdate) +{ + MockContext* mockContext = new MockContext(); + RefPtr<CCGraphicsContext> context = CCGraphicsContext::create3D(GraphicsContext3DPrivate::createGraphicsContextFromWebContext(adoptPtr(mockContext), GraphicsContext3D::RenderDirectlyToHostWindow)); + MockContextHarness harness(mockContext); + + harness.mustDrawSolidQuad(); + harness.mustSetScissor(0, 8, 2, 2); + harness.mustDrawSolidQuad(); + harness.mustSetScissor(0, 0, 10, 10); + + OwnPtr<CCLayerTreeHostImpl> myHostImpl = createLayerTreeHost(true, context, FakeLayerWithQuads::create(1)); + + // Draw once to make sure layer is not new + CCLayerTreeHostImpl::FrameData frame; + EXPECT_TRUE(myHostImpl->prepareToDraw(frame)); + myHostImpl->drawLayers(frame); + myHostImpl->didDrawAllLayers(frame); + + // Generate update in layer + CCLayerImpl* root = myHostImpl->rootLayer(); + root->setUpdateRect(FloatRect(0, 0, 2, 2)); + + // This draw should generate no new udpates + EXPECT_TRUE(myHostImpl->prepareToDraw(frame)); + myHostImpl->drawLayers(frame); + myHostImpl->didDrawAllLayers(frame); + + Mock::VerifyAndClearExpectations(&mockContext); +} + +class PartialSwapContext: public FakeWebGraphicsContext3D { +public: + WebString getString(WGC3Denum name) + { + if (name == GraphicsContext3D::EXTENSIONS) + return WebString("GL_CHROMIUM_post_sub_buffer"); + return WebString(); + } + + WebString getRequestableExtensionsCHROMIUM() + { + return WebString("GL_CHROMIUM_post_sub_buffer"); + } +}; + +static PassOwnPtr<CCLayerTreeHostImpl> setupLayersForOpacity(bool partialSwap, CCLayerTreeHostImplClient* client) +{ + CCSettings::setPartialSwapEnabled(partialSwap); + + RefPtr<CCGraphicsContext> context = CCGraphicsContext::create3D(GraphicsContext3DPrivate::createGraphicsContextFromWebContext(adoptPtr(new PartialSwapContext()), GraphicsContext3D::RenderDirectlyToHostWindow)); + + CCLayerTreeSettings settings; + OwnPtr<CCLayerTreeHostImpl> myHostImpl = CCLayerTreeHostImpl::create(settings, client); + myHostImpl->initializeLayerRenderer(context.release(), UnthrottledUploader); + myHostImpl->setViewportSize(IntSize(100, 100)); + + /* + 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 = FakeLayerWithQuads::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->setContentBounds(root->bounds()); + root->setVisibleLayerRect(rootRect); + root->setDrawsContent(false); + 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->setContentBounds(child->bounds()); + child->setVisibleLayerRect(childRect); + child->setDrawsContent(false); + + grandChild->setAnchorPoint(FloatPoint(0, 0)); + grandChild->setPosition(IntPoint(grandChildRect.x(), grandChildRect.y())); + grandChild->setBounds(IntSize(grandChildRect.width(), grandChildRect.height())); + grandChild->setContentBounds(grandChild->bounds()); + grandChild->setVisibleLayerRect(grandChildRect); + grandChild->setDrawsContent(true); + + child->addChild(grandChild.release()); + root->addChild(child.release()); + + myHostImpl->setRootLayer(root.release()); + return myHostImpl.release(); +} + +TEST_F(CCLayerTreeHostImplTest, contributingLayerEmptyScissorPartialSwap) +{ + OwnPtr<CCLayerTreeHostImpl> myHostImpl = setupLayersForOpacity(true, this); + + { + CCLayerTreeHostImpl::FrameData frame; + EXPECT_TRUE(myHostImpl->prepareToDraw(frame)); + + // Just for consistency, the most interesting stuff already happened + myHostImpl->drawLayers(frame); + myHostImpl->didDrawAllLayers(frame); + + // Verify all quads have been computed + ASSERT_EQ(2U, frame.renderPasses.size()); + ASSERT_EQ(1U, frame.renderPasses[0]->quadList().size()); + ASSERT_EQ(1U, frame.renderPasses[1]->quadList().size()); + EXPECT_EQ(CCDrawQuad::SolidColor, frame.renderPasses[0]->quadList()[0]->material()); + EXPECT_EQ(CCDrawQuad::RenderPass, frame.renderPasses[1]->quadList()[0]->material()); + } +} + +TEST_F(CCLayerTreeHostImplTest, contributingLayerEmptyScissorNoPartialSwap) +{ + OwnPtr<CCLayerTreeHostImpl> myHostImpl = setupLayersForOpacity(false, this); + + { + CCLayerTreeHostImpl::FrameData frame; + EXPECT_TRUE(myHostImpl->prepareToDraw(frame)); + + // Just for consistency, the most interesting stuff already happened + myHostImpl->drawLayers(frame); + myHostImpl->didDrawAllLayers(frame); + + // Verify all quads have been computed + ASSERT_EQ(2U, frame.renderPasses.size()); + ASSERT_EQ(1U, frame.renderPasses[0]->quadList().size()); + ASSERT_EQ(1U, frame.renderPasses[1]->quadList().size()); + EXPECT_EQ(CCDrawQuad::SolidColor, frame.renderPasses[0]->quadList()[0]->material()); + EXPECT_EQ(CCDrawQuad::RenderPass, frame.renderPasses[1]->quadList()[0]->material()); + } +} + +TEST_F(CCLayerTreeHostImplTest, didDrawNotCalledOnScissoredLayer) +{ + CCLayerTreeSettings settings; + CCSettings::setPartialSwapEnabled(true); + + RefPtr<CCGraphicsContext> context = CCGraphicsContext::create3D(GraphicsContext3DPrivate::createGraphicsContextFromWebContext(adoptPtr(new PartialSwapContext()), GraphicsContext3D::RenderDirectlyToHostWindow)); + OwnPtr<CCLayerTreeHostImpl> myHostImpl = CCLayerTreeHostImpl::create(settings, this); + myHostImpl->initializeLayerRenderer(context.release(), UnthrottledUploader); + myHostImpl->setViewportSize(IntSize(10, 10)); + + myHostImpl->setRootLayer(DidDrawCheckLayer::create(1)); + DidDrawCheckLayer* root = static_cast<DidDrawCheckLayer*>(myHostImpl->rootLayer()); + root->setMasksToBounds(true); + + root->addChild(DidDrawCheckLayer::create(2)); + DidDrawCheckLayer* layer = static_cast<DidDrawCheckLayer*>(root->children()[0].get()); + + CCLayerTreeHostImpl::FrameData frame; + + EXPECT_FALSE(root->willDrawCalled()); + EXPECT_FALSE(root->didDrawCalled()); + EXPECT_FALSE(layer->willDrawCalled()); + EXPECT_FALSE(layer->didDrawCalled()); + + // We should draw everything the first frame. + EXPECT_TRUE(myHostImpl->prepareToDraw(frame)); + myHostImpl->drawLayers(frame); + myHostImpl->didDrawAllLayers(frame); + + EXPECT_TRUE(root->willDrawCalled()); + EXPECT_TRUE(root->didDrawCalled()); + EXPECT_TRUE(layer->willDrawCalled()); + EXPECT_TRUE(layer->didDrawCalled()); + + root->clearDidDrawCheck(); + layer->clearDidDrawCheck(); + + EXPECT_FALSE(root->willDrawCalled()); + EXPECT_FALSE(root->didDrawCalled()); + EXPECT_FALSE(layer->willDrawCalled()); + EXPECT_FALSE(layer->didDrawCalled()); + + // Drawing again, we should scissor out everything since there is no damage. + EXPECT_TRUE(myHostImpl->prepareToDraw(frame)); + myHostImpl->drawLayers(frame); + myHostImpl->didDrawAllLayers(frame); + + EXPECT_FALSE(root->willDrawCalled()); + EXPECT_FALSE(root->didDrawCalled()); + EXPECT_FALSE(layer->willDrawCalled()); + EXPECT_FALSE(layer->didDrawCalled()); +} + // Make sure that context lost notifications are propagated through the tree. class ContextLostNotificationCheckLayer : public CCLayerImpl { public: @@ -1151,7 +2021,7 @@ public: TEST_F(CCLayerTreeHostImplTest, finishAllRenderingAfterContextLost) { // The context initialization will fail, but we should still be able to call finishAllRendering() without any ill effects. - m_hostImpl->initializeLayerRenderer(GraphicsContext3DPrivate::createGraphicsContextFromWebContext(adoptPtr(new FakeWebGraphicsContext3DMakeCurrentFails), GraphicsContext3D::RenderDirectlyToHostWindow), UnthrottledUploader); + m_hostImpl->initializeLayerRenderer(CCGraphicsContext::create3D(GraphicsContext3DPrivate::createGraphicsContextFromWebContext(adoptPtr(new FakeWebGraphicsContext3DMakeCurrentFails), GraphicsContext3D::RenderDirectlyToHostWindow)), UnthrottledUploader); m_hostImpl->finishAllRendering(); } @@ -1165,26 +2035,6 @@ private: ScrollbarLayerFakePaint(int id) : CCScrollbarLayerImpl(id) { } }; -TEST_F(CCLayerTreeHostImplTest, scrollbarLayerLostContext) -{ - m_hostImpl->setRootLayer(ScrollbarLayerFakePaint::create(0)); - ScrollbarLayerFakePaint* scrollbar = static_cast<ScrollbarLayerFakePaint*>(m_hostImpl->rootLayer()); - scrollbar->setBounds(IntSize(1, 1)); - scrollbar->setContentBounds(IntSize(1, 1)); - scrollbar->setDrawsContent(true); - - for (int i = 0; i < 2; ++i) { - CCLayerTreeHostImpl::FrameData frame; - EXPECT_TRUE(m_hostImpl->prepareToDraw(frame)); - ASSERT(frame.renderPasses.size() == 1); - CCRenderPass* renderPass = frame.renderPasses[0].get(); - // Scrollbar layer should always generate quads, even after lost context - EXPECT_GT(renderPass->quadList().size(), 0u); - m_hostImpl->didDrawAllLayers(frame); - m_hostImpl->initializeLayerRenderer(createContext(), UnthrottledUploader); - } -} - // Fake WebGraphicsContext3D that will cause a failure if trying to use a // resource that wasn't created by it (resources created by // FakeWebGraphicsContext3D have an id of 1). @@ -1312,6 +2162,38 @@ private: Client* m_client; }; +class StrictWebGraphicsContext3DWithIOSurface : public StrictWebGraphicsContext3D { +public: + virtual WebString getString(WGC3Denum name) OVERRIDE + { + if (name == WebCore::GraphicsContext3D::EXTENSIONS) + return WebString("GL_CHROMIUM_iosurface GL_ARB_texture_rectangle"); + + return WebString(); + } + + static PassRefPtr<GraphicsContext3D> createGraphicsContext() + { + return GraphicsContext3DPrivate::createGraphicsContextFromWebContext(adoptPtr(new StrictWebGraphicsContext3DWithIOSurface()), GraphicsContext3D::RenderDirectlyToHostWindow); + } +}; + +class FakeWebGraphicsContext3DWithIOSurface : public FakeWebGraphicsContext3D { +public: + virtual WebString getString(WGC3Denum name) OVERRIDE + { + if (name == WebCore::GraphicsContext3D::EXTENSIONS) + return WebString("GL_CHROMIUM_iosurface GL_ARB_texture_rectangle"); + + return WebString(); + } + + static PassRefPtr<GraphicsContext3D> createGraphicsContext() + { + return GraphicsContext3DPrivate::createGraphicsContextFromWebContext(adoptPtr(new FakeWebGraphicsContext3DWithIOSurface()), GraphicsContext3D::RenderDirectlyToHostWindow); + } +}; + TEST_F(CCLayerTreeHostImplTest, dontUseOldResourcesAfterLostContext) { OwnPtr<CCLayerImpl> rootLayer(CCLayerImpl::create(0)); @@ -1344,8 +2226,21 @@ TEST_F(CCLayerTreeHostImplTest, dontUseOldResourcesAfterLostContext) videoLayer->setAnchorPoint(FloatPoint(0, 0)); videoLayer->setContentBounds(IntSize(10, 10)); videoLayer->setDrawsContent(true); + videoLayer->setLayerTreeHostImpl(m_hostImpl.get()); rootLayer->addChild(videoLayer.release()); + OwnPtr<CCIOSurfaceLayerImpl> ioSurfaceLayer = CCIOSurfaceLayerImpl::create(4); + ioSurfaceLayer->setBounds(IntSize(10, 10)); + ioSurfaceLayer->setAnchorPoint(FloatPoint(0, 0)); + ioSurfaceLayer->setContentBounds(IntSize(10, 10)); + ioSurfaceLayer->setDrawsContent(true); + ioSurfaceLayer->setIOSurfaceProperties(1, IntSize(10, 10)); + ioSurfaceLayer->setLayerTreeHostImpl(m_hostImpl.get()); + rootLayer->addChild(ioSurfaceLayer.release()); + + // Use a context that supports IOSurfaces + m_hostImpl->initializeLayerRenderer(CCGraphicsContext::create3D(FakeWebGraphicsContext3DWithIOSurface::createGraphicsContext()), UnthrottledUploader); + m_hostImpl->setRootLayer(rootLayer.release()); CCLayerTreeHostImpl::FrameData frame; @@ -1354,13 +2249,757 @@ TEST_F(CCLayerTreeHostImplTest, dontUseOldResourcesAfterLostContext) m_hostImpl->didDrawAllLayers(frame); m_hostImpl->swapBuffers(); - // Lose the context, replacing it with a StrictWebGraphicsContext3D, that - // will warn if any resource from the previous context gets used. - m_hostImpl->initializeLayerRenderer(StrictWebGraphicsContext3D::createGraphicsContext(), UnthrottledUploader); + // Lose the context, replacing it with a StrictWebGraphicsContext3DWithIOSurface, + // that will warn if any resource from the previous context gets used. + m_hostImpl->initializeLayerRenderer(CCGraphicsContext::create3D(StrictWebGraphicsContext3DWithIOSurface::createGraphicsContext()), UnthrottledUploader); + EXPECT_TRUE(m_hostImpl->prepareToDraw(frame)); + m_hostImpl->drawLayers(frame); + m_hostImpl->didDrawAllLayers(frame); + m_hostImpl->swapBuffers(); +} + +// Fake WebGraphicsContext3D that tracks the number of textures in use. +class TrackingWebGraphicsContext3D : public FakeWebGraphicsContext3D { +public: + TrackingWebGraphicsContext3D() + : m_nextTextureId(1) + , m_numTextures(0) + { } + + virtual WebGLId createTexture() OVERRIDE + { + WebGLId id = m_nextTextureId; + ++m_nextTextureId; + + m_textures.set(id, true); + ++m_numTextures; + return id; + } + + virtual void deleteTexture(WebGLId id) OVERRIDE + { + if (!m_textures.get(id)) + return; + + m_textures.set(id, false); + --m_numTextures; + } + + virtual WebString getString(WGC3Denum name) OVERRIDE + { + if (name == WebCore::GraphicsContext3D::EXTENSIONS) + return WebString("GL_CHROMIUM_iosurface GL_ARB_texture_rectangle"); + + return WebString(); + } + + PassRefPtr<GraphicsContext3D> createGraphicsContext() + { + return GraphicsContext3DPrivate::createGraphicsContextFromWebContext(adoptPtr(this), GraphicsContext3D::RenderDirectlyToHostWindow); + } + + unsigned numTextures() const { return m_numTextures; } + +private: + WebGLId m_nextTextureId; + HashMap<WebGLId, bool> m_textures; + unsigned m_numTextures; +}; + +TEST_F(CCLayerTreeHostImplTest, layersFreeTextures) +{ + OwnPtr<CCLayerImpl> rootLayer(CCLayerImpl::create(1)); + rootLayer->setBounds(IntSize(10, 10)); + rootLayer->setAnchorPoint(FloatPoint(0, 0)); + + OwnPtr<CCTiledLayerImpl> tileLayer = CCTiledLayerImpl::create(2); + tileLayer->setBounds(IntSize(10, 10)); + tileLayer->setAnchorPoint(FloatPoint(0, 0)); + tileLayer->setContentBounds(IntSize(10, 10)); + tileLayer->setDrawsContent(true); + tileLayer->setSkipsDraw(false); + OwnPtr<CCLayerTilingData> tilingData(CCLayerTilingData::create(IntSize(10, 10), CCLayerTilingData::NoBorderTexels)); + tilingData->setBounds(IntSize(10, 10)); + tileLayer->setTilingData(*tilingData); + tileLayer->pushTileProperties(0, 0, 1, IntRect(0, 0, 10, 10)); + rootLayer->addChild(tileLayer.release()); + + OwnPtr<CCTextureLayerImpl> textureLayer = CCTextureLayerImpl::create(3); + textureLayer->setBounds(IntSize(10, 10)); + textureLayer->setAnchorPoint(FloatPoint(0, 0)); + textureLayer->setContentBounds(IntSize(10, 10)); + textureLayer->setDrawsContent(true); + textureLayer->setTextureId(1); + rootLayer->addChild(textureLayer.release()); + + FakeVideoFrameProvider provider; + OwnPtr<CCVideoLayerImpl> videoLayer = CCVideoLayerImpl::create(4, &provider); + videoLayer->setBounds(IntSize(10, 10)); + videoLayer->setAnchorPoint(FloatPoint(0, 0)); + videoLayer->setContentBounds(IntSize(10, 10)); + videoLayer->setDrawsContent(true); + videoLayer->setLayerTreeHostImpl(m_hostImpl.get()); + rootLayer->addChild(videoLayer.release()); + + OwnPtr<CCIOSurfaceLayerImpl> ioSurfaceLayer = CCIOSurfaceLayerImpl::create(5); + ioSurfaceLayer->setBounds(IntSize(10, 10)); + ioSurfaceLayer->setAnchorPoint(FloatPoint(0, 0)); + ioSurfaceLayer->setContentBounds(IntSize(10, 10)); + ioSurfaceLayer->setDrawsContent(true); + ioSurfaceLayer->setIOSurfaceProperties(1, IntSize(10, 10)); + ioSurfaceLayer->setLayerTreeHostImpl(m_hostImpl.get()); + rootLayer->addChild(ioSurfaceLayer.release()); + + // Lose the context, replacing it with a TrackingWebGraphicsContext3D, that + // tracks the number of textures allocated. This pointer is owned by its + // GraphicsContext3D. + TrackingWebGraphicsContext3D* trackingWebGraphicsContext = new TrackingWebGraphicsContext3D(); + m_hostImpl->initializeLayerRenderer(CCGraphicsContext::create3D(trackingWebGraphicsContext->createGraphicsContext()), UnthrottledUploader); + + m_hostImpl->setRootLayer(rootLayer.release()); + + CCLayerTreeHostImpl::FrameData frame; EXPECT_TRUE(m_hostImpl->prepareToDraw(frame)); m_hostImpl->drawLayers(frame); m_hostImpl->didDrawAllLayers(frame); m_hostImpl->swapBuffers(); + + EXPECT_GT(trackingWebGraphicsContext->numTextures(), 0u); + + // Kill the layer tree. + m_hostImpl->setRootLayer(CCLayerImpl::create(100)); + // FIXME: Remove this when we don't use ManagedTextures in impl layers. + m_hostImpl->layerRenderer()->implTextureManager()->deleteEvictedTextures(m_hostImpl->layerRenderer()->implTextureAllocator()); + // There should be no textures left in use after. + EXPECT_EQ(0u, trackingWebGraphicsContext->numTextures()); +} + +class MockDrawQuadsToFillScreenContext : public FakeWebGraphicsContext3D { +public: + MOCK_METHOD1(useProgram, void(WebGLId program)); + MOCK_METHOD4(drawElements, void(WGC3Denum mode, WGC3Dsizei count, WGC3Denum type, WGC3Dintptr offset)); +}; + +TEST_F(CCLayerTreeHostImplTest, hasTransparentBackground) +{ + MockDrawQuadsToFillScreenContext* mockContext = new MockDrawQuadsToFillScreenContext(); + RefPtr<CCGraphicsContext> context = CCGraphicsContext::create3D(GraphicsContext3DPrivate::createGraphicsContextFromWebContext(adoptPtr(mockContext), GraphicsContext3D::RenderDirectlyToHostWindow)); + + // Run test case + OwnPtr<CCLayerTreeHostImpl> myHostImpl = createLayerTreeHost(false, context, CCLayerImpl::create(1)); + myHostImpl->setBackgroundColor(Color::white); + + // Verify one quad is drawn when transparent background set is not set. + myHostImpl->setHasTransparentBackground(false); + EXPECT_CALL(*mockContext, useProgram(_)) + .Times(1); + EXPECT_CALL(*mockContext, drawElements(_, _, _, _)) + .Times(1); + CCLayerTreeHostImpl::FrameData frame; + EXPECT_TRUE(myHostImpl->prepareToDraw(frame)); + myHostImpl->drawLayers(frame); + myHostImpl->didDrawAllLayers(frame); + Mock::VerifyAndClearExpectations(&mockContext); + + // Verify no quads are drawn when transparent background is set. + myHostImpl->setHasTransparentBackground(true); + EXPECT_TRUE(myHostImpl->prepareToDraw(frame)); + myHostImpl->drawLayers(frame); + myHostImpl->didDrawAllLayers(frame); + Mock::VerifyAndClearExpectations(&mockContext); +} + +TEST_F(CCLayerTreeHostImplTest, surfaceTextureCaching) +{ + CCSettings::setPartialSwapEnabled(true); + + CCLayerTreeSettings settings; + OwnPtr<CCLayerTreeHostImpl> myHostImpl = CCLayerTreeHostImpl::create(settings, this); + + RefPtr<CCGraphicsContext> context = CCGraphicsContext::create3D(GraphicsContext3DPrivate::createGraphicsContextFromWebContext(adoptPtr(new PartialSwapContext()), GraphicsContext3D::RenderDirectlyToHostWindow)); + + myHostImpl->initializeLayerRenderer(context.release(), UnthrottledUploader); + myHostImpl->setViewportSize(IntSize(100, 100)); + + OwnPtr<CCLayerImpl> root = CCLayerImpl::create(1); + CCLayerImpl* rootPtr = root.get(); + + root->setAnchorPoint(FloatPoint(0, 0)); + root->setPosition(FloatPoint(0, 0)); + root->setBounds(IntSize(100, 100)); + root->setContentBounds(IntSize(100, 100)); + root->setVisibleLayerRect(IntRect(0, 0, 100, 100)); + root->setDrawsContent(true); + myHostImpl->setRootLayer(root.release()); + + // Intermediate layer does not own a surface, and does not draw content. + OwnPtr<CCLayerImpl> intermediateLayer = CCLayerImpl::create(2); + CCLayerImpl* intermediateLayerPtr = intermediateLayer.get(); + + intermediateLayerPtr->setAnchorPoint(FloatPoint(0, 0)); + intermediateLayerPtr->setPosition(FloatPoint(10, 10)); + intermediateLayerPtr->setBounds(IntSize(100, 100)); + intermediateLayerPtr->setContentBounds(IntSize(100, 100)); + intermediateLayerPtr->setVisibleLayerRect(IntRect(0, 0, 100, 100)); + intermediateLayerPtr->setDrawsContent(false); // only children draw content + rootPtr->addChild(intermediateLayer.release()); + + OwnPtr<CCLayerImpl> surfaceLayer = CCLayerImpl::create(3); + CCLayerImpl* surfaceLayerPtr = surfaceLayer.get(); + + // Surface layer is the layer that changes its opacity + // It will contain other layers that draw content. + surfaceLayerPtr->setAnchorPoint(FloatPoint(0, 0)); + surfaceLayerPtr->setPosition(FloatPoint(10, 10)); + surfaceLayerPtr->setBounds(IntSize(50, 50)); + surfaceLayerPtr->setContentBounds(IntSize(50, 50)); + surfaceLayerPtr->setVisibleLayerRect(IntRect(0, 0, 50, 50)); + surfaceLayerPtr->setDrawsContent(false); // only children draw content + surfaceLayerPtr->setOpacity(0.5f); // This will cause it to have a surface + intermediateLayerPtr->addChild(surfaceLayer.release()); + + // Child of the surface layer will produce some quads + OwnPtr<FakeLayerWithQuads> child = FakeLayerWithQuads::create(4); + FakeLayerWithQuads* childPtr = child.get(); + + childPtr->setAnchorPoint(FloatPoint(0, 0)); + childPtr->setPosition(FloatPoint(5, 5)); + childPtr->setBounds(IntSize(10, 10)); + childPtr->setContentBounds(IntSize(10, 10)); + childPtr->setVisibleLayerRect(IntRect(0, 0, 10, 10)); + childPtr->setDrawsContent(true); + + surfaceLayerPtr->addChild(child.release()); + + { + CCLayerTreeHostImpl::FrameData frame; + EXPECT_TRUE(myHostImpl->prepareToDraw(frame)); + + // Must receive two render passes, each with one quad + ASSERT_EQ(2U, frame.renderPasses.size()); + EXPECT_EQ(1U, frame.renderPasses[0]->quadList().size()); + EXPECT_EQ(1U, frame.renderPasses[1]->quadList().size()); + + EXPECT_EQ(CCDrawQuad::RenderPass, frame.renderPasses[1]->quadList()[0]->material()); + CCRenderPassDrawQuad* quad = static_cast<CCRenderPassDrawQuad*>(frame.renderPasses[1]->quadList()[0].get()); + EXPECT_TRUE(quad->renderPass()->targetSurface()->contentsChanged()); + + myHostImpl->drawLayers(frame); + myHostImpl->didDrawAllLayers(frame); + } + + // Draw without any change + { + CCLayerTreeHostImpl::FrameData frame; + EXPECT_TRUE(myHostImpl->prepareToDraw(frame)); + + // Must receive two EMPTY render passes + ASSERT_EQ(2U, frame.renderPasses.size()); + EXPECT_EQ(0U, frame.renderPasses[0]->quadList().size()); + EXPECT_EQ(0U, frame.renderPasses[1]->quadList().size()); + + myHostImpl->drawLayers(frame); + myHostImpl->didDrawAllLayers(frame); + } + + // Change opacity and draw + surfaceLayerPtr->setOpacity(0.6f); + { + CCLayerTreeHostImpl::FrameData frame; + EXPECT_TRUE(myHostImpl->prepareToDraw(frame)); + + // Must receive one render pass, as the other one should be culled + ASSERT_EQ(1U, frame.renderPasses.size()); + + EXPECT_EQ(1U, frame.renderPasses[0]->quadList().size()); + EXPECT_EQ(CCDrawQuad::RenderPass, frame.renderPasses[0]->quadList()[0]->material()); + CCRenderPassDrawQuad* quad = static_cast<CCRenderPassDrawQuad*>(frame.renderPasses[0]->quadList()[0].get()); + EXPECT_FALSE(quad->renderPass()->targetSurface()->contentsChanged()); + + myHostImpl->drawLayers(frame); + myHostImpl->didDrawAllLayers(frame); + } + + // Change less benign property and draw - should have contents changed flag + surfaceLayerPtr->setStackingOrderChanged(true); + { + CCLayerTreeHostImpl::FrameData frame; + EXPECT_TRUE(myHostImpl->prepareToDraw(frame)); + + // Must receive two render passes, each with one quad + ASSERT_EQ(2U, frame.renderPasses.size()); + + EXPECT_EQ(1U, frame.renderPasses[0]->quadList().size()); + EXPECT_EQ(CCDrawQuad::SolidColor, frame.renderPasses[0]->quadList()[0]->material()); + + EXPECT_EQ(CCDrawQuad::RenderPass, frame.renderPasses[1]->quadList()[0]->material()); + CCRenderPassDrawQuad* quad = static_cast<CCRenderPassDrawQuad*>(frame.renderPasses[1]->quadList()[0].get()); + EXPECT_TRUE(quad->renderPass()->targetSurface()->contentsChanged()); + + myHostImpl->drawLayers(frame); + myHostImpl->didDrawAllLayers(frame); + } + + // Change opacity again, but evict the cached surface texture + surfaceLayerPtr->setOpacity(0.5f); + ManagedTexture* contentsTexture = surfaceLayerPtr->renderSurface()->contentsTexture(); + ASSERT_TRUE(contentsTexture->isValid(contentsTexture->size(), contentsTexture->format())); + CCRenderer* renderer = myHostImpl->layerRenderer(); + TextureManager* textureManager = renderer->implTextureManager(); + size_t maxMemoryLimit = textureManager->maxMemoryLimitBytes(); + + // This should evice all cached surfaces + textureManager->setMaxMemoryLimitBytes(0); + + // Restore original limit + textureManager->setMaxMemoryLimitBytes(maxMemoryLimit); + + // Was our surface evicted? + ASSERT_FALSE(contentsTexture->isValid(contentsTexture->size(), contentsTexture->format())); + + // Change opacity and draw + surfaceLayerPtr->setOpacity(0.6f); + { + CCLayerTreeHostImpl::FrameData frame; + EXPECT_TRUE(myHostImpl->prepareToDraw(frame)); + + // Must receive two render passes + ASSERT_EQ(2U, frame.renderPasses.size()); + + // Even though not enough properties changed, the entire thing must be + // redrawn as we don't have cached textures + EXPECT_EQ(1U, frame.renderPasses[0]->quadList().size()); + EXPECT_EQ(1U, frame.renderPasses[1]->quadList().size()); + + EXPECT_EQ(CCDrawQuad::RenderPass, frame.renderPasses[1]->quadList()[0]->material()); + CCRenderPassDrawQuad* quad = static_cast<CCRenderPassDrawQuad*>(frame.renderPasses[1]->quadList()[0].get()); + EXPECT_FALSE(quad->renderPass()->targetSurface()->contentsChanged()); + + myHostImpl->drawLayers(frame); + myHostImpl->didDrawAllLayers(frame); + } + + // Draw without any change, to make sure the state is clear + { + CCLayerTreeHostImpl::FrameData frame; + EXPECT_TRUE(myHostImpl->prepareToDraw(frame)); + + // Must receive two EMPTY render passes + ASSERT_EQ(2U, frame.renderPasses.size()); + EXPECT_EQ(0U, frame.renderPasses[0]->quadList().size()); + EXPECT_EQ(0U, frame.renderPasses[1]->quadList().size()); + + myHostImpl->drawLayers(frame); + myHostImpl->didDrawAllLayers(frame); + } + + // Change opacity on the intermediate layer + WebTransformationMatrix transform = intermediateLayerPtr->transform(); + transform.setM11(1.0001); + intermediateLayerPtr->setTransform(transform); + { + CCLayerTreeHostImpl::FrameData frame; + EXPECT_TRUE(myHostImpl->prepareToDraw(frame)); + + // Must receive one render pass, as the other one should be culled. + ASSERT_EQ(1U, frame.renderPasses.size()); + EXPECT_EQ(1U, frame.renderPasses[0]->quadList().size()); + + EXPECT_EQ(CCDrawQuad::RenderPass, frame.renderPasses[0]->quadList()[0]->material()); + CCRenderPassDrawQuad* quad = static_cast<CCRenderPassDrawQuad*>(frame.renderPasses[0]->quadList()[0].get()); + EXPECT_FALSE(quad->renderPass()->targetSurface()->contentsChanged()); + + myHostImpl->drawLayers(frame); + myHostImpl->didDrawAllLayers(frame); + } +} + +struct RenderPassCacheEntry { + mutable OwnPtr<CCRenderPass> renderPassPtr; + CCRenderPass* renderPass; + + RenderPassCacheEntry(PassOwnPtr<CCRenderPass> r) + : renderPassPtr(r), + renderPass(renderPassPtr.get()) + { + } + + RenderPassCacheEntry() + { + } + + RenderPassCacheEntry(const RenderPassCacheEntry& entry) + : renderPassPtr(entry.renderPassPtr.release()), + renderPass(entry.renderPass) + { + } + + RenderPassCacheEntry& operator=(const RenderPassCacheEntry& entry) + { + renderPassPtr = entry.renderPassPtr.release(); + renderPass = entry.renderPass; + return *this; + } +}; + +struct RenderPassRemovalTestData { + CCRenderPassList renderPassList; + std::map<char, RenderPassCacheEntry> renderPassCache; + std::map<const CCRenderPass*, char> renderPassId; + Vector<OwnPtr<CCRenderSurface> > renderSurfaceStore; + Vector<OwnPtr<CCLayerImpl> > layerStore; + OwnPtr<CCSharedQuadState> sharedQuadState; +}; + +class FakeRenderSurface : public CCRenderSurface { +private: + bool m_hasCachedTexture; + bool m_contentsChanged; + +public: + FakeRenderSurface(CCLayerImpl* layerImpl) + : CCRenderSurface(layerImpl), + m_hasCachedTexture(false) + { + } + + virtual bool hasCachedContentsTexture() const OVERRIDE + { + return m_hasCachedTexture; + } + + virtual bool prepareContentsTexture(LayerRendererChromium* lrc) OVERRIDE + { + return true; + } + + virtual bool contentsChanged() const OVERRIDE + { + return m_contentsChanged; + } + + void setHasCachedTexture(bool hasCachedTexture) + { + m_hasCachedTexture = hasCachedTexture; + } + + void setContentsChanged(bool contentsChanged) + { + m_contentsChanged = contentsChanged; + } +}; + +class CCTestRenderPass: public CCRenderPass { +public: + static PassOwnPtr<CCRenderPass> create(CCRenderSurface* targetSurface) + { + return adoptPtr(new CCTestRenderPass(targetSurface)); + } + +protected: + CCTestRenderPass(CCRenderSurface* surface) + : CCRenderPass(surface) + { + } + +public: + void appendQuad(PassOwnPtr<CCDrawQuad> quad) + { + m_quadList.append(quad); + } +}; + +static PassOwnPtr<CCRenderPass> createDummyRenderPass(RenderPassRemovalTestData& testData) +{ + OwnPtr<CCLayerImpl> layerImpl = CCLayerImpl::create(1); + CCRenderSurface* renderSurface = new FakeRenderSurface(layerImpl.get()); + OwnPtr<CCRenderPass> renderPassPtr = CCTestRenderPass::create(renderSurface); + + testData.renderSurfaceStore.append(adoptPtr(renderSurface)); + testData.layerStore.append(layerImpl.release()); + return renderPassPtr.release(); +} + +static void configureRenderPassTestData(const char* testScript, RenderPassRemovalTestData& testData) +{ + // One shared state for all quads - we don't need the correct details + testData.sharedQuadState = CCSharedQuadState::create(WebTransformationMatrix(), WebTransformationMatrix(), IntRect(), IntRect(), 1.0, true); + + const char* currentChar = testScript; + + // Pre-create root pass + OwnPtr<CCRenderPass> rootRenderPass = createDummyRenderPass(testData); + testData.renderPassId.insert(std::pair<CCRenderPass*, char>(rootRenderPass.get(), testScript[0])); + testData.renderPassCache.insert(std::pair<char, RenderPassCacheEntry>(testScript[0], RenderPassCacheEntry(rootRenderPass.release()))); + while (*currentChar != '\0') { + char renderPassId = currentChar[0]; + currentChar++; + + OwnPtr<CCRenderPass> renderPass; + + bool isReplica = false; + if (!testData.renderPassCache[renderPassId].renderPassPtr.get()) + isReplica = true; + + renderPass = testData.renderPassCache[renderPassId].renderPassPtr.release(); + + // Cycle through quad data and create all quads + while (*currentChar != '\n' && *currentChar != '\0') { + if (*currentChar == 's') { + // Solid color draw quad + OwnPtr<CCDrawQuad> quad = CCSolidColorDrawQuad::create(testData.sharedQuadState.get(), IntRect(0, 0, 10, 10), Color::white); + + static_cast<CCTestRenderPass*>(renderPass.get())->appendQuad(quad.release()); + currentChar++; + } else if ((*currentChar >= 'A') && (*currentChar <= 'Z')) { + // RenderPass draw quad + char newRenderPassId = *currentChar; + currentChar++; + bool hasTexture = false; + bool contentsChanged = true; + + if (*currentChar == '[') { + currentChar++; + while ((*currentChar != ']') && (*currentChar != '\0')) { + switch (*currentChar) { + case 'c': + contentsChanged = false; + break; + case 't': + hasTexture = true; + break; + } + currentChar++; + } + if (*currentChar == ']') + currentChar++; + } + + CCRenderPass* refRenderPassPtr; + + if (testData.renderPassCache.find(newRenderPassId) == testData.renderPassCache.end()) { + OwnPtr<CCRenderPass> refRenderPass = createDummyRenderPass(testData); + refRenderPassPtr = refRenderPass.get(); + FakeRenderSurface* refRenderSurface = static_cast<FakeRenderSurface*>(refRenderPass->targetSurface()); + refRenderSurface->setHasCachedTexture(hasTexture); + refRenderSurface->setContentsChanged(contentsChanged); + testData.renderPassId.insert(std::pair<CCRenderPass*, char>(refRenderPass.get(), newRenderPassId)); + testData.renderPassCache.insert(std::pair<char, RenderPassCacheEntry>(newRenderPassId, RenderPassCacheEntry(refRenderPass.release()))); + } else + refRenderPassPtr = testData.renderPassCache[newRenderPassId].renderPass; + + OwnPtr<CCRenderPassDrawQuad> quad = CCRenderPassDrawQuad::create(testData.sharedQuadState.get(), IntRect(), refRenderPassPtr, isReplica, WebKit::WebFilterOperations(), WebKit::WebFilterOperations(), 1); + static_cast<CCTestRenderPass*>(renderPass.get())->appendQuad(quad.release()); + } + } + testData.renderPassList.insert(0, renderPass.release()); + if (*currentChar != '\0') + currentChar++; + } +} + +void dumpRenderPassTestData(const RenderPassRemovalTestData& testData, char* buffer) +{ + char* pos = buffer; + CCRenderPassList::const_reverse_iterator it = testData.renderPassList.rbegin(); + while (it != testData.renderPassList.rend()) { + CCRenderPass* currentPass = it->get(); + char passId = testData.renderPassId.find(currentPass)->second; + *pos = passId; + pos++; + + CCQuadList::const_iterator quadListIterator = currentPass->quadList().begin(); + while (quadListIterator != currentPass->quadList().end()) { + CCDrawQuad* currentQuad = (*quadListIterator).get(); + switch (currentQuad->material()) { + case CCDrawQuad::SolidColor: + *pos = 's'; + pos++; + break; + case CCDrawQuad::RenderPass: + { + CCRenderPassDrawQuad* renderPassDrawQuad = static_cast<CCRenderPassDrawQuad*>(currentQuad); + const CCRenderPass* refPass = renderPassDrawQuad->renderPass(); + char refPassId = testData.renderPassId.find(refPass)->second; + *pos = refPassId; + pos++; + } + break; + default: + *pos = 'x'; + pos++; + break; + } + + quadListIterator++; + } + *pos = '\n'; + pos++; + it++; + } + *pos = '\0'; +} + +// Each CCRenderPassList is represented by a string which describes the configuration. +// The syntax of the string is as follows: +// +// RsssssX[c]ssYsssZ[t]ssW[ct] +// Identifies the render pass---------------------------^ ^^^ ^ ^ ^ ^ ^ +// These are solid color quads-----------------------------+ | | | | | +// Identifies RenderPassDrawQuad's RenderPass-----------------+ | | | | +// This quad's contents didn't change---------------------------+ | | | +// This quad's contents changed and it has no texture---------------+ | | +// This quad has texture but its contents changed-------------------------+ | +// This quad's contents didn't change and it has texture - will be removed------+ +// +// Expected results have exactly the same syntax, except they do not use square brackets, +// since we only check the structure, not attributes. +// +// Test case configuration consists of initialization script and expected results, +// all in the same format. +struct TestCase { + const char* name; + const char* initScript; + const char* expectedResult; +}; + +TestCase removeRenderPassesCases[] = + { + { + "Single root pass", + "Rssss\n", + "Rssss\n" + }, { + "Single pass - no quads", + "R\n", + "R\n" + }, { + "Two passes, no removal", + "RssssAsss\n" + "Assss\n", + "RssssAsss\n" + "Assss\n" + }, { + "Two passes, remove last", + "RssssA[ct]sss\n" + "Assss\n", + "RssssAsss\n" + }, { + "Have texture but contents changed - leave pass", + "RssssA[t]sss\n" + "Assss\n", + "RssssAsss\n" + "Assss\n" + }, { + "Contents didn't change but no texture - leave pass", + "RssssA[c]sss\n" + "Assss\n", + "RssssAsss\n" + "Assss\n" + }, { + "Replica: two quads reference the same pass; remove", + "RssssA[ct]A[ct]sss\n" + "Assss\n", + "RssssAAsss\n" + }, { + "Replica: two quads reference the same pass; leave", + "RssssA[c]A[c]sss\n" + "Assss\n", + "RssssAAsss\n" + "Assss\n", + }, { + "Many passes, remove all", + "RssssA[ct]sss\n" + "AsssB[ct]C[ct]s\n" + "BsssD[ct]ssE[ct]F[ct]\n" + "Essssss\n" + "CG[ct]\n" + "Dsssssss\n" + "Fsssssss\n" + "Gsss\n", + + "RssssAsss\n" + }, { + "Deep recursion, remove all", + + "RsssssA[ct]ssss\n" + "AssssBsss\n" + "BC\n" + "CD\n" + "DE\n" + "EF\n" + "FG\n" + "GH\n" + "HsssIsss\n" + "IJ\n" + "Jssss\n", + + "RsssssAssss\n" + }, { + "Wide recursion, remove all", + "RA[ct]B[ct]C[ct]D[ct]E[ct]F[ct]G[ct]H[ct]I[ct]J[ct]\n" + "As\n" + "Bs\n" + "Cssss\n" + "Dssss\n" + "Es\n" + "F\n" + "Gs\n" + "Hs\n" + "Is\n" + "Jssss\n", + + "RABCDEFGHIJ\n" + }, { + "Remove passes regardless of cache state", + "RssssA[ct]sss\n" + "AsssBCs\n" + "BsssD[c]ssE[t]F\n" + "Essssss\n" + "CG\n" + "Dsssssss\n" + "Fsssssss\n" + "Gsss\n", + + "RssssAsss\n" + }, { + "Leave some passes, remove others", + + "RssssA[c]sss\n" + "AsssB[t]C[ct]s\n" + "BsssD[c]ss\n" + "CG\n" + "Dsssssss\n" + "Gsss\n", + + "RssssAsss\n" + "AsssBCs\n" + "BsssDss\n" + "Dsssssss\n" + }, { + 0, 0, 0 + } + }; + +static void verifyRenderPassTestData(TestCase& testCase, RenderPassRemovalTestData& testData) +{ + char actualResult[1024]; + dumpRenderPassTestData(testData, actualResult); + EXPECT_STREQ(testCase.expectedResult, actualResult) << "In test case: " << testCase.name; +} + +TEST(RenderPassRemovalTest, testRemoveRenderPasses) +{ + int testCaseIndex = 0; + while (removeRenderPassesCases[testCaseIndex].name) { + DebugScopedSetImplThread implThread; + RenderPassRemovalTestData testData; + CCRenderPassList skippedPasses; + configureRenderPassTestData(removeRenderPassesCases[testCaseIndex].initScript, testData); + CCLayerTreeHostImpl::removePassesWithCachedTextures(testData.renderPassList, skippedPasses); + verifyRenderPassTestData(removeRenderPassesCases[testCaseIndex], testData); + testCaseIndex++; + } } } // namespace diff --git a/Source/WebKit/chromium/tests/CCLayerTreeHostTest.cpp b/Source/WebKit/chromium/tests/CCLayerTreeHostTest.cpp index b996006ae..544eca88b 100644 --- a/Source/WebKit/chromium/tests/CCLayerTreeHostTest.cpp +++ b/Source/WebKit/chromium/tests/CCLayerTreeHostTest.cpp @@ -27,38 +27,20 @@ #include "cc/CCLayerTreeHost.h" #include "AnimationIdVendor.h" -#include "CCAnimationTestCommon.h" #include "CCOcclusionTrackerTestCommon.h" -#include "CCTiledLayerTestCommon.h" -#include "CompositorFakeWebGraphicsContext3D.h" +#include "CCThreadedTest.h" #include "ContentLayerChromium.h" -#include "FakeWebGraphicsContext3D.h" #include "GraphicsContext3DPrivate.h" -#include "LayerChromium.h" -#include "TextureManager.h" -#include "WebCompositor.h" -#include "WebKit.h" -#include "cc/CCActiveAnimation.h" -#include "cc/CCLayerAnimationController.h" -#include "cc/CCLayerAnimationDelegate.h" -#include "cc/CCLayerImpl.h" #include "cc/CCLayerTreeHostImpl.h" -#include "cc/CCScopedThreadProxy.h" -#include "cc/CCSingleThreadProxy.h" +#include "cc/CCSettings.h" #include "cc/CCTextureUpdater.h" -#include "cc/CCThreadTask.h" #include "cc/CCTimingFunction.h" #include "platform/WebThread.h" #include <gmock/gmock.h> #include <gtest/gtest.h> #include <public/Platform.h> -#include <public/WebFilterOperation.h> -#include <public/WebFilterOperations.h> -#include <wtf/Locker.h> #include <wtf/MainThread.h> -#include <wtf/PassRefPtr.h> -#include <wtf/ThreadingPrimitives.h> -#include <wtf/Vector.h> +#include <wtf/OwnArrayPtr.h> using namespace WebCore; using namespace WebKit; @@ -73,683 +55,8 @@ using namespace WTF; namespace { -class CompositorFakeWebGraphicsContext3DWithTextureTracking : public CompositorFakeWebGraphicsContext3D { -public: - static PassOwnPtr<CompositorFakeWebGraphicsContext3DWithTextureTracking> create(Attributes attrs) - { - return adoptPtr(new CompositorFakeWebGraphicsContext3DWithTextureTracking(attrs)); - } - - virtual WebGLId createTexture() - { - WebGLId texture = m_textures.size() + 1; - m_textures.append(texture); - return texture; - } - - virtual void deleteTexture(WebGLId texture) - { - for (size_t i = 0; i < m_textures.size(); i++) { - if (m_textures[i] == texture) { - m_textures.remove(i); - break; - } - } - } - - virtual void bindTexture(WGC3Denum /* target */, WebGLId texture) - { - m_usedTextures.add(texture); - } - - int numTextures() const { return static_cast<int>(m_textures.size()); } - int texture(int i) const { return m_textures[i]; } - void resetTextures() { m_textures.clear(); } - - int numUsedTextures() const { return static_cast<int>(m_usedTextures.size()); } - bool usedTexture(int texture) const { return m_usedTextures.find(texture) != m_usedTextures.end(); } - void resetUsedTextures() { m_usedTextures.clear(); } - -private: - explicit CompositorFakeWebGraphicsContext3DWithTextureTracking(Attributes attrs) : CompositorFakeWebGraphicsContext3D(attrs) - { - } - - Vector<WebGLId> m_textures; - HashSet<WebGLId, DefaultHash<WebGLId>::Hash, UnsignedWithZeroKeyHashTraits<WebGLId> > m_usedTextures; -}; - -// Used by test stubs to notify the test when something interesting happens. -class TestHooks : public CCLayerAnimationDelegate { -public: - virtual void beginCommitOnCCThread(CCLayerTreeHostImpl*) { } - virtual void commitCompleteOnCCThread(CCLayerTreeHostImpl*) { } - virtual void prepareToDrawOnCCThread(CCLayerTreeHostImpl*) { } - virtual void drawLayersOnCCThread(CCLayerTreeHostImpl*) { } - virtual void animateLayers(CCLayerTreeHostImpl*, double monotonicTime) { } - virtual void willAnimateLayers(CCLayerTreeHostImpl*, double monotonicTime) { } - virtual void applyScrollAndScale(const IntSize&, float) { } - virtual void updateAnimations(double monotonicTime) { } - virtual void layout() { } - virtual void didRecreateContext(bool succeeded) { } - virtual void didCommit() { } - virtual void didCommitAndDrawFrame() { } - virtual void scheduleComposite() { } - - // Implementation of CCLayerAnimationDelegate - virtual void notifyAnimationStarted(double time) { } - virtual void notifyAnimationFinished(double time) { } - - virtual PassRefPtr<GraphicsContext3D> createContext() - { - GraphicsContext3D::Attributes attrs; - WebGraphicsContext3D::Attributes webAttrs; - webAttrs.alpha = attrs.alpha; - - OwnPtr<WebGraphicsContext3D> webContext = CompositorFakeWebGraphicsContext3DWithTextureTracking::create(webAttrs); - return GraphicsContext3DPrivate::createGraphicsContextFromWebContext(webContext.release(), GraphicsContext3D::RenderDirectlyToHostWindow); - } -}; - -// Adapts CCLayerTreeHostImpl for test. Runs real code, then invokes test hooks. -class MockLayerTreeHostImpl : public CCLayerTreeHostImpl { -public: - static PassOwnPtr<MockLayerTreeHostImpl> create(TestHooks* testHooks, const CCSettings& settings, CCLayerTreeHostImplClient* client) - { - return adoptPtr(new MockLayerTreeHostImpl(testHooks, settings, client)); - } - - virtual void beginCommit() - { - CCLayerTreeHostImpl::beginCommit(); - m_testHooks->beginCommitOnCCThread(this); - } - - virtual void commitComplete() - { - CCLayerTreeHostImpl::commitComplete(); - m_testHooks->commitCompleteOnCCThread(this); - } - - virtual bool prepareToDraw(FrameData& frame) - { - bool result = CCLayerTreeHostImpl::prepareToDraw(frame); - m_testHooks->prepareToDrawOnCCThread(this); - return result; - } - - virtual void drawLayers(const FrameData& frame) - { - CCLayerTreeHostImpl::drawLayers(frame); - m_testHooks->drawLayersOnCCThread(this); - } - - // Make these public. - typedef Vector<CCLayerImpl*> CCLayerList; - using CCLayerTreeHostImpl::calculateRenderSurfaceLayerList; - -protected: - virtual void animateLayers(double monotonicTime, double wallClockTime) - { - m_testHooks->willAnimateLayers(this, monotonicTime); - CCLayerTreeHostImpl::animateLayers(monotonicTime, wallClockTime); - m_testHooks->animateLayers(this, monotonicTime); - } - - virtual double lowFrequencyAnimationInterval() const - { - return 1.0 / 60; - } - -private: - MockLayerTreeHostImpl(TestHooks* testHooks, const CCSettings& settings, CCLayerTreeHostImplClient* client) - : CCLayerTreeHostImpl(settings, client) - , m_testHooks(testHooks) - { - } - - TestHooks* m_testHooks; -}; - -// Adapts CCLayerTreeHost for test. Injects MockLayerTreeHostImpl. -class MockLayerTreeHost : public CCLayerTreeHost { -public: - static PassOwnPtr<MockLayerTreeHost> create(TestHooks* testHooks, CCLayerTreeHostClient* client, PassRefPtr<LayerChromium> rootLayer, const CCSettings& settings) - { - // For these tests, we will enable threaded animations. - CCSettings settingsCopy = settings; - settingsCopy.threadedAnimationEnabled = true; - - OwnPtr<MockLayerTreeHost> layerTreeHost(adoptPtr(new MockLayerTreeHost(testHooks, client, settingsCopy))); - bool success = layerTreeHost->initialize(); - EXPECT_TRUE(success); - layerTreeHost->setRootLayer(rootLayer); - - // LayerTreeHostImpl won't draw if it has 1x1 viewport. - layerTreeHost->setViewportSize(IntSize(1, 1)); - - layerTreeHost->rootLayer()->setLayerAnimationDelegate(testHooks); - - return layerTreeHost.release(); - } - - virtual PassOwnPtr<CCLayerTreeHostImpl> createLayerTreeHostImpl(CCLayerTreeHostImplClient* client) - { - // For these tests, we will enable threaded animations. - CCSettings copySettings = settings(); - copySettings.threadedAnimationEnabled = true; - return MockLayerTreeHostImpl::create(m_testHooks, copySettings, client); - } - - virtual void didAddAnimation() OVERRIDE - { - m_didAddAnimationWasCalled = true; - CCLayerTreeHost::didAddAnimation(); - } - - bool didAddAnimationWasCalled() - { - return m_didAddAnimationWasCalled; - } - -private: - MockLayerTreeHost(TestHooks* testHooks, CCLayerTreeHostClient* client, const CCSettings& settings) - : CCLayerTreeHost(client, settings) - , m_testHooks(testHooks) - , m_didAddAnimationWasCalled(false) - { - } - - TestHooks* m_testHooks; - bool m_didAddAnimationWasCalled; -}; - -// Implementation of CCLayerTreeHost callback interface. -class MockLayerTreeHostClient : public CCLayerTreeHostClient { -public: - static PassOwnPtr<MockLayerTreeHostClient> create(TestHooks* testHooks) - { - return adoptPtr(new MockLayerTreeHostClient(testHooks)); - } - - virtual void willBeginFrame() OVERRIDE - { - } - - virtual void didBeginFrame() OVERRIDE - { - } - - virtual void updateAnimations(double monotonicTime) OVERRIDE - { - m_testHooks->updateAnimations(monotonicTime); - } - - virtual void layout() OVERRIDE - { - m_testHooks->layout(); - } - - virtual void applyScrollAndScale(const IntSize& scrollDelta, float scale) OVERRIDE - { - m_testHooks->applyScrollAndScale(scrollDelta, scale); - } - - virtual PassRefPtr<GraphicsContext3D> createContext() OVERRIDE - { - return m_testHooks->createContext(); - } - - virtual void willCommit() OVERRIDE - { - } - - virtual void didCommit() OVERRIDE - { - m_testHooks->didCommit(); - } - - virtual void didCommitAndDrawFrame() OVERRIDE - { - m_testHooks->didCommitAndDrawFrame(); - } - - virtual void didCompleteSwapBuffers() OVERRIDE - { - } - - virtual void didRecreateContext(bool succeeded) OVERRIDE - { - m_testHooks->didRecreateContext(succeeded); - } - - virtual void scheduleComposite() OVERRIDE - { - m_testHooks->scheduleComposite(); - } - -private: - explicit MockLayerTreeHostClient(TestHooks* testHooks) : m_testHooks(testHooks) { } - - TestHooks* m_testHooks; -}; - -// The CCLayerTreeHostTest runs with the main loop running. It instantiates a single MockLayerTreeHost and associated -// MockLayerTreeHostImpl/MockLayerTreeHostClient. -// -// beginTest() is called once the main message loop is running and the layer tree host is initialized. -// -// Key stages of the drawing loop, e.g. drawing or commiting, redirect to CCLayerTreeHostTest methods of similar names. -// To track the commit process, override these functions. -// -// The test continues until someone calls endTest. endTest can be called on any thread, but be aware that -// ending the test is an asynchronous process. -class CCLayerTreeHostTest : public testing::Test, TestHooks { -public: - virtual void afterTest() = 0; - virtual void beginTest() = 0; - - void endTest(); - void endTestAfterDelay(int delayMilliseconds); - - void postSetNeedsAnimateToMainThread() - { - callOnMainThread(CCLayerTreeHostTest::dispatchSetNeedsAnimate, this); - } - - void postAddAnimationToMainThread() - { - callOnMainThread(CCLayerTreeHostTest::dispatchAddAnimation, this); - } - - void postAddInstantAnimationToMainThread() - { - callOnMainThread(CCLayerTreeHostTest::dispatchAddInstantAnimation, this); - } - - void postSetNeedsCommitToMainThread() - { - callOnMainThread(CCLayerTreeHostTest::dispatchSetNeedsCommit, this); - } - - void AcquireLayerTextures() - { - callOnMainThread(CCLayerTreeHostTest::dispatchAcquireLayerTextures, this); - } - - void postSetNeedsRedrawToMainThread() - { - callOnMainThread(CCLayerTreeHostTest::dispatchSetNeedsRedraw, this); - } - - void postSetNeedsAnimateAndCommitToMainThread() - { - callOnMainThread(CCLayerTreeHostTest::dispatchSetNeedsAnimateAndCommit, this); - } - - void postSetVisibleToMainThread(bool visible) - { - callOnMainThread(visible ? CCLayerTreeHostTest::dispatchSetVisible : CCLayerTreeHostTest::dispatchSetInvisible, this); - } - - void timeout() - { - m_timedOut = true; - endTest(); - } - - void clearTimeout() - { - m_timeoutTask = 0; - } - - void clearEndTestTask() - { - m_endTestTask = 0; - } - - CCLayerTreeHost* layerTreeHost() { return m_layerTreeHost.get(); } - - -protected: - CCLayerTreeHostTest() - : m_beginning(false) - , m_endWhenBeginReturns(false) - , m_timedOut(false) - , m_finished(false) - , m_scheduled(false) - , m_started(false) - , m_endTestTask(0) - { } - - void doBeginTest(); - - virtual void scheduleComposite() - { - if (!m_started || m_scheduled || m_finished) - return; - m_scheduled = true; - callOnMainThread(&CCLayerTreeHostTest::dispatchComposite, this); - } - - static void onEndTest(void* self) - { - ASSERT(isMainThread()); - WebKit::Platform::current()->currentThread()->exitRunLoop(); - } - - static void dispatchSetNeedsAnimate(void* self) - { - ASSERT(isMainThread()); - - CCLayerTreeHostTest* test = static_cast<CCLayerTreeHostTest*>(self); - ASSERT(test); - if (test->m_finished) - return; - - if (test->m_layerTreeHost) - test->m_layerTreeHost->setNeedsAnimate(); - } - - static void dispatchAddInstantAnimation(void* self) - { - ASSERT(isMainThread()); - - CCLayerTreeHostTest* test = static_cast<CCLayerTreeHostTest*>(self); - ASSERT(test); - if (test->m_finished) - return; - - if (test->m_layerTreeHost && test->m_layerTreeHost->rootLayer()) - addOpacityTransitionToLayer(*test->m_layerTreeHost->rootLayer(), 0, 0, 0.5, false); - } - - static void dispatchAddAnimation(void* self) - { - ASSERT(isMainThread()); - - CCLayerTreeHostTest* test = static_cast<CCLayerTreeHostTest*>(self); - ASSERT(test); - if (test->m_finished) - return; - - if (test->m_layerTreeHost && test->m_layerTreeHost->rootLayer()) - addOpacityTransitionToLayer(*test->m_layerTreeHost->rootLayer(), 10, 0, 0.5, true); - } - - static void dispatchSetNeedsAnimateAndCommit(void* self) - { - ASSERT(isMainThread()); - - CCLayerTreeHostTest* test = static_cast<CCLayerTreeHostTest*>(self); - ASSERT(test); - if (test->m_finished) - return; - - if (test->m_layerTreeHost) { - test->m_layerTreeHost->setNeedsAnimate(); - test->m_layerTreeHost->setNeedsCommit(); - } - } - - static void dispatchSetNeedsCommit(void* self) - { - ASSERT(isMainThread()); - - CCLayerTreeHostTest* test = static_cast<CCLayerTreeHostTest*>(self); - ASSERT_TRUE(test); - if (test->m_finished) - return; - - if (test->m_layerTreeHost) - test->m_layerTreeHost->setNeedsCommit(); - } - - static void dispatchAcquireLayerTextures(void* self) - { - ASSERT(isMainThread()); - - CCLayerTreeHostTest* test = static_cast<CCLayerTreeHostTest*>(self); - ASSERT_TRUE(test); - if (test->m_finished) - return; - - if (test->m_layerTreeHost) - test->m_layerTreeHost->acquireLayerTextures(); - } - - static void dispatchSetNeedsRedraw(void* self) - { - ASSERT(isMainThread()); - - CCLayerTreeHostTest* test = static_cast<CCLayerTreeHostTest*>(self); - ASSERT_TRUE(test); - if (test->m_finished) - return; - - if (test->m_layerTreeHost) - test->m_layerTreeHost->setNeedsRedraw(); - } - - static void dispatchSetVisible(void* self) - { - ASSERT(isMainThread()); - - CCLayerTreeHostTest* test = static_cast<CCLayerTreeHostTest*>(self); - ASSERT(test); - if (test->m_finished) - return; - - if (test->m_layerTreeHost) - test->m_layerTreeHost->setVisible(true); - } - - static void dispatchSetInvisible(void* self) - { - ASSERT(isMainThread()); - - CCLayerTreeHostTest* test = static_cast<CCLayerTreeHostTest*>(self); - ASSERT(test); - if (test->m_finished) - return; - - if (test->m_layerTreeHost) - test->m_layerTreeHost->setVisible(false); - } - - static void dispatchComposite(void* self) - { - CCLayerTreeHostTest* test = static_cast<CCLayerTreeHostTest*>(self); - ASSERT(isMainThread()); - ASSERT(test); - test->m_scheduled = false; - if (test->m_layerTreeHost && !test->m_finished) - test->m_layerTreeHost->composite(); - } - - class TimeoutTask : public WebThread::Task { - public: - explicit TimeoutTask(CCLayerTreeHostTest* test) - : m_test(test) - { - } - - void clearTest() - { - m_test = 0; - } - - virtual ~TimeoutTask() - { - if (m_test) - m_test->clearTimeout(); - } - - virtual void run() - { - if (m_test) - m_test->timeout(); - } - - private: - CCLayerTreeHostTest* m_test; - }; - - class BeginTask : public WebThread::Task { - public: - explicit BeginTask(CCLayerTreeHostTest* test) - : m_test(test) - { - } - - virtual ~BeginTask() { } - virtual void run() - { - m_test->doBeginTest(); - } - private: - CCLayerTreeHostTest* m_test; - }; - - class EndTestTask : public WebThread::Task { - public: - explicit EndTestTask(CCLayerTreeHostTest* test) - : m_test(test) - { - } - - virtual ~EndTestTask() - { - if (m_test) - m_test->clearEndTestTask(); - } - - void clearTest() - { - m_test = 0; - } - - virtual void run() - { - if (m_test) - m_test->endTest(); - } - - private: - CCLayerTreeHostTest* m_test; - }; - - virtual void runTest(bool threaded) - { - if (threaded) { - m_webThread = adoptPtr(WebKit::Platform::current()->createThread("CCLayerTreeHostTest")); - WebCompositor::initialize(m_webThread.get()); - } else - WebCompositor::initialize(0); - - ASSERT(CCProxy::isMainThread()); - m_mainThreadProxy = CCScopedThreadProxy::create(CCProxy::mainThread()); - - m_beginTask = new BeginTask(this); - WebKit::Platform::current()->currentThread()->postDelayedTask(m_beginTask, 0); // postDelayedTask takes ownership of the task - m_timeoutTask = new TimeoutTask(this); - WebKit::Platform::current()->currentThread()->postDelayedTask(m_timeoutTask, 5000); - WebKit::Platform::current()->currentThread()->enterRunLoop(); - - if (m_layerTreeHost && m_layerTreeHost->rootLayer()) - m_layerTreeHost->rootLayer()->setLayerTreeHost(0); - m_layerTreeHost.clear(); - - if (m_timeoutTask) - m_timeoutTask->clearTest(); - - if (m_endTestTask) - m_endTestTask->clearTest(); - - ASSERT_FALSE(m_layerTreeHost.get()); - m_client.clear(); - if (m_timedOut) { - FAIL() << "Test timed out"; - WebCompositor::shutdown(); - return; - } - afterTest(); - WebCompositor::shutdown(); - } - - CCSettings m_settings; - OwnPtr<MockLayerTreeHostClient> m_client; - OwnPtr<CCLayerTreeHost> m_layerTreeHost; - -private: - bool m_beginning; - bool m_endWhenBeginReturns; - bool m_timedOut; - bool m_finished; - bool m_scheduled; - bool m_started; - - OwnPtr<WebThread> m_webThread; - RefPtr<CCScopedThreadProxy> m_mainThreadProxy; - TimeoutTask* m_timeoutTask; - BeginTask* m_beginTask; - EndTestTask* m_endTestTask; -}; - -void CCLayerTreeHostTest::doBeginTest() -{ - ASSERT(isMainThread()); - m_client = MockLayerTreeHostClient::create(this); - - RefPtr<LayerChromium> rootLayer = LayerChromium::create(); - m_layerTreeHost = MockLayerTreeHost::create(this, m_client.get(), rootLayer, m_settings); - ASSERT_TRUE(m_layerTreeHost); - rootLayer->setLayerTreeHost(m_layerTreeHost.get()); - m_layerTreeHost->setSurfaceReady(); - - m_started = true; - m_beginning = true; - beginTest(); - m_beginning = false; - if (m_endWhenBeginReturns) - onEndTest(static_cast<void*>(this)); -} - -void CCLayerTreeHostTest::endTest() -{ - m_finished = true; - - // If we are called from the CCThread, re-call endTest on the main thread. - if (!isMainThread()) - m_mainThreadProxy->postTask(createCCThreadTask(this, &CCLayerTreeHostTest::endTest)); - else { - // For the case where we endTest during beginTest(), set a flag to indicate that - // the test should end the second beginTest regains control. - if (m_beginning) - m_endWhenBeginReturns = true; - else - onEndTest(static_cast<void*>(this)); - } -} - -void CCLayerTreeHostTest::endTestAfterDelay(int delayMilliseconds) -{ - // If we are called from the CCThread, re-call endTest on the main thread. - if (!isMainThread()) - m_mainThreadProxy->postTask(createCCThreadTask(this, &CCLayerTreeHostTest::endTestAfterDelay, delayMilliseconds)); - else { - m_endTestTask = new EndTestTask(this); - WebKit::Platform::current()->currentThread()->postDelayedTask(m_endTestTask, delayMilliseconds); - } -} - -class CCLayerTreeHostTestThreadOnly : public CCLayerTreeHostTest { -public: - void runTestThreaded() - { - CCLayerTreeHostTest::runTest(true); - } -}; +class CCLayerTreeHostTest : public CCThreadedTest { }; +class CCLayerTreeHostTestThreadOnly : public CCThreadedTestThreadOnly { }; // Shortlived layerTreeHosts shouldn't die. class CCLayerTreeHostTestShortlived1 : public CCLayerTreeHostTest { @@ -770,18 +77,6 @@ public: } }; -#define SINGLE_AND_MULTI_THREAD_TEST_F(TEST_FIXTURE_NAME) \ - TEST_F(TEST_FIXTURE_NAME, runSingleThread) \ - { \ - runTest(false); \ - } \ - TEST_F(TEST_FIXTURE_NAME, runMultiThread) \ - { \ - runTest(true); \ - } - -SINGLE_AND_MULTI_THREAD_TEST_F(CCLayerTreeHostTestShortlived1) - // Shortlived layerTreeHosts shouldn't die with a commit in flight. class CCLayerTreeHostTestShortlived2 : public CCLayerTreeHostTest { public: @@ -1076,7 +371,7 @@ public: virtual void beginTest() { - AcquireLayerTextures(); + postAcquireLayerTextures(); postSetNeedsRedrawToMainThread(); // should be inhibited without blocking postSetNeedsCommitToMainThread(); } @@ -1133,7 +428,7 @@ public: else { postSetVisibleToMainThread(false); postSetVisibleToMainThread(true); - AcquireLayerTextures(); + postAcquireLayerTextures(); postSetNeedsCommitToMainThread(); } } @@ -1287,6 +582,50 @@ TEST_F(CCLayerTreeHostTestAddAnimation, runMultiThread) runTestThreaded(); } +// Add a layer animation to a layer, but continually fail to draw. Confirm that after +// a while, we do eventually force a draw. +class CCLayerTreeHostTestCheckerboardDoesNotStarveDraws : public CCLayerTreeHostTestThreadOnly { +public: + CCLayerTreeHostTestCheckerboardDoesNotStarveDraws() + : m_startedAnimating(false) + { + } + + virtual void beginTest() + { + postAddAnimationToMainThread(); + } + + virtual void afterTest() + { + } + + virtual void animateLayers(CCLayerTreeHostImpl* layerTreeHostImpl, double monotonicTime) + { + m_startedAnimating = true; + } + + virtual void drawLayersOnCCThread(CCLayerTreeHostImpl*) + { + if (m_startedAnimating) + endTest(); + } + + virtual bool prepareToDrawOnCCThread(CCLayerTreeHostImpl*) + { + return false; + } + +private: + bool m_startedAnimating; +}; + +// Starvation can only be an issue with the MT compositor. +TEST_F(CCLayerTreeHostTestCheckerboardDoesNotStarveDraws, runMultiThread) +{ + runTestThreaded(); +} + // Ensures that animations continue to be ticked when we are backgrounded. class CCLayerTreeHostTestTickAnimationWhileBackgrounded : public CCLayerTreeHostTestThreadOnly { public: @@ -1446,56 +785,6 @@ private: SINGLE_AND_MULTI_THREAD_TEST_F(CCLayerTreeHostTestSynchronizeAnimationStartTimes) -class FakeWebGraphicsContext3DMakeCurrentFails : public FakeWebGraphicsContext3D { -public: - virtual bool makeContextCurrent() { return false; } -}; - -// Ensures that we do not animate -class CCLayerTreeHostTestInitializeLayerRendererFailsAfterAddAnimation : public CCLayerTreeHostTest { -public: - CCLayerTreeHostTestInitializeLayerRendererFailsAfterAddAnimation() - { - } - - virtual void beginTest() - { - // This will cause the animation timer to be set which will fire in - // CCSingleThreadProxy::animationTimerDelay() seconds. - postAddAnimationToMainThread(); - } - - virtual void animateLayers(CCLayerTreeHostImpl* layerTreeHostImpl, double monotonicTime) - { - ASSERT_NOT_REACHED(); - } - - virtual void didRecreateContext(bool succeeded) - { - EXPECT_FALSE(succeeded); - - // Make sure we wait CCSingleThreadProxy::animationTimerDelay() seconds - // (use ceil just to be sure). If the timer was not disabled, we will - // attempt to call CCSingleThreadProxy::compositeImmediately and the - // test will fail. - endTestAfterDelay(ceil(CCSingleThreadProxy::animationTimerDelay() * 1000)); - } - - virtual PassRefPtr<GraphicsContext3D> createContext() OVERRIDE - { - return GraphicsContext3DPrivate::createGraphicsContextFromWebContext(adoptPtr(new FakeWebGraphicsContext3DMakeCurrentFails), GraphicsContext3D::RenderDirectlyToHostWindow); - } - - virtual void afterTest() - { - } -}; - -TEST_F(CCLayerTreeHostTestInitializeLayerRendererFailsAfterAddAnimation, runSingleThread) -{ - runTest(false); -} - // Ensures that main thread animations have their start times synchronized with impl thread animations. class CCLayerTreeHostTestAnimationFinishedEvents : public CCLayerTreeHostTestThreadOnly { public: @@ -1886,8 +1175,7 @@ class CCLayerTreeHostTestSetVisible : public CCLayerTreeHostTest { public: CCLayerTreeHostTestSetVisible() - : m_numCommits(0) - , m_numDraws(0) + : m_numDraws(0) { } @@ -1911,7 +1199,6 @@ public: } private: - int m_numCommits; int m_numDraws; }; @@ -1927,7 +1214,7 @@ public: { } - virtual void paintContents(GraphicsContext&, const IntRect&) + virtual void paintContents(SkCanvas*, const IntRect&, IntRect&) { // Set layer opacity to 0. m_test->layerTreeHost()->rootLayer()->setOpacity(0); @@ -2021,7 +1308,7 @@ class MockContentLayerDelegate : public ContentLayerDelegate { public: bool drawsContent() const { return true; } MOCK_CONST_METHOD0(preserves3D, bool()); - void paintContents(GraphicsContext&, const IntRect&) { } + void paintContents(SkCanvas*, const IntRect&, IntRect&) { } void notifySyncRequired() { } }; @@ -2032,13 +1319,13 @@ public: : m_rootLayer(ContentLayerChromium::create(&m_delegate)) , m_childLayer(ContentLayerChromium::create(&m_delegate)) { - m_settings.deviceScaleFactor = 1.5; } virtual void beginTest() { // The device viewport should be scaled by the device scale factor. m_layerTreeHost->setViewportSize(IntSize(40, 40)); + m_layerTreeHost->setDeviceScaleFactor(1.5); EXPECT_EQ(IntSize(40, 40), m_layerTreeHost->viewportSize()); EXPECT_EQ(IntSize(60, 60), m_layerTreeHost->deviceViewportSize()); @@ -2064,7 +1351,7 @@ public: // Should only do one commit. EXPECT_EQ(0, impl->sourceFrameNumber()); // Device scale factor should come over to impl. - EXPECT_NEAR(impl->settings().deviceScaleFactor, 1.5, 0.00001); + EXPECT_NEAR(impl->deviceScaleFactor(), 1.5, 0.00001); // Both layers are on impl. ASSERT_EQ(1u, impl->rootLayer()->children().size()); @@ -2093,7 +1380,7 @@ public: EXPECT_EQ_RECT(IntRect(0, 0, 60, 60), root->renderSurface()->contentRect()); WebTransformationMatrix scaleTransform; - scaleTransform.scale(impl->settings().deviceScaleFactor); + scaleTransform.scale(impl->deviceScaleFactor()); // The root layer is scaled by 2x. WebTransformationMatrix rootScreenSpaceTransform = scaleTransform; @@ -2154,7 +1441,7 @@ public: virtual void commitCompleteOnCCThread(CCLayerTreeHostImpl* impl) { - CompositorFakeWebGraphicsContext3DWithTextureTracking* context = static_cast<CompositorFakeWebGraphicsContext3DWithTextureTracking*>(GraphicsContext3DPrivate::extractWebGraphicsContext3D(impl->context())); + CompositorFakeWebGraphicsContext3DWithTextureTracking* context = static_cast<CompositorFakeWebGraphicsContext3DWithTextureTracking*>(GraphicsContext3DPrivate::extractWebGraphicsContext3D(impl->context()->context3D())); switch (impl->sourceFrameNumber()) { case 0: @@ -2188,7 +1475,7 @@ public: virtual void drawLayersOnCCThread(CCLayerTreeHostImpl* impl) { - CompositorFakeWebGraphicsContext3DWithTextureTracking* context = static_cast<CompositorFakeWebGraphicsContext3DWithTextureTracking*>(GraphicsContext3DPrivate::extractWebGraphicsContext3D(impl->context())); + CompositorFakeWebGraphicsContext3DWithTextureTracking* context = static_cast<CompositorFakeWebGraphicsContext3DWithTextureTracking*>(GraphicsContext3DPrivate::extractWebGraphicsContext3D(impl->context()->context3D())); // Number of textures used for draw should always be one. EXPECT_EQ(1, context->numUsedTextures()); @@ -2258,7 +1545,7 @@ public: virtual void commitCompleteOnCCThread(CCLayerTreeHostImpl* impl) { - CompositorFakeWebGraphicsContext3DWithTextureTracking* context = static_cast<CompositorFakeWebGraphicsContext3DWithTextureTracking*>(GraphicsContext3DPrivate::extractWebGraphicsContext3D(impl->context())); + CompositorFakeWebGraphicsContext3DWithTextureTracking* context = static_cast<CompositorFakeWebGraphicsContext3DWithTextureTracking*>(GraphicsContext3DPrivate::extractWebGraphicsContext3D(impl->context()->context3D())); switch (impl->sourceFrameNumber()) { case 0: @@ -2327,7 +1614,7 @@ public: virtual void drawLayersOnCCThread(CCLayerTreeHostImpl* impl) { - CompositorFakeWebGraphicsContext3DWithTextureTracking* context = static_cast<CompositorFakeWebGraphicsContext3DWithTextureTracking*>(GraphicsContext3DPrivate::extractWebGraphicsContext3D(impl->context())); + CompositorFakeWebGraphicsContext3DWithTextureTracking* context = static_cast<CompositorFakeWebGraphicsContext3DWithTextureTracking*>(GraphicsContext3DPrivate::extractWebGraphicsContext3D(impl->context()->context3D())); // Number of textures used for drawing should two except for frame 4 // where the viewport only contains one layer. @@ -2940,33 +2227,115 @@ SINGLE_AND_MULTI_THREAD_TEST_F(CCLayerTreeHostTestFinishAllRendering) // correctly recognized. class CCLayerTreeHostTestLayerAddedWithAnimation : public CCLayerTreeHostTest { public: - CCLayerTreeHostTestLayerAddedWithAnimation() { } + CCLayerTreeHostTestLayerAddedWithAnimation() + : m_addedAnimation(false) + { + } virtual void beginTest() { - EXPECT_FALSE(static_cast<MockLayerTreeHost*>(layerTreeHost())->didAddAnimationWasCalled()); + EXPECT_FALSE(m_addedAnimation); RefPtr<LayerChromium> layer = LayerChromium::create(); - layer->setLayerAnimationDelegate(&m_animationDelegate); + layer->setLayerAnimationDelegate(this); // Any valid CCAnimationCurve will do here. OwnPtr<CCAnimationCurve> curve(CCEaseTimingFunction::create()); OwnPtr<CCActiveAnimation> animation(CCActiveAnimation::create(curve.release(), AnimationIdVendor::getNextAnimationId(), AnimationIdVendor::getNextGroupId(), CCActiveAnimation::Opacity)); - layer->layerAnimationController()->add(animation.release()); + layer->layerAnimationController()->addAnimation(animation.release()); // We add the animation *before* attaching the layer to the tree. m_layerTreeHost->rootLayer()->addChild(layer); - EXPECT_TRUE(static_cast<MockLayerTreeHost*>(layerTreeHost())->didAddAnimationWasCalled()); + EXPECT_TRUE(m_addedAnimation); endTest(); } + virtual void didAddAnimation() + { + m_addedAnimation = true; + } + virtual void afterTest() { } private: - ::TestHooks m_animationDelegate; + bool m_addedAnimation; }; SINGLE_AND_MULTI_THREAD_TEST_F(CCLayerTreeHostTestLayerAddedWithAnimation) +class CCLayerTreeHostTestScrollChildLayer : public CCLayerTreeHostTest, public LayerChromiumScrollDelegate { +public: + CCLayerTreeHostTestScrollChildLayer() + : m_scrollAmount(2, 1) + { + } + + virtual void beginTest() OVERRIDE + { + m_layerTreeHost->setViewportSize(IntSize(10, 10)); + m_rootScrollLayer = ContentLayerChromium::create(&m_mockDelegate); + m_rootScrollLayer->setBounds(IntSize(10, 10)); + m_rootScrollLayer->setIsDrawable(true); + m_rootScrollLayer->setScrollable(true); + m_rootScrollLayer->setMaxScrollPosition(IntSize(100, 100)); + m_layerTreeHost->rootLayer()->addChild(m_rootScrollLayer); + m_childLayer = ContentLayerChromium::create(&m_mockDelegate); + m_childLayer->setLayerScrollDelegate(this); + m_childLayer->setBounds(IntSize(50, 50)); + m_childLayer->setIsDrawable(true); + m_childLayer->setScrollable(true); + m_childLayer->setMaxScrollPosition(IntSize(100, 100)); + m_rootScrollLayer->addChild(m_childLayer); + postSetNeedsCommitToMainThread(); + } + + virtual void didScroll(const IntSize& scrollDelta) OVERRIDE + { + m_reportedScrollAmount = scrollDelta; + } + + virtual void applyScrollAndScale(const IntSize& scrollDelta, float) OVERRIDE + { + IntPoint position = m_rootScrollLayer->scrollPosition(); + m_rootScrollLayer->setScrollPosition(position + scrollDelta); + } + + virtual void beginCommitOnCCThread(CCLayerTreeHostImpl* impl) OVERRIDE + { + EXPECT_EQ(m_rootScrollLayer->scrollPosition(), IntPoint()); + if (!m_layerTreeHost->frameNumber()) + EXPECT_EQ(m_childLayer->scrollPosition(), IntPoint()); + else + EXPECT_EQ(m_childLayer->scrollPosition(), IntPoint() + m_scrollAmount); + } + + virtual void drawLayersOnCCThread(CCLayerTreeHostImpl* impl) OVERRIDE + { + if (impl->frameNumber() == 1) { + EXPECT_EQ(impl->scrollBegin(IntPoint(5, 5), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollStarted); + impl->scrollBy(m_scrollAmount); + impl->scrollEnd(); + } else if (impl->frameNumber() == 2) + endTest(); + } + + virtual void afterTest() OVERRIDE + { + EXPECT_EQ(m_scrollAmount, m_reportedScrollAmount); + } + +private: + const IntSize m_scrollAmount; + IntSize m_reportedScrollAmount; + MockContentLayerDelegate m_mockDelegate; + RefPtr<LayerChromium> m_childLayer; + RefPtr<LayerChromium> m_rootScrollLayer; +}; + +TEST_F(CCLayerTreeHostTestScrollChildLayer, runMultiThread) +{ + runTest(true); +} + } // namespace diff --git a/Source/WebKit/chromium/tests/CCOcclusionTrackerTest.cpp b/Source/WebKit/chromium/tests/CCOcclusionTrackerTest.cpp index 80317a008..8631b1942 100644 --- a/Source/WebKit/chromium/tests/CCOcclusionTrackerTest.cpp +++ b/Source/WebKit/chromium/tests/CCOcclusionTrackerTest.cpp @@ -132,7 +132,10 @@ struct CCOcclusionTrackerTestMainThreadTypes { typedef CCLayerIterator<LayerChromium, Vector<RefPtr<LayerChromium> >, RenderSurfaceChromium, CCLayerIteratorActions::FrontToBack> LayerIterator; typedef CCOcclusionTracker OcclusionTrackerType; - static PassLayerPtrType createLayer() { return LayerChromium::create(); } + static PassLayerPtrType createLayer() + { + return LayerChromium::create(); + } static PassContentLayerPtrType createContentLayer() { return adoptRef(new ContentLayerType()); } }; @@ -261,7 +264,10 @@ protected: root->setClipRect(IntRect(IntPoint::zero(), root->bounds())); m_renderSurfaceLayerListImpl.append(m_root.get()); - CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(root, root, identityMatrix, identityMatrix, m_renderSurfaceLayerListImpl, dummyLayerList, 0, dummyMaxTextureSize); + CCLayerTreeHostCommon::calculateDrawTransforms(root, root, identityMatrix, identityMatrix, m_renderSurfaceLayerListImpl, dummyLayerList, 0, dummyMaxTextureSize); + + CCLayerTreeHostCommon::calculateVisibleAndScissorRects(m_renderSurfaceLayerListImpl, root->renderSurface()->contentRect()); + m_layerIterator = m_layerIteratorBegin = Types::LayerIterator::begin(&m_renderSurfaceLayerListImpl); } @@ -277,7 +283,10 @@ protected: root->setClipRect(IntRect(IntPoint::zero(), root->bounds())); m_renderSurfaceLayerListChromium.append(m_root); - CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(root, root, identityMatrix, identityMatrix, m_renderSurfaceLayerListChromium, dummyLayerList, dummyMaxTextureSize); + CCLayerTreeHostCommon::calculateDrawTransforms(root, root, identityMatrix, identityMatrix, m_renderSurfaceLayerListChromium, dummyLayerList, dummyMaxTextureSize); + + CCLayerTreeHostCommon::calculateVisibleAndScissorRects(m_renderSurfaceLayerListChromium, root->renderSurface()->contentRect()); + m_layerIterator = m_layerIteratorBegin = Types::LayerIterator::begin(&m_renderSurfaceLayerListChromium); } diff --git a/Source/WebKit/chromium/tests/CCRenderSurfaceTest.cpp b/Source/WebKit/chromium/tests/CCRenderSurfaceTest.cpp index 5cbe7fc54..c8b0edaed 100644 --- a/Source/WebKit/chromium/tests/CCRenderSurfaceTest.cpp +++ b/Source/WebKit/chromium/tests/CCRenderSurfaceTest.cpp @@ -111,6 +111,7 @@ TEST(CCRenderSurfaceTest, sanityCheckSurfaceCreatesCorrectSharedQuadState) renderSurface->setOriginTransform(origin); renderSurface->setContentRect(contentRect); renderSurface->setClipRect(clipRect); + renderSurface->setScissorRect(clipRect); renderSurface->setDrawOpacity(1); OwnPtr<CCSharedQuadState> sharedQuadState = renderSurface->createSharedQuadState(); @@ -119,7 +120,7 @@ TEST(CCRenderSurfaceTest, sanityCheckSurfaceCreatesCorrectSharedQuadState) EXPECT_EQ(30, sharedQuadState->layerTransform().m41()); EXPECT_EQ(40, sharedQuadState->layerTransform().m42()); EXPECT_EQ(contentRect, sharedQuadState->layerRect()); - EXPECT_EQ(clipRect, sharedQuadState->clipRect()); + EXPECT_EQ(clipRect, sharedQuadState->scissorRect()); EXPECT_EQ(1, sharedQuadState->opacity()); EXPECT_FALSE(sharedQuadState->isOpaque()); } diff --git a/Source/WebKit/chromium/tests/CCSchedulerStateMachineTest.cpp b/Source/WebKit/chromium/tests/CCSchedulerStateMachineTest.cpp index 2731c76fd..a99149ba9 100644 --- a/Source/WebKit/chromium/tests/CCSchedulerStateMachineTest.cpp +++ b/Source/WebKit/chromium/tests/CCSchedulerStateMachineTest.cpp @@ -285,6 +285,48 @@ TEST(CCSchedulerStateMachineTest, TestCommitAfterFailedAndSuccessfulDrawDoesNotA EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction()); } +TEST(CCSchedulerStateMachineTest, TestFailedDrawsWillEventuallyForceADrawAfterTheNextCommit) +{ + CCSchedulerStateMachine state; + state.setCanBeginFrame(true); + state.setVisible(true); + state.setMaximumNumberOfFailedDrawsBeforeDrawIsForced(1); + + // Start a commit. + state.setNeedsCommit(); + EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction()); + state.updateState(CCSchedulerStateMachine::ACTION_BEGIN_FRAME); + EXPECT_TRUE(state.commitPending()); + + // Then initiate a draw. + state.setNeedsRedraw(); + EXPECT_TRUE(state.vsyncCallbackNeeded()); + state.didEnterVSync(); + EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction()); + EXPECT_TRUE(state.redrawPending()); + + // Fail the draw. + state.updateState(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE); + EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction()); + state.didDrawIfPossibleCompleted(false); + EXPECT_TRUE(state.redrawPending()); + // But the commit is ongoing. + EXPECT_TRUE(state.commitPending()); + + // Finish the commit. Note, we should not yet be forcing a draw, but should + // continue the commit as usual. + state.beginFrameComplete(); + EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_UPDATE_MORE_RESOURCES, state.nextAction()); + state.updateState(CCSchedulerStateMachine::ACTION_BEGIN_UPDATE_MORE_RESOURCES); + state.beginUpdateMoreResourcesComplete(false); + EXPECT_EQ(CCSchedulerStateMachine::ACTION_COMMIT, state.nextAction()); + state.updateState(CCSchedulerStateMachine::ACTION_COMMIT); + EXPECT_TRUE(state.redrawPending()); + + // The redraw should be forced in this case. + EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_FORCED, state.nextAction()); +} + TEST(CCSchedulerStateMachineTest, TestFailedDrawIsRetriedNextVSync) { CCSchedulerStateMachine state; diff --git a/Source/WebKit/chromium/tests/CCSingleThreadProxyTest.cpp b/Source/WebKit/chromium/tests/CCSingleThreadProxyTest.cpp new file mode 100644 index 000000000..4fe40f241 --- /dev/null +++ b/Source/WebKit/chromium/tests/CCSingleThreadProxyTest.cpp @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include "cc/CCSingleThreadProxy.h" + +#include "CCThreadedTest.h" +#include "CompositorFakeWebGraphicsContext3D.h" +#include "FakeWebGraphicsContext3D.h" +#include "GraphicsContext3DPrivate.h" +#include "platform/WebThread.h" + +using namespace WebCore; +using namespace WebKit; +using namespace WebKitTests; + +class FakeWebGraphicsContext3DMakeCurrentFails : public FakeWebGraphicsContext3D { +public: + virtual bool makeContextCurrent() { return false; } +}; + +class CCSingleThreadProxyTestInitializeLayerRendererFailsAfterAddAnimation : public CCThreadedTest { +public: + CCSingleThreadProxyTestInitializeLayerRendererFailsAfterAddAnimation() + { + } + + virtual void beginTest() + { + // This will cause the animation timer to be set which will fire in + // CCSingleThreadProxy::animationTimerDelay() seconds. + postAddAnimationToMainThread(); + } + + virtual void animateLayers(CCLayerTreeHostImpl* layerTreeHostImpl, double monotonicTime) + { + ASSERT_NOT_REACHED(); + } + + virtual void didRecreateContext(bool succeeded) + { + EXPECT_FALSE(succeeded); + + // Make sure we wait CCSingleThreadProxy::animationTimerDelay() seconds + // (use ceil just to be sure). If the timer was not disabled, we will + // attempt to call CCSingleThreadProxy::compositeImmediately and the + // test will fail. + endTestAfterDelay(ceil(CCSingleThreadProxy::animationTimerDelay() * 1000)); + } + + virtual PassRefPtr<GraphicsContext3D> createContext() OVERRIDE + { + return GraphicsContext3DPrivate::createGraphicsContextFromWebContext(adoptPtr(new FakeWebGraphicsContext3DMakeCurrentFails), GraphicsContext3D::RenderDirectlyToHostWindow); + } + + virtual void afterTest() + { + } +}; + +TEST_F(CCSingleThreadProxyTestInitializeLayerRendererFailsAfterAddAnimation, runSingleThread) +{ + runTest(false); +} + +class CCSingleThreadProxyTestDidAddAnimationBeforeInitializingLayerRenderer : public CCThreadedTest { +public: + CCSingleThreadProxyTestDidAddAnimationBeforeInitializingLayerRenderer() + { + } + + virtual void beginTest() + { + // This will cause the animation timer to be set which will fire in + // CCSingleThreadProxy::animationTimerDelay() seconds. + postDidAddAnimationToMainThread(); + } + + virtual void animateLayers(CCLayerTreeHostImpl*, double) + { + ASSERT_NOT_REACHED(); + } + + virtual void didRecreateContext(bool) + { + ASSERT_NOT_REACHED(); + } + + virtual void didAddAnimation() + { + // Make sure we wait CCSingleThreadProxy::animationTimerDelay() seconds + // (use ceil just to be sure). If the timer was not disabled, we will + // attempt to call CCSingleThreadProxy::compositeImmediately and the + // test will fail. + endTestAfterDelay(ceil(CCSingleThreadProxy::animationTimerDelay() * 1000)); + } + + virtual PassRefPtr<GraphicsContext3D> createContext() OVERRIDE + { + return GraphicsContext3DPrivate::createGraphicsContextFromWebContext(adoptPtr(new FakeWebGraphicsContext3DMakeCurrentFails), GraphicsContext3D::RenderDirectlyToHostWindow); + } + + virtual void afterTest() + { + } +}; + +TEST_F(CCSingleThreadProxyTestDidAddAnimationBeforeInitializingLayerRenderer, runSingleThread) +{ + runTest(false); +} diff --git a/Source/WebKit/chromium/tests/CCTestCommon.h b/Source/WebKit/chromium/tests/CCTestCommon.h new file mode 100644 index 000000000..89fce9818 --- /dev/null +++ b/Source/WebKit/chromium/tests/CCTestCommon.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2012 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CCTestCommon_h +#define CCTestCommon_h + +#include "cc/CCSettings.h" + +namespace WebKitTests { + +// If you have a test that modifies or uses global settings, keep an instance +// of this class to ensure that you start and end with a clean slate. +class CCScopedSettings { +public: + CCScopedSettings() { WebCore::CCSettings::reset(); } + ~CCScopedSettings() { WebCore::CCSettings::reset(); } +}; + +} // namespace WebKitTests + +#endif // CCTestCommon_h diff --git a/Source/WebKit/chromium/tests/CCThreadedTest.cpp b/Source/WebKit/chromium/tests/CCThreadedTest.cpp new file mode 100644 index 000000000..57b6df14e --- /dev/null +++ b/Source/WebKit/chromium/tests/CCThreadedTest.cpp @@ -0,0 +1,651 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include "CCThreadedTest.h" + +#include "AnimationIdVendor.h" +#include "CCAnimationTestCommon.h" +#include "CCOcclusionTrackerTestCommon.h" +#include "CCTiledLayerTestCommon.h" +#include "ContentLayerChromium.h" +#include "FakeWebGraphicsContext3D.h" +#include "GraphicsContext3DPrivate.h" +#include "LayerChromium.h" +#include "TextureManager.h" +#include "WebCompositor.h" +#include "WebKit.h" +#include "cc/CCActiveAnimation.h" +#include "cc/CCLayerAnimationController.h" +#include "cc/CCLayerAnimationDelegate.h" +#include "cc/CCLayerImpl.h" +#include "cc/CCLayerTreeHostImpl.h" +#include "cc/CCScopedThreadProxy.h" +#include "cc/CCSingleThreadProxy.h" +#include "cc/CCTextureUpdater.h" +#include "cc/CCThreadTask.h" +#include "cc/CCTimingFunction.h" +#include "platform/WebThread.h" +#include <gmock/gmock.h> +#include <public/Platform.h> +#include <public/WebFilterOperation.h> +#include <public/WebFilterOperations.h> +#include <wtf/Locker.h> +#include <wtf/MainThread.h> +#include <wtf/PassRefPtr.h> +#include <wtf/ThreadingPrimitives.h> +#include <wtf/Vector.h> + +using namespace WebCore; +using namespace WebKit; +using namespace WTF; + +namespace WebKitTests { + +PassOwnPtr<CompositorFakeWebGraphicsContext3DWithTextureTracking> CompositorFakeWebGraphicsContext3DWithTextureTracking::create(Attributes attrs) +{ + return adoptPtr(new CompositorFakeWebGraphicsContext3DWithTextureTracking(attrs)); +} + +WebGLId CompositorFakeWebGraphicsContext3DWithTextureTracking::createTexture() +{ + WebGLId texture = m_textures.size() + 1; + m_textures.append(texture); + return texture; +} + +void CompositorFakeWebGraphicsContext3DWithTextureTracking::deleteTexture(WebGLId texture) +{ + for (size_t i = 0; i < m_textures.size(); i++) { + if (m_textures[i] == texture) { + m_textures.remove(i); + break; + } + } +} + +void CompositorFakeWebGraphicsContext3DWithTextureTracking::bindTexture(WGC3Denum /* target */, WebGLId texture) +{ + m_usedTextures.add(texture); +} + +int CompositorFakeWebGraphicsContext3DWithTextureTracking::numTextures() const { return static_cast<int>(m_textures.size()); } +int CompositorFakeWebGraphicsContext3DWithTextureTracking::texture(int i) const { return m_textures[i]; } +void CompositorFakeWebGraphicsContext3DWithTextureTracking::resetTextures() { m_textures.clear(); } + +int CompositorFakeWebGraphicsContext3DWithTextureTracking::numUsedTextures() const { return static_cast<int>(m_usedTextures.size()); } +bool CompositorFakeWebGraphicsContext3DWithTextureTracking::usedTexture(int texture) const { return m_usedTextures.find(texture) != m_usedTextures.end(); } +void CompositorFakeWebGraphicsContext3DWithTextureTracking::resetUsedTextures() { m_usedTextures.clear(); } + +CompositorFakeWebGraphicsContext3DWithTextureTracking::CompositorFakeWebGraphicsContext3DWithTextureTracking(Attributes attrs) : CompositorFakeWebGraphicsContext3D(attrs) +{ +} + +PassRefPtr<GraphicsContext3D> TestHooks::createContext() +{ + GraphicsContext3D::Attributes attrs; + WebGraphicsContext3D::Attributes webAttrs; + webAttrs.alpha = attrs.alpha; + + OwnPtr<WebGraphicsContext3D> webContext = CompositorFakeWebGraphicsContext3DWithTextureTracking::create(webAttrs); + return GraphicsContext3DPrivate::createGraphicsContextFromWebContext(webContext.release(), GraphicsContext3D::RenderDirectlyToHostWindow); +} + +PassOwnPtr<MockLayerTreeHostImpl> MockLayerTreeHostImpl::create(TestHooks* testHooks, const CCLayerTreeSettings& settings, CCLayerTreeHostImplClient* client) +{ + return adoptPtr(new MockLayerTreeHostImpl(testHooks, settings, client)); +} + +void MockLayerTreeHostImpl::beginCommit() +{ + CCLayerTreeHostImpl::beginCommit(); + m_testHooks->beginCommitOnCCThread(this); +} + +void MockLayerTreeHostImpl::commitComplete() +{ + CCLayerTreeHostImpl::commitComplete(); + m_testHooks->commitCompleteOnCCThread(this); +} + +bool MockLayerTreeHostImpl::prepareToDraw(FrameData& frame) +{ + bool result = CCLayerTreeHostImpl::prepareToDraw(frame); + if (!m_testHooks->prepareToDrawOnCCThread(this)) + result = false; + return result; +} + +void MockLayerTreeHostImpl::drawLayers(const FrameData& frame) +{ + CCLayerTreeHostImpl::drawLayers(frame); + m_testHooks->drawLayersOnCCThread(this); +} + +void MockLayerTreeHostImpl::animateLayers(double monotonicTime, double wallClockTime) +{ + m_testHooks->willAnimateLayers(this, monotonicTime); + CCLayerTreeHostImpl::animateLayers(monotonicTime, wallClockTime); + m_testHooks->animateLayers(this, monotonicTime); +} + +double MockLayerTreeHostImpl::lowFrequencyAnimationInterval() const +{ + return 1.0 / 60; +} + +MockLayerTreeHostImpl::MockLayerTreeHostImpl(TestHooks* testHooks, const CCLayerTreeSettings& settings, CCLayerTreeHostImplClient* client) + : CCLayerTreeHostImpl(settings, client) + , m_testHooks(testHooks) +{ +} + +// Adapts CCLayerTreeHost for test. Injects MockLayerTreeHostImpl. +class MockLayerTreeHost : public WebCore::CCLayerTreeHost { +public: + static PassOwnPtr<MockLayerTreeHost> create(TestHooks* testHooks, WebCore::CCLayerTreeHostClient* client, PassRefPtr<WebCore::LayerChromium> rootLayer, const WebCore::CCLayerTreeSettings& settings) + { + OwnPtr<MockLayerTreeHost> layerTreeHost(adoptPtr(new MockLayerTreeHost(testHooks, client, settings))); + bool success = layerTreeHost->initialize(); + EXPECT_TRUE(success); + layerTreeHost->setRootLayer(rootLayer); + + // LayerTreeHostImpl won't draw if it has 1x1 viewport. + layerTreeHost->setViewportSize(IntSize(1, 1)); + + layerTreeHost->rootLayer()->setLayerAnimationDelegate(testHooks); + + return layerTreeHost.release(); + } + + virtual PassOwnPtr<WebCore::CCLayerTreeHostImpl> createLayerTreeHostImpl(WebCore::CCLayerTreeHostImplClient* client) + { + return MockLayerTreeHostImpl::create(m_testHooks, settings(), client); + } + + virtual void didAddAnimation() OVERRIDE + { + CCLayerTreeHost::didAddAnimation(); + m_testHooks->didAddAnimation(); + } + +private: + MockLayerTreeHost(TestHooks* testHooks, WebCore::CCLayerTreeHostClient* client, const WebCore::CCLayerTreeSettings& settings) + : CCLayerTreeHost(client, settings) + , m_testHooks(testHooks) + { + } + + TestHooks* m_testHooks; +}; + +// Implementation of CCLayerTreeHost callback interface. +class MockLayerTreeHostClient : public MockCCLayerTreeHostClient { +public: + static PassOwnPtr<MockLayerTreeHostClient> create(TestHooks* testHooks) + { + return adoptPtr(new MockLayerTreeHostClient(testHooks)); + } + + virtual void willBeginFrame() OVERRIDE + { + } + + virtual void didBeginFrame() OVERRIDE + { + } + + virtual void updateAnimations(double monotonicTime) OVERRIDE + { + m_testHooks->updateAnimations(monotonicTime); + } + + virtual void layout() OVERRIDE + { + m_testHooks->layout(); + } + + virtual void applyScrollAndScale(const IntSize& scrollDelta, float scale) OVERRIDE + { + m_testHooks->applyScrollAndScale(scrollDelta, scale); + } + + virtual PassRefPtr<GraphicsContext3D> createContext3D() OVERRIDE + { + return m_testHooks->createContext(); + } + + virtual void willCommit() OVERRIDE + { + } + + virtual void didCommit() OVERRIDE + { + m_testHooks->didCommit(); + } + + virtual void didCommitAndDrawFrame() OVERRIDE + { + m_testHooks->didCommitAndDrawFrame(); + } + + virtual void didCompleteSwapBuffers() OVERRIDE + { + } + + virtual void didRecreateContext(bool succeeded) OVERRIDE + { + m_testHooks->didRecreateContext(succeeded); + } + + virtual void scheduleComposite() OVERRIDE + { + m_testHooks->scheduleComposite(); + } + +private: + explicit MockLayerTreeHostClient(TestHooks* testHooks) : m_testHooks(testHooks) { } + + TestHooks* m_testHooks; +}; + +class TimeoutTask : public WebThread::Task { +public: + explicit TimeoutTask(CCThreadedTest* test) + : m_test(test) + { + } + + void clearTest() + { + m_test = 0; + } + + virtual ~TimeoutTask() + { + if (m_test) + m_test->clearTimeout(); + } + + virtual void run() + { + if (m_test) + m_test->timeout(); + } + +private: + CCThreadedTest* m_test; +}; + +class BeginTask : public WebThread::Task { +public: + explicit BeginTask(CCThreadedTest* test) + : m_test(test) + { + } + + virtual ~BeginTask() { } + virtual void run() + { + m_test->doBeginTest(); + } +private: + CCThreadedTest* m_test; +}; + +class EndTestTask : public WebThread::Task { +public: + explicit EndTestTask(CCThreadedTest* test) + : m_test(test) + { + } + + virtual ~EndTestTask() + { + if (m_test) + m_test->clearEndTestTask(); + } + + void clearTest() + { + m_test = 0; + } + + virtual void run() + { + if (m_test) + m_test->endTest(); + } + +private: + CCThreadedTest* m_test; +}; + +CCThreadedTest::CCThreadedTest() + : m_beginning(false) + , m_endWhenBeginReturns(false) + , m_timedOut(false) + , m_finished(false) + , m_scheduled(false) + , m_started(false) + , m_endTestTask(0) +{ } + +void CCThreadedTest::endTest() +{ + m_finished = true; + + // If we are called from the CCThread, re-call endTest on the main thread. + if (!isMainThread()) + m_mainThreadProxy->postTask(createCCThreadTask(this, &CCThreadedTest::endTest)); + else { + // For the case where we endTest during beginTest(), set a flag to indicate that + // the test should end the second beginTest regains control. + if (m_beginning) + m_endWhenBeginReturns = true; + else + onEndTest(static_cast<void*>(this)); + } +} + +void CCThreadedTest::endTestAfterDelay(int delayMilliseconds) +{ + // If we are called from the CCThread, re-call endTest on the main thread. + if (!isMainThread()) + m_mainThreadProxy->postTask(createCCThreadTask(this, &CCThreadedTest::endTestAfterDelay, delayMilliseconds)); + else { + m_endTestTask = new EndTestTask(this); + WebKit::Platform::current()->currentThread()->postDelayedTask(m_endTestTask, delayMilliseconds); + } +} + +void CCThreadedTest::postSetNeedsAnimateToMainThread() +{ + callOnMainThread(CCThreadedTest::dispatchSetNeedsAnimate, this); +} + +void CCThreadedTest::postAddAnimationToMainThread() +{ + callOnMainThread(CCThreadedTest::dispatchAddAnimation, this); +} + +void CCThreadedTest::postAddInstantAnimationToMainThread() +{ + callOnMainThread(CCThreadedTest::dispatchAddInstantAnimation, this); +} + +void CCThreadedTest::postSetNeedsCommitToMainThread() +{ + callOnMainThread(CCThreadedTest::dispatchSetNeedsCommit, this); +} + +void CCThreadedTest::postAcquireLayerTextures() +{ + callOnMainThread(CCThreadedTest::dispatchAcquireLayerTextures, this); +} + +void CCThreadedTest::postSetNeedsRedrawToMainThread() +{ + callOnMainThread(CCThreadedTest::dispatchSetNeedsRedraw, this); +} + +void CCThreadedTest::postSetNeedsAnimateAndCommitToMainThread() +{ + callOnMainThread(CCThreadedTest::dispatchSetNeedsAnimateAndCommit, this); +} + +void CCThreadedTest::postSetVisibleToMainThread(bool visible) +{ + callOnMainThread(visible ? CCThreadedTest::dispatchSetVisible : CCThreadedTest::dispatchSetInvisible, this); +} + +void CCThreadedTest::postDidAddAnimationToMainThread() +{ + callOnMainThread(CCThreadedTest::dispatchDidAddAnimation, this); +} + +void CCThreadedTest::doBeginTest() +{ + ASSERT(isMainThread()); + m_client = MockLayerTreeHostClient::create(this); + + RefPtr<LayerChromium> rootLayer = LayerChromium::create(); + m_layerTreeHost = MockLayerTreeHost::create(this, m_client.get(), rootLayer, m_settings); + ASSERT_TRUE(m_layerTreeHost); + rootLayer->setLayerTreeHost(m_layerTreeHost.get()); + m_layerTreeHost->setSurfaceReady(); + + m_started = true; + m_beginning = true; + beginTest(); + m_beginning = false; + if (m_endWhenBeginReturns) + onEndTest(static_cast<void*>(this)); +} + +void CCThreadedTest::timeout() +{ + m_timedOut = true; + endTest(); +} + +void CCThreadedTest::scheduleComposite() +{ + if (!m_started || m_scheduled || m_finished) + return; + m_scheduled = true; + callOnMainThread(&CCThreadedTest::dispatchComposite, this); +} + +void CCThreadedTest::onEndTest(void* self) +{ + ASSERT(isMainThread()); + WebKit::Platform::current()->currentThread()->exitRunLoop(); +} + +void CCThreadedTest::dispatchSetNeedsAnimate(void* self) +{ + ASSERT(isMainThread()); + + CCThreadedTest* test = static_cast<CCThreadedTest*>(self); + ASSERT(test); + if (test->m_finished) + return; + + if (test->m_layerTreeHost) + test->m_layerTreeHost->setNeedsAnimate(); +} + +void CCThreadedTest::dispatchAddInstantAnimation(void* self) +{ + ASSERT(isMainThread()); + + CCThreadedTest* test = static_cast<CCThreadedTest*>(self); + ASSERT(test); + if (test->m_finished) + return; + + if (test->m_layerTreeHost && test->m_layerTreeHost->rootLayer()) + addOpacityTransitionToLayer(*test->m_layerTreeHost->rootLayer(), 0, 0, 0.5, false); +} + +void CCThreadedTest::dispatchAddAnimation(void* self) +{ + ASSERT(isMainThread()); + + CCThreadedTest* test = static_cast<CCThreadedTest*>(self); + ASSERT(test); + if (test->m_finished) + return; + + if (test->m_layerTreeHost && test->m_layerTreeHost->rootLayer()) + addOpacityTransitionToLayer(*test->m_layerTreeHost->rootLayer(), 10, 0, 0.5, true); +} + +void CCThreadedTest::dispatchSetNeedsAnimateAndCommit(void* self) +{ + ASSERT(isMainThread()); + + CCThreadedTest* test = static_cast<CCThreadedTest*>(self); + ASSERT(test); + if (test->m_finished) + return; + + if (test->m_layerTreeHost) { + test->m_layerTreeHost->setNeedsAnimate(); + test->m_layerTreeHost->setNeedsCommit(); + } +} + +void CCThreadedTest::dispatchSetNeedsCommit(void* self) +{ + ASSERT(isMainThread()); + + CCThreadedTest* test = static_cast<CCThreadedTest*>(self); + ASSERT_TRUE(test); + if (test->m_finished) + return; + + if (test->m_layerTreeHost) + test->m_layerTreeHost->setNeedsCommit(); +} + +void CCThreadedTest::dispatchAcquireLayerTextures(void* self) +{ + ASSERT(isMainThread()); + + CCThreadedTest* test = static_cast<CCThreadedTest*>(self); + ASSERT_TRUE(test); + if (test->m_finished) + return; + + if (test->m_layerTreeHost) + test->m_layerTreeHost->acquireLayerTextures(); +} + +void CCThreadedTest::dispatchSetNeedsRedraw(void* self) +{ + ASSERT(isMainThread()); + + CCThreadedTest* test = static_cast<CCThreadedTest*>(self); + ASSERT_TRUE(test); + if (test->m_finished) + return; + + if (test->m_layerTreeHost) + test->m_layerTreeHost->setNeedsRedraw(); +} + +void CCThreadedTest::dispatchSetVisible(void* self) +{ + ASSERT(isMainThread()); + + CCThreadedTest* test = static_cast<CCThreadedTest*>(self); + ASSERT(test); + if (test->m_finished) + return; + + if (test->m_layerTreeHost) + test->m_layerTreeHost->setVisible(true); +} + +void CCThreadedTest::dispatchSetInvisible(void* self) +{ + ASSERT(isMainThread()); + + CCThreadedTest* test = static_cast<CCThreadedTest*>(self); + ASSERT(test); + if (test->m_finished) + return; + + if (test->m_layerTreeHost) + test->m_layerTreeHost->setVisible(false); +} + +void CCThreadedTest::dispatchComposite(void* self) +{ + CCThreadedTest* test = static_cast<CCThreadedTest*>(self); + ASSERT(isMainThread()); + ASSERT(test); + test->m_scheduled = false; + if (test->m_layerTreeHost && !test->m_finished) + test->m_layerTreeHost->composite(); +} + +void CCThreadedTest::dispatchDidAddAnimation(void* self) +{ + ASSERT(isMainThread()); + + CCThreadedTest* test = static_cast<CCThreadedTest*>(self); + ASSERT(test); + if (test->m_finished) + return; + + if (test->m_layerTreeHost) + test->m_layerTreeHost->didAddAnimation(); +} + +void CCThreadedTest::runTest(bool threaded) +{ + // For these tests, we will enable threaded animations. + WebCompositor::setAcceleratedAnimationEnabled(true); + + if (threaded) { + m_webThread = adoptPtr(WebKit::Platform::current()->createThread("CCThreadedTest")); + WebCompositor::initialize(m_webThread.get()); + } else + WebCompositor::initialize(0); + + ASSERT(CCProxy::isMainThread()); + m_mainThreadProxy = CCScopedThreadProxy::create(CCProxy::mainThread()); + + m_beginTask = new BeginTask(this); + WebKit::Platform::current()->currentThread()->postDelayedTask(m_beginTask, 0); // postDelayedTask takes ownership of the task + m_timeoutTask = new TimeoutTask(this); + WebKit::Platform::current()->currentThread()->postDelayedTask(m_timeoutTask, 5000); + WebKit::Platform::current()->currentThread()->enterRunLoop(); + + if (m_layerTreeHost && m_layerTreeHost->rootLayer()) + m_layerTreeHost->rootLayer()->setLayerTreeHost(0); + m_layerTreeHost.clear(); + + if (m_timeoutTask) + m_timeoutTask->clearTest(); + + if (m_endTestTask) + m_endTestTask->clearTest(); + + ASSERT_FALSE(m_layerTreeHost.get()); + m_client.clear(); + if (m_timedOut) { + FAIL() << "Test timed out"; + WebCompositor::shutdown(); + return; + } + afterTest(); + WebCompositor::shutdown(); +} + +} // namespace WebKitTests diff --git a/Source/WebKit/chromium/tests/CCThreadedTest.h b/Source/WebKit/chromium/tests/CCThreadedTest.h new file mode 100644 index 000000000..c0ca44fee --- /dev/null +++ b/Source/WebKit/chromium/tests/CCThreadedTest.h @@ -0,0 +1,226 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CCThreadedTest_h +#define CCThreadedTest_h + +#include "CompositorFakeWebGraphicsContext3D.h" +#include "cc/CCLayerAnimationDelegate.h" +#include "cc/CCLayerTreeHost.h" +#include "cc/CCLayerTreeHostImpl.h" +#include "cc/CCScopedThreadProxy.h" +#include <gtest/gtest.h> + +namespace WebCore { +class CCLayerImpl; +class CCLayerTreeHost; +class CCLayerTreeHostClient; +class CCLayerTreeHostImpl; +class GraphicsContext3D; +} + +namespace WebKit { +class WebThread; +} + +namespace WebKitTests { + +// Used by test stubs to notify the test when something interesting happens. +class TestHooks : public WebCore::CCLayerAnimationDelegate { +public: + virtual void beginCommitOnCCThread(WebCore::CCLayerTreeHostImpl*) { } + virtual void commitCompleteOnCCThread(WebCore::CCLayerTreeHostImpl*) { } + virtual bool prepareToDrawOnCCThread(WebCore::CCLayerTreeHostImpl*) { return true; } + virtual void drawLayersOnCCThread(WebCore::CCLayerTreeHostImpl*) { } + virtual void animateLayers(WebCore::CCLayerTreeHostImpl*, double monotonicTime) { } + virtual void willAnimateLayers(WebCore::CCLayerTreeHostImpl*, double monotonicTime) { } + virtual void applyScrollAndScale(const WebCore::IntSize&, float) { } + virtual void updateAnimations(double monotonicTime) { } + virtual void layout() { } + virtual void didRecreateContext(bool succeeded) { } + virtual void didAddAnimation() { } + virtual void didCommit() { } + virtual void didCommitAndDrawFrame() { } + virtual void scheduleComposite() { } + + // Implementation of CCLayerAnimationDelegate + virtual void notifyAnimationStarted(double time) { } + virtual void notifyAnimationFinished(double time) { } + + virtual PassRefPtr<WebCore::GraphicsContext3D> createContext(); +}; + +class TimeoutTask; +class BeginTask; +class EndTestTask; + +class MockCCLayerTreeHostClient : public WebCore::CCLayerTreeHostClient { +}; + +// The CCThreadedTests runs with the main loop running. It instantiates a single MockLayerTreeHost and associated +// MockLayerTreeHostImpl/MockLayerTreeHostClient. +// +// beginTest() is called once the main message loop is running and the layer tree host is initialized. +// +// Key stages of the drawing loop, e.g. drawing or commiting, redirect to CCThreadedTest methods of similar names. +// To track the commit process, override these functions. +// +// The test continues until someone calls endTest. endTest can be called on any thread, but be aware that +// ending the test is an asynchronous process. +class CCThreadedTest : public testing::Test, public TestHooks { +public: + virtual void afterTest() = 0; + virtual void beginTest() = 0; + + void endTest(); + void endTestAfterDelay(int delayMilliseconds); + + void postSetNeedsAnimateToMainThread(); + void postAddAnimationToMainThread(); + void postAddInstantAnimationToMainThread(); + void postSetNeedsCommitToMainThread(); + void postAcquireLayerTextures(); + void postSetNeedsRedrawToMainThread(); + void postSetNeedsAnimateAndCommitToMainThread(); + void postSetVisibleToMainThread(bool visible); + void postDidAddAnimationToMainThread(); + + void doBeginTest(); + void timeout(); + + void clearTimeout() { m_timeoutTask = 0; } + void clearEndTestTask() { m_endTestTask = 0; } + + WebCore::CCLayerTreeHost* layerTreeHost() { return m_layerTreeHost.get(); } + +protected: + CCThreadedTest(); + + virtual void scheduleComposite(); + + static void onEndTest(void* self); + + static void dispatchSetNeedsAnimate(void* self); + static void dispatchAddInstantAnimation(void* self); + static void dispatchAddAnimation(void* self); + static void dispatchSetNeedsAnimateAndCommit(void* self); + static void dispatchSetNeedsCommit(void* self); + static void dispatchAcquireLayerTextures(void* self); + static void dispatchSetNeedsRedraw(void* self); + static void dispatchSetVisible(void* self); + static void dispatchSetInvisible(void* self); + static void dispatchComposite(void* self); + static void dispatchDidAddAnimation(void* self); + + virtual void runTest(bool threaded); + + WebCore::CCLayerTreeSettings m_settings; + OwnPtr<MockCCLayerTreeHostClient> m_client; + OwnPtr<WebCore::CCLayerTreeHost> m_layerTreeHost; + +private: + bool m_beginning; + bool m_endWhenBeginReturns; + bool m_timedOut; + bool m_finished; + bool m_scheduled; + bool m_started; + + OwnPtr<WebKit::WebThread> m_webThread; + RefPtr<WebCore::CCScopedThreadProxy> m_mainThreadProxy; + TimeoutTask* m_timeoutTask; + BeginTask* m_beginTask; + EndTestTask* m_endTestTask; +}; + +class CCThreadedTestThreadOnly : public CCThreadedTest { +public: + void runTestThreaded() + { + CCThreadedTest::runTest(true); + } +}; + +// Adapts CCLayerTreeHostImpl for test. Runs real code, then invokes test hooks. +class MockLayerTreeHostImpl : public WebCore::CCLayerTreeHostImpl { +public: + static PassOwnPtr<MockLayerTreeHostImpl> create(TestHooks*, const WebCore::CCLayerTreeSettings&, WebCore::CCLayerTreeHostImplClient*); + + virtual void beginCommit(); + virtual void commitComplete(); + virtual bool prepareToDraw(FrameData&); + virtual void drawLayers(const FrameData&); + + // Make these public. + typedef Vector<WebCore::CCLayerImpl*> CCLayerList; + using CCLayerTreeHostImpl::calculateRenderSurfaceLayerList; + +protected: + virtual void animateLayers(double monotonicTime, double wallClockTime); + virtual double lowFrequencyAnimationInterval() const; + +private: + MockLayerTreeHostImpl(TestHooks*, const WebCore::CCLayerTreeSettings&, WebCore::CCLayerTreeHostImplClient*); + + TestHooks* m_testHooks; +}; + +class CompositorFakeWebGraphicsContext3DWithTextureTracking : public WebKit::CompositorFakeWebGraphicsContext3D { +public: + static PassOwnPtr<CompositorFakeWebGraphicsContext3DWithTextureTracking> create(Attributes); + + virtual WebKit::WebGLId createTexture(); + + virtual void deleteTexture(WebKit::WebGLId texture); + + virtual void bindTexture(WebKit::WGC3Denum target, WebKit::WebGLId texture); + + int numTextures() const; + int texture(int texture) const; + void resetTextures(); + + int numUsedTextures() const; + bool usedTexture(int texture) const; + void resetUsedTextures(); + +private: + explicit CompositorFakeWebGraphicsContext3DWithTextureTracking(Attributes attrs); + + Vector<WebKit::WebGLId> m_textures; + HashSet<WebKit::WebGLId, DefaultHash<WebKit::WebGLId>::Hash, WTF::UnsignedWithZeroKeyHashTraits<WebKit::WebGLId> > m_usedTextures; +}; + +} // namespace WebKitTests + +#define SINGLE_AND_MULTI_THREAD_TEST_F(TEST_FIXTURE_NAME) \ + TEST_F(TEST_FIXTURE_NAME, runSingleThread) \ + { \ + runTest(false); \ + } \ + TEST_F(TEST_FIXTURE_NAME, runMultiThread) \ + { \ + runTest(true); \ + } + +#endif // CCThreadedTest_h diff --git a/Source/WebKit/chromium/tests/CCTiledLayerTestCommon.cpp b/Source/WebKit/chromium/tests/CCTiledLayerTestCommon.cpp index 46d11a96e..7484b70f3 100644 --- a/Source/WebKit/chromium/tests/CCTiledLayerTestCommon.cpp +++ b/Source/WebKit/chromium/tests/CCTiledLayerTestCommon.cpp @@ -40,7 +40,7 @@ FakeLayerTextureUpdater::Texture::~Texture() { } -void FakeLayerTextureUpdater::Texture::updateRect(GraphicsContext3D*, TextureAllocator* allocator, const IntRect&, const IntRect&) +void FakeLayerTextureUpdater::Texture::updateRect(CCGraphicsContext*, TextureAllocator* allocator, const IntRect&, const IntRect&) { if (allocator) texture()->allocate(allocator); @@ -63,7 +63,7 @@ FakeLayerTextureUpdater::~FakeLayerTextureUpdater() { } -void FakeLayerTextureUpdater::prepareToUpdate(const IntRect& contentRect, const IntSize&, int, float, IntRect& resultingOpaqueRect) +void FakeLayerTextureUpdater::prepareToUpdate(const IntRect& contentRect, const IntSize&, float, IntRect& resultingOpaqueRect) { m_prepareCount++; m_lastUpdateRect = contentRect; diff --git a/Source/WebKit/chromium/tests/CCTiledLayerTestCommon.h b/Source/WebKit/chromium/tests/CCTiledLayerTestCommon.h index 4eb875b1e..68fef6420 100644 --- a/Source/WebKit/chromium/tests/CCTiledLayerTestCommon.h +++ b/Source/WebKit/chromium/tests/CCTiledLayerTestCommon.h @@ -25,7 +25,6 @@ #ifndef CCTiledLayerTestCommon_h #define CCTiledLayerTestCommon_h -#include "GraphicsContext3D.h" #include "IntRect.h" #include "IntSize.h" #include "LayerTextureUpdater.h" @@ -34,6 +33,7 @@ #include "TextureManager.h" #include "TextureUploader.h" #include "TiledLayerChromium.h" +#include "cc/CCGraphicsContext.h" #include "cc/CCTextureUpdater.h" #include "cc/CCTiledLayerImpl.h" @@ -48,8 +48,8 @@ public: Texture(FakeLayerTextureUpdater*, PassOwnPtr<WebCore::ManagedTexture>); virtual ~Texture(); - virtual void updateRect(WebCore::GraphicsContext3D*, WebCore::TextureAllocator* , const WebCore::IntRect&, const WebCore::IntRect&); - virtual void prepareRect(const WebCore::IntRect&); + virtual void updateRect(WebCore::CCGraphicsContext*, WebCore::TextureAllocator* , const WebCore::IntRect&, const WebCore::IntRect&) OVERRIDE; + virtual void prepareRect(const WebCore::IntRect&) OVERRIDE; private: FakeLayerTextureUpdater* m_layer; @@ -58,10 +58,10 @@ public: FakeLayerTextureUpdater(); virtual ~FakeLayerTextureUpdater(); - virtual PassOwnPtr<WebCore::LayerTextureUpdater::Texture> createTexture(WebCore::TextureManager*); - virtual SampledTexelFormat sampledTexelFormat(GC3Denum) { return SampledTexelFormatRGBA; } + virtual PassOwnPtr<WebCore::LayerTextureUpdater::Texture> createTexture(WebCore::TextureManager*) OVERRIDE; + virtual SampledTexelFormat sampledTexelFormat(GC3Denum) OVERRIDE { return SampledTexelFormatRGBA; } - virtual void prepareToUpdate(const WebCore::IntRect& contentRect, const WebCore::IntSize&, int, float, WebCore::IntRect& resultingOpaqueRect); + virtual void prepareToUpdate(const WebCore::IntRect& contentRect, const WebCore::IntSize&, float, WebCore::IntRect& resultingOpaqueRect) OVERRIDE; // Sets the rect to invalidate during the next call to prepareToUpdate(). After the next // call to prepareToUpdate() the rect is reset. void setRectToInvalidate(const WebCore::IntRect&, FakeTiledLayerChromium*); @@ -156,7 +156,8 @@ public: class FakeTextureCopier : public WebCore::TextureCopier { public: - virtual void copyTexture(WebCore::GraphicsContext3D*, unsigned, unsigned, const WebCore::IntSize&) { } + virtual void copyTexture(WebCore::CCGraphicsContext*, unsigned, unsigned, const WebCore::IntSize&) { } + virtual void copyToTexture(WebCore::CCGraphicsContext*, const void*, unsigned, const WebCore::IntSize&, GC3Denum) { } }; class FakeTextureUploader : public WebCore::TextureUploader { @@ -164,7 +165,7 @@ public: virtual bool isBusy() { return false; } virtual void beginUploads() { } virtual void endUploads() { } - virtual void uploadTexture(WebCore::GraphicsContext3D* context, WebCore::LayerTextureUpdater::Texture* texture, WebCore::TextureAllocator* allocator, const WebCore::IntRect sourceRect, const WebCore::IntRect destRect) { texture->updateRect(context, allocator, sourceRect, destRect); } + virtual void uploadTexture(WebCore::CCGraphicsContext* context, WebCore::LayerTextureUpdater::Texture* texture, WebCore::TextureAllocator* allocator, const WebCore::IntRect sourceRect, const WebCore::IntRect destRect) { texture->updateRect(context, allocator, sourceRect, destRect); } }; } diff --git a/Source/WebKit/chromium/tests/Canvas2DLayerBridgeTest.cpp b/Source/WebKit/chromium/tests/Canvas2DLayerBridgeTest.cpp new file mode 100644 index 000000000..bacf873fc --- /dev/null +++ b/Source/WebKit/chromium/tests/Canvas2DLayerBridgeTest.cpp @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include "Canvas2DLayerBridge.h" + +#include "FakeWebGraphicsContext3D.h" +#include "GraphicsContext3DPrivate.h" +#include "ImageBuffer.h" +#include "LayerChromium.h" +#include "TextureManager.h" +#include "WebCompositor.h" +#include "WebKit.h" +#include "cc/CCGraphicsContext.h" +#include "cc/CCTextureUpdater.h" +#include "platform/WebKitPlatformSupport.h" +#include "platform/WebThread.h" + +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <wtf/RefPtr.h> + +using namespace WebCore; +using namespace WebKit; +using testing::InSequence; +using testing::Return; +using testing::Test; + +namespace { + +class MockCanvasContext : public FakeWebGraphicsContext3D { +public: + MOCK_METHOD0(flush, void(void)); + MOCK_METHOD0(createTexture, unsigned(void)); + MOCK_METHOD1(deleteTexture, void(unsigned)); + + virtual GrGLInterface* onCreateGrGLInterface() OVERRIDE { return 0; } +}; + +class MockWebTextureUpdater : public WebTextureUpdater { +public: + MOCK_METHOD3(appendCopy, void(unsigned, unsigned, WebSize)); +}; + +} // namespace + +enum ThreadMode { + SingleThreaded, Threaded +}; + +class Canvas2DLayerBridgeTest : public Test { +protected: + void fullLifecycleTest(ThreadMode threadMode, DeferralMode deferralMode) + { + GraphicsContext3D::Attributes attrs; + + RefPtr<GraphicsContext3D> mainContext = GraphicsContext3DPrivate::createGraphicsContextFromWebContext(adoptPtr(new MockCanvasContext()), GraphicsContext3D::RenderDirectlyToHostWindow); + RefPtr<CCGraphicsContext> ccMainContext = CCGraphicsContext::create3D(mainContext); + RefPtr<GraphicsContext3D> implContext = GraphicsContext3DPrivate::createGraphicsContextFromWebContext(adoptPtr(new MockCanvasContext()), GraphicsContext3D::RenderDirectlyToHostWindow); + RefPtr<CCGraphicsContext> ccImplContext = CCGraphicsContext::create3D(implContext); + + MockCanvasContext& mainMock = *static_cast<MockCanvasContext*>(GraphicsContext3DPrivate::extractWebGraphicsContext3D(mainContext.get())); + MockCanvasContext& implMock = *static_cast<MockCanvasContext*>(GraphicsContext3DPrivate::extractWebGraphicsContext3D(implContext.get())); + + MockWebTextureUpdater updater; + + const IntSize size(300, 150); + + OwnPtr<WebThread> thread; + if (threadMode == Threaded) + thread = adoptPtr(WebKit::Platform::current()->createThread("Canvas2DLayerBridgeTest")); + WebCompositor::initialize(thread.get()); + + WebGLId backTextureId = 1; + WebGLId frontTextureId = 1; + + // Threaded and non deferred canvases are double buffered. + if (threadMode == Threaded && deferralMode == NonDeferred) { + frontTextureId = 2; + // Create texture (on the main thread) and do the copy (on the impl thread). + EXPECT_CALL(mainMock, createTexture()).WillOnce(Return(frontTextureId)); + } + + OwnPtr<Canvas2DLayerBridge> bridge = Canvas2DLayerBridge::create(mainContext.get(), size, deferralMode, backTextureId); + + ::testing::Mock::VerifyAndClearExpectations(&mainMock); + + EXPECT_CALL(mainMock, flush()); + if (threadMode == Threaded && deferralMode == NonDeferred) + EXPECT_CALL(updater, appendCopy(backTextureId, frontTextureId, WebSize(300, 150))); + EXPECT_EQ(frontTextureId, bridge->prepareTexture(updater)); + ::testing::Mock::VerifyAndClearExpectations(&mainMock); + ::testing::Mock::VerifyAndClearExpectations(&updater); + + if (threadMode == Threaded && deferralMode == NonDeferred) { + EXPECT_CALL(mainMock, deleteTexture(frontTextureId)); + EXPECT_CALL(mainMock, flush()); + } + bridge.clear(); + ::testing::Mock::VerifyAndClearExpectations(&implMock); + + WebCompositor::shutdown(); + } +}; + +namespace { + +TEST_F(Canvas2DLayerBridgeTest, testFullLifecycleSingleThreadedDeferred) +{ + fullLifecycleTest(SingleThreaded, NonDeferred); +} + +TEST_F(Canvas2DLayerBridgeTest, testFullLifecycleSingleThreadedNonDeferred) +{ + fullLifecycleTest(SingleThreaded, Deferred); +} + +TEST_F(Canvas2DLayerBridgeTest, testFullLifecycleThreadedNonDeferred) +{ + fullLifecycleTest(Threaded, NonDeferred); +} + +TEST_F(Canvas2DLayerBridgeTest, testFullLifecycleThreadedDeferred) +{ + fullLifecycleTest(Threaded, Deferred); +} + +TEST(Canvas2DLayerBridgeTest2, testClearClient) +{ + GraphicsContext3D::Attributes attrs; + + RefPtr<GraphicsContext3D> mainContext = GraphicsContext3DPrivate::createGraphicsContextFromWebContext(adoptPtr(new MockCanvasContext()), GraphicsContext3D::RenderDirectlyToHostWindow); + OwnPtr<Canvas2DLayerBridge> bridge = Canvas2DLayerBridge::create(mainContext.get(), IntSize(100, 100), Deferred, 1); + RefPtr<LayerChromium> layer = bridge->layer(); + bridge.clear(); + CCTextureUpdater updater; + layer->update(updater, 0); +} + +} // namespace + diff --git a/Source/WebKit/chromium/tests/Canvas2DLayerChromiumTest.cpp b/Source/WebKit/chromium/tests/Canvas2DLayerChromiumTest.cpp deleted file mode 100644 index b42485b71..000000000 --- a/Source/WebKit/chromium/tests/Canvas2DLayerChromiumTest.cpp +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" - -#include "Canvas2DLayerChromium.h" - -#include "CCSchedulerTestCommon.h" -#include "FakeCCLayerTreeHostClient.h" -#include "FakeWebGraphicsContext3D.h" -#include "GraphicsContext3DPrivate.h" -#include "Region.h" -#include "TextureCopier.h" -#include "TextureManager.h" -#include "WebCompositor.h" -#include "WebKit.h" -#include "cc/CCSingleThreadProxy.h" -#include "cc/CCTextureLayerImpl.h" -#include "cc/CCTextureUpdater.h" -#include "platform/WebKitPlatformSupport.h" -#include "platform/WebThread.h" - -#include <gmock/gmock.h> -#include <gtest/gtest.h> -#include <wtf/RefPtr.h> - -using namespace WebCore; -using namespace WebKit; -using namespace WebKitTests; -using testing::InSequence; -using testing::Return; -using testing::Test; - -namespace { - -class FakeCCLayerTreeHost : public CCLayerTreeHost { -public: - static PassOwnPtr<FakeCCLayerTreeHost> create() - { - OwnPtr<FakeCCLayerTreeHost> host(adoptPtr(new FakeCCLayerTreeHost)); - host->initialize(); - return host.release(); - } - -private: - FakeCCLayerTreeHost() - : CCLayerTreeHost(&m_client, CCSettings()) - { - } - - FakeCCLayerTreeHostClient m_client; -}; - -class MockCanvasContext : public FakeWebGraphicsContext3D { -public: - MOCK_METHOD0(flush, void(void)); -}; - -class MockTextureAllocator : public TextureAllocator { -public: - MOCK_METHOD2(createTexture, unsigned(const IntSize&, GC3Denum)); - MOCK_METHOD3(deleteTexture, void(unsigned, const IntSize&, GC3Denum)); -}; - -class MockTextureCopier : public TextureCopier { -public: - MOCK_METHOD4(copyTexture, void(GraphicsContext3D*, unsigned, unsigned, const IntSize&)); -}; - -class MockTextureUploader : public TextureUploader { -public: - MOCK_METHOD0(isBusy, bool()); - MOCK_METHOD0(beginUploads, void()); - MOCK_METHOD0(endUploads, void()); - MOCK_METHOD5(uploadTexture, void(GraphicsContext3D*, LayerTextureUpdater::Texture*, TextureAllocator*, const IntRect, const IntRect)); -}; - -} // namespace - -class Canvas2DLayerChromiumTest : public Test { -protected: - void fullLifecycleTest(bool threaded, bool deferred) - { - GraphicsContext3D::Attributes attrs; - - RefPtr<GraphicsContext3D> mainContext = GraphicsContext3DPrivate::createGraphicsContextFromWebContext(adoptPtr(new MockCanvasContext()), GraphicsContext3D::RenderDirectlyToHostWindow); - RefPtr<GraphicsContext3D> implContext = GraphicsContext3DPrivate::createGraphicsContextFromWebContext(adoptPtr(new MockCanvasContext()), GraphicsContext3D::RenderDirectlyToHostWindow); - - MockCanvasContext& mainMock = *static_cast<MockCanvasContext*>(GraphicsContext3DPrivate::extractWebGraphicsContext3D(mainContext.get())); - MockCanvasContext& implMock = *static_cast<MockCanvasContext*>(GraphicsContext3DPrivate::extractWebGraphicsContext3D(implContext.get())); - - MockTextureAllocator allocatorMock; - MockTextureCopier copierMock; - MockTextureUploader uploaderMock; - CCTextureUpdater updater; - - const IntSize size(300, 150); - - OwnPtr<WebThread> thread; - if (threaded) - thread = adoptPtr(WebKit::Platform::current()->createThread("Canvas2DLayerChromiumTest")); - WebCompositor::initialize(thread.get()); - - OwnPtr<FakeCCLayerTreeHost> layerTreeHost(FakeCCLayerTreeHost::create()); - // Force an update, so that we get a valid TextureManager. - layerTreeHost->updateLayers(updater); - - const WebGLId backTextureId = 1; - const WebGLId frontTextureId = 2; - { - InSequence sequence; - - // Paint canvas contents on the main thread. - EXPECT_CALL(mainMock, flush()); - - // Note that the canvas backing texture is doublebuffered only when using the threaded - // compositor and not using deferred canvas rendering - if (threaded && !deferred) { - // Create texture and do the copy (on the impl thread). - EXPECT_CALL(allocatorMock, createTexture(size, GraphicsContext3D::RGBA)) - .WillOnce(Return(frontTextureId)); - EXPECT_CALL(copierMock, copyTexture(implContext.get(), backTextureId, frontTextureId, size)); - EXPECT_CALL(implMock, flush()); - - // Teardown TextureManager. - EXPECT_CALL(allocatorMock, deleteTexture(frontTextureId, size, GraphicsContext3D::RGBA)); - } - } - - RefPtr<Canvas2DLayerChromium> canvas = Canvas2DLayerChromium::create(mainContext.get(), size, deferred ? Deferred : NonDeferred); - canvas->setIsDrawable(true); - canvas->setLayerTreeHost(layerTreeHost.get()); - canvas->setBounds(IntSize(600, 300)); - canvas->setTextureId(backTextureId); - - canvas->setNeedsDisplay(); - EXPECT_TRUE(canvas->needsDisplay()); - canvas->update(updater, 0); - EXPECT_FALSE(canvas->needsDisplay()); - { - DebugScopedSetImplThread scopedImplThread; - - OwnPtr<CCLayerImpl> layerImpl = canvas->createCCLayerImpl(); - EXPECT_EQ(0u, static_cast<CCTextureLayerImpl*>(layerImpl.get())->textureId()); - - updater.update(implContext.get(), &allocatorMock, &copierMock, &uploaderMock, 1); - canvas->pushPropertiesTo(layerImpl.get()); - - if (threaded && !deferred) - EXPECT_EQ(frontTextureId, static_cast<CCTextureLayerImpl*>(layerImpl.get())->textureId()); - else - EXPECT_EQ(backTextureId, static_cast<CCTextureLayerImpl*>(layerImpl.get())->textureId()); - } - canvas.clear(); - layerTreeHost->contentsTextureManager()->reduceMemoryToLimit(0); - layerTreeHost->contentsTextureManager()->deleteEvictedTextures(&allocatorMock); - layerTreeHost.clear(); - WebCompositor::shutdown(); - } -}; - -namespace { - -TEST_F(Canvas2DLayerChromiumTest, testFullLifecycleSingleThread) -{ - fullLifecycleTest(false, false); -} - -TEST_F(Canvas2DLayerChromiumTest, testFullLifecycleThreaded) -{ - fullLifecycleTest(true, false); -} - -TEST_F(Canvas2DLayerChromiumTest, testFullLifecycleSingleThreadDeferred) -{ - fullLifecycleTest(false, true); -} - -TEST_F(Canvas2DLayerChromiumTest, testFullLifecycleThreadedDeferred) -{ - fullLifecycleTest(true, true); -} - -} // namespace diff --git a/Source/WebKit/chromium/tests/DecimalTest.cpp b/Source/WebKit/chromium/tests/DecimalTest.cpp index 18457fdf4..db80f5bfe 100644 --- a/Source/WebKit/chromium/tests/DecimalTest.cpp +++ b/Source/WebKit/chromium/tests/DecimalTest.cpp @@ -556,6 +556,32 @@ TEST_F(DecimalTest, FloorSpecialValues) EXPECT_EQ(Decimal::nan(), Decimal::nan().floor()); } +TEST_F(DecimalTest, FromDouble) +{ + EXPECT_EQ(encode(0, 0, Positive), Decimal::fromDouble(0.0)); + EXPECT_EQ(encode(0, 0, Negative), Decimal::fromDouble(-0.0)); + EXPECT_EQ(encode(1, 0, Positive), Decimal::fromDouble(1)); + EXPECT_EQ(encode(1, 0, Negative), Decimal::fromDouble(-1)); + EXPECT_EQ(encode(123, 0, Positive), Decimal::fromDouble(123)); + EXPECT_EQ(encode(123, 0, Negative), Decimal::fromDouble(-123)); + EXPECT_EQ(encode(1, -1, Positive), Decimal::fromDouble(0.1)); + EXPECT_EQ(encode(1, -1, Negative), Decimal::fromDouble(-0.1)); +} + +TEST_F(DecimalTest, FromDoubleLimits) +{ + EXPECT_EQ(encode(UINT64_C(2220446049250313), -31, Positive), Decimal::fromDouble(std::numeric_limits<double>::epsilon())); + EXPECT_EQ(encode(UINT64_C(2220446049250313), -31, Negative), Decimal::fromDouble(-std::numeric_limits<double>::epsilon())); + EXPECT_EQ(encode(UINT64_C(17976931348623157), 292, Positive), Decimal::fromDouble(std::numeric_limits<double>::max())); + EXPECT_EQ(encode(UINT64_C(17976931348623157), 292, Negative), Decimal::fromDouble(-std::numeric_limits<double>::max())); + EXPECT_EQ(encode(UINT64_C(22250738585072014), -324, Positive), Decimal::fromDouble(std::numeric_limits<double>::min())); + EXPECT_EQ(encode(UINT64_C(22250738585072014), -324, Negative), Decimal::fromDouble(-std::numeric_limits<double>::min())); + EXPECT_TRUE(Decimal::fromDouble(std::numeric_limits<double>::infinity()).isInfinity()); + EXPECT_TRUE(Decimal::fromDouble(-std::numeric_limits<double>::infinity()).isInfinity()); + EXPECT_TRUE(Decimal::fromDouble(std::numeric_limits<double>::quiet_NaN()).isNaN()); + EXPECT_TRUE(Decimal::fromDouble(-std::numeric_limits<double>::quiet_NaN()).isNaN()); +} + TEST_F(DecimalTest, FromInt32) { EXPECT_EQ(encode(0, 0, Positive), Decimal(0)); @@ -750,24 +776,32 @@ TEST_F(DecimalTest, NegateSpecialValues) TEST_F(DecimalTest, Predicates) { EXPECT_TRUE(Decimal::zero(Positive).isFinite()); + EXPECT_FALSE(Decimal::zero(Positive).isInfinity()); + EXPECT_FALSE(Decimal::zero(Positive).isNaN()); EXPECT_TRUE(Decimal::zero(Positive).isPositive()); EXPECT_FALSE(Decimal::zero(Positive).isNegative()); EXPECT_FALSE(Decimal::zero(Positive).isSpecial()); EXPECT_TRUE(Decimal::zero(Positive).isZero()); EXPECT_TRUE(Decimal::zero(Negative).isFinite()); + EXPECT_FALSE(Decimal::zero(Negative).isInfinity()); + EXPECT_FALSE(Decimal::zero(Negative).isNaN()); EXPECT_FALSE(Decimal::zero(Negative).isPositive()); EXPECT_TRUE(Decimal::zero(Negative).isNegative()); EXPECT_FALSE(Decimal::zero(Negative).isSpecial()); EXPECT_TRUE(Decimal::zero(Negative).isZero()); EXPECT_TRUE(Decimal(123).isFinite()); + EXPECT_FALSE(Decimal(123).isInfinity()); + EXPECT_FALSE(Decimal(123).isNaN()); EXPECT_TRUE(Decimal(123).isPositive()); EXPECT_FALSE(Decimal(123).isNegative()); EXPECT_FALSE(Decimal(123).isSpecial()); EXPECT_FALSE(Decimal(123).isZero()); EXPECT_TRUE(Decimal(-123).isFinite()); + EXPECT_FALSE(Decimal(-123).isInfinity()); + EXPECT_FALSE(Decimal(-123).isNaN()); EXPECT_FALSE(Decimal(-123).isPositive()); EXPECT_TRUE(Decimal(-123).isNegative()); EXPECT_FALSE(Decimal(-123).isSpecial()); @@ -777,18 +811,24 @@ TEST_F(DecimalTest, Predicates) TEST_F(DecimalTest, PredicatesSpecialValues) { EXPECT_FALSE(Decimal::infinity(Positive).isFinite()); + EXPECT_TRUE(Decimal::infinity(Positive).isInfinity()); + EXPECT_FALSE(Decimal::infinity(Positive).isNaN()); EXPECT_TRUE(Decimal::infinity(Positive).isPositive()); EXPECT_FALSE(Decimal::infinity(Positive).isNegative()); EXPECT_TRUE(Decimal::infinity(Positive).isSpecial()); EXPECT_FALSE(Decimal::infinity(Positive).isZero()); EXPECT_FALSE(Decimal::infinity(Negative).isFinite()); + EXPECT_TRUE(Decimal::infinity(Negative).isInfinity()); + EXPECT_FALSE(Decimal::infinity(Negative).isNaN()); EXPECT_FALSE(Decimal::infinity(Negative).isPositive()); EXPECT_TRUE(Decimal::infinity(Negative).isNegative()); EXPECT_TRUE(Decimal::infinity(Negative).isSpecial()); EXPECT_FALSE(Decimal::infinity(Negative).isZero()); EXPECT_FALSE(Decimal::nan().isFinite()); + EXPECT_FALSE(Decimal::nan().isInfinity()); + EXPECT_TRUE(Decimal::nan().isNaN()); EXPECT_TRUE(Decimal::nan().isSpecial()); EXPECT_FALSE(Decimal::nan().isZero()); } @@ -940,6 +980,42 @@ TEST_F(DecimalTest, SubtractSpecialValues) EXPECT_EQ(NaN, MinusInfinity - NaN); } +TEST_F(DecimalTest, ToDouble) +{ + EXPECT_EQ(0.0, encode(0, 0, Positive).toDouble()); + EXPECT_EQ(-0.0, encode(0, 0, Negative).toDouble()); + + EXPECT_EQ(1.0, encode(1, 0, Positive).toDouble()); + EXPECT_EQ(-1.0, encode(1, 0, Negative).toDouble()); + + EXPECT_EQ(0.1, encode(1, -1, Positive).toDouble()); + EXPECT_EQ(-0.1, encode(1, -1, Negative).toDouble()); + EXPECT_EQ(0.3, encode(3, -1, Positive).toDouble()); + EXPECT_EQ(-0.3, encode(3, -1, Negative).toDouble()); + EXPECT_EQ(0.6, encode(6, -1, Positive).toDouble()); + EXPECT_EQ(-0.6, encode(6, -1, Negative).toDouble()); + EXPECT_EQ(0.7, encode(7, -1, Positive).toDouble()); + EXPECT_EQ(-0.7, encode(7, -1, Negative).toDouble()); + + EXPECT_EQ(0.01, encode(1, -2, Positive).toDouble()); + EXPECT_EQ(0.001, encode(1, -3, Positive).toDouble()); + EXPECT_EQ(0.0001, encode(1, -4, Positive).toDouble()); + EXPECT_EQ(0.00001, encode(1, -5, Positive).toDouble()); + + EXPECT_EQ(1e+308, encode(1, 308, Positive).toDouble()); + EXPECT_EQ(1e-307, encode(1, -307, Positive).toDouble()); + + EXPECT_TRUE(isinf(encode(1, 1000, Positive).toDouble())); + EXPECT_EQ(0.0, encode(1, -1000, Positive).toDouble()); +} + +TEST_F(DecimalTest, ToDoubleSpecialValues) +{ + EXPECT_TRUE(isinf(Decimal::infinity(Decimal::Positive).toDouble())); + EXPECT_TRUE(isinf(Decimal::infinity(Decimal::Negative).toDouble())); + EXPECT_TRUE(isnan(Decimal::nan().toDouble())); +} + TEST_F(DecimalTest, ToString) { EXPECT_EQ(String("0"), Decimal::zero(Positive).toString()); diff --git a/Source/WebKit/chromium/tests/FakeCCLayerTreeHostClient.h b/Source/WebKit/chromium/tests/FakeCCLayerTreeHostClient.h index 992509c4f..6ab616d3c 100755 --- a/Source/WebKit/chromium/tests/FakeCCLayerTreeHostClient.h +++ b/Source/WebKit/chromium/tests/FakeCCLayerTreeHostClient.h @@ -39,7 +39,7 @@ public: virtual void updateAnimations(double monotonicFrameBeginTime) OVERRIDE { } virtual void layout() OVERRIDE { } virtual void applyScrollAndScale(const IntSize& scrollDelta, float pageScale) OVERRIDE { } - virtual PassRefPtr<GraphicsContext3D> createContext() OVERRIDE + virtual PassRefPtr<GraphicsContext3D> createContext3D() OVERRIDE { GraphicsContext3D::Attributes attrs; return createCompositorMockGraphicsContext3D(attrs); diff --git a/Source/WebKit/chromium/tests/FrameLoaderClientImplTest.cpp b/Source/WebKit/chromium/tests/FrameLoaderClientImplTest.cpp index 8fc9e42b2..12ca68418 100644 --- a/Source/WebKit/chromium/tests/FrameLoaderClientImplTest.cpp +++ b/Source/WebKit/chromium/tests/FrameLoaderClientImplTest.cpp @@ -46,13 +46,12 @@ namespace { class TestWebFrameClient : public WebFrameClient { public: - bool userAgent(const WebURL& url, WebString* userAgent) OVERRIDE + WebString userAgentOverride(WebFrame* frame, const WebURL& url) OVERRIDE { if (m_userAgentOverride.isEmpty()) - return false; + return WebString(); - *userAgent = m_userAgentOverride; - return true; + return m_userAgentOverride; } void setUserAgentOverride(const WebString& userAgent) diff --git a/Source/WebKit/chromium/tests/GraphicsLayerChromiumTest.cpp b/Source/WebKit/chromium/tests/GraphicsLayerChromiumTest.cpp index b7aa85746..4fdcf4f41 100644 --- a/Source/WebKit/chromium/tests/GraphicsLayerChromiumTest.cpp +++ b/Source/WebKit/chromium/tests/GraphicsLayerChromiumTest.cpp @@ -27,56 +27,372 @@ #include "GraphicsLayerChromium.h" #include "CCAnimationTestCommon.h" +#include "CompositorFakeGraphicsContext3D.h" +#include "GraphicsContext3D.h" +#include "GraphicsContext3DPrivate.h" #include "GraphicsLayer.h" #include "LayerChromium.h" +#include "Matrix3DTransformOperation.h" +#include "RotateTransformOperation.h" +#include "TranslateTransformOperation.h" +#include "WebCompositor.h" + +#include "cc/CCLayerTreeHost.h" +#include "cc/CCLayerTreeHostImpl.h" +#include "cc/CCSingleThreadProxy.h" + #include <gtest/gtest.h> #include <wtf/PassOwnPtr.h> using namespace WebCore; +using namespace WebKit; using namespace WebKitTests; namespace { class MockGraphicsLayerClient : public GraphicsLayerClient { public: - virtual void notifyAnimationStarted(const GraphicsLayer*, double time) { } - virtual void notifySyncRequired(const GraphicsLayer*) { } - virtual void paintContents(const GraphicsLayer*, GraphicsContext&, GraphicsLayerPaintingPhase, const IntRect& inClip) { } - virtual bool showDebugBorders(const GraphicsLayer*) const { return false; } - virtual bool showRepaintCounter(const GraphicsLayer*) const { return false; } + virtual void notifyAnimationStarted(const GraphicsLayer*, double time) OVERRIDE { } + virtual void notifySyncRequired(const GraphicsLayer*) OVERRIDE { } + virtual void paintContents(const GraphicsLayer*, GraphicsContext&, GraphicsLayerPaintingPhase, const IntRect& inClip) OVERRIDE { } + virtual bool showDebugBorders(const GraphicsLayer*) const OVERRIDE { return false; } + virtual bool showRepaintCounter(const GraphicsLayer*) const OVERRIDE { return false; } }; -TEST(GraphicsLayerChromiumTest, updateLayerPreserves3DWithAnimations) -{ - MockGraphicsLayerClient client; - OwnPtr<GraphicsLayerChromium> graphicsLayer = static_pointer_cast<GraphicsLayerChromium>(GraphicsLayer::create(&client)); - ASSERT_TRUE(graphicsLayer.get()); +class MockLayerTreeHostClient : public CCLayerTreeHostClient { +public: + virtual void willBeginFrame() OVERRIDE { } + virtual void didBeginFrame() OVERRIDE { } + virtual void updateAnimations(double frameBeginTime) OVERRIDE { } + virtual void layout() OVERRIDE { } + virtual void applyScrollAndScale(const IntSize& scrollDelta, float pageScale) OVERRIDE { } + virtual PassRefPtr<GraphicsContext3D> createContext3D() OVERRIDE + { + GraphicsContext3D::Attributes attrs; + return createCompositorMockGraphicsContext3D(attrs); + } + virtual void didRecreateContext(bool success) OVERRIDE { } + virtual void willCommit() OVERRIDE { } + virtual void didCommit() OVERRIDE { } + virtual void didCommitAndDrawFrame() OVERRIDE { } + virtual void didCompleteSwapBuffers() OVERRIDE { } + virtual void scheduleComposite() OVERRIDE { } +}; + +class MockLayerTreeHost : public CCLayerTreeHost { +public: + static PassOwnPtr<MockLayerTreeHost> create() + { + CCLayerTreeSettings settings; + OwnPtr<MockLayerTreeHost> layerTreeHost(adoptPtr(new MockLayerTreeHost(new MockLayerTreeHostClient(), settings))); + bool success = layerTreeHost->initialize(); + EXPECT_TRUE(success); + layerTreeHost->setRootLayer(LayerChromium::create()); + layerTreeHost->setViewportSize(IntSize(1, 1)); + return layerTreeHost.release(); + } + + virtual PassOwnPtr<CCLayerTreeHostImpl> createLayerTreeHostImpl(CCLayerTreeHostImplClient* client) + { + return CCLayerTreeHostImpl::create(settings(), client); + } + +private: + MockLayerTreeHost(CCLayerTreeHostClient* client, const CCLayerTreeSettings& settings) + : CCLayerTreeHost(client, settings) + { + } +}; + +class GraphicsLayerChromiumTest : public testing::Test { +public: + GraphicsLayerChromiumTest() + { + // For these tests, we will enable threaded animations. + WebCompositor::setAcceleratedAnimationEnabled(true); + WebCompositor::initialize(0); + m_graphicsLayer = static_pointer_cast<GraphicsLayerChromium>(GraphicsLayer::create(&m_client)); + m_platformLayer = static_cast<LayerChromium*>(m_graphicsLayer->platformLayer()); + m_layerTreeHost = MockLayerTreeHost::create(); + m_platformLayer->setLayerTreeHost(m_layerTreeHost.get()); + } + + virtual ~GraphicsLayerChromiumTest() + { + m_graphicsLayer.clear(); + m_layerTreeHost.clear(); + WebCompositor::shutdown(); + } + +protected: + static void expectTranslateX(double translateX, const WebTransformationMatrix& matrix) + { + EXPECT_FLOAT_EQ(translateX, matrix.m41()); + } + + LayerChromium* m_platformLayer; + OwnPtr<GraphicsLayerChromium> m_graphicsLayer; + OwnPtr<CCLayerTreeHost> m_layerTreeHost; - LayerChromium* platformLayer = static_cast<LayerChromium*>(graphicsLayer->platformLayer()); - ASSERT_TRUE(platformLayer); +private: + MockGraphicsLayerClient m_client; + DebugScopedSetMainThread m_main; +}; - ASSERT_FALSE(platformLayer->hasActiveAnimation()); +TEST_F(GraphicsLayerChromiumTest, updateLayerPreserves3DWithAnimations) +{ + ASSERT_FALSE(m_platformLayer->hasActiveAnimation()); OwnPtr<CCActiveAnimation> floatAnimation(CCActiveAnimation::create(adoptPtr(new FakeFloatAnimationCurve), 0, 1, CCActiveAnimation::Opacity)); - platformLayer->layerAnimationController()->add(floatAnimation.release()); + m_platformLayer->layerAnimationController()->addAnimation(floatAnimation.release()); + + ASSERT_TRUE(m_platformLayer->hasActiveAnimation()); + + m_graphicsLayer->setPreserves3D(true); + + m_platformLayer = static_cast<LayerChromium*>(m_graphicsLayer->platformLayer()); + ASSERT_TRUE(m_platformLayer); + + ASSERT_TRUE(m_platformLayer->hasActiveAnimation()); + m_platformLayer->removeAnimation(0); + ASSERT_FALSE(m_platformLayer->hasActiveAnimation()); + + m_graphicsLayer->setPreserves3D(false); + + m_platformLayer = static_cast<LayerChromium*>(m_graphicsLayer->platformLayer()); + ASSERT_TRUE(m_platformLayer); + + ASSERT_FALSE(m_platformLayer->hasActiveAnimation()); +} + +TEST_F(GraphicsLayerChromiumTest, createOpacityAnimation) +{ + const double duration = 1; + WebCore::KeyframeValueList values(AnimatedPropertyOpacity); + values.insert(new FloatAnimationValue(0, 0)); + values.insert(new FloatAnimationValue(duration, 1)); + + RefPtr<Animation> animation = Animation::create(); + animation->setDuration(duration); + + IntSize boxSize; + bool addedAnimation = m_graphicsLayer->addAnimation(values, boxSize, animation.get(), "", 0); + + EXPECT_TRUE(addedAnimation); + + EXPECT_TRUE(m_platformLayer->layerAnimationController()->hasActiveAnimation()); + + CCActiveAnimation* activeAnimation = m_platformLayer->layerAnimationController()->getActiveAnimation(CCActiveAnimation::Opacity); + EXPECT_TRUE(activeAnimation); + + EXPECT_EQ(1, activeAnimation->iterations()); + EXPECT_EQ(CCActiveAnimation::Opacity, activeAnimation->targetProperty()); + + EXPECT_EQ(CCAnimationCurve::Float, activeAnimation->curve()->type()); + + const CCFloatAnimationCurve* curve = activeAnimation->curve()->toFloatAnimationCurve(); + EXPECT_TRUE(curve); + + EXPECT_EQ(0, curve->getValue(0)); + EXPECT_EQ(1, curve->getValue(duration)); +} + +TEST_F(GraphicsLayerChromiumTest, createTransformAnimation) +{ + const double duration = 1; + WebCore::KeyframeValueList values(AnimatedPropertyWebkitTransform); + + TransformOperations operations1; + operations1.operations().append(TranslateTransformOperation::create(Length(2, WebCore::Fixed), Length(0, WebCore::Fixed), TransformOperation::TRANSLATE_X)); + values.insert(new TransformAnimationValue(0, &operations1)); + + TransformOperations operations2; + operations2.operations().append(TranslateTransformOperation::create(Length(4, WebCore::Fixed), Length(0, WebCore::Fixed), TransformOperation::TRANSLATE_X)); + values.insert(new TransformAnimationValue(duration, &operations2)); + + RefPtr<Animation> animation = Animation::create(); + animation->setDuration(duration); + + IntSize boxSize; + m_graphicsLayer->addAnimation(values, boxSize, animation.get(), "", 0); + + EXPECT_TRUE(m_platformLayer->layerAnimationController()->hasActiveAnimation()); + + CCActiveAnimation* activeAnimation = m_platformLayer->layerAnimationController()->getActiveAnimation(CCActiveAnimation::Transform); + EXPECT_TRUE(activeAnimation); + + EXPECT_EQ(1, activeAnimation->iterations()); + EXPECT_EQ(CCActiveAnimation::Transform, activeAnimation->targetProperty()); + + EXPECT_EQ(CCAnimationCurve::Transform, activeAnimation->curve()->type()); + + const CCTransformAnimationCurve* curve = activeAnimation->curve()->toTransformAnimationCurve(); + EXPECT_TRUE(curve); + + expectTranslateX(2, curve->getValue(0)); + expectTranslateX(4, curve->getValue(duration)); +} + +TEST_F(GraphicsLayerChromiumTest, createTransformAnimationWithBigRotation) +{ + const double duration = 1; + WebCore::KeyframeValueList values(AnimatedPropertyWebkitTransform); + + TransformOperations operations1; + operations1.operations().append(RotateTransformOperation::create(0, TransformOperation::ROTATE)); + values.insert(new TransformAnimationValue(0, &operations1)); + + TransformOperations operations2; + operations2.operations().append(RotateTransformOperation::create(270, TransformOperation::ROTATE)); + values.insert(new TransformAnimationValue(duration, &operations2)); + + RefPtr<Animation> animation = Animation::create(); + animation->setDuration(duration); + + IntSize boxSize; + m_graphicsLayer->addAnimation(values, boxSize, animation.get(), "", 0); + + EXPECT_FALSE(m_platformLayer->layerAnimationController()->hasActiveAnimation()); +} + +TEST_F(GraphicsLayerChromiumTest, createTransformAnimationWithSingularMatrix) +{ + const double duration = 1; + WebCore::KeyframeValueList values(AnimatedPropertyWebkitTransform); + + TransformationMatrix matrix1; + TransformOperations operations1; + operations1.operations().append(Matrix3DTransformOperation::create(matrix1)); + values.insert(new TransformAnimationValue(0, &operations1)); + + TransformationMatrix matrix2; + matrix2.setM11(0); + TransformOperations operations2; + operations2.operations().append(Matrix3DTransformOperation::create(matrix2)); + values.insert(new TransformAnimationValue(duration, &operations2)); + + RefPtr<Animation> animation = Animation::create(); + animation->setDuration(duration); + + IntSize boxSize; + m_graphicsLayer->addAnimation(values, boxSize, animation.get(), "", 0); + + EXPECT_FALSE(m_platformLayer->layerAnimationController()->hasActiveAnimation()); +} + +TEST_F(GraphicsLayerChromiumTest, createReversedAnimation) +{ + const double duration = 1; + WebCore::KeyframeValueList values(AnimatedPropertyWebkitTransform); + + TransformOperations operations1; + operations1.operations().append(TranslateTransformOperation::create(Length(2, WebCore::Fixed), Length(0, WebCore::Fixed), TransformOperation::TRANSLATE_X)); + values.insert(new TransformAnimationValue(0, &operations1)); + + TransformOperations operations2; + operations2.operations().append(TranslateTransformOperation::create(Length(4, WebCore::Fixed), Length(0, WebCore::Fixed), TransformOperation::TRANSLATE_X)); + values.insert(new TransformAnimationValue(duration, &operations2)); + + RefPtr<Animation> animation = Animation::create(); + animation->setDuration(duration); + animation->setDirection(Animation::AnimationDirectionReverse); + + IntSize boxSize; + m_graphicsLayer->addAnimation(values, boxSize, animation.get(), "", 0); + + EXPECT_TRUE(m_platformLayer->layerAnimationController()->hasActiveAnimation()); + + CCActiveAnimation* activeAnimation = m_platformLayer->layerAnimationController()->getActiveAnimation(CCActiveAnimation::Transform); + EXPECT_TRUE(activeAnimation); + + EXPECT_EQ(1, activeAnimation->iterations()); + EXPECT_EQ(CCActiveAnimation::Transform, activeAnimation->targetProperty()); + + EXPECT_EQ(CCAnimationCurve::Transform, activeAnimation->curve()->type()); + + const CCTransformAnimationCurve* curve = activeAnimation->curve()->toTransformAnimationCurve(); + EXPECT_TRUE(curve); + + expectTranslateX(4, curve->getValue(0)); + expectTranslateX(2, curve->getValue(duration)); +} + +TEST_F(GraphicsLayerChromiumTest, createAlternatingAnimation) +{ + const double duration = 1; + WebCore::KeyframeValueList values(AnimatedPropertyWebkitTransform); + + TransformOperations operations1; + operations1.operations().append(TranslateTransformOperation::create(Length(2, WebCore::Fixed), Length(0, WebCore::Fixed), TransformOperation::TRANSLATE_X)); + values.insert(new TransformAnimationValue(0, &operations1)); + + TransformOperations operations2; + operations2.operations().append(TranslateTransformOperation::create(Length(4, WebCore::Fixed), Length(0, WebCore::Fixed), TransformOperation::TRANSLATE_X)); + values.insert(new TransformAnimationValue(duration, &operations2)); + + RefPtr<Animation> animation = Animation::create(); + animation->setDuration(duration); + animation->setDirection(Animation::AnimationDirectionAlternate); + animation->setIterationCount(2); + + IntSize boxSize; + m_graphicsLayer->addAnimation(values, boxSize, animation.get(), "", 0); + + EXPECT_TRUE(m_platformLayer->layerAnimationController()->hasActiveAnimation()); + + CCActiveAnimation* activeAnimation = m_platformLayer->layerAnimationController()->getActiveAnimation(CCActiveAnimation::Transform); + EXPECT_TRUE(activeAnimation); + EXPECT_TRUE(activeAnimation->alternatesDirection()); + + EXPECT_EQ(2, activeAnimation->iterations()); + EXPECT_EQ(CCActiveAnimation::Transform, activeAnimation->targetProperty()); + + EXPECT_EQ(CCAnimationCurve::Transform, activeAnimation->curve()->type()); + + const CCTransformAnimationCurve* curve = activeAnimation->curve()->toTransformAnimationCurve(); + EXPECT_TRUE(curve); + + expectTranslateX(2, curve->getValue(0)); + expectTranslateX(4, curve->getValue(duration)); +} + +TEST_F(GraphicsLayerChromiumTest, createReversedAlternatingAnimation) +{ + const double duration = 1; + WebCore::KeyframeValueList values(AnimatedPropertyWebkitTransform); + + TransformOperations operations1; + operations1.operations().append(TranslateTransformOperation::create(Length(2, WebCore::Fixed), Length(0, WebCore::Fixed), TransformOperation::TRANSLATE_X)); + values.insert(new TransformAnimationValue(0, &operations1)); + + TransformOperations operations2; + operations2.operations().append(TranslateTransformOperation::create(Length(4, WebCore::Fixed), Length(0, WebCore::Fixed), TransformOperation::TRANSLATE_X)); + values.insert(new TransformAnimationValue(duration, &operations2)); + + RefPtr<Animation> animation = Animation::create(); + animation->setDuration(duration); + animation->setDirection(Animation::AnimationDirectionAlternateReverse); + animation->setIterationCount(2); - ASSERT_TRUE(platformLayer->hasActiveAnimation()); + IntSize boxSize; + m_graphicsLayer->addAnimation(values, boxSize, animation.get(), "", 0); - graphicsLayer->setPreserves3D(true); + EXPECT_TRUE(m_platformLayer->layerAnimationController()->hasActiveAnimation()); - platformLayer = static_cast<LayerChromium*>(graphicsLayer->platformLayer()); - ASSERT_TRUE(platformLayer); + CCActiveAnimation* activeAnimation = m_platformLayer->layerAnimationController()->getActiveAnimation(CCActiveAnimation::Transform); + EXPECT_TRUE(activeAnimation); + EXPECT_TRUE(activeAnimation->alternatesDirection()); - ASSERT_TRUE(platformLayer->hasActiveAnimation()); - platformLayer->removeAnimation(0); - ASSERT_FALSE(platformLayer->hasActiveAnimation()); + EXPECT_EQ(2, activeAnimation->iterations()); + EXPECT_EQ(CCActiveAnimation::Transform, activeAnimation->targetProperty()); - graphicsLayer->setPreserves3D(false); + EXPECT_EQ(CCAnimationCurve::Transform, activeAnimation->curve()->type()); - platformLayer = static_cast<LayerChromium*>(graphicsLayer->platformLayer()); - ASSERT_TRUE(platformLayer); + const CCTransformAnimationCurve* curve = activeAnimation->curve()->toTransformAnimationCurve(); + EXPECT_TRUE(curve); - ASSERT_FALSE(platformLayer->hasActiveAnimation()); + expectTranslateX(4, curve->getValue(0)); + expectTranslateX(2, curve->getValue(duration)); } } // namespace diff --git a/Source/WebKit/chromium/tests/IDBDatabaseBackendTest.cpp b/Source/WebKit/chromium/tests/IDBDatabaseBackendTest.cpp new file mode 100644 index 000000000..098a7327f --- /dev/null +++ b/Source/WebKit/chromium/tests/IDBDatabaseBackendTest.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2012 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "IDBBackingStore.h" +#include "IDBDatabaseBackendImpl.h" +#include "IDBFactoryBackendImpl.h" +#include "IDBFakeBackingStore.h" +#include "IDBIndexBackendImpl.h" +#include "IDBObjectStoreBackendImpl.h" +#include "IDBTransactionCoordinator.h" + +#include <gtest/gtest.h> + +#if ENABLE(INDEXED_DATABASE) + +using namespace WebCore; + +namespace { + +TEST(IDBDatabaseBackendTest, BackingStoreRetention) +{ + RefPtr<IDBFakeBackingStore> backingStore = adoptRef(new IDBFakeBackingStore()); + EXPECT_TRUE(backingStore->hasOneRef()); + + IDBTransactionCoordinator* coordinator = 0; + IDBFactoryBackendImpl* factory = 0; + RefPtr<IDBDatabaseBackendImpl> db = IDBDatabaseBackendImpl::create("db", backingStore.get(), coordinator, factory, "uniqueid"); + EXPECT_GT(backingStore->refCount(), 1); + + const bool autoIncrement = false; + RefPtr<IDBObjectStoreBackendImpl> store = IDBObjectStoreBackendImpl::create(db.get(), "store", String("keyPath"), autoIncrement); + EXPECT_GT(backingStore->refCount(), 1); + + const bool unique = false; + const bool multiEntry = false; + RefPtr<IDBIndexBackendImpl> index = IDBIndexBackendImpl::create(db.get(), store.get(), "index", String("keyPath"), unique, multiEntry); + EXPECT_GT(backingStore->refCount(), 1); + + db.clear(); + EXPECT_TRUE(backingStore->hasOneRef()); + store.clear(); + EXPECT_TRUE(backingStore->hasOneRef()); + index.clear(); + EXPECT_TRUE(backingStore->hasOneRef()); +} + +} // namespace + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebKit/chromium/tests/IDBLevelDBCodingTest.cpp b/Source/WebKit/chromium/tests/IDBLevelDBCodingTest.cpp index 8f5b8ef01..9934e5c79 100644 --- a/Source/WebKit/chromium/tests/IDBLevelDBCodingTest.cpp +++ b/Source/WebKit/chromium/tests/IDBLevelDBCodingTest.cpp @@ -620,10 +620,17 @@ TEST(IDBLevelDBCodingTest, ComparisonTest) keys.append(DatabaseNameKey::encode("a", "a")); keys.append(DatabaseMetaDataKey::encode(1, DatabaseMetaDataKey::kOriginName)); keys.append(ObjectStoreMetaDataKey::encode(1, 1, 0)); + keys.append(ObjectStoreMetaDataKey::encode(1, 1, 1)); + keys.append(ObjectStoreMetaDataKey::encodeMaxKey(1, 1)); + keys.append(ObjectStoreMetaDataKey::encodeMaxKey(1, 2)); keys.append(ObjectStoreMetaDataKey::encodeMaxKey(1)); keys.append(IndexMetaDataKey::encode(1, 1, 30, 0)); keys.append(IndexMetaDataKey::encode(1, 1, 31, 0)); keys.append(IndexMetaDataKey::encode(1, 1, 31, 1)); + keys.append(IndexMetaDataKey::encodeMaxKey(1, 1, 31)); + keys.append(IndexMetaDataKey::encodeMaxKey(1, 1, 32)); + keys.append(IndexMetaDataKey::encodeMaxKey(1, 1)); + keys.append(IndexMetaDataKey::encodeMaxKey(1, 2)); keys.append(ObjectStoreFreeListKey::encode(1, 1)); keys.append(ObjectStoreFreeListKey::encodeMaxKey(1)); keys.append(IndexFreeListKey::encode(1, 1, kMinimumIndexId)); diff --git a/Source/WebKit/chromium/tests/LayerChromiumTest.cpp b/Source/WebKit/chromium/tests/LayerChromiumTest.cpp index 8213dd812..cd4fcc340 100644 --- a/Source/WebKit/chromium/tests/LayerChromiumTest.cpp +++ b/Source/WebKit/chromium/tests/LayerChromiumTest.cpp @@ -57,7 +57,7 @@ namespace { class MockCCLayerTreeHost : public CCLayerTreeHost { public: MockCCLayerTreeHost() - : CCLayerTreeHost(&m_fakeClient, CCSettings()) + : CCLayerTreeHost(&m_fakeClient, CCLayerTreeSettings()) { initialize(); } @@ -70,7 +70,7 @@ private: class MockLayerPainterChromium : public LayerPainterChromium { public: - virtual void paint(GraphicsContext&, const IntRect&) { } + virtual void paint(SkCanvas*, const IntRect&, IntRect&) { } }; @@ -80,11 +80,13 @@ protected: { // Initialize without threading support. WebKit::WebCompositor::initialize(0); + DebugScopedSetMainThread main; m_layerTreeHost = adoptPtr(new MockCCLayerTreeHost); } virtual void TearDown() { + DebugScopedSetMainThread main; Mock::VerifyAndClearExpectations(m_layerTreeHost.get()); EXPECT_CALL(*m_layerTreeHost, setNeedsCommit()).Times(AnyNumber()); m_parent.clear(); @@ -614,7 +616,7 @@ public: private: FakeCCLayerTreeHost() - : CCLayerTreeHost(&m_client, CCSettings()) + : CCLayerTreeHost(&m_client, CCLayerTreeSettings()) { } @@ -639,6 +641,7 @@ void assertLayerTreeHostMatchesForSubtree(LayerChromium* layer, CCLayerTreeHost* TEST(LayerChromiumLayerTreeHostTest, enteringTree) { WebKit::WebCompositor::initialize(0); + DebugScopedSetMainThread main; RefPtr<LayerChromium> parent = LayerChromium::create(); RefPtr<LayerChromium> child = LayerChromium::create(); RefPtr<LayerChromium> mask = LayerChromium::create(); @@ -671,6 +674,7 @@ TEST(LayerChromiumLayerTreeHostTest, enteringTree) TEST(LayerChromiumLayerTreeHostTest, addingLayerSubtree) { WebKit::WebCompositor::initialize(0); + DebugScopedSetMainThread main; RefPtr<LayerChromium> parent = LayerChromium::create(); OwnPtr<FakeCCLayerTreeHost> layerTreeHost(FakeCCLayerTreeHost::create()); @@ -702,6 +706,7 @@ TEST(LayerChromiumLayerTreeHostTest, addingLayerSubtree) TEST(LayerChromiumLayerTreeHostTest, changeHost) { WebKit::WebCompositor::initialize(0); + DebugScopedSetMainThread main; RefPtr<LayerChromium> parent = LayerChromium::create(); RefPtr<LayerChromium> child = LayerChromium::create(); RefPtr<LayerChromium> mask = LayerChromium::create(); @@ -735,6 +740,7 @@ TEST(LayerChromiumLayerTreeHostTest, changeHost) TEST(LayerChromiumLayerTreeHostTest, changeHostInSubtree) { WebKit::WebCompositor::initialize(0); + DebugScopedSetMainThread main; RefPtr<LayerChromium> firstParent = LayerChromium::create(); RefPtr<LayerChromium> firstChild = LayerChromium::create(); RefPtr<LayerChromium> secondParent = LayerChromium::create(); @@ -772,6 +778,7 @@ TEST(LayerChromiumLayerTreeHostTest, changeHostInSubtree) TEST(LayerChromiumLayerTreeHostTest, replaceMaskAndReplicaLayer) { WebKit::WebCompositor::initialize(0); + DebugScopedSetMainThread main; RefPtr<LayerChromium> parent = LayerChromium::create(); RefPtr<LayerChromium> mask = LayerChromium::create(); RefPtr<LayerChromium> replica = LayerChromium::create(); diff --git a/Source/WebKit/chromium/tests/LayerRendererChromiumTest.cpp b/Source/WebKit/chromium/tests/LayerRendererChromiumTest.cpp index 4ed35986c..9269217c3 100644 --- a/Source/WebKit/chromium/tests/LayerRendererChromiumTest.cpp +++ b/Source/WebKit/chromium/tests/LayerRendererChromiumTest.cpp @@ -25,9 +25,12 @@ #include "config.h" #include "LayerRendererChromium.h" +#include "CCTestCommon.h" #include "FakeWebGraphicsContext3D.h" #include "GraphicsContext3D.h" #include "GraphicsContext3DPrivate.h" +#include "WebCompositor.h" +#include "cc/CCSettings.h" #include "cc/CCSingleThreadProxy.h" #include <gmock/gmock.h> @@ -35,6 +38,7 @@ using namespace WebCore; using namespace WebKit; +using namespace WebKitTests; class FrameCountingMemoryAllocationSettingContext : public FakeWebGraphicsContext3D { public: @@ -54,44 +58,56 @@ public: // Methods added for test. int frameCount() { return m_frame; } - void setMemoryAllocation(WebGraphicsMemoryAllocation allocation) { m_memoryAllocationChangedCallback->onMemoryAllocationChanged(allocation); } + void setMemoryAllocation(WebGraphicsMemoryAllocation allocation) + { + ASSERT(CCProxy::isImplThread()); + // In single threaded mode we expect this callback on main thread. + DebugScopedSetMainThread main; + m_memoryAllocationChangedCallback->onMemoryAllocationChanged(allocation); + } private: int m_frame; WebGraphicsMemoryAllocationChangedCallbackCHROMIUM* m_memoryAllocationChangedCallback; }; -class FakeLayerRendererChromiumClient : public LayerRendererChromiumClient { +class FakeCCRendererClient : public CCRendererClient { public: - FakeLayerRendererChromiumClient() + FakeCCRendererClient() : m_setFullRootLayerDamageCount(0) , m_rootLayer(CCLayerImpl::create(1)) + , m_memoryAllocationLimitBytes(0) { m_rootLayer->createRenderSurface(); + m_rootRenderPass = CCRenderPass::create(m_rootLayer->renderSurface()); } - // LayerRendererChromiumClient methods. + // CCRendererClient methods. virtual const IntSize& deviceViewportSize() const OVERRIDE { static IntSize fakeSize; return fakeSize; } - virtual const CCSettings& settings() const OVERRIDE { static CCSettings fakeSettings; return fakeSettings; } + virtual const CCLayerTreeSettings& settings() const OVERRIDE { static CCLayerTreeSettings fakeSettings; return fakeSettings; } virtual void didLoseContext() OVERRIDE { } virtual void onSwapBuffersComplete() OVERRIDE { } virtual void setFullRootLayerDamage() OVERRIDE { m_setFullRootLayerDamageCount++; } - virtual void setContentsMemoryAllocationLimitBytes(size_t) OVERRIDE { } + virtual void setContentsMemoryAllocationLimitBytes(size_t bytes) OVERRIDE { m_memoryAllocationLimitBytes = bytes; } // Methods added for test. int setFullRootLayerDamageCount() const { return m_setFullRootLayerDamageCount; } - CCLayerImpl* rootLayer() { return m_rootLayer.get(); } + CCRenderPass* rootRenderPass() { return m_rootRenderPass.get(); } + + size_t memoryAllocationLimitBytes() const { return m_memoryAllocationLimitBytes; } private: int m_setFullRootLayerDamageCount; DebugScopedSetImplThread m_implThread; OwnPtr<CCLayerImpl> m_rootLayer; + OwnPtr<CCRenderPass> m_rootRenderPass; + size_t m_memoryAllocationLimitBytes; }; class FakeLayerRendererChromium : public LayerRendererChromium { public: - FakeLayerRendererChromium(LayerRendererChromiumClient* client, PassRefPtr<GraphicsContext3D> context) : LayerRendererChromium(client, context, UnthrottledUploader) { } + FakeLayerRendererChromium(CCRendererClient* client, PassRefPtr<GraphicsContext3D> context) : LayerRendererChromium(client, context, UnthrottledUploader) { } // LayerRendererChromium methods. @@ -113,9 +129,15 @@ protected: virtual void SetUp() { + WebKit::WebCompositor::initialize(0); m_layerRendererChromium.initialize(); } + virtual void TearDown() + { + WebKit::WebCompositor::shutdown(); + } + void swapBuffers() { m_layerRendererChromium.swapBuffers(IntRect()); @@ -126,8 +148,9 @@ protected: RefPtr<GraphicsContext3D> m_context; FrameCountingMemoryAllocationSettingContext& m_mockContext; - FakeLayerRendererChromiumClient m_mockClient; + FakeCCRendererClient m_mockClient; FakeLayerRendererChromium m_layerRendererChromium; + CCScopedSettings m_scopedSettings; }; // Test LayerRendererChromium discardFramebuffer functionality: @@ -179,7 +202,7 @@ TEST_F(LayerRendererChromiumTest, SwapBuffersWhileBackbufferDiscardedShouldIgnor swapBuffers(); EXPECT_EQ(0, m_mockContext.frameCount()); EXPECT_EQ(2, m_mockClient.setFullRootLayerDamageCount()); - + swapBuffers(); EXPECT_EQ(0, m_mockContext.frameCount()); EXPECT_EQ(3, m_mockClient.setFullRootLayerDamageCount()); @@ -194,7 +217,7 @@ TEST_F(LayerRendererChromiumTest, DiscardedBackbufferIsRecreatredForScopeDuratio EXPECT_TRUE(m_layerRendererChromium.isFramebufferDiscarded()); EXPECT_EQ(1, m_mockClient.setFullRootLayerDamageCount()); - m_layerRendererChromium.beginDrawingFrame(m_mockClient.rootLayer()->renderSurface()); + m_layerRendererChromium.beginDrawingFrame(m_mockClient.rootRenderPass()); EXPECT_FALSE(m_layerRendererChromium.isFramebufferDiscarded()); swapBuffers(); @@ -270,7 +293,8 @@ public: // This test isn't using the same fixture as LayerRendererChromiumTest, and you can't mix TEST() and TEST_F() with the same name, hence LRC2. TEST(LayerRendererChromiumTest2, initializationDoesNotMakeSynchronousCalls) { - FakeLayerRendererChromiumClient mockClient; + CCScopedSettings scopedSettings; + FakeCCRendererClient mockClient; FakeLayerRendererChromium layerRendererChromium(&mockClient, GraphicsContext3DPrivate::createGraphicsContextFromWebContext(adoptPtr(new ForbidSynchronousCallContext), GraphicsContext3D::RenderDirectlyToHostWindow)); EXPECT_TRUE(layerRendererChromium.initialize()); @@ -311,8 +335,86 @@ private: TEST(LayerRendererChromiumTest2, initializationWithQuicklyLostContextDoesNotAssert) { - FakeLayerRendererChromiumClient mockClient; + CCScopedSettings scopedSettings; + FakeCCRendererClient mockClient; FakeLayerRendererChromium layerRendererChromium(&mockClient, GraphicsContext3DPrivate::createGraphicsContextFromWebContext(adoptPtr(new LoseContextOnFirstGetContext), GraphicsContext3D::RenderDirectlyToHostWindow)); layerRendererChromium.initialize(); } + +class ContextThatDoesNotSupportMemoryManagmentExtensions : public FakeWebGraphicsContext3D { +public: + ContextThatDoesNotSupportMemoryManagmentExtensions() { } + + // WebGraphicsContext3D methods. + + // This method would normally do a glSwapBuffers under the hood. + virtual void prepareTexture() { } + virtual void setMemoryAllocationChangedCallbackCHROMIUM(WebGraphicsMemoryAllocationChangedCallbackCHROMIUM* callback) { } + virtual WebString getString(WebKit::WGC3Denum name) { return WebString(); } +}; + +TEST(LayerRendererChromiumTest2, initializationWithoutGpuMemoryManagerExtensionSupportShouldDefaultToNonZeroAllocation) +{ + FakeCCRendererClient mockClient; + FakeLayerRendererChromium layerRendererChromium(&mockClient, GraphicsContext3DPrivate::createGraphicsContextFromWebContext(adoptPtr(new ContextThatDoesNotSupportMemoryManagmentExtensions), GraphicsContext3D::RenderDirectlyToHostWindow)); + + layerRendererChromium.initialize(); + + EXPECT_GT(mockClient.memoryAllocationLimitBytes(), 0ul); +} + +class ClearCountingContext : public FakeWebGraphicsContext3D { +public: + ClearCountingContext() : m_clear(0) { } + + virtual void clear(WGC3Dbitfield) + { + m_clear++; + } + + int clearCount() const { return m_clear; } + +private: + int m_clear; +}; + +TEST(LayerRendererChromiumTest2, opaqueBackground) +{ + FakeCCRendererClient mockClient; + RefPtr<GraphicsContext3D> context = GraphicsContext3DPrivate::createGraphicsContextFromWebContext(adoptPtr(new ClearCountingContext), GraphicsContext3D::RenderDirectlyToHostWindow); + FakeLayerRendererChromium layerRendererChromium(&mockClient, context); + + mockClient.rootRenderPass()->setHasTransparentBackground(false); + + EXPECT_TRUE(layerRendererChromium.initialize()); + + layerRendererChromium.beginDrawingFrame(mockClient.rootRenderPass()); + layerRendererChromium.drawRenderPass(mockClient.rootRenderPass(), FloatRect()); + layerRendererChromium.finishDrawingFrame(); + + // On DEBUG builds, render passes with opaque background clear to blue to + // easily see regions that were not drawn on the screen. +#if defined(NDEBUG) + EXPECT_EQ(0, static_cast<ClearCountingContext*>(GraphicsContext3DPrivate::extractWebGraphicsContext3D(context.get()))->clearCount()); +#else + EXPECT_EQ(1, static_cast<ClearCountingContext*>(GraphicsContext3DPrivate::extractWebGraphicsContext3D(context.get()))->clearCount()); +#endif +} + +TEST(LayerRendererChromiumTest2, transparentBackground) +{ + FakeCCRendererClient mockClient; + RefPtr<GraphicsContext3D> context = GraphicsContext3DPrivate::createGraphicsContextFromWebContext(adoptPtr(new ClearCountingContext), GraphicsContext3D::RenderDirectlyToHostWindow); + FakeLayerRendererChromium layerRendererChromium(&mockClient, context); + + mockClient.rootRenderPass()->setHasTransparentBackground(true); + + EXPECT_TRUE(layerRendererChromium.initialize()); + + layerRendererChromium.beginDrawingFrame(mockClient.rootRenderPass()); + layerRendererChromium.drawRenderPass(mockClient.rootRenderPass(), FloatRect()); + layerRendererChromium.finishDrawingFrame(); + + EXPECT_EQ(1, static_cast<ClearCountingContext*>(GraphicsContext3DPrivate::extractWebGraphicsContext3D(context.get()))->clearCount()); +} diff --git a/Source/WebKit/chromium/tests/LayerTextureUpdaterTest.cpp b/Source/WebKit/chromium/tests/LayerTextureUpdaterTest.cpp deleted file mode 100644 index d1a97bb5b..000000000 --- a/Source/WebKit/chromium/tests/LayerTextureUpdaterTest.cpp +++ /dev/null @@ -1,267 +0,0 @@ -/* - * Copyright (C) 2012 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" - -#include "LayerTextureUpdater.h" - -#include "BitmapCanvasLayerTextureUpdater.h" -#include "BitmapSkPictureCanvasLayerTextureUpdater.h" -#include "FrameBufferSkPictureCanvasLayerTextureUpdater.h" -#include "GraphicsContext.h" -#include "LayerPainterChromium.h" -#include "PlatformContextSkia.h" -#include "SkPictureCanvasLayerTextureUpdater.h" - -#include <gmock/gmock.h> -#include <gtest/gtest.h> - -using namespace WebCore; - -namespace { - -struct PaintCallback { - virtual void operator()(GraphicsContext&, const IntRect&) = 0; -}; - -class TestLayerPainterChromium : public LayerPainterChromium { -public: - TestLayerPainterChromium(PaintCallback& callback) : m_callback(callback) { } - - virtual void paint(GraphicsContext& context, const IntRect& contentRect) - { - m_callback(context, contentRect); - } - - private: - PaintCallback& m_callback; -}; - -// Paint callback functions - -struct PaintFillOpaque : public PaintCallback { - virtual void operator()(GraphicsContext& context, const IntRect& contentRect) - { - Color opaque(255, 0, 0, 255); - IntRect top(contentRect.x(), contentRect.y(), contentRect.width(), contentRect.height() / 2); - IntRect bottom(contentRect.x(), contentRect.y() + contentRect.height() / 2, contentRect.width(), contentRect.height() / 2); - context.fillRect(top, opaque, ColorSpaceDeviceRGB); - context.fillRect(bottom, opaque, ColorSpaceDeviceRGB); - } -}; - -struct PaintFillAlpha : public PaintCallback { - virtual void operator()(GraphicsContext& context, const IntRect& contentRect) - { - Color alpha(0, 0, 0, 0); - context.fillRect(contentRect, alpha, ColorSpaceDeviceRGB); - } -}; - -struct PaintFillPartialOpaque : public PaintCallback { - PaintFillPartialOpaque(IntRect opaqueRect) - : m_opaqueRect(opaqueRect) - { - } - - virtual void operator()(GraphicsContext& context, const IntRect& contentRect) - { - Color alpha(0, 0, 0, 0); - context.fillRect(contentRect, alpha, ColorSpaceDeviceRGB); - - IntRect fillOpaque = m_opaqueRect; - fillOpaque.intersect(contentRect); - - Color opaque(255, 255, 255, 255); - context.fillRect(fillOpaque, opaque, ColorSpaceDeviceRGB); - } - - IntRect m_opaqueRect; -}; - -#define EXPECT_EQ_RECT(a, b) \ - EXPECT_EQ(a.x(), b.x()); \ - EXPECT_EQ(a.maxX(), b.maxX()); \ - EXPECT_EQ(a.y(), b.y()); \ - EXPECT_EQ(a.maxY(), b.maxY()); - -TEST(LayerTextureUpdaterTest, testOpaqueRectPresentAfterOpaquePaint) -{ - PaintFillOpaque fillOpaque; - RefPtr<LayerTextureUpdater> updater; - IntRect opaqueRect; - OwnPtr<TestLayerPainterChromium> painter; - - opaqueRect = IntRect(); - painter = adoptPtr(new TestLayerPainterChromium(fillOpaque)); - updater = BitmapCanvasLayerTextureUpdater::create(painter.release(), false); - updater->prepareToUpdate(IntRect(0, 0, 400, 400), IntSize(400, 400), 0, 1, opaqueRect); - EXPECT_EQ_RECT(IntRect(0, 0, 400, 400), opaqueRect); - - opaqueRect = IntRect(); - painter = adoptPtr(new TestLayerPainterChromium(fillOpaque)); - updater = BitmapSkPictureCanvasLayerTextureUpdater::create(painter.release(), false); - updater->prepareToUpdate(IntRect(0, 0, 400, 400), IntSize(400, 400), 0, 1, opaqueRect); - EXPECT_EQ_RECT(IntRect(0, 0, 400, 400), opaqueRect); - - opaqueRect = IntRect(); - painter = adoptPtr(new TestLayerPainterChromium(fillOpaque)); - updater = FrameBufferSkPictureCanvasLayerTextureUpdater::create(painter.release()); - updater->prepareToUpdate(IntRect(0, 0, 400, 400), IntSize(400, 400), 0, 1, opaqueRect); - EXPECT_EQ_RECT(IntRect(0, 0, 400, 400), opaqueRect); -} - -TEST(LayerTextureUpdaterTest, testOpaqueRectNotPresentAfterNonOpaquePaint) -{ - PaintFillAlpha fillAlpha; - RefPtr<LayerTextureUpdater> updater; - IntRect opaqueRect; - OwnPtr<TestLayerPainterChromium> painter; - - opaqueRect = IntRect(); - painter = adoptPtr(new TestLayerPainterChromium(fillAlpha)); - updater = BitmapCanvasLayerTextureUpdater::create(painter.release(), false); - updater->prepareToUpdate(IntRect(0, 0, 400, 400), IntSize(400, 400), 0, 1, opaqueRect); - EXPECT_EQ_RECT(IntRect(0, 0, 0, 0), opaqueRect); - - opaqueRect = IntRect(); - painter = adoptPtr(new TestLayerPainterChromium(fillAlpha)); - updater = BitmapSkPictureCanvasLayerTextureUpdater::create(painter.release(), false); - updater->prepareToUpdate(IntRect(0, 0, 400, 400), IntSize(400, 400), 0, 1, opaqueRect); - EXPECT_EQ_RECT(IntRect(0, 0, 0, 0), opaqueRect); - - opaqueRect = IntRect(); - painter = adoptPtr(new TestLayerPainterChromium(fillAlpha)); - updater = FrameBufferSkPictureCanvasLayerTextureUpdater::create(painter.release()); - updater->prepareToUpdate(IntRect(0, 0, 400, 400), IntSize(400, 400), 0, 1, opaqueRect); - EXPECT_EQ_RECT(IntRect(0, 0, 0, 0), opaqueRect); -} - -TEST(LayerTextureUpdaterTest, testOpaqueRectNotPresentForOpaqueLayerWithOpaquePaint) -{ - PaintFillOpaque fillOpaque; - RefPtr<LayerTextureUpdater> updater; - IntRect opaqueRect; - OwnPtr<TestLayerPainterChromium> painter; - - opaqueRect = IntRect(); - painter = adoptPtr(new TestLayerPainterChromium(fillOpaque)); - updater = BitmapCanvasLayerTextureUpdater::create(painter.release(), false); - updater->setOpaque(true); - updater->prepareToUpdate(IntRect(0, 0, 400, 400), IntSize(400, 400), 0, 1, opaqueRect); - EXPECT_EQ_RECT(IntRect(0, 0, 0, 0), opaqueRect); - - opaqueRect = IntRect(); - painter = adoptPtr(new TestLayerPainterChromium(fillOpaque)); - updater = BitmapSkPictureCanvasLayerTextureUpdater::create(painter.release(), false); - updater->setOpaque(true); - updater->prepareToUpdate(IntRect(0, 0, 400, 400), IntSize(400, 400), 0, 1, opaqueRect); - EXPECT_EQ_RECT(IntRect(0, 0, 0, 0), opaqueRect); - - opaqueRect = IntRect(); - painter = adoptPtr(new TestLayerPainterChromium(fillOpaque)); - updater = FrameBufferSkPictureCanvasLayerTextureUpdater::create(painter.release()); - updater->setOpaque(true); - updater->prepareToUpdate(IntRect(0, 0, 400, 400), IntSize(400, 400), 0, 1, opaqueRect); - EXPECT_EQ_RECT(IntRect(0, 0, 0, 0), opaqueRect); -} - -TEST(LayerTextureUpdaterTest, testOpaqueRectNotPresentForOpaqueLayerWithNonOpaquePaint) -{ - PaintFillAlpha fillAlpha; - RefPtr<LayerTextureUpdater> updater; - IntRect opaqueRect; - OwnPtr<TestLayerPainterChromium> painter; - - opaqueRect = IntRect(); - painter = adoptPtr(new TestLayerPainterChromium(fillAlpha)); - updater = BitmapCanvasLayerTextureUpdater::create(painter.release(), false); - updater->setOpaque(true); - updater->prepareToUpdate(IntRect(0, 0, 400, 400), IntSize(400, 400), 0, 1, opaqueRect); - EXPECT_EQ_RECT(IntRect(0, 0, 0, 0), opaqueRect); - - opaqueRect = IntRect(); - painter = adoptPtr(new TestLayerPainterChromium(fillAlpha)); - updater = BitmapSkPictureCanvasLayerTextureUpdater::create(painter.release(), false); - updater->setOpaque(true); - updater->prepareToUpdate(IntRect(0, 0, 400, 400), IntSize(400, 400), 0, 1, opaqueRect); - EXPECT_EQ_RECT(IntRect(0, 0, 0, 0), opaqueRect); - - opaqueRect = IntRect(); - painter = adoptPtr(new TestLayerPainterChromium(fillAlpha)); - updater = FrameBufferSkPictureCanvasLayerTextureUpdater::create(painter.release()); - updater->setOpaque(true); - updater->prepareToUpdate(IntRect(0, 0, 400, 400), IntSize(400, 400), 0, 1, opaqueRect); - EXPECT_EQ_RECT(IntRect(0, 0, 0, 0), opaqueRect); -} - -TEST(LayerTextureUpdaterTest, testPartialOpaqueRectNoTransform) -{ - IntRect partialRect(100, 200, 50, 75); - PaintFillPartialOpaque fillPartial(partialRect); - OwnPtr<TestLayerPainterChromium> painter(adoptPtr(new TestLayerPainterChromium(fillPartial))); - RefPtr<LayerTextureUpdater> updater = BitmapCanvasLayerTextureUpdater::create(painter.release(), false); - - IntRect opaqueRect; - updater->prepareToUpdate(IntRect(0, 0, 400, 400), IntSize(400, 400), 0, 1, opaqueRect); - EXPECT_EQ_RECT(partialRect, opaqueRect); -} - -TEST(LayerTextureUpdaterTest, testPartialOpaqueRectTranslation) -{ - IntRect partialRect(100, 200, 50, 75); - PaintFillPartialOpaque fillPartial(partialRect); - - OwnPtr<TestLayerPainterChromium> painter(adoptPtr(new TestLayerPainterChromium(fillPartial))); - RefPtr<LayerTextureUpdater> updater = BitmapCanvasLayerTextureUpdater::create(painter.release(), false); - - IntRect opaqueRect; - IntRect contentRect(11, 12, 389, 388); - updater->prepareToUpdate(contentRect, IntSize(400, 400), 0, 1, opaqueRect); - EXPECT_EQ_RECT(partialRect, opaqueRect); -} - -TEST(LayerTextureUpdaterTest, testPartialOpaqueRectScale) -{ - float contentsScale = 0.5; - - IntRect partialRect(9, 20, 50, 75); - IntRect partialDeviceRect(partialRect); - PaintFillPartialOpaque fillPartial(partialDeviceRect); - OwnPtr<TestLayerPainterChromium> painter(adoptPtr(new TestLayerPainterChromium(fillPartial))); - RefPtr<LayerTextureUpdater> updater = BitmapCanvasLayerTextureUpdater::create(painter.release(), false); - - IntRect opaqueRect; - IntRect contentRect(4, 6, 396, 394); - updater->prepareToUpdate(contentRect, IntSize(400, 400), 0, contentsScale, opaqueRect); - - // Original rect: 9, 20, 50, 75 - // Scaled down to half size: 4.5, 10, 25, 37.5 - // Enclosed int rect: 5, 10, 24, 37 - // Scaled back up to content: 10, 20, 48, 74 - IntRect scaledRect(10, 20, 48, 74); - EXPECT_EQ_RECT(scaledRect, opaqueRect); -} - -} // namespace diff --git a/Source/WebKit/chromium/tests/OpaqueRectTrackingContentLayerDelegateTest.cpp b/Source/WebKit/chromium/tests/OpaqueRectTrackingContentLayerDelegateTest.cpp new file mode 100644 index 000000000..dd60a97b7 --- /dev/null +++ b/Source/WebKit/chromium/tests/OpaqueRectTrackingContentLayerDelegateTest.cpp @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2012 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include "OpaqueRectTrackingContentLayerDelegate.h" + +#include "Color.h" +#include "GraphicsContext.h" +#include "IntRect.h" +#include "skia/ext/platform_canvas.h" +#include <public/WebRect.h> + +#include <gtest/gtest.h> + +using WebKit::WebRect; +using namespace WebCore; + +namespace { + +struct PaintCallback { + virtual void operator()(GraphicsContext&, const IntRect&) = 0; +}; + +class TestLayerPainterChromium : public GraphicsContextPainter { +public: + TestLayerPainterChromium(PaintCallback& callback) : m_callback(callback) { } + + virtual void paint(GraphicsContext& context, const IntRect& contentRect) OVERRIDE + { + m_callback(context, contentRect); + } + + private: + PaintCallback& m_callback; +}; + +// Paint callback functions + +struct PaintFillOpaque : public PaintCallback { + virtual void operator()(GraphicsContext& context, const IntRect& contentRect) OVERRIDE + { + Color opaque(255, 0, 0, 255); + IntRect top(contentRect.x(), contentRect.y(), contentRect.width(), contentRect.height() / 2); + IntRect bottom(contentRect.x(), contentRect.y() + contentRect.height() / 2, contentRect.width(), contentRect.height() / 2); + context.fillRect(top, opaque, ColorSpaceDeviceRGB); + context.fillRect(bottom, opaque, ColorSpaceDeviceRGB); + } +}; + +struct PaintFillAlpha : public PaintCallback { + virtual void operator()(GraphicsContext& context, const IntRect& contentRect) + { + Color alpha(0, 0, 0, 0); + context.fillRect(contentRect, alpha, ColorSpaceDeviceRGB); + } +}; + +struct PaintFillPartialOpaque : public PaintCallback { + PaintFillPartialOpaque(IntRect opaqueRect) + : m_opaqueRect(opaqueRect) + { + } + + virtual void operator()(GraphicsContext& context, const IntRect& contentRect) + { + Color alpha(0, 0, 0, 0); + context.fillRect(contentRect, alpha, ColorSpaceDeviceRGB); + + IntRect fillOpaque = m_opaqueRect; + fillOpaque.intersect(contentRect); + + Color opaque(255, 255, 255, 255); + context.fillRect(fillOpaque, opaque, ColorSpaceDeviceRGB); + } + + IntRect m_opaqueRect; +}; + +#define EXPECT_EQ_RECT(a, b) \ + EXPECT_EQ(a.x, b.x); \ + EXPECT_EQ(a.width, b.width); \ + EXPECT_EQ(a.y, b.y); \ + EXPECT_EQ(a.height, b.height); + +class OpaqueRectTrackingContentLayerDelegateTest : public testing::Test { +public: + OpaqueRectTrackingContentLayerDelegateTest() + : m_skCanvas(adoptPtr(skia::CreateBitmapCanvas(canvasRect().width, canvasRect().height, false))) + { + } + + SkCanvas* skCanvas() { return m_skCanvas.get(); } + WebRect canvasRect() { return WebRect(0, 0, 400, 400); } + +private: + OwnPtr<SkCanvas> m_skCanvas; +}; + +TEST_F(OpaqueRectTrackingContentLayerDelegateTest, testOpaqueRectPresentAfterOpaquePaint) +{ + PaintFillOpaque fillOpaque; + TestLayerPainterChromium painter(fillOpaque); + + OpaqueRectTrackingContentLayerDelegate delegate(&painter); + + WebRect opaqueRect; + delegate.paintContents(skCanvas(), canvasRect(), opaqueRect); + EXPECT_EQ_RECT(WebRect(0, 0, 400, 400), opaqueRect); +} + +TEST_F(OpaqueRectTrackingContentLayerDelegateTest, testOpaqueRectNotPresentAfterNonOpaquePaint) +{ + PaintFillAlpha fillAlpha; + TestLayerPainterChromium painter(fillAlpha); + OpaqueRectTrackingContentLayerDelegate delegate(&painter); + + WebRect opaqueRect; + delegate.paintContents(skCanvas(), canvasRect(), opaqueRect); + EXPECT_EQ_RECT(WebRect(0, 0, 0, 0), opaqueRect); +} + +TEST_F(OpaqueRectTrackingContentLayerDelegateTest, testOpaqueRectNotPresentForOpaqueLayerWithOpaquePaint) +{ + PaintFillOpaque fillOpaque; + TestLayerPainterChromium painter(fillOpaque); + OpaqueRectTrackingContentLayerDelegate delegate(&painter); + + delegate.setOpaque(true); + + WebRect opaqueRect; + delegate.paintContents(skCanvas(), canvasRect(), opaqueRect); + EXPECT_EQ_RECT(WebRect(0, 0, 0, 0), opaqueRect); +} + +TEST_F(OpaqueRectTrackingContentLayerDelegateTest, testOpaqueRectNotPresentForOpaqueLayerWithNonOpaquePaint) +{ + PaintFillOpaque fillAlpha; + TestLayerPainterChromium painter(fillAlpha); + OpaqueRectTrackingContentLayerDelegate delegate(&painter); + + delegate.setOpaque(true); + + WebRect opaqueRect; + delegate.paintContents(skCanvas(), canvasRect(), opaqueRect); + EXPECT_EQ_RECT(WebRect(0, 0, 0, 0), opaqueRect); +} + +TEST_F(OpaqueRectTrackingContentLayerDelegateTest, testPartialOpaqueRectNoTransform) +{ + IntRect partialRect(100, 200, 50, 75); + PaintFillPartialOpaque fillPartial(partialRect); + TestLayerPainterChromium painter(fillPartial); + OpaqueRectTrackingContentLayerDelegate delegate(&painter); + + WebRect opaqueRect; + delegate.paintContents(skCanvas(), canvasRect(), opaqueRect); + EXPECT_EQ_RECT(WebRect(partialRect.x(), partialRect.y(), partialRect.width(), partialRect.height()), opaqueRect); +} + +TEST_F(OpaqueRectTrackingContentLayerDelegateTest, testPartialOpaqueRectTranslation) +{ + IntRect partialRect(100, 200, 50, 75); + PaintFillPartialOpaque fillPartial(partialRect); + TestLayerPainterChromium painter(fillPartial); + OpaqueRectTrackingContentLayerDelegate delegate(&painter); + + WebRect opaqueRect; + WebRect contentRect(11, 12, 389, 388); + delegate.paintContents(skCanvas(), contentRect, opaqueRect); + EXPECT_EQ_RECT(WebRect(partialRect.x(), partialRect.y(), partialRect.width(), partialRect.height()), opaqueRect); +} + +} // namespace diff --git a/Source/WebKit/chromium/tests/ScrollAnimatorNoneTest.cpp b/Source/WebKit/chromium/tests/ScrollAnimatorNoneTest.cpp index 0827267ed..6c6c359a9 100644 --- a/Source/WebKit/chromium/tests/ScrollAnimatorNoneTest.cpp +++ b/Source/WebKit/chromium/tests/ScrollAnimatorNoneTest.cpp @@ -110,7 +110,7 @@ TEST(ScrollAnimatorEnabled, Enabled) MockScrollAnimatorNone scrollAnimatorNone(&scrollableArea); EXPECT_CALL(scrollableArea, scrollSize(_)).Times(AtLeast(1)).WillRepeatedly(Return(1000)); - EXPECT_CALL(scrollableArea, setScrollOffset(_)).Times(3); + EXPECT_CALL(scrollableArea, setScrollOffset(_)).Times(4); scrollAnimatorNone.scroll(HorizontalScrollbar, ScrollByLine, 100, 1); EXPECT_NE(100, scrollAnimatorNone.currentX()); @@ -125,6 +125,12 @@ TEST(ScrollAnimatorEnabled, Enabled) scrollAnimatorNone.reset(); scrollAnimatorNone.scroll(HorizontalScrollbar, ScrollByPixel, 4, 25); + EXPECT_NE(100, scrollAnimatorNone.currentX()); + EXPECT_NE(0, scrollAnimatorNone.currentX()); + EXPECT_EQ(0, scrollAnimatorNone.currentY()); + scrollAnimatorNone.reset(); + + scrollAnimatorNone.scroll(HorizontalScrollbar, ScrollByPrecisePixel, 4, 25); EXPECT_EQ(100, scrollAnimatorNone.currentX()); EXPECT_NE(0, scrollAnimatorNone.currentX()); EXPECT_EQ(0, scrollAnimatorNone.currentY()); diff --git a/Source/WebKit/chromium/tests/ScrollbarLayerChromiumTest.cpp b/Source/WebKit/chromium/tests/ScrollbarLayerChromiumTest.cpp index 5bf990cde..d2cd9befc 100644 --- a/Source/WebKit/chromium/tests/ScrollbarLayerChromiumTest.cpp +++ b/Source/WebKit/chromium/tests/ScrollbarLayerChromiumTest.cpp @@ -100,7 +100,7 @@ TEST(ScrollbarLayerChromiumTest, resolveScrollLayerPointer) layerTreeRoot->addChild(child1); layerTreeRoot->addChild(child2); - OwnPtr<CCLayerImpl> ccLayerTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), nullptr); + OwnPtr<CCLayerImpl> ccLayerTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), nullptr, 0); CCLayerImpl* ccChild1 = ccLayerTreeRoot->children()[0].get(); CCScrollbarLayerImpl* ccChild2 = static_cast<CCScrollbarLayerImpl*>(ccLayerTreeRoot->children()[1].get()); @@ -116,7 +116,7 @@ TEST(ScrollbarLayerChromiumTest, resolveScrollLayerPointer) layerTreeRoot->addChild(child1); layerTreeRoot->addChild(child2); - OwnPtr<CCLayerImpl> ccLayerTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), nullptr); + OwnPtr<CCLayerImpl> ccLayerTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), nullptr, 0); CCScrollbarLayerImpl* ccChild1 = static_cast<CCScrollbarLayerImpl*>(ccLayerTreeRoot->children()[0].get()); CCLayerImpl* ccChild2 = ccLayerTreeRoot->children()[1].get(); diff --git a/Source/WebKit/chromium/tests/TextureCopierTest.cpp b/Source/WebKit/chromium/tests/TextureCopierTest.cpp index 17173c0a5..d71b8ce45 100644 --- a/Source/WebKit/chromium/tests/TextureCopierTest.cpp +++ b/Source/WebKit/chromium/tests/TextureCopierTest.cpp @@ -54,6 +54,7 @@ TEST(TextureCopierTest, testDrawArraysCopy) GraphicsContext3D::Attributes attrs; RefPtr<GraphicsContext3D> context = GraphicsContext3DPrivate::createGraphicsContextFromWebContext(adoptPtr(new MockContext()), GraphicsContext3D::RenderDirectlyToHostWindow); MockContext& mockContext = *static_cast<MockContext*>(GraphicsContext3DPrivate::extractWebGraphicsContext3D(context.get())); + RefPtr<CCGraphicsContext> ccContext = CCGraphicsContext::create3D(context); { InSequence sequence; @@ -79,7 +80,7 @@ TEST(TextureCopierTest, testDrawArraysCopy) int destTextureId = 2; IntSize size(256, 128); OwnPtr<AcceleratedTextureCopier> copier(AcceleratedTextureCopier::create(context)); - copier->copyTexture(context.get(), sourceTextureId, destTextureId, size); + copier->copyTexture(ccContext.get(), sourceTextureId, destTextureId, size); } } // namespace diff --git a/Source/WebKit/chromium/tests/TextureLayerChromiumTest.cpp b/Source/WebKit/chromium/tests/TextureLayerChromiumTest.cpp index b60017fd9..d0cc3d96f 100644 --- a/Source/WebKit/chromium/tests/TextureLayerChromiumTest.cpp +++ b/Source/WebKit/chromium/tests/TextureLayerChromiumTest.cpp @@ -43,7 +43,7 @@ namespace { class MockCCLayerTreeHost : public CCLayerTreeHost { public: MockCCLayerTreeHost() - : CCLayerTreeHost(&m_fakeClient, CCSettings()) + : CCLayerTreeHost(&m_fakeClient, CCLayerTreeSettings()) { initialize(); } diff --git a/Source/WebKit/chromium/tests/TiledLayerChromiumTest.cpp b/Source/WebKit/chromium/tests/TiledLayerChromiumTest.cpp index 80b099f19..5e301d8eb 100644 --- a/Source/WebKit/chromium/tests/TiledLayerChromiumTest.cpp +++ b/Source/WebKit/chromium/tests/TiledLayerChromiumTest.cpp @@ -673,12 +673,13 @@ TEST(TiledLayerChromiumTest, invalidateFromPrepare) FakeTextureCopier fakeCopier; FakeTextureUploader fakeUploader; RefPtr<GraphicsContext3D> context = createCompositorMockGraphicsContext3D(GraphicsContext3D::Attributes()); + RefPtr<CCGraphicsContext> ccContext = CCGraphicsContext::create3D(context); // The tile size is 100x100, so this invalidates and then paints two tiles. layer->setBounds(IntSize(100, 200)); layer->invalidateRect(IntRect(0, 0, 100, 200)); layer->updateLayerRect(updater, IntRect(0, 0, 100, 200), 0); - updater.update(context.get(), &fakeAllocator, &fakeCopier, &fakeUploader, 1000); + updater.update(ccContext.get(), &fakeAllocator, &fakeCopier, &fakeUploader, 1000); layer->pushPropertiesTo(layerImpl.get()); // We should have both tiles on the impl side. @@ -691,7 +692,7 @@ TEST(TiledLayerChromiumTest, invalidateFromPrepare) // Invoke updateLayerRect again. As the layer is valid updateLayerRect shouldn't be invoked on // the LayerTextureUpdater. layer->updateLayerRect(updater, IntRect(0, 0, 100, 200), 0); - updater.update(context.get(), &fakeAllocator, &fakeCopier, &fakeUploader, 1000); + updater.update(ccContext.get(), &fakeAllocator, &fakeCopier, &fakeUploader, 1000); EXPECT_EQ(0, layer->fakeLayerTextureUpdater()->prepareCount()); layer->invalidateRect(IntRect(0, 0, 50, 50)); @@ -699,12 +700,12 @@ TEST(TiledLayerChromiumTest, invalidateFromPrepare) layer->fakeLayerTextureUpdater()->setRectToInvalidate(IntRect(25, 25, 50, 50), layer.get()); layer->fakeLayerTextureUpdater()->clearPrepareCount(); layer->updateLayerRect(updater, IntRect(0, 0, 100, 200), 0); - updater.update(context.get(), &fakeAllocator, &fakeCopier, &fakeUploader, 1000); + updater.update(ccContext.get(), &fakeAllocator, &fakeCopier, &fakeUploader, 1000); EXPECT_EQ(1, layer->fakeLayerTextureUpdater()->prepareCount()); layer->fakeLayerTextureUpdater()->clearPrepareCount(); // The layer should still be invalid as updateLayerRect invoked invalidate. layer->updateLayerRect(updater, IntRect(0, 0, 100, 200), 0); - updater.update(context.get(), &fakeAllocator, &fakeCopier, &fakeUploader, 1000); + updater.update(ccContext.get(), &fakeAllocator, &fakeCopier, &fakeUploader, 1000); EXPECT_EQ(1, layer->fakeLayerTextureUpdater()->prepareCount()); } @@ -796,7 +797,7 @@ TEST(TiledLayerChromiumTest, skipsDrawGetsReset) // Initialize without threading support. WebKit::WebCompositor::initialize(0); FakeCCLayerTreeHostClient fakeCCLayerTreeHostClient; - OwnPtr<CCLayerTreeHost> ccLayerTreeHost = CCLayerTreeHost::create(&fakeCCLayerTreeHostClient, CCSettings()); + OwnPtr<CCLayerTreeHost> ccLayerTreeHost = CCLayerTreeHost::create(&fakeCCLayerTreeHostClient, CCLayerTreeSettings()); // Create two 300 x 300 tiled layers. IntSize contentBounds(300, 300); @@ -872,10 +873,12 @@ TEST(TiledLayerChromiumTest, hugeLayerUpdateCrash) TEST(TiledLayerChromiumTest, partialUpdates) { - CCSettings settings; - settings.maxPartialTextureUpdates = 4; // Initialize without threading support. WebKit::WebCompositor::initialize(0); + + CCLayerTreeSettings settings; + settings.maxPartialTextureUpdates = 4; + FakeCCLayerTreeHostClient fakeCCLayerTreeHostClient; OwnPtr<CCLayerTreeHost> ccLayerTreeHost = CCLayerTreeHost::create(&fakeCCLayerTreeHostClient, settings); diff --git a/Source/WebKit/chromium/tests/TreeSynchronizerTest.cpp b/Source/WebKit/chromium/tests/TreeSynchronizerTest.cpp index 098dd2731..08597f1dc 100644 --- a/Source/WebKit/chromium/tests/TreeSynchronizerTest.cpp +++ b/Source/WebKit/chromium/tests/TreeSynchronizerTest.cpp @@ -120,22 +120,23 @@ private: bool m_synchronizedAnimations; }; -void expectTreesAreIdentical(LayerChromium* layer, CCLayerImpl* ccLayer) +void expectTreesAreIdentical(LayerChromium* layer, CCLayerImpl* ccLayer, CCLayerTreeHostImpl* hostImpl) { ASSERT_TRUE(layer); ASSERT_TRUE(ccLayer); EXPECT_EQ(layer->id(), ccLayer->id()); + EXPECT_EQ(ccLayer->layerTreeHostImpl(), hostImpl); EXPECT_EQ(layer->nonFastScrollableRegion(), ccLayer->nonFastScrollableRegion()); ASSERT_EQ(!!layer->maskLayer(), !!ccLayer->maskLayer()); if (layer->maskLayer()) - expectTreesAreIdentical(layer->maskLayer(), ccLayer->maskLayer()); + expectTreesAreIdentical(layer->maskLayer(), ccLayer->maskLayer(), hostImpl); ASSERT_EQ(!!layer->replicaLayer(), !!ccLayer->replicaLayer()); if (layer->replicaLayer()) - expectTreesAreIdentical(layer->replicaLayer(), ccLayer->replicaLayer()); + expectTreesAreIdentical(layer->replicaLayer(), ccLayer->replicaLayer(), hostImpl); const Vector<RefPtr<LayerChromium> >& layerChildren = layer->children(); const Vector<OwnPtr<CCLayerImpl> >& ccLayerChildren = ccLayer->children(); @@ -143,7 +144,7 @@ void expectTreesAreIdentical(LayerChromium* layer, CCLayerImpl* ccLayer) ASSERT_EQ(layerChildren.size(), ccLayerChildren.size()); for (size_t i = 0; i < layerChildren.size(); ++i) - expectTreesAreIdentical(layerChildren[i].get(), ccLayerChildren[i].get()); + expectTreesAreIdentical(layerChildren[i].get(), ccLayerChildren[i].get(), hostImpl); } // Attempts to synchronizes a null tree. This should not crash, and should @@ -152,7 +153,7 @@ TEST(TreeSynchronizerTest, syncNullTree) { DebugScopedSetImplThread impl; - OwnPtr<CCLayerImpl> ccLayerTreeRoot = TreeSynchronizer::synchronizeTrees(0, nullptr); + OwnPtr<CCLayerImpl> ccLayerTreeRoot = TreeSynchronizer::synchronizeTrees(0, nullptr, 0); EXPECT_TRUE(!ccLayerTreeRoot.get()); } @@ -161,13 +162,17 @@ TEST(TreeSynchronizerTest, syncNullTree) TEST(TreeSynchronizerTest, syncSimpleTreeFromEmpty) { DebugScopedSetImplThread impl; + + CCLayerTreeSettings settings; + OwnPtr<CCLayerTreeHostImpl> hostImpl = CCLayerTreeHostImpl::create(settings, 0); + RefPtr<LayerChromium> layerTreeRoot = LayerChromium::create(); layerTreeRoot->addChild(LayerChromium::create()); layerTreeRoot->addChild(LayerChromium::create()); - OwnPtr<CCLayerImpl> ccLayerTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), nullptr); + OwnPtr<CCLayerImpl> ccLayerTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), nullptr, hostImpl.get()); - expectTreesAreIdentical(layerTreeRoot.get(), ccLayerTreeRoot.get()); + expectTreesAreIdentical(layerTreeRoot.get(), ccLayerTreeRoot.get(), hostImpl.get()); } // Constructs a very simple tree and synchronizes it attempting to reuse some layers @@ -176,12 +181,15 @@ TEST(TreeSynchronizerTest, syncSimpleTreeReusingLayers) DebugScopedSetImplThread impl; Vector<int> ccLayerDestructionList; + CCLayerTreeSettings settings; + OwnPtr<CCLayerTreeHostImpl> hostImpl = CCLayerTreeHostImpl::create(settings, 0); + RefPtr<LayerChromium> layerTreeRoot = MockLayerChromium::create(&ccLayerDestructionList); layerTreeRoot->addChild(MockLayerChromium::create(&ccLayerDestructionList)); layerTreeRoot->addChild(MockLayerChromium::create(&ccLayerDestructionList)); - OwnPtr<CCLayerImpl> ccLayerTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), nullptr); - expectTreesAreIdentical(layerTreeRoot.get(), ccLayerTreeRoot.get()); + OwnPtr<CCLayerImpl> ccLayerTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), nullptr, hostImpl.get()); + expectTreesAreIdentical(layerTreeRoot.get(), ccLayerTreeRoot.get(), hostImpl.get()); // Add a new layer to the LayerChromium side layerTreeRoot->children()[0]->addChild(MockLayerChromium::create(&ccLayerDestructionList)); @@ -190,8 +198,8 @@ TEST(TreeSynchronizerTest, syncSimpleTreeReusingLayers) int secondCCLayerId = ccLayerTreeRoot->children()[1]->id(); // Synchronize again. After the sync the trees should be equivalent and we should have created and destroyed one CCLayerImpl. - ccLayerTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), ccLayerTreeRoot.release()); - expectTreesAreIdentical(layerTreeRoot.get(), ccLayerTreeRoot.get()); + ccLayerTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), ccLayerTreeRoot.release(), hostImpl.get()); + expectTreesAreIdentical(layerTreeRoot.get(), ccLayerTreeRoot.get(), hostImpl.get()); ASSERT_EQ(1u, ccLayerDestructionList.size()); EXPECT_EQ(secondCCLayerId, ccLayerDestructionList[0]); @@ -203,21 +211,24 @@ TEST(TreeSynchronizerTest, syncSimpleTreeAndTrackStackingOrderChange) DebugScopedSetImplThread impl; Vector<int> ccLayerDestructionList; + CCLayerTreeSettings settings; + OwnPtr<CCLayerTreeHostImpl> hostImpl = CCLayerTreeHostImpl::create(settings, 0); + // Set up the tree and sync once. child2 needs to be synced here, too, even though we // remove it to set up the intended scenario. RefPtr<LayerChromium> layerTreeRoot = MockLayerChromium::create(&ccLayerDestructionList); RefPtr<LayerChromium> child2 = MockLayerChromium::create(&ccLayerDestructionList); layerTreeRoot->addChild(MockLayerChromium::create(&ccLayerDestructionList)); layerTreeRoot->addChild(child2); - OwnPtr<CCLayerImpl> ccLayerTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), nullptr); - expectTreesAreIdentical(layerTreeRoot.get(), ccLayerTreeRoot.get()); + OwnPtr<CCLayerImpl> ccLayerTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), nullptr, hostImpl.get()); + expectTreesAreIdentical(layerTreeRoot.get(), ccLayerTreeRoot.get(), hostImpl.get()); ccLayerTreeRoot->resetAllChangeTrackingForSubtree(); // re-insert the layer and sync again. child2->removeFromParent(); layerTreeRoot->addChild(child2); - ccLayerTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), ccLayerTreeRoot.release()); - expectTreesAreIdentical(layerTreeRoot.get(), ccLayerTreeRoot.get()); + ccLayerTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), ccLayerTreeRoot.release(), hostImpl.get()); + expectTreesAreIdentical(layerTreeRoot.get(), ccLayerTreeRoot.get(), hostImpl.get()); // Check that the impl thread properly tracked the change. EXPECT_FALSE(ccLayerTreeRoot->layerPropertyChanged()); @@ -228,6 +239,10 @@ TEST(TreeSynchronizerTest, syncSimpleTreeAndTrackStackingOrderChange) TEST(TreeSynchronizerTest, syncSimpleTreeAndProperties) { DebugScopedSetImplThread impl; + + CCLayerTreeSettings settings; + OwnPtr<CCLayerTreeHostImpl> hostImpl = CCLayerTreeHostImpl::create(settings, 0); + RefPtr<LayerChromium> layerTreeRoot = LayerChromium::create(); layerTreeRoot->addChild(LayerChromium::create()); layerTreeRoot->addChild(LayerChromium::create()); @@ -242,8 +257,8 @@ TEST(TreeSynchronizerTest, syncSimpleTreeAndProperties) IntSize secondChildBounds = IntSize(25, 53); layerTreeRoot->children()[1]->setBounds(secondChildBounds); - OwnPtr<CCLayerImpl> ccLayerTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), nullptr); - expectTreesAreIdentical(layerTreeRoot.get(), ccLayerTreeRoot.get()); + OwnPtr<CCLayerImpl> ccLayerTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), nullptr, hostImpl.get()); + expectTreesAreIdentical(layerTreeRoot.get(), ccLayerTreeRoot.get(), hostImpl.get()); // Check that the property values we set on the LayerChromium tree are reflected in the CCLayerImpl tree. FloatPoint rootCCLayerPosition = ccLayerTreeRoot->position(); @@ -262,6 +277,9 @@ TEST(TreeSynchronizerTest, reuseCCLayersAfterStructuralChange) DebugScopedSetImplThread impl; Vector<int> ccLayerDestructionList; + CCLayerTreeSettings settings; + OwnPtr<CCLayerTreeHostImpl> hostImpl = CCLayerTreeHostImpl::create(settings, 0); + // Set up a tree with this sort of structure: // root --- A --- B ---+--- C // | @@ -279,8 +297,8 @@ TEST(TreeSynchronizerTest, reuseCCLayersAfterStructuralChange) layerB->addChild(MockLayerChromium::create(&ccLayerDestructionList)); RefPtr<LayerChromium> layerD = layerB->children()[1].get(); - OwnPtr<CCLayerImpl> ccLayerTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), nullptr); - expectTreesAreIdentical(layerTreeRoot.get(), ccLayerTreeRoot.get()); + OwnPtr<CCLayerImpl> ccLayerTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), nullptr, hostImpl.get()); + expectTreesAreIdentical(layerTreeRoot.get(), ccLayerTreeRoot.get(), hostImpl.get()); // Now restructure the tree to look like this: // root --- D ---+--- A @@ -297,8 +315,8 @@ TEST(TreeSynchronizerTest, reuseCCLayersAfterStructuralChange) layerC->addChild(layerB); // After another synchronize our trees should match and we should not have destroyed any CCLayerImpls - ccLayerTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), ccLayerTreeRoot.release()); - expectTreesAreIdentical(layerTreeRoot.get(), ccLayerTreeRoot.get()); + ccLayerTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), ccLayerTreeRoot.release(), hostImpl.get()); + expectTreesAreIdentical(layerTreeRoot.get(), ccLayerTreeRoot.get(), hostImpl.get()); EXPECT_EQ(0u, ccLayerDestructionList.size()); } @@ -309,6 +327,9 @@ TEST(TreeSynchronizerTest, syncSimpleTreeThenDestroy) DebugScopedSetImplThread impl; Vector<int> ccLayerDestructionList; + CCLayerTreeSettings settings; + OwnPtr<CCLayerTreeHostImpl> hostImpl = CCLayerTreeHostImpl::create(settings, 0); + RefPtr<LayerChromium> oldLayerTreeRoot = MockLayerChromium::create(&ccLayerDestructionList); oldLayerTreeRoot->addChild(MockLayerChromium::create(&ccLayerDestructionList)); oldLayerTreeRoot->addChild(MockLayerChromium::create(&ccLayerDestructionList)); @@ -317,16 +338,16 @@ TEST(TreeSynchronizerTest, syncSimpleTreeThenDestroy) int oldTreeFirstChildLayerId = oldLayerTreeRoot->children()[0]->id(); int oldTreeSecondChildLayerId = oldLayerTreeRoot->children()[1]->id(); - OwnPtr<CCLayerImpl> ccLayerTreeRoot = TreeSynchronizer::synchronizeTrees(oldLayerTreeRoot.get(), nullptr); - expectTreesAreIdentical(oldLayerTreeRoot.get(), ccLayerTreeRoot.get()); + OwnPtr<CCLayerImpl> ccLayerTreeRoot = TreeSynchronizer::synchronizeTrees(oldLayerTreeRoot.get(), nullptr, hostImpl.get()); + expectTreesAreIdentical(oldLayerTreeRoot.get(), ccLayerTreeRoot.get(), hostImpl.get()); // Remove all children on the LayerChromium side. oldLayerTreeRoot->removeAllChildren(); // Synchronize again. After the sync all CCLayerImpls from the old tree should be deleted. RefPtr<LayerChromium> newLayerTreeRoot = LayerChromium::create(); - ccLayerTreeRoot = TreeSynchronizer::synchronizeTrees(newLayerTreeRoot.get(), ccLayerTreeRoot.release()); - expectTreesAreIdentical(newLayerTreeRoot.get(), ccLayerTreeRoot.get()); + ccLayerTreeRoot = TreeSynchronizer::synchronizeTrees(newLayerTreeRoot.get(), ccLayerTreeRoot.release(), hostImpl.get()); + expectTreesAreIdentical(newLayerTreeRoot.get(), ccLayerTreeRoot.get(), hostImpl.get()); ASSERT_EQ(3u, ccLayerDestructionList.size()); EXPECT_TRUE(ccLayerDestructionList.contains(oldTreeRootLayerId)); @@ -338,6 +359,10 @@ TEST(TreeSynchronizerTest, syncSimpleTreeThenDestroy) TEST(TreeSynchronizerTest, syncMaskReplicaAndReplicaMaskLayers) { DebugScopedSetImplThread impl; + + CCLayerTreeSettings settings; + OwnPtr<CCLayerTreeHostImpl> hostImpl = CCLayerTreeHostImpl::create(settings, 0); + RefPtr<LayerChromium> layerTreeRoot = LayerChromium::create(); layerTreeRoot->addChild(LayerChromium::create()); layerTreeRoot->addChild(LayerChromium::create()); @@ -357,29 +382,33 @@ TEST(TreeSynchronizerTest, syncMaskReplicaAndReplicaMaskLayers) replicaLayerWithMask->setMaskLayer(replicaMaskLayer.get()); layerTreeRoot->children()[2]->setReplicaLayer(replicaLayerWithMask.get()); - OwnPtr<CCLayerImpl> ccLayerTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), nullptr); + OwnPtr<CCLayerImpl> ccLayerTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), nullptr, hostImpl.get()); - expectTreesAreIdentical(layerTreeRoot.get(), ccLayerTreeRoot.get()); + expectTreesAreIdentical(layerTreeRoot.get(), ccLayerTreeRoot.get(), hostImpl.get()); // Remove the mask layer. layerTreeRoot->children()[0]->setMaskLayer(0); - ccLayerTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), ccLayerTreeRoot.release()); - expectTreesAreIdentical(layerTreeRoot.get(), ccLayerTreeRoot.get()); + ccLayerTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), ccLayerTreeRoot.release(), hostImpl.get()); + expectTreesAreIdentical(layerTreeRoot.get(), ccLayerTreeRoot.get(), hostImpl.get()); // Remove the replica layer. layerTreeRoot->children()[1]->setReplicaLayer(0); - ccLayerTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), ccLayerTreeRoot.release()); - expectTreesAreIdentical(layerTreeRoot.get(), ccLayerTreeRoot.get()); + ccLayerTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), ccLayerTreeRoot.release(), hostImpl.get()); + expectTreesAreIdentical(layerTreeRoot.get(), ccLayerTreeRoot.get(), hostImpl.get()); // Remove the replica mask. replicaLayerWithMask->setMaskLayer(0); - ccLayerTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), ccLayerTreeRoot.release()); - expectTreesAreIdentical(layerTreeRoot.get(), ccLayerTreeRoot.get()); + ccLayerTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), ccLayerTreeRoot.release(), hostImpl.get()); + expectTreesAreIdentical(layerTreeRoot.get(), ccLayerTreeRoot.get(), hostImpl.get()); } TEST(TreeSynchronizerTest, synchronizeAnimations) { DebugScopedSetImplThread impl; + + CCLayerTreeSettings settings; + OwnPtr<CCLayerTreeHostImpl> hostImpl = CCLayerTreeHostImpl::create(settings, 0); + RefPtr<LayerChromium> layerTreeRoot = LayerChromium::create(); FakeLayerAnimationControllerClient dummy; @@ -387,8 +416,8 @@ TEST(TreeSynchronizerTest, synchronizeAnimations) EXPECT_FALSE(static_cast<FakeLayerAnimationController*>(layerTreeRoot->layerAnimationController())->synchronizedAnimations()); - OwnPtr<CCLayerImpl> ccLayerTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), nullptr); - ccLayerTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), ccLayerTreeRoot.release()); + OwnPtr<CCLayerImpl> ccLayerTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), nullptr, hostImpl.get()); + ccLayerTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), ccLayerTreeRoot.release(), hostImpl.get()); EXPECT_TRUE(static_cast<FakeLayerAnimationController*>(layerTreeRoot->layerAnimationController())->synchronizedAnimations()); } diff --git a/Source/WebKit/chromium/tests/WebCompositorInputHandlerImplTest.cpp b/Source/WebKit/chromium/tests/WebCompositorInputHandlerImplTest.cpp index 92f006184..d5b68a867 100644 --- a/Source/WebKit/chromium/tests/WebCompositorInputHandlerImplTest.cpp +++ b/Source/WebKit/chromium/tests/WebCompositorInputHandlerImplTest.cpp @@ -202,14 +202,14 @@ TEST_F(WebCompositorInputHandlerImplTest, gestureScrollStarted) m_inputHandler->handleInputEvent(gesture); } -TEST_F(WebCompositorInputHandlerImplTest, gestureScrollFailed) +TEST_F(WebCompositorInputHandlerImplTest, gestureScrollOnMainThread) { // We should send all events to the widget for this gesture. m_expectedDisposition = DidNotHandle; VERIFY_AND_RESET_MOCKS(); EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(::testing::_, ::testing::_)) - .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollFailed)); + .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollOnMainThread)); gesture.type = WebInputEvent::GestureScrollBegin; m_inputHandler->handleInputEvent(gesture); @@ -306,7 +306,7 @@ TEST_F(WebCompositorInputHandlerImplTest, gestureFlingFailed) VERIFY_AND_RESET_MOCKS(); EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollFailed)); + .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollOnMainThread)); gesture.type = WebInputEvent::GestureFlingStart; m_inputHandler->handleInputEvent(gesture); @@ -387,7 +387,7 @@ TEST_F(WebCompositorInputHandlerImplTest, gestureFlingAnimates) // transferred to the main thread. EXPECT_CALL(m_mockCCInputHandlerClient, scheduleAnimation()); EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollFailed)); + .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollOnMainThread)); EXPECT_CALL(m_mockCCInputHandlerClient, scrollBy(testing::_)).Times(0); EXPECT_CALL(m_mockCCInputHandlerClient, scrollEnd()).Times(0); @@ -469,7 +469,7 @@ TEST_F(WebCompositorInputHandlerImplTest, gestureFlingTransferResets) // transferred to the main thread. EXPECT_CALL(m_mockCCInputHandlerClient, scheduleAnimation()); EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollFailed)); + .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollOnMainThread)); EXPECT_CALL(m_mockCCInputHandlerClient, scrollBy(testing::_)).Times(0); EXPECT_CALL(m_mockCCInputHandlerClient, scrollEnd()).Times(0); @@ -546,7 +546,7 @@ TEST_F(WebCompositorInputHandlerImplTest, gestureFlingTransferResets) // Then abort the second fling. EXPECT_CALL(m_mockCCInputHandlerClient, scheduleAnimation()); EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollFailed)); + .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollOnMainThread)); EXPECT_CALL(m_mockCCInputHandlerClient, scrollBy(testing::_)).Times(0); EXPECT_CALL(m_mockCCInputHandlerClient, scrollEnd()).Times(0); diff --git a/Source/WebKit/chromium/tests/WebFrameTest.cpp b/Source/WebKit/chromium/tests/WebFrameTest.cpp index 4aa905c78..40705808d 100644 --- a/Source/WebKit/chromium/tests/WebFrameTest.cpp +++ b/Source/WebKit/chromium/tests/WebFrameTest.cpp @@ -36,6 +36,7 @@ #include "FrameTestHelpers.h" #include "FrameView.h" #include "ResourceError.h" +#include "WebDataSource.h" #include "WebDocument.h" #include "WebFindOptions.h" #include "WebFormElement.h" @@ -225,13 +226,12 @@ TEST_F(WebFrameTest, DeviceScaleFactorUsesDefaultWithoutViewportTag) int viewportHeight = 480; FixedLayoutTestWebViewClient client; - client.m_screenInfo.horizontalDPI = 160; + client.m_screenInfo.horizontalDPI = 320; client.m_windowRect = WebRect(0, 0, viewportWidth, viewportHeight); WebView* webView = static_cast<WebView*>(FrameTestHelpers::createWebViewAndLoad(m_baseURL + "no_viewport_tag.html", true, 0, &client)); webView->settings()->setViewportEnabled(true); - webView->settings()->setDefaultDeviceScaleFactor(2); webView->enableFixedLayoutMode(true); webView->resize(WebSize(viewportWidth, viewportHeight)); webView->layout(); @@ -397,6 +397,28 @@ TEST_F(WebFrameTest, ReloadDoesntSetRedirect) webkit_support::ServeAsynchronousMockedRequests(); } +TEST_F(WebFrameTest, IframeRedirect) +{ + registerMockedHttpURLLoad("iframe_redirect.html"); + registerMockedHttpURLLoad("visible_iframe.html"); + + WebView* webView = FrameTestHelpers::createWebViewAndLoad(m_baseURL + "iframe_redirect.html", true); + webkit_support::RunAllPendingMessages(); // Queue the iframe. + webkit_support::ServeAsynchronousMockedRequests(); // Load the iframe. + + WebFrame* iframe = webView->findFrameByName(WebString::fromUTF8("ifr")); + ASSERT_TRUE(iframe); + WebDataSource* iframeDataSource = iframe->dataSource(); + ASSERT_TRUE(iframeDataSource); + WebVector<WebURL> redirects; + iframeDataSource->redirectChain(redirects); + ASSERT_EQ(2U, redirects.size()); + EXPECT_EQ(GURL("about:blank"), GURL(redirects[0])); + EXPECT_EQ(GURL("http://www.test.com/visible_iframe.html"), GURL(redirects[1])); + + webView->close(); +} + TEST_F(WebFrameTest, ClearFocusedNodeTest) { registerMockedHttpURLLoad("iframe_clear_focused_node_test.html"); diff --git a/Source/WebKit/chromium/tests/WebLayerTest.cpp b/Source/WebKit/chromium/tests/WebLayerTest.cpp index 8fc7c93bd..bc4929311 100644 --- a/Source/WebKit/chromium/tests/WebLayerTest.cpp +++ b/Source/WebKit/chromium/tests/WebLayerTest.cpp @@ -65,7 +65,7 @@ public: class MockWebContentLayerClient : public WebContentLayerClient { public: - MOCK_METHOD2(paintContents, void(WebCanvas*, const WebRect& clip)); + MOCK_METHOD3(paintContents, void(WebCanvas*, const WebRect& clip, WebRect& opaque)); }; class WebLayerTest : public Test { @@ -175,7 +175,7 @@ TEST_F(WebLayerTest, Client) // Content layer. MockWebContentLayerClient contentClient; - EXPECT_CALL(contentClient, paintContents(_, _)).Times(AnyNumber()); + EXPECT_CALL(contentClient, paintContents(_, _, _)).Times(AnyNumber()); EXPECT_CALL(m_client, scheduleComposite()).Times(AnyNumber()); WebContentLayer contentLayer = WebContentLayer::create(&contentClient); m_rootLayer.addChild(contentLayer); @@ -209,7 +209,7 @@ TEST_F(WebLayerTest, Hierarchy) EXPECT_TRUE(layer2.parent().isNull()); MockWebContentLayerClient contentClient; - EXPECT_CALL(contentClient, paintContents(_, _)).Times(AnyNumber()); + EXPECT_CALL(contentClient, paintContents(_, _, _)).Times(AnyNumber()); WebContentLayer contentLayer = WebContentLayer::create(&contentClient); WebExternalTextureLayer textureLayer = WebExternalTextureLayer::create(); diff --git a/Source/WebKit/chromium/tests/data/iframe_redirect.html b/Source/WebKit/chromium/tests/data/iframe_redirect.html new file mode 100644 index 000000000..8176253ea --- /dev/null +++ b/Source/WebKit/chromium/tests/data/iframe_redirect.html @@ -0,0 +1,10 @@ +<html> + <body> + <iframe id='ifr'> + </iframe> + <script> + var ifr = document.getElementById('ifr'); + ifr.contentWindow.document.location = "visible_iframe.html"; + </script> + </body> +</html> |