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

Side by Side 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: Fix layout test by not dumping throttled FrameViews. 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "config.h"
6
7 #include "core/dom/Document.h"
8 #include "core/frame/FrameView.h"
9 #include "platform/testing/UnitTestHelpers.h"
10 #include "public/web/WebElement.h"
11 #include "public/web/WebHitTestResult.h"
12 #include "web/WebLocalFrameImpl.h"
13 #include "web/tests/FrameTestHelpers.h"
14 #include "web/tests/sim/SimCompositor.h"
15 #include "web/tests/sim/SimDisplayItemList.h"
16 #include "web/tests/sim/SimLayerTreeView.h"
17 #include "web/tests/sim/SimNetwork.h"
18 #include "web/tests/sim/SimRequest.h"
19 #include "web/tests/sim/SimWebViewClient.h"
20 #include <gtest/gtest.h>
21
22 namespace blink {
23
24 class FrameThrottlingTest : public ::testing::Test {
25 protected:
26 FrameThrottlingTest()
27 : m_webViewClient(m_layerTreeView)
28 , m_compositor(m_layerTreeView)
29 {
30 m_webViewHelper.initialize(true, &m_webFrameClient, &m_webViewClient);
31 m_compositor.setWebViewImpl(webView());
32 m_layerTreeView.setDeferCommits(false);
33 webView().resize(WebSize(640, 480));
34 }
35
36 ~FrameThrottlingTest() override { }
37
38 void loadURL(const String& url)
39 {
40 WebURLRequest request;
41 request.initialize();
42 request.setURL(KURL(ParsedURLString, url));
43 webView().mainFrameImpl()->loadRequest(request);
44 }
45
46 void loadHTML(const String& html)
47 {
48 SimRequest mainResource("https://example.com/test.html", "text/html");
49 loadURL("https://example.com/test.html");
50 mainResource.start();
51 mainResource.write(html);
52 mainResource.finish();
53 m_webFrameClient.waitForLoadToComplete();
54 webView().layout();
55 }
56
57 WebElement setupPageWithThrottleableFrame()
58 {
59 loadHTML("<iframe sandbox id=frame></iframe>");
60 WebExceptionCode ec;
61 WebElement frameElement = root().querySelector("#frame", ec);
62 EXPECT_EQ(0, ec);
63 return frameElement;
64 }
65
66 Document& document()
67 {
68 return *webView().mainFrameImpl()->frame()->document();
69 }
70
71 WebNode root()
72 {
73 return WebNode(document().documentElement());
74 }
75
76 WebViewImpl& webView()
77 {
78 return *m_webViewHelper.webViewImpl();
79 }
80
81 SimDisplayItemList compositeFrame()
82 {
83 SimDisplayItemList displayItems = m_compositor.beginFrame();
84 // Ensure intersection observer notifications get delivered.
85 testing::runPendingTasks();
86 return displayItems;
87 }
88
89 SimNetwork m_network;
90 SimLayerTreeView m_layerTreeView;
91 SimWebViewClient m_webViewClient;
92 SimCompositor m_compositor;
93 FrameTestHelpers::TestWebFrameClient m_webFrameClient;
94 FrameTestHelpers::WebViewHelper m_webViewHelper;
95 };
96
97 TEST_F(FrameThrottlingTest, throttleInvisibleFrames)
98 {
99 WebElement frameElement = setupPageWithThrottleableFrame();
100
101 // Initially both frames are visible.
102 FrameView* frameView = document().view();
103 FrameView* childFrameView = toLocalFrame(frameView->frame().tree().firstChil d())->view();
104 EXPECT_FALSE(frameView->isHiddenForThrottling());
105 EXPECT_FALSE(childFrameView->isHiddenForThrottling());
106
107 // Moving the child fully outside the parent makes it invisible.
108 frameElement.setAttribute("style", "transform: translateY(480px)");
109 compositeFrame();
110 EXPECT_FALSE(frameView->isHiddenForThrottling());
111 EXPECT_TRUE(childFrameView->isHiddenForThrottling());
112
113 // A partially visible child is considered visible.
114 frameElement.setAttribute("style", "transform: translate(-50px, 0px, 0px)");
115 compositeFrame();
116 EXPECT_FALSE(frameView->isHiddenForThrottling());
117 EXPECT_FALSE(childFrameView->isHiddenForThrottling());
118 }
119
120 TEST_F(FrameThrottlingTest, viewportVisibilityFullyClipped)
121 {
122 setupPageWithThrottleableFrame();
123
124 // A child which is fully clipped away by its ancestor should become invisib le.
125 FrameView* frameView = document().view();
126 FrameView* childFrameView = toLocalFrame(frameView->frame().tree().firstChil d())->view();
127 webView().resize(WebSize(0, 0));
128 compositeFrame();
129 EXPECT_TRUE(frameView->isHiddenForThrottling());
130 EXPECT_TRUE(childFrameView->isHiddenForThrottling());
131 }
132
133 TEST_F(FrameThrottlingTest, hiddenSameOriginFramesAreNotThrottled)
134 {
135 // Create a document with doubly nested iframes.
136 loadHTML("<iframe id=frame srcdoc='<iframe></iframe>'></iframe>");
137 WebExceptionCode ec;
138 WebElement frameElement = root().querySelector("#frame", ec);
139 EXPECT_EQ(0, ec);
140
141 FrameView* frameView = document().view();
142 LocalFrame* childFrame = toLocalFrame(frameView->frame().tree().firstChild() );
143 LocalFrame* grandChildFrame = toLocalFrame(childFrame->tree().firstChild());
144 FrameView* childFrameView = childFrame->view();
145 FrameView* grandChildFrameView = grandChildFrame->view();
146 EXPECT_FALSE(frameView->canThrottleRendering());
147 EXPECT_FALSE(childFrameView->canThrottleRendering());
148 EXPECT_FALSE(grandChildFrameView->canThrottleRendering());
149
150 // Hidden same origin frames are not throttled.
151 frameElement.setAttribute("style", "transform: translateY(480px)");
152 compositeFrame();
153 EXPECT_FALSE(frameView->canThrottleRendering());
154 EXPECT_FALSE(childFrameView->canThrottleRendering());
155 EXPECT_FALSE(grandChildFrameView->canThrottleRendering());
156 }
157
158 TEST_F(FrameThrottlingTest, hiddenCrossOriginFramesAreThrottled)
159 {
160 // Create a document with doubly nested iframes.
161 loadHTML("<iframe id=frame srcdoc='<iframe sandbox></iframe>'></iframe>");
162 WebExceptionCode ec;
163 WebElement frameElement = root().querySelector("#frame", ec);
164 EXPECT_EQ(0, ec);
165
166 FrameView* frameView = document().view();
167 LocalFrame* childFrame = toLocalFrame(frameView->frame().tree().firstChild() );
168 LocalFrame* grandChildFrame = toLocalFrame(childFrame->tree().firstChild());
169 FrameView* childFrameView = childFrame->view();
170 FrameView* grandChildFrameView = grandChildFrame->view();
171 EXPECT_FALSE(frameView->canThrottleRendering());
172 EXPECT_FALSE(childFrameView->canThrottleRendering());
173 EXPECT_FALSE(grandChildFrameView->canThrottleRendering());
174
175 // Hidden cross origin frames are throttled.
176 frameElement.setAttribute("style", "transform: translateY(480px)");
177 compositeFrame();
178 EXPECT_FALSE(frameView->canThrottleRendering());
179 EXPECT_FALSE(childFrameView->canThrottleRendering());
180 EXPECT_TRUE(grandChildFrameView->canThrottleRendering());
181 }
182
183 TEST_F(FrameThrottlingTest, throttledLifecycleUpdate)
184 {
185 WebElement frameElement = setupPageWithThrottleableFrame();
186 FrameView* frameView = document().view();
187 LocalFrame* childFrame = toLocalFrame(frameView->frame().tree().firstChild() );
188 FrameView* childFrameView = childFrame->view();
189
190 // Enable throttling for the child frame.
191 frameElement.setAttribute("style", "transform: translateY(480px)");
192 compositeFrame();
193 EXPECT_TRUE(childFrameView->canThrottleRendering());
194 EXPECT_EQ(DocumentLifecycle::PaintInvalidationClean, childFrame->document()- >lifecycle().state());
195
196 // Mutating the throttled frame followed by a beginFrame will not result in
197 // a complete lifecycle update.
198 frameElement.setAttribute("width", "50");
199 compositeFrame();
200 EXPECT_EQ(DocumentLifecycle::StyleClean, childFrame->document()->lifecycle() .state());
201
202 // A hit test will force a complete lifecycle update.
203 webView().hitTestResultAt(WebPoint(0, 0));
204 EXPECT_EQ(DocumentLifecycle::CompositingClean, childFrame->document()->lifec ycle().state());
205 }
206
207 TEST_F(FrameThrottlingTest, unthrottlingFrameSchedulesAnimation)
208 {
209 WebElement frameElement = setupPageWithThrottleableFrame();
210 FrameView* frameView = document().view();
211 LocalFrame* childFrame = toLocalFrame(frameView->frame().tree().firstChild() );
212 FrameView* childFrameView = childFrame->view();
213
214 // First make the child hidden to enable throttling.
215 frameElement.setAttribute("style", "transform: translateY(480px)");
216 compositeFrame();
217 EXPECT_TRUE(childFrameView->canThrottleRendering());
218 EXPECT_FALSE(m_layerTreeView.needsAnimate());
219
220 // Then bring it back on-screen. This should schedule an animation update.
221 frameElement.setAttribute("style", "");
222 compositeFrame();
223 EXPECT_TRUE(m_layerTreeView.needsAnimate());
224 }
225
226 TEST_F(FrameThrottlingTest, mutatingThrottledFrameDoesNotCauseAnimation)
227 {
228 loadHTML("<iframe id=frame sandbox srcdoc='<style> html { background: red; } </style>'></iframe>");
229 WebExceptionCode ec;
230 WebElement frameElement = root().querySelector("#frame", ec);
231 EXPECT_EQ(0, ec);
232
233 // Check that the frame initially shows up.
234 m_layerTreeView.setDeferCommits(false);
235 SimDisplayItemList displayItems1 = compositeFrame();
236 EXPECT_TRUE(displayItems1.contains(SimCanvas::Rect, "red"));
237
238 // Move the frame offscreen to throttle it.
239 FrameView* frameView = document().view();
240 LocalFrame* childFrame = toLocalFrame(frameView->frame().tree().firstChild() );
241 frameElement.setAttribute("style", "transform: translateY(480px)");
242 compositeFrame();
243 EXPECT_TRUE(childFrame->view()->canThrottleRendering());
244
245 // Mutating the throttled frame should not cause an animation to be schedule d.
246 childFrame->document()->documentElement()->setInnerHTML("<style> html { back ground: green; } </style>", ASSERT_NO_EXCEPTION);
247 EXPECT_FALSE(m_layerTreeView.needsAnimate());
248
249 // Moving the frame back on screen to unthrottle it.
250 frameElement.setAttribute("style", "");
251 EXPECT_TRUE(m_layerTreeView.needsAnimate());
252
253 // The first frame we composite after unthrottling won't contain the
254 // frame's new contents because unthrottling happens at the end of the
255 // lifecycle update. We need to do another composite to refresh the frame's
256 // contents.
257 SimDisplayItemList displayItems2 = compositeFrame();
258 EXPECT_FALSE(displayItems2.contains(SimCanvas::Rect, "green"));
259 EXPECT_TRUE(m_layerTreeView.needsAnimate());
260
261 SimDisplayItemList displayItems3 = compositeFrame();
262 EXPECT_TRUE(displayItems3.contains(SimCanvas::Rect, "green"));
263 }
264
265 TEST_F(FrameThrottlingTest, synchronousLayoutInThrottledFrame)
266 {
267 // Create a hidden frame which is throttled.
268 loadHTML("<iframe id=frame sandbox srcdoc='<div id=div></div>'></iframe>");
269 WebExceptionCode ec;
270 WebElement frameElement = root().querySelector("#frame", ec);
271 EXPECT_EQ(0, ec);
272 frameElement.setAttribute("style", "transform: translateY(480px)");
273 compositeFrame();
274
275 FrameView* frameView = document().view();
276 LocalFrame* childFrame = toLocalFrame(frameView->frame().tree().firstChild() );
277
278 // Change the size of a div in the throttled frame.
279 Element* divElement = childFrame->document()->getElementById("div");
280 divElement->setAttribute(HTMLNames::styleAttr, "width: 50px");
281
282 // Querying the width of the div should do a synchronous layout update even
283 // though the frame is being throttled.
284 EXPECT_EQ(50, divElement->clientWidth());
285 }
286
287 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698