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