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