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