Index: third_party/WebKit/Source/web/tests/FrameThrottlingTest.cpp |
diff --git a/third_party/WebKit/Source/web/tests/FrameThrottlingTest.cpp b/third_party/WebKit/Source/web/tests/FrameThrottlingTest.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..824d8bfef4287d1c8b970b81679f657982391bf8 |
--- /dev/null |
+++ b/third_party/WebKit/Source/web/tests/FrameThrottlingTest.cpp |
@@ -0,0 +1,225 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "config.h" |
+ |
+#include "core/dom/Document.h" |
+#include "core/frame/FrameView.h" |
+#include "public/web/WebElement.h" |
+#include "web/WebLocalFrameImpl.h" |
+#include "web/tests/FrameTestHelpers.h" |
+#include "web/tests/sim/SimCompositor.h" |
+#include "web/tests/sim/SimDisplayItemList.h" |
+#include "web/tests/sim/SimLayerTreeView.h" |
+#include "web/tests/sim/SimWebViewClient.h" |
+#include <gtest/gtest.h> |
+ |
+namespace blink { |
+ |
+class FrameThrottlingTest : public ::testing::Test { |
+protected: |
+ using SecurityOriginStatus = FrameView::SecurityOriginStatus; |
+ using ViewportVisibility = FrameView::ViewportVisibility; |
+ |
+ FrameThrottlingTest() |
+ : m_webViewClient(m_layerTreeView) |
+ , m_compositor(m_layerTreeView) |
+ { |
+ m_webViewHelper.initialize(true, &m_webFrameClient, &m_webViewClient); |
+ m_compositor.setWebViewImpl(webView()); |
+ webView().resize(WebSize(640, 480)); |
+ } |
+ |
+ ~FrameThrottlingTest() override { } |
+ |
+ void setInnerHTML(const String& html) |
+ { |
+ document().documentElement()->setInnerHTML(html, ASSERT_NO_EXCEPTION); |
+ m_webFrameClient.waitForLoadToComplete(); |
+ webView().layout(); |
+ } |
+ |
+ void setupPageWithThrottleableFrame() |
+ { |
+ setInnerHTML("<iframe sandbox=\"\"></iframe>"); |
+ } |
+ |
+ Document& document() |
+ { |
+ return *webView().mainFrameImpl()->frame()->document(); |
+ } |
+ |
+ WebNode root() |
+ { |
+ return WebNode(document().documentElement()); |
+ } |
+ |
+ WebViewImpl& webView() |
+ { |
+ return *m_webViewHelper.webViewImpl(); |
+ } |
+ |
+ ViewportVisibility viewportVisibility(FrameView* frameView) |
+ { |
+ return frameView->m_viewportVisibility; |
+ } |
+ |
+ SimLayerTreeView m_layerTreeView; |
+ SimWebViewClient m_webViewClient; |
+ SimCompositor m_compositor; |
+ FrameTestHelpers::TestWebFrameClient m_webFrameClient; |
+ FrameTestHelpers::WebViewHelper m_webViewHelper; |
+}; |
+ |
+TEST_F(FrameThrottlingTest, ThrottleInvisibleFrames) |
+{ |
+ setupPageWithThrottleableFrame(); |
+ |
+ // Initially both frames are visible. |
+ FrameView* frameView = document().view(); |
+ FrameView* childFrameView = toLocalFrame(frameView->frame().tree().firstChild())->view(); |
+ EXPECT_EQ(ViewportVisibility::Visible, viewportVisibility(frameView)); |
+ EXPECT_EQ(ViewportVisibility::Visible, viewportVisibility(childFrameView)); |
+ |
+ // Moving the child fully outside the parent makes it invisible. |
+ childFrameView->move(0, frameView->height()); |
+ frameView->updateAllLifecyclePhases(); |
+ frameView->finalizeLifecycleUpdate(); |
+ EXPECT_EQ(ViewportVisibility::Visible, viewportVisibility(frameView)); |
+ EXPECT_EQ(ViewportVisibility::Hidden, viewportVisibility(childFrameView)); |
+ |
+ // A partially visible child is considered visible. |
+ childFrameView->move(-childFrameView->width() / 2, 0); |
+ frameView->updateAllLifecyclePhases(); |
+ frameView->finalizeLifecycleUpdate(); |
esprehn
2015/10/01 08:21:37
m_compositor.beginFrame()
Sami
2015/10/05 17:58:24
Done.
|
+ EXPECT_EQ(ViewportVisibility::Visible, viewportVisibility(frameView)); |
+ EXPECT_EQ(ViewportVisibility::Visible, viewportVisibility(childFrameView)); |
+} |
+ |
+TEST_F(FrameThrottlingTest, viewportVisibilityFullyClipped) |
+{ |
+ setupPageWithThrottleableFrame(); |
+ |
+ // A child which is fully clipped away by its ancestor should become invisible. |
+ FrameView* frameView = document().view(); |
+ FrameView* childFrameView = toLocalFrame(frameView->frame().tree().firstChild())->view(); |
+ webView().resize(WebSize(0, 0)); |
+ webView().layout(); |
esprehn
2015/10/01 08:21:37
ditto, layout is only triggered from frames, so yo
Sami
2015/10/05 17:58:24
Done.
|
+ frameView->updateAllLifecyclePhases(); |
+ frameView->finalizeLifecycleUpdate(); |
esprehn
2015/10/01 08:21:36
and then you don't need this
Sami
2015/10/05 17:58:24
Done.
|
+ EXPECT_EQ(ViewportVisibility::Visible, viewportVisibility(frameView)); |
+ EXPECT_EQ(ViewportVisibility::Hidden, viewportVisibility(childFrameView)); |
+} |
+ |
+TEST_F(FrameThrottlingTest, hiddenCrossOriginFramesAreThrottled) |
+{ |
+ // Create a document with doubly nested iframes. |
+ setInnerHTML("<iframe srcdoc=\"<iframe></iframe>\"></iframe>"); |
+ |
+ FrameView* frameView = document().view(); |
+ LocalFrame* childFrame = toLocalFrame(frameView->frame().tree().firstChild()); |
+ LocalFrame* grandChildFrame = toLocalFrame(childFrame->tree().firstChild()); |
+ FrameView* childFrameView = childFrame->view(); |
+ FrameView* grandChildFrameView = grandChildFrame->view(); |
+ EXPECT_FALSE(frameView->shouldThrottleRenderingPipeline()); |
+ EXPECT_FALSE(childFrameView->shouldThrottleRenderingPipeline()); |
+ EXPECT_FALSE(grandChildFrameView->shouldThrottleRenderingPipeline()); |
+ |
+ // Just being hidden doesn't make a frame throttled. |
+ grandChildFrameView->move(0, frameView->height()); |
esprehn
2015/10/01 08:21:36
this can't happen in a real page, set some style t
Sami
2015/10/05 17:58:24
Done.
|
+ frameView->finalizeLifecycleUpdate(); |
+ EXPECT_FALSE(frameView->shouldThrottleRenderingPipeline()); |
+ EXPECT_FALSE(childFrameView->shouldThrottleRenderingPipeline()); |
+ EXPECT_FALSE(grandChildFrameView->shouldThrottleRenderingPipeline()); |
+ |
+ // Making the middle frame cross origin enables throttling for the grand child. |
+ childFrame->securityContext()->setSecurityOrigin(SecurityOrigin::createUnique()); |
+ frameView->finalizeLifecycleUpdate(); |
esprehn
2015/10/01 08:21:36
why would this happen again in a real page?
Sami
2015/10/05 17:58:24
You're right, it wouldn't. To make this more reali
|
+ EXPECT_FALSE(frameView->shouldThrottleRenderingPipeline()); |
+ EXPECT_FALSE(childFrameView->shouldThrottleRenderingPipeline()); |
+ EXPECT_TRUE(grandChildFrameView->shouldThrottleRenderingPipeline()); |
+} |
+ |
+TEST_F(FrameThrottlingTest, throttledLifecycleUpdate) |
+{ |
+ setupPageWithThrottleableFrame(); |
+ |
+ FrameView* frameView = document().view(); |
+ LocalFrame* childFrame = toLocalFrame(frameView->frame().tree().firstChild()); |
+ FrameView* childFrameView = childFrame->view(); |
+ |
+ // Enable throttling for the child frame. |
+ childFrameView->move(0, frameView->height()); |
esprehn
2015/10/01 08:21:36
move with style, real pages can't go through ->mov
Sami
2015/10/05 17:58:25
Done.
|
+ frameView->updateAllLifecyclePhases(); |
+ frameView->finalizeLifecycleUpdate(); |
esprehn
2015/10/01 08:21:36
ditto, beginFrame()
Sami
2015/10/05 17:58:24
Done.
|
+ EXPECT_TRUE(childFrameView->shouldThrottleRenderingPipeline()); |
+ |
+ // Doing a lifecycle update while allowing throttling shouldn't clear the |
+ // needs layout bit on the child frame. |
+ childFrameView->setNeedsLayout(); |
esprehn
2015/10/01 08:21:37
don't artificially cause layout, do something real
Sami
2015/10/05 17:58:24
Done.
|
+ EXPECT_TRUE(childFrameView->needsLayout()); |
+ frameView->updateAllLifecyclePhases(DocumentLifecycle::ThrottlingMode::Allow); |
esprehn
2015/10/01 08:21:37
this can't really happen
Sami
2015/10/05 17:58:24
Replaced with another beginFrame().
|
+ EXPECT_TRUE(childFrameView->needsLayout()); |
+ |
+ frameView->updateAllLifecyclePhases(DocumentLifecycle::ThrottlingMode::Disallow); |
+ EXPECT_FALSE(childFrameView->needsLayout()); |
+} |
+ |
+TEST_F(FrameThrottlingTest, unthrottlingFrameSchedulesAnimation) |
+{ |
+ setupPageWithThrottleableFrame(); |
+ FrameView* frameView = document().view(); |
+ LocalFrame* childFrame = toLocalFrame(frameView->frame().tree().firstChild()); |
+ FrameView* childFrameView = childFrame->view(); |
+ |
+ // First make the child hidden to enable throttling. |
+ childFrameView->move(0, frameView->height()); |
esprehn
2015/10/01 08:21:37
ditto
Sami
2015/10/05 17:58:24
Done.
|
+ frameView->updateAllLifecyclePhases(DocumentLifecycle::ThrottlingMode::Allow); |
+ m_layerTreeView.clearNeedsAnimate(); |
+ frameView->finalizeLifecycleUpdate(); |
+ EXPECT_FALSE(m_layerTreeView.needsAnimate()); |
+ |
+ // Then bring it back on-screen. This should schedule an animation update. |
+ childFrameView->move(0, 0); |
+ frameView->updateAllLifecyclePhases(DocumentLifecycle::ThrottlingMode::Allow); |
+ m_layerTreeView.clearNeedsAnimate(); |
esprehn
2015/10/01 08:21:36
beginFrame() clears this bit, real pages never cle
Sami
2015/10/05 17:58:24
Thanks, replaced this with beginFrame too, but had
|
+ frameView->finalizeLifecycleUpdate(); |
+ EXPECT_TRUE(m_layerTreeView.needsAnimate()); |
+} |
+ |
+TEST_F(FrameThrottlingTest, mutatingThrottledFrameDoesNotCauseAnimation) |
+{ |
+ setInnerHTML("<iframe id=\"frame\" sandbox=\"\" srcdoc=\"<style> html { background: red; } </style>\"></iframe>"); |
+ WebExceptionCode ec; |
+ WebElement frameElement = root().querySelector("#frame", ec); |
+ EXPECT_EQ(0, ec); |
+ |
+ // Check that the frame initially shows up. |
+ m_layerTreeView.setDeferCommits(false); |
+ SimDisplayItemList displayItems1 = m_compositor.beginFrame(); |
+ EXPECT_TRUE(displayItems1.contains(SimCanvas::Rect, "red")); |
+ |
+ // Move the frame offscreen to throttle it. |
+ FrameView* frameView = document().view(); |
+ LocalFrame* childFrame = toLocalFrame(frameView->frame().tree().firstChild()); |
+ frameElement.setAttribute("style", "transform: translateY(480px)"); |
+ webView().layout(); |
+ frameView->finalizeLifecycleUpdate(); |
+ |
+ // Mutating the throttled frame should not cause an animation to be scheduled. |
+ m_layerTreeView.clearNeedsAnimate(); |
+ childFrame->document()->documentElement()->setInnerHTML("<style> html { background: green; } </style>", ASSERT_NO_EXCEPTION); |
+ EXPECT_FALSE(m_layerTreeView.needsAnimate()); |
+ |
+ // Moving the frame back on screen should make its new contents show up. |
+ frameElement.setAttribute("style", ""); |
+ EXPECT_TRUE(m_layerTreeView.needsAnimate()); |
+ webView().layout(); |
esprehn
2015/10/01 08:21:36
beginFrame()
|
+ frameView->finalizeLifecycleUpdate(); |
esprehn
2015/10/01 08:21:36
How does this happen in a real page? You cause an
Sami
2015/10/05 17:58:24
Replaced this with a series of composites that sho
|
+ |
+ SimDisplayItemList displayItems2 = m_compositor.beginFrame(); |
+ EXPECT_TRUE(displayItems2.contains(SimCanvas::Rect, "green")); |
+} |
+ |
+} // namespace blink |