OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2016 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 "core/svg/graphics/SVGImage.h" | |
6 | |
7 #include "core/svg/graphics/SVGImageChromeClient.h" | |
8 #include "platform/SharedBuffer.h" | |
9 #include "platform/Timer.h" | |
10 #include "platform/geometry/FloatRect.h" | |
11 #include "testing/gtest/include/gtest/gtest.h" | |
12 #include "third_party/skia/include/core/SkCanvas.h" | |
13 #include "third_party/skia/include/utils/SkNullCanvas.h" | |
14 | |
15 namespace blink { | |
16 namespace { | |
17 | |
18 class MockTaskRunner : public WebTaskRunner { | |
19 public: | |
20 void setTime(double newTime) { m_time = newTime; } | |
21 | |
22 MockTaskRunner() | |
23 : WebTaskRunner(), m_time(0.0), m_currentTask(nullptr) | |
24 { } | |
25 | |
26 virtual ~MockTaskRunner() | |
27 { | |
28 if (m_currentTask) | |
29 delete m_currentTask; | |
30 } | |
31 | |
32 private: | |
33 void postTask(const WebTraceLocation&, Task*) override { } | |
34 void postDelayedTask(const WebTraceLocation&, Task* task, double) override | |
35 { | |
36 if (m_currentTask) | |
37 delete m_currentTask; | |
38 m_currentTask = task; | |
39 | |
40 } | |
41 WebTaskRunner* clone() override { return nullptr; } | |
42 double virtualTimeSeconds() const override { return 0.0; } | |
43 double monotonicallyIncreasingVirtualTimeSeconds() const override { return m _time; } | |
44 | |
45 double m_time; | |
46 Task* m_currentTask; | |
47 }; | |
48 | |
49 class MockTimer : public Timer<SVGImageChromeClient> { | |
50 typedef void (SVGImageChromeClient::*TimerFiredFunction)(Timer*); | |
51 public: | |
52 MockTimer(SVGImageChromeClient* o, TimerFiredFunction f) | |
53 : Timer<SVGImageChromeClient>(o, f, &m_taskRunner) | |
54 { | |
55 } | |
56 | |
57 void fire() | |
58 { | |
59 this->Timer<SVGImageChromeClient>::fired(); | |
60 stop(); | |
61 } | |
62 | |
63 void setTime(double newTime) | |
64 { | |
65 m_taskRunner.setTime(newTime); | |
66 } | |
67 | |
68 private: | |
69 MockTaskRunner m_taskRunner; | |
70 }; | |
71 | |
72 } // namespace | |
73 | |
74 class SVGImageTest : public testing::Test { | |
75 public: | |
76 SVGImage& image() { return *m_image; } | |
77 | |
78 void load(const char* data, bool shouldPause) | |
79 { | |
80 m_observer = new PauseControlImageObserver(shouldPause); | |
81 m_image = SVGImage::create(m_observer); | |
82 m_image->setData(SharedBuffer::create(data, strlen(data)), true); | |
83 } | |
84 | |
85 void pumpFrame() | |
86 { | |
87 Image* image = m_image.get(); | |
88 RefPtr<SkCanvas> nullCanvas = adoptRef(SkCreateNullCanvas()); | |
89 SkPaint paint; | |
90 FloatRect dummyRect(0, 0, 100, 100); | |
91 image->draw( | |
92 nullCanvas.get(), paint, | |
93 dummyRect, dummyRect, | |
94 DoNotRespectImageOrientation, Image::DoNotClampImageToSourceRect); | |
95 } | |
96 | |
97 private: | |
98 class PauseControlImageObserver | |
99 : public GarbageCollectedFinalized<PauseControlImageObserver>, public Im ageObserver { | |
100 USING_GARBAGE_COLLECTED_MIXIN(PauseControlImageObserver); | |
101 public: | |
102 PauseControlImageObserver(bool shouldPause) : m_shouldPause(shouldPause) { } | |
103 | |
104 void decodedSizeChangedTo(const Image*, size_t newSize) override { } | |
105 void didDraw(const Image*) override { } | |
106 | |
107 bool shouldPauseAnimation(const Image*) override { return m_shouldPause; } | |
108 void animationAdvanced(const Image*) override { } | |
109 | |
110 void changedInRect(const Image*, const IntRect&) override { } | |
111 | |
112 DEFINE_INLINE_VIRTUAL_TRACE() { ImageObserver::trace(visitor); } | |
113 private: | |
114 bool m_shouldPause; | |
115 }; | |
116 Persistent<PauseControlImageObserver> m_observer; | |
117 RefPtr<SVGImage> m_image; | |
118 }; | |
119 | |
120 const char kAnimatedDocument[] = | |
121 "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'>" | |
122 "<style>" | |
123 "@keyframes rot {" | |
124 " from { transform: rotate(0deg); } to { transform: rotate(-360deg); }" | |
125 "}" | |
126 ".spinner {" | |
127 " transform-origin: 50%% 50%%;" | |
128 " animation-name: rot;" | |
129 " animation-duration: 4s;" | |
130 " animation-iteration-count: infinite;" | |
131 " animation-timing-function: linear;" | |
132 "}" | |
133 "</style>" | |
134 "<path class='spinner' fill='none' d='M 8,1.125 A 6.875,6.875 0 1 1 1.125,8' stroke-width='2' stroke='blue'/>" | |
135 "</svg>"; | |
136 | |
137 TEST_F(SVGImageTest, TimelineSuspendAndResume) | |
138 { | |
139 const bool shouldPause = true; | |
140 load(kAnimatedDocument, shouldPause); | |
141 SVGImageChromeClient& chromeClient = image().chromeClientForTesting(); | |
142 MockTimer* timer = new MockTimer(&chromeClient, &SVGImageChromeClient::anima tionTimerFired); | |
143 chromeClient.setTimer(timer); | |
144 | |
145 // Simulate a draw. Cause a frame (timer) to be scheduled. | |
146 pumpFrame(); | |
147 EXPECT_TRUE(image().hasAnimations()); | |
148 EXPECT_TRUE(timer->isActive()); | |
149 | |
150 // Fire the timer/trigger a frame update. Since the observer always returns | |
151 // true for shouldPauseAnimation, this will result in the timeline being | |
152 // suspended. | |
153 timer->fire(); | |
154 EXPECT_TRUE(chromeClient.isSuspended()); | |
155 EXPECT_FALSE(timer->isActive()); | |
156 | |
157 // Simulate a draw. This should resume the animation again. | |
158 pumpFrame(); | |
159 EXPECT_TRUE(timer->isActive()); | |
160 EXPECT_FALSE(chromeClient.isSuspended()); | |
chrishtr
2016/05/25 20:23:47
Why doesn't it pause again?
fs
2016/05/25 20:30:16
It will/would when the timer fires (i.e shouldPaus
chrishtr
2016/05/25 22:18:13
oh right, you're calling pumpFrame() not timer->fi
| |
161 } | |
162 | |
163 TEST_F(SVGImageTest, ResetAnimation) | |
164 { | |
165 const bool shouldPause = false; | |
166 load(kAnimatedDocument, shouldPause); | |
167 SVGImageChromeClient& chromeClient = image().chromeClientForTesting(); | |
168 MockTimer* timer = new MockTimer(&chromeClient, &SVGImageChromeClient::anima tionTimerFired); | |
169 chromeClient.setTimer(timer); | |
170 | |
171 // Simulate a draw. Cause a frame (timer) to be scheduled. | |
172 pumpFrame(); | |
173 EXPECT_TRUE(image().hasAnimations()); | |
174 EXPECT_TRUE(timer->isActive()); | |
175 | |
176 // Reset the animation. This will suspend the timeline but not cancel the | |
177 // timer. | |
178 image().resetAnimation(); | |
179 EXPECT_TRUE(chromeClient.isSuspended()); | |
180 EXPECT_TRUE(timer->isActive()); | |
181 | |
182 // Fire the timer/trigger a frame update. The timeline will remain | |
183 // suspended and no frame will be scheduled. | |
184 timer->fire(); | |
185 EXPECT_TRUE(chromeClient.isSuspended()); | |
186 EXPECT_FALSE(timer->isActive()); | |
187 | |
188 // Simulate a draw. This should resume the animation again. | |
189 pumpFrame(); | |
190 EXPECT_FALSE(chromeClient.isSuspended()); | |
191 EXPECT_TRUE(timer->isActive()); | |
192 } | |
193 | |
194 } // namespace blink | |
OLD | NEW |