Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(88)

Unified Diff: third_party/WebKit/Source/web/tests/FrameThrottlingTest.cpp

Issue 1364063007: Throttle rendering pipeline for invisible frames (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Early-out for intersection update walk. Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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..fe44fd2906ebb278f90ab2b5864d998c1a4f69c7
--- /dev/null
+++ b/third_party/WebKit/Source/web/tests/FrameThrottlingTest.cpp
@@ -0,0 +1,280 @@
+// 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 "platform/testing/UnitTestHelpers.h"
+#include "public/web/WebElement.h"
+#include "public/web/WebHitTestResult.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());
+ m_layerTreeView.setDeferCommits(false);
+ webView().resize(WebSize(640, 480));
+ }
+
+ ~FrameThrottlingTest() override { }
+
+ void setInnerHTML(const String& html)
+ {
+ document().documentElement()->setInnerHTML(html, ASSERT_NO_EXCEPTION);
+ m_webFrameClient.waitForLoadToComplete();
+ webView().layout();
esprehn 2015/10/14 22:09:47 You want to use SimRequest instead, don't use inne
Sami 2015/10/16 16:48:09 Done.
+ }
+
+ WebElement setupPageWithThrottleableFrame()
+ {
+ setInnerHTML("<iframe sandbox=\"\" id=\"frame\"></iframe>");
esprehn 2015/10/14 22:09:46 use single quotes, (or no quotes) you don't need t
Sami 2015/10/16 16:48:09 Done.
+ WebExceptionCode ec;
+ WebElement frameElement = root().querySelector("#frame", ec);
+ EXPECT_EQ(0, ec);
+ return frameElement;
+ }
+
+ 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_viewportVisibilityForThrottling;
esprehn 2015/10/14 22:09:47 add a getter, don't reach into members
Sami 2015/10/16 16:48:09 Done.
+ }
+
+ SimDisplayItemList compositeFrame()
+ {
+ SimDisplayItemList displayItems = m_compositor.beginFrame();
+ // Ensure intersection observer notifications get delivered.
+ testing::runPendingTasks();
+ return displayItems;
+ }
+
+ SimLayerTreeView m_layerTreeView;
+ SimWebViewClient m_webViewClient;
+ SimCompositor m_compositor;
+ FrameTestHelpers::TestWebFrameClient m_webFrameClient;
+ FrameTestHelpers::WebViewHelper m_webViewHelper;
+};
+
+TEST_F(FrameThrottlingTest, throttleInvisibleFrames)
+{
+ WebElement frameElement = 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.
+ frameElement.setAttribute("style", "transform: translateY(480px)");
+ compositeFrame();
+ EXPECT_EQ(ViewportVisibility::Visible, viewportVisibility(frameView));
+ EXPECT_EQ(ViewportVisibility::Hidden, viewportVisibility(childFrameView));
+
+ // A partially visible child is considered visible.
+ frameElement.setAttribute("style", "transform: translate(-50px, 0px, 0px)");
+ compositeFrame();
+ 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));
+ compositeFrame();
+ EXPECT_EQ(ViewportVisibility::Hidden, viewportVisibility(frameView));
+ EXPECT_EQ(ViewportVisibility::Hidden, viewportVisibility(childFrameView));
+}
+
+TEST_F(FrameThrottlingTest, hiddenSameOriginFramesAreNotThrottled)
+{
+ // Create a document with doubly nested iframes.
+ setInnerHTML("<iframe id=\"frame\" srcdoc=\"<iframe></iframe>\"></iframe>");
+ WebExceptionCode ec;
+ WebElement frameElement = root().querySelector("#frame", ec);
+ EXPECT_EQ(0, ec);
+
+ 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->shouldThrottleRendering());
+ EXPECT_FALSE(childFrameView->shouldThrottleRendering());
+ EXPECT_FALSE(grandChildFrameView->shouldThrottleRendering());
+
+ // Hidden same origin frames are not throttled.
+ frameElement.setAttribute("style", "transform: translateY(480px)");
+ compositeFrame();
+ EXPECT_FALSE(frameView->shouldThrottleRendering());
+ EXPECT_FALSE(childFrameView->shouldThrottleRendering());
+ EXPECT_FALSE(grandChildFrameView->shouldThrottleRendering());
+}
+
+TEST_F(FrameThrottlingTest, hiddenCrossOriginFramesAreThrottled)
+{
+ // Create a document with doubly nested iframes.
+ setInnerHTML("<iframe id=\"frame\" srcdoc=\"<iframe sandbox></iframe>\"></iframe>");
esprehn 2015/10/14 22:09:47 ditto
Sami 2015/10/16 16:48:09 Done.
+ WebExceptionCode ec;
+ WebElement frameElement = root().querySelector("#frame", ec);
+ EXPECT_EQ(0, ec);
+
+ 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->shouldThrottleRendering());
+ EXPECT_FALSE(childFrameView->shouldThrottleRendering());
+ EXPECT_FALSE(grandChildFrameView->shouldThrottleRendering());
+
+ // Hidden cross origin frames are throttled.
+ frameElement.setAttribute("style", "transform: translateY(480px)");
+ compositeFrame();
+ EXPECT_FALSE(frameView->shouldThrottleRendering());
+ EXPECT_FALSE(childFrameView->shouldThrottleRendering());
+ EXPECT_TRUE(grandChildFrameView->shouldThrottleRendering());
+}
+
+TEST_F(FrameThrottlingTest, throttledLifecycleUpdate)
+{
+ WebElement frameElement = setupPageWithThrottleableFrame();
+ FrameView* frameView = document().view();
+ LocalFrame* childFrame = toLocalFrame(frameView->frame().tree().firstChild());
+ FrameView* childFrameView = childFrame->view();
+
+ // Enable throttling for the child frame.
+ frameElement.setAttribute("style", "transform: translateY(480px)");
+ compositeFrame();
+ EXPECT_TRUE(childFrameView->shouldThrottleRendering());
+ EXPECT_EQ(DocumentLifecycle::PaintInvalidationClean, childFrame->document()->lifecycle().state());
+
+ // Mutating the throttled frame followed by a beginFrame will not result in
+ // a complete lifecycle update.
+ frameElement.setAttribute("width", "50");
+ compositeFrame();
+ EXPECT_EQ(DocumentLifecycle::StyleClean, childFrame->document()->lifecycle().state());
+
+ // A hit test will force a complete lifecycle update.
+ webView().hitTestResultAt(WebPoint(0, 0));
+ EXPECT_EQ(DocumentLifecycle::CompositingClean, childFrame->document()->lifecycle().state());
+}
+
+TEST_F(FrameThrottlingTest, unthrottlingFrameSchedulesAnimation)
+{
+ WebElement frameElement = setupPageWithThrottleableFrame();
+ FrameView* frameView = document().view();
+ LocalFrame* childFrame = toLocalFrame(frameView->frame().tree().firstChild());
+ FrameView* childFrameView = childFrame->view();
+
+ // First make the child hidden to enable throttling.
+ frameElement.setAttribute("style", "transform: translateY(480px)");
+ compositeFrame();
+ EXPECT_TRUE(childFrameView->shouldThrottleRendering());
+ EXPECT_FALSE(m_layerTreeView.needsAnimate());
+
+ // Then bring it back on-screen. This should schedule an animation update.
+ frameElement.setAttribute("style", "");
+ compositeFrame();
+ EXPECT_TRUE(m_layerTreeView.needsAnimate());
+}
+
+TEST_F(FrameThrottlingTest, mutatingThrottledFrameDoesNotCauseAnimation)
+{
+ setInnerHTML("<iframe id=\"frame\" sandbox=\"\" srcdoc=\"<style> html { background: red; } </style>\"></iframe>");
esprehn 2015/10/14 22:09:47 remove quotes and us single quotes, remove black s
Sami 2015/10/16 16:48:09 Done.
+ 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 = compositeFrame();
+ 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)");
+ compositeFrame();
+ EXPECT_TRUE(childFrame->view()->shouldThrottleRendering());
+
+ // Mutating the throttled frame should not cause an animation to be scheduled.
+ childFrame->document()->documentElement()->setInnerHTML("<style> html { background: green; } </style>", ASSERT_NO_EXCEPTION);
+ EXPECT_FALSE(m_layerTreeView.needsAnimate());
+
+ // Moving the frame back on screen to unthrottle it.
+ frameElement.setAttribute("style", "");
+ EXPECT_TRUE(m_layerTreeView.needsAnimate());
+
+ // The first frame we composite after unthrottling won't contain the
+ // frame's new contents because unthrottling happens at the end of the
+ // lifecycle update. We need to do another composite to refresh the frame's
+ // contents.
+ SimDisplayItemList displayItems2 = compositeFrame();
+ EXPECT_FALSE(displayItems2.contains(SimCanvas::Rect, "green"));
+ EXPECT_TRUE(m_layerTreeView.needsAnimate());
+
+ SimDisplayItemList displayItems3 = compositeFrame();
+ EXPECT_TRUE(displayItems3.contains(SimCanvas::Rect, "green"));
+}
+
+TEST_F(FrameThrottlingTest, synchronousLayoutInThrottledFrame)
+{
+ // Create a hidden frame which is throttled.
+ setInnerHTML("<iframe id=\"frame\" sandbox=\"\" srcdoc=\"<div id=div></div>\"></iframe>");
+ WebExceptionCode ec;
+ WebElement frameElement = root().querySelector("#frame", ec);
+ EXPECT_EQ(0, ec);
+ frameElement.setAttribute("style", "transform: translateY(480px)");
+ compositeFrame();
+
+ FrameView* frameView = document().view();
+ LocalFrame* childFrame = toLocalFrame(frameView->frame().tree().firstChild());
+
+ // Change the size of a div in the throttled frame.
+ Element* divElement = childFrame->document()->getElementById("div");
+ divElement->setAttribute(HTMLNames::styleAttr, "width: 50px");
+
+ // Querying the width of the div should do a synchronous layout update even
+ // though the frame is being throttled.
+ EXPECT_EQ(50, divElement->clientWidth());
+}
+
+} // namespace blink

Powered by Google App Engine
This is Rietveld 408576698