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/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 |
OLD | NEW |