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 "public/web/WebElement.h" | |
| 10 #include "web/WebLocalFrameImpl.h" | |
| 11 #include "web/tests/FrameTestHelpers.h" | |
| 12 #include "web/tests/sim/SimCompositor.h" | |
| 13 #include "web/tests/sim/SimDisplayItemList.h" | |
| 14 #include "web/tests/sim/SimLayerTreeView.h" | |
| 15 #include "web/tests/sim/SimWebViewClient.h" | |
| 16 #include <gtest/gtest.h> | |
| 17 | |
| 18 namespace blink { | |
| 19 | |
| 20 class FrameThrottlingTest : public ::testing::Test { | |
| 21 protected: | |
| 22 using SecurityOriginStatus = FrameView::SecurityOriginStatus; | |
| 23 using ViewportVisibility = FrameView::ViewportVisibility; | |
| 24 | |
| 25 FrameThrottlingTest() | |
| 26 : m_webViewClient(m_layerTreeView) | |
| 27 , m_compositor(m_layerTreeView) | |
| 28 { | |
| 29 m_webViewHelper.initialize(true, &m_webFrameClient, &m_webViewClient); | |
| 30 m_compositor.setWebViewImpl(webView()); | |
| 31 webView().resize(WebSize(640, 480)); | |
| 32 } | |
| 33 | |
| 34 ~FrameThrottlingTest() override { } | |
| 35 | |
| 36 void setInnerHTML(const String& html) | |
| 37 { | |
| 38 document().documentElement()->setInnerHTML(html, ASSERT_NO_EXCEPTION); | |
| 39 m_webFrameClient.waitForLoadToComplete(); | |
| 40 webView().layout(); | |
| 41 } | |
| 42 | |
| 43 void setupPageWithThrottleableFrame() | |
| 44 { | |
| 45 setInnerHTML("<iframe sandbox=\"\"></iframe>"); | |
| 46 } | |
| 47 | |
| 48 Document& document() | |
| 49 { | |
| 50 return *webView().mainFrameImpl()->frame()->document(); | |
| 51 } | |
| 52 | |
| 53 WebNode root() | |
| 54 { | |
| 55 return WebNode(document().documentElement()); | |
| 56 } | |
| 57 | |
| 58 WebViewImpl& webView() | |
| 59 { | |
| 60 return *m_webViewHelper.webViewImpl(); | |
| 61 } | |
| 62 | |
| 63 ViewportVisibility viewportVisibility(FrameView* frameView) | |
| 64 { | |
| 65 return frameView->m_viewportVisibility; | |
| 66 } | |
| 67 | |
| 68 SimLayerTreeView m_layerTreeView; | |
| 69 SimWebViewClient m_webViewClient; | |
| 70 SimCompositor m_compositor; | |
| 71 FrameTestHelpers::TestWebFrameClient m_webFrameClient; | |
| 72 FrameTestHelpers::WebViewHelper m_webViewHelper; | |
| 73 }; | |
| 74 | |
| 75 TEST_F(FrameThrottlingTest, ThrottleInvisibleFrames) | |
| 76 { | |
| 77 setupPageWithThrottleableFrame(); | |
| 78 | |
| 79 // Initially both frames are visible. | |
| 80 FrameView* frameView = document().view(); | |
| 81 FrameView* childFrameView = toLocalFrame(frameView->frame().tree().firstChil d())->view(); | |
| 82 EXPECT_EQ(ViewportVisibility::Visible, viewportVisibility(frameView)); | |
| 83 EXPECT_EQ(ViewportVisibility::Visible, viewportVisibility(childFrameView)); | |
| 84 | |
| 85 // Moving the child fully outside the parent makes it invisible. | |
| 86 childFrameView->move(0, frameView->height()); | |
| 87 frameView->updateAllLifecyclePhases(); | |
| 88 frameView->finalizeLifecycleUpdate(); | |
| 89 EXPECT_EQ(ViewportVisibility::Visible, viewportVisibility(frameView)); | |
| 90 EXPECT_EQ(ViewportVisibility::Hidden, viewportVisibility(childFrameView)); | |
| 91 | |
| 92 // A partially visible child is considered visible. | |
| 93 childFrameView->move(-childFrameView->width() / 2, 0); | |
| 94 frameView->updateAllLifecyclePhases(); | |
| 95 frameView->finalizeLifecycleUpdate(); | |
|
esprehn
2015/10/01 08:21:37
m_compositor.beginFrame()
Sami
2015/10/05 17:58:24
Done.
| |
| 96 EXPECT_EQ(ViewportVisibility::Visible, viewportVisibility(frameView)); | |
| 97 EXPECT_EQ(ViewportVisibility::Visible, viewportVisibility(childFrameView)); | |
| 98 } | |
| 99 | |
| 100 TEST_F(FrameThrottlingTest, viewportVisibilityFullyClipped) | |
| 101 { | |
| 102 setupPageWithThrottleableFrame(); | |
| 103 | |
| 104 // A child which is fully clipped away by its ancestor should become invisib le. | |
| 105 FrameView* frameView = document().view(); | |
| 106 FrameView* childFrameView = toLocalFrame(frameView->frame().tree().firstChil d())->view(); | |
| 107 webView().resize(WebSize(0, 0)); | |
| 108 webView().layout(); | |
|
esprehn
2015/10/01 08:21:37
ditto, layout is only triggered from frames, so yo
Sami
2015/10/05 17:58:24
Done.
| |
| 109 frameView->updateAllLifecyclePhases(); | |
| 110 frameView->finalizeLifecycleUpdate(); | |
|
esprehn
2015/10/01 08:21:36
and then you don't need this
Sami
2015/10/05 17:58:24
Done.
| |
| 111 EXPECT_EQ(ViewportVisibility::Visible, viewportVisibility(frameView)); | |
| 112 EXPECT_EQ(ViewportVisibility::Hidden, viewportVisibility(childFrameView)); | |
| 113 } | |
| 114 | |
| 115 TEST_F(FrameThrottlingTest, hiddenCrossOriginFramesAreThrottled) | |
| 116 { | |
| 117 // Create a document with doubly nested iframes. | |
| 118 setInnerHTML("<iframe srcdoc=\"<iframe></iframe>\"></iframe>"); | |
| 119 | |
| 120 FrameView* frameView = document().view(); | |
| 121 LocalFrame* childFrame = toLocalFrame(frameView->frame().tree().firstChild() ); | |
| 122 LocalFrame* grandChildFrame = toLocalFrame(childFrame->tree().firstChild()); | |
| 123 FrameView* childFrameView = childFrame->view(); | |
| 124 FrameView* grandChildFrameView = grandChildFrame->view(); | |
| 125 EXPECT_FALSE(frameView->shouldThrottleRenderingPipeline()); | |
| 126 EXPECT_FALSE(childFrameView->shouldThrottleRenderingPipeline()); | |
| 127 EXPECT_FALSE(grandChildFrameView->shouldThrottleRenderingPipeline()); | |
| 128 | |
| 129 // Just being hidden doesn't make a frame throttled. | |
| 130 grandChildFrameView->move(0, frameView->height()); | |
|
esprehn
2015/10/01 08:21:36
this can't happen in a real page, set some style t
Sami
2015/10/05 17:58:24
Done.
| |
| 131 frameView->finalizeLifecycleUpdate(); | |
| 132 EXPECT_FALSE(frameView->shouldThrottleRenderingPipeline()); | |
| 133 EXPECT_FALSE(childFrameView->shouldThrottleRenderingPipeline()); | |
| 134 EXPECT_FALSE(grandChildFrameView->shouldThrottleRenderingPipeline()); | |
| 135 | |
| 136 // Making the middle frame cross origin enables throttling for the grand chi ld. | |
| 137 childFrame->securityContext()->setSecurityOrigin(SecurityOrigin::createUniqu e()); | |
| 138 frameView->finalizeLifecycleUpdate(); | |
|
esprehn
2015/10/01 08:21:36
why would this happen again in a real page?
Sami
2015/10/05 17:58:24
You're right, it wouldn't. To make this more reali
| |
| 139 EXPECT_FALSE(frameView->shouldThrottleRenderingPipeline()); | |
| 140 EXPECT_FALSE(childFrameView->shouldThrottleRenderingPipeline()); | |
| 141 EXPECT_TRUE(grandChildFrameView->shouldThrottleRenderingPipeline()); | |
| 142 } | |
| 143 | |
| 144 TEST_F(FrameThrottlingTest, throttledLifecycleUpdate) | |
| 145 { | |
| 146 setupPageWithThrottleableFrame(); | |
| 147 | |
| 148 FrameView* frameView = document().view(); | |
| 149 LocalFrame* childFrame = toLocalFrame(frameView->frame().tree().firstChild() ); | |
| 150 FrameView* childFrameView = childFrame->view(); | |
| 151 | |
| 152 // Enable throttling for the child frame. | |
| 153 childFrameView->move(0, frameView->height()); | |
|
esprehn
2015/10/01 08:21:36
move with style, real pages can't go through ->mov
Sami
2015/10/05 17:58:25
Done.
| |
| 154 frameView->updateAllLifecyclePhases(); | |
| 155 frameView->finalizeLifecycleUpdate(); | |
|
esprehn
2015/10/01 08:21:36
ditto, beginFrame()
Sami
2015/10/05 17:58:24
Done.
| |
| 156 EXPECT_TRUE(childFrameView->shouldThrottleRenderingPipeline()); | |
| 157 | |
| 158 // Doing a lifecycle update while allowing throttling shouldn't clear the | |
| 159 // needs layout bit on the child frame. | |
| 160 childFrameView->setNeedsLayout(); | |
|
esprehn
2015/10/01 08:21:37
don't artificially cause layout, do something real
Sami
2015/10/05 17:58:24
Done.
| |
| 161 EXPECT_TRUE(childFrameView->needsLayout()); | |
| 162 frameView->updateAllLifecyclePhases(DocumentLifecycle::ThrottlingMode::Allow ); | |
|
esprehn
2015/10/01 08:21:37
this can't really happen
Sami
2015/10/05 17:58:24
Replaced with another beginFrame().
| |
| 163 EXPECT_TRUE(childFrameView->needsLayout()); | |
| 164 | |
| 165 frameView->updateAllLifecyclePhases(DocumentLifecycle::ThrottlingMode::Disal low); | |
| 166 EXPECT_FALSE(childFrameView->needsLayout()); | |
| 167 } | |
| 168 | |
| 169 TEST_F(FrameThrottlingTest, unthrottlingFrameSchedulesAnimation) | |
| 170 { | |
| 171 setupPageWithThrottleableFrame(); | |
| 172 FrameView* frameView = document().view(); | |
| 173 LocalFrame* childFrame = toLocalFrame(frameView->frame().tree().firstChild() ); | |
| 174 FrameView* childFrameView = childFrame->view(); | |
| 175 | |
| 176 // First make the child hidden to enable throttling. | |
| 177 childFrameView->move(0, frameView->height()); | |
|
esprehn
2015/10/01 08:21:37
ditto
Sami
2015/10/05 17:58:24
Done.
| |
| 178 frameView->updateAllLifecyclePhases(DocumentLifecycle::ThrottlingMode::Allow ); | |
| 179 m_layerTreeView.clearNeedsAnimate(); | |
| 180 frameView->finalizeLifecycleUpdate(); | |
| 181 EXPECT_FALSE(m_layerTreeView.needsAnimate()); | |
| 182 | |
| 183 // Then bring it back on-screen. This should schedule an animation update. | |
| 184 childFrameView->move(0, 0); | |
| 185 frameView->updateAllLifecyclePhases(DocumentLifecycle::ThrottlingMode::Allow ); | |
| 186 m_layerTreeView.clearNeedsAnimate(); | |
|
esprehn
2015/10/01 08:21:36
beginFrame() clears this bit, real pages never cle
Sami
2015/10/05 17:58:24
Thanks, replaced this with beginFrame too, but had
| |
| 187 frameView->finalizeLifecycleUpdate(); | |
| 188 EXPECT_TRUE(m_layerTreeView.needsAnimate()); | |
| 189 } | |
| 190 | |
| 191 TEST_F(FrameThrottlingTest, mutatingThrottledFrameDoesNotCauseAnimation) | |
| 192 { | |
| 193 setInnerHTML("<iframe id=\"frame\" sandbox=\"\" srcdoc=\"<style> html { back ground: red; } </style>\"></iframe>"); | |
| 194 WebExceptionCode ec; | |
| 195 WebElement frameElement = root().querySelector("#frame", ec); | |
| 196 EXPECT_EQ(0, ec); | |
| 197 | |
| 198 // Check that the frame initially shows up. | |
| 199 m_layerTreeView.setDeferCommits(false); | |
| 200 SimDisplayItemList displayItems1 = m_compositor.beginFrame(); | |
| 201 EXPECT_TRUE(displayItems1.contains(SimCanvas::Rect, "red")); | |
| 202 | |
| 203 // Move the frame offscreen to throttle it. | |
| 204 FrameView* frameView = document().view(); | |
| 205 LocalFrame* childFrame = toLocalFrame(frameView->frame().tree().firstChild() ); | |
| 206 frameElement.setAttribute("style", "transform: translateY(480px)"); | |
| 207 webView().layout(); | |
| 208 frameView->finalizeLifecycleUpdate(); | |
| 209 | |
| 210 // Mutating the throttled frame should not cause an animation to be schedule d. | |
| 211 m_layerTreeView.clearNeedsAnimate(); | |
| 212 childFrame->document()->documentElement()->setInnerHTML("<style> html { back ground: green; } </style>", ASSERT_NO_EXCEPTION); | |
| 213 EXPECT_FALSE(m_layerTreeView.needsAnimate()); | |
| 214 | |
| 215 // Moving the frame back on screen should make its new contents show up. | |
| 216 frameElement.setAttribute("style", ""); | |
| 217 EXPECT_TRUE(m_layerTreeView.needsAnimate()); | |
| 218 webView().layout(); | |
|
esprehn
2015/10/01 08:21:36
beginFrame()
| |
| 219 frameView->finalizeLifecycleUpdate(); | |
|
esprehn
2015/10/01 08:21:36
How does this happen in a real page? You cause an
Sami
2015/10/05 17:58:24
Replaced this with a series of composites that sho
| |
| 220 | |
| 221 SimDisplayItemList displayItems2 = m_compositor.beginFrame(); | |
| 222 EXPECT_TRUE(displayItems2.contains(SimCanvas::Rect, "green")); | |
| 223 } | |
| 224 | |
| 225 } // namespace blink | |
| OLD | NEW |