Chromium Code Reviews| 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 |