Source/WebCore/ChangeLog

 12012-03-18 James Robinson <jamesr@chromium.org>
 2
 3 [chromium] Implement fling-by-wheel on compositor thread
 4 https://bugs.webkit.org/show_bug.cgi?id=81462
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 Adds compositor thread support for a fling type implemented as a sequence of wheel scrolls. Covered by new tests
 9 in WebCompositorInputHandlerImplTests.
 10
 11 * platform/graphics/chromium/cc/CCInputHandler.h:
 12 (CCInputHandler):
 13 * platform/graphics/chromium/cc/CCLayerTreeHostImpl.cpp:
 14 (WebCore::CCLayerTreeHostImpl::scheduleAnimation):
 15 (WebCore):
 16 * platform/graphics/chromium/cc/CCLayerTreeHostImpl.h:
 17 (CCLayerTreeHostImpl):
 18 * platform/graphics/chromium/cc/CCThreadProxy.cpp:
 19 (WebCore::CCThreadProxy::scheduledActionDrawAndSwap):
 20
1212012-03-19 David Reveman <reveman@chromium.org>
222
323 [Chromium] GL_EXT_occlusion_query_boolean and GL_CHROMIUM_command_buffer_query support.

Source/WebKit/chromium/ChangeLog

 12012-03-18 James Robinson <jamesr@chromium.org>
 2
 3 [chromium] Implement fling-by-wheel on compositor thread
 4 https://bugs.webkit.org/show_bug.cgi?id=81462
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 Implement fling-by-wheel and beef up existing unit test coverage considerably by using gmock instead of mocking
 9 by hand.
 10
 11 * src/WebCompositorInputHandlerImpl.cpp:
 12 (WebKit::WebCompositorInputHandlerImpl::handleInputEvent):
 13 (WebKit):
 14 (WebKit::WebCompositorInputHandlerImpl::handleInputEventInternal):
 15 (WebKit::WebCompositorInputHandlerImpl::handleGestureFling):
 16 (WebKit::WebCompositorInputHandlerImpl::animate):
 17 (WebKit::WebCompositorInputHandlerImpl::cancelCurrentFling):
 18 (WebKit::WebCompositorInputHandlerImpl::scrollBy):
 19 * src/WebCompositorInputHandlerImpl.h:
 20 (WebCompositorInputHandlerImpl):
 21 * tests/WebCompositorInputHandlerImplTest.cpp:
 22
1232012-03-19 David Reveman <reveman@chromium.org>
224
325 [Chromium] GL_EXT_occlusion_query_boolean and GL_CHROMIUM_command_buffer_query support.

Source/WebCore/platform/graphics/chromium/cc/CCInputHandler.h

@@class IntSize;
4545class CCInputHandlerClient {
4646 WTF_MAKE_NONCOPYABLE(CCInputHandlerClient);
4747public:
48  virtual void setNeedsRedraw() = 0;
49 
5048 enum ScrollStatus { ScrollFailed, ScrollStarted, ScrollIgnored };
5149 enum ScrollInputType { Gesture, Wheel };
5250

@@public:
8078 virtual CCActiveGestureAnimation* activeGestureAnimation() = 0;
8179 virtual void setActiveGestureAnimation(PassOwnPtr<CCActiveGestureAnimation>) = 0;
8280
 81 // Request another callback to CCInputHandler::animate().
 82 virtual void scheduleAnimation() = 0;
 83
8384protected:
8485 CCInputHandlerClient() { }
8586 virtual ~CCInputHandlerClient() { }

@@public:
9293 virtual ~CCInputHandler() { }
9394
9495 virtual int identifier() const = 0;
95  virtual void willDraw(double monotonicTime) = 0;
 96 virtual void animate(double monotonicTime) = 0;
9697
9798protected:
9899 CCInputHandler() { }

Source/WebCore/platform/graphics/chromium/cc/CCLayerTreeHostImpl.cpp

@@void CCLayerTreeHostImpl::setActiveGestureAnimation(PassOwnPtr<CCActiveGestureAn
181181 m_client->setNeedsRedrawOnImplThread();
182182}
183183
 184void CCLayerTreeHostImpl::scheduleAnimation()
 185{
 186 m_client->setNeedsRedrawOnImplThread();
 187}
 188
184189void CCLayerTreeHostImpl::trackDamageForAllSurfaces(CCLayerImpl* rootDrawLayer, const CCLayerList& renderSurfaceLayerList)
185190{
186191 // For now, we use damage tracking to compute a global scissor. To do this, we must

Source/WebCore/platform/graphics/chromium/cc/CCLayerTreeHostImpl.h

@@public:
6565 virtual ~CCLayerTreeHostImpl();
6666
6767 // CCInputHandlerClient implementation
68  virtual void setNeedsRedraw();
6968 virtual CCInputHandlerClient::ScrollStatus scrollBegin(const IntPoint&, CCInputHandlerClient::ScrollInputType);
7069 virtual void scrollBy(const IntSize&);
7170 virtual void scrollEnd();

@@public:
7675 virtual CCActiveGestureAnimation* activeGestureAnimation() { return m_activeGestureAnimation.get(); }
7776 // To clear an active animation, pass nullptr.
7877 virtual void setActiveGestureAnimation(PassOwnPtr<CCActiveGestureAnimation>);
 78 virtual void scheduleAnimation();
7979
8080 // Virtual for testing.
8181 virtual void beginCommit();

@@public:
135135 bool needsAnimateLayers() const { return m_needsAnimateLayers; }
136136 void setNeedsAnimateLayers() { m_needsAnimateLayers = true; }
137137
 138 void setNeedsRedraw();
 139
138140protected:
139141 CCLayerTreeHostImpl(const CCSettings&, CCLayerTreeHostImplClient*);
140142

Source/WebCore/platform/graphics/chromium/cc/CCThreadProxy.cpp

@@void CCThreadProxy::scheduledActionDrawAndSwap()
550550 double monotonicTime = monotonicallyIncreasingTime();
551551 double wallClockTime = currentTime();
552552
553  m_inputHandlerOnImplThread->willDraw(monotonicTime);
 553 m_inputHandlerOnImplThread->animate(monotonicTime);
554554 m_layerTreeHostImpl->animate(monotonicTime, wallClockTime);
555555 m_layerTreeHostImpl->drawLayers();
556556

Source/WebKit/chromium/src/WebCompositorInputHandlerImpl.cpp

2929
3030#include "PlatformGestureCurveTarget.h"
3131#include "TouchFlingPlatformGestureCurve.h"
 32#include "TraceEvent.h"
3233#include "WebCompositorImpl.h"
3334#include "WebCompositorInputHandlerClient.h"
3435#include "WebInputEvent.h"

@@WebCompositorInputHandlerImpl::WebCompositorInputHandlerImpl(CCInputHandlerClien
118119 , m_expectScrollUpdateEnd(false)
119120 , m_expectPinchUpdateEnd(false)
120121#endif
121  , m_scrollStarted(false)
 122 , m_gestureScrollStarted(false)
122123{
123124 ASSERT(CCProxy::isImplThread());
124125

@@void WebCompositorInputHandlerImpl::handleInputEvent(const WebInputEvent& event)
154155 ASSERT(CCProxy::isImplThread());
155156 ASSERT(m_client);
156157
 158 WebCompositorInputHandlerImpl::EventDisposition disposition = handleInputEventInternal(event);
 159 switch (disposition) {
 160 case DidHandle:
 161 m_client->didHandleInputEvent();
 162 break;
 163 case DidNotHandle:
 164 m_client->didNotHandleInputEvent(true /* sendToWidget */);
 165 break;
 166 case DropEvent:
 167 m_client->didNotHandleInputEvent(false /* sendToWidget */);
 168 break;
 169 }
 170}
 171
 172WebCompositorInputHandlerImpl::EventDisposition WebCompositorInputHandlerImpl::handleInputEventInternal(const WebInputEvent& event)
 173{
157174 if (event.type == WebInputEvent::MouseWheel) {
158175 const WebMouseWheelEvent& wheelEvent = *static_cast<const WebMouseWheelEvent*>(&event);
159176 CCInputHandlerClient::ScrollStatus scrollStatus = m_inputHandlerClient->scrollBegin(IntPoint(wheelEvent.x, wheelEvent.y), CCInputHandlerClient::Wheel);
160177 switch (scrollStatus) {
161178 case CCInputHandlerClient::ScrollStarted:
162  // FIXME: use WheelFlingPlatformGestureCurve here to get smooth wheel scrolling?
 179 TRACE_EVENT_INSTANT2("cc", "WebCompositorInputHandlerImpl::handleInput wheel scroll", "deltaX", -wheelEvent.deltaX, "deltaY", -wheelEvent.deltaY);
163180 m_inputHandlerClient->scrollBy(IntSize(-wheelEvent.deltaX, -wheelEvent.deltaY));
164181 m_inputHandlerClient->scrollEnd();
165  m_client->didHandleInputEvent();
166  return;
 182 return DidHandle;
167183 case CCInputHandlerClient::ScrollIgnored:
168  m_client->didNotHandleInputEvent(false /* sendToWidget */);
169  return;
 184 return DidNotHandle;
170185 case CCInputHandlerClient::ScrollFailed:
171  break;
 186 return DropEvent;
172187 }
173188 } else if (event.type == WebInputEvent::GestureScrollBegin) {
174  ASSERT(!m_scrollStarted);
 189 ASSERT(!m_gestureScrollStarted);
175190 ASSERT(!m_expectScrollUpdateEnd);
176191#ifndef NDEBUG
177192 m_expectScrollUpdateEnd = true;

@@void WebCompositorInputHandlerImpl::handleInputEvent(const WebInputEvent& event)
180195 CCInputHandlerClient::ScrollStatus scrollStatus = m_inputHandlerClient->scrollBegin(IntPoint(gestureEvent.x, gestureEvent.y), CCInputHandlerClient::Gesture);
181196 switch (scrollStatus) {
182197 case CCInputHandlerClient::ScrollStarted:
183  m_scrollStarted = true;
184  m_client->didHandleInputEvent();
185  return;
186  case CCInputHandlerClient::ScrollIgnored:
187  m_client->didNotHandleInputEvent(false /* sendToWidget */);
188  return;
 198 m_gestureScrollStarted = true;
 199 return DidHandle;
189200 case CCInputHandlerClient::ScrollFailed:
190  break;
 201 return DidNotHandle;
 202 case CCInputHandlerClient::ScrollIgnored:
 203 return DropEvent;
191204 }
192205 } else if (event.type == WebInputEvent::GestureScrollUpdate) {
193206 ASSERT(m_expectScrollUpdateEnd);
194  if (m_scrollStarted) {
195  const WebGestureEvent& gestureEvent = *static_cast<const WebGestureEvent*>(&event);
196  m_inputHandlerClient->scrollBy(IntSize(-gestureEvent.deltaX, -gestureEvent.deltaY));
197  m_client->didHandleInputEvent();
198  return;
199  }
 207
 208 if (!m_gestureScrollStarted)
 209 return DidNotHandle;
 210
 211 const WebGestureEvent& gestureEvent = *static_cast<const WebGestureEvent*>(&event);
 212 m_inputHandlerClient->scrollBy(IntSize(-gestureEvent.deltaX, -gestureEvent.deltaY));
 213 return DidHandle;
200214 } else if (event.type == WebInputEvent::GestureScrollEnd) {
201215 ASSERT(m_expectScrollUpdateEnd);
202216#ifndef NDEBUG
203217 m_expectScrollUpdateEnd = false;
204218#endif
205  if (m_scrollStarted) {
206  m_inputHandlerClient->scrollEnd();
207  m_scrollStarted = false;
 219 if (!m_gestureScrollStarted)
 220 return DidNotHandle;
208221
209  const WebGestureEvent& gestureEvent = *static_cast<const WebGestureEvent*>(&event);
210  if (handleGestureFling(gestureEvent))
211  return;
212  }
 222 m_inputHandlerClient->scrollEnd();
 223 m_gestureScrollStarted = false;
 224 return DidHandle;
213225 } else if (event.type == WebInputEvent::GesturePinchBegin) {
214226 ASSERT(!m_expectPinchUpdateEnd);
215227#ifndef NDEBUG
216228 m_expectPinchUpdateEnd = true;
217229#endif
218230 m_inputHandlerClient->pinchGestureBegin();
219  m_client->didHandleInputEvent();
220  return;
 231 return DidHandle;
221232 } else if (event.type == WebInputEvent::GesturePinchEnd) {
222233 ASSERT(m_expectPinchUpdateEnd);
223234#ifndef NDEBUG
224235 m_expectPinchUpdateEnd = false;
225236#endif
226237 m_inputHandlerClient->pinchGestureEnd();
227  m_client->didHandleInputEvent();
228  return;
 238 return DidHandle;
229239 } else if (event.type == WebInputEvent::GesturePinchUpdate) {
230240 ASSERT(m_expectPinchUpdateEnd);
231241 const WebGestureEvent& gestureEvent = *static_cast<const WebGestureEvent*>(&event);
232242 m_inputHandlerClient->pinchGestureUpdate(gestureEvent.deltaX, IntPoint(gestureEvent.x, gestureEvent.y));
233  m_client->didHandleInputEvent();
234  return;
235  } else if (event.type == WebInputEvent::GestureTapDown) {
236  if (m_inputHandlerClient->activeGestureAnimation()) {
237  m_inputHandlerClient->setActiveGestureAnimation(nullptr);
238  m_client->didHandleInputEvent();
239  return;
240  }
 243 return DidHandle;
 244 } else if (event.type == WebInputEvent::GestureFlingStart) {
 245 const WebGestureEvent& gestureEvent = *static_cast<const WebGestureEvent*>(&event);
 246 return handleGestureFling(gestureEvent);
 247 } else if (event.type == WebInputEvent::GestureFlingCancel) {
 248 if (cancelCurrentFling())
 249 return DidHandle;
241250 }
242251
243  m_client->didNotHandleInputEvent(true /* sendToWidget */);
 252 return DidNotHandle;
244253}
245254
246 bool WebCompositorInputHandlerImpl::handleGestureFling(const WebGestureEvent& gestureEvent)
 255WebCompositorInputHandlerImpl::EventDisposition WebCompositorInputHandlerImpl::handleGestureFling(const WebGestureEvent& gestureEvent)
247256{
248  // GestureScrollEnd with non-zero velocity (deltaX/Y) signals start of Fling.
249  if (!gestureEvent.deltaX && !gestureEvent.deltaY) {
250  m_client->didHandleInputEvent();
251  return true;
252  }
253 
254257 CCInputHandlerClient::ScrollStatus scrollStatus = m_inputHandlerClient->scrollBegin(IntPoint(gestureEvent.x, gestureEvent.y), CCInputHandlerClient::Gesture);
255258 switch (scrollStatus) {
256259 case CCInputHandlerClient::ScrollStarted: {
257  OwnPtr<PlatformGestureCurve> flingCurve = TouchFlingPlatformGestureCurve::create(FloatPoint(gestureEvent.deltaX, gestureEvent.deltaY));
258  OwnPtr<CCActiveGestureAnimation> animation = CCActiveGestureAnimation::create(PlatformGestureToCCGestureAdapter::create(flingCurve.release()), this);
259  m_inputHandlerClient->setActiveGestureAnimation(animation.release());
260  m_client->didHandleInputEvent();
261  return true;
 260 TRACE_EVENT_INSTANT0("cc", "WebCompositorInputHandlerImpl::handleGestureFling::started");
 261 OwnPtr<PlatformGestureCurve> flingCurve = TouchFlingPlatformGestureCurve::create(FloatPoint(-gestureEvent.deltaX, -gestureEvent.deltaY));
 262 m_wheelFlingAnimation = CCActiveGestureAnimation::create(PlatformGestureToCCGestureAdapter::create(flingCurve.release()), this);
 263 m_wheelFlingPoint = IntPoint(gestureEvent.x, gestureEvent.y);
 264 m_inputHandlerClient->scheduleAnimation();
 265 return DidHandle;
262266 }
263  case CCInputHandlerClient::ScrollIgnored:
264  m_client->didNotHandleInputEvent(false /* sendToWidget */);
265  return true;
266267 case CCInputHandlerClient::ScrollFailed:
267  break;
 268 TRACE_EVENT_INSTANT0("cc", "WebCompositorInputHandlerImpl::handleGestureFling::failed");
 269 return DidNotHandle;
 270 case CCInputHandlerClient::ScrollIgnored:
 271 TRACE_EVENT_INSTANT0("cc", "WebCompositorInputHandlerImpl::handleGestureFling::ignored");
 272 return DropEvent;
268273 }
269 
270  return false;
 274 return DidNotHandle;
271275}
272276
273277int WebCompositorInputHandlerImpl::identifier() const

@@int WebCompositorInputHandlerImpl::identifier() const
276280 return m_identifier;
277281}
278282
279 void WebCompositorInputHandlerImpl::willDraw(double monotonicTime)
 283void WebCompositorInputHandlerImpl::animate(double monotonicTime)
 284{
 285 if (!m_wheelFlingAnimation)
 286 return;
 287
 288 if (m_wheelFlingAnimation->animate(monotonicTime))
 289 m_inputHandlerClient->scheduleAnimation();
 290 else {
 291 TRACE_EVENT_INSTANT0("cc", "WebCompositorInputHandlerImpl::animate::flingOver");
 292 m_wheelFlingAnimation.clear();
 293 }
 294}
 295
 296bool WebCompositorInputHandlerImpl::cancelCurrentFling()
280297{
 298 bool hadFlingAnimation = m_wheelFlingAnimation;
 299 TRACE_EVENT_INSTANT1("cc", "WebCompositorInputHandlerImpl::cancelCurrentFling", "hadFlingAnimation", hadFlingAnimation);
 300 m_wheelFlingAnimation.clear();
 301 return hadFlingAnimation;
281302}
282303
283304void WebCompositorInputHandlerImpl::scrollBy(const IntPoint& increment)
284305{
285  if (m_inputHandlerClient)
286  m_inputHandlerClient->scrollBy(toSize(increment));
 306 if (increment == IntPoint::zero())
 307 return;
 308
 309 TRACE_EVENT2("cc", "WebCompositorInputHandlerImpl::scrollBy", "x", increment.x(), "y", increment.y());
 310
 311 WebMouseWheelEvent event;
 312 event.type = WebInputEvent::MouseWheel;
 313 event.deltaX = -increment.x();
 314 event.deltaY = -increment.y();
 315 event.hasPreciseScrollingDeltas = true;
 316 event.x = m_wheelFlingPoint.x();
 317 event.y = m_wheelFlingPoint.y();
 318
 319 WebCompositorInputHandlerImpl::EventDisposition disposition = handleInputEventInternal(event);
 320 switch (disposition) {
 321 case DidHandle:
 322 case DropEvent:
 323 break;
 324 case DidNotHandle:
 325 TRACE_EVENT_INSTANT0("cc", "WebCompositorInputHandlerImpl::scrollBy::AbortFling");
 326 // FIXME: If we got a DidNotHandle, that means we need to deliver wheels on the main thread.
 327 // In this case we need to schedule a commit and transfer the fling curve over to the main
 328 // thread and run the rest of the wheels from there.
 329 // This can happen when flinging a page that contains a scrollable subarea that we can't
 330 // scroll on the thread if the fling starts outside the subarea but then is flung "under" the
 331 // pointer.
 332 // For now, just abort the fling.
 333 cancelCurrentFling();
 334 }
287335}
288336
289337}

Source/WebKit/chromium/src/WebCompositorInputHandlerImpl.h

2626#ifndef WebCompositorInputHandlerImpl_h
2727#define WebCompositorInputHandlerImpl_h
2828
 29#include "IntPoint.h"
2930#include "WebCompositor.h"
3031#include "WebCompositorInputHandler.h"
3132#include "WebInputEvent.h"

@@public:
6465
6566 // WebCore::CCInputHandler implementation
6667 virtual int identifier() const;
67  virtual void willDraw(double monotonicTime);
 68 virtual void animate(double monotonicTime);
6869
6970 // WebCore::CCGestureCurveTarget implementation.
7071 virtual void scrollBy(const WebCore::IntPoint&);

@@public:
7273private:
7374 explicit WebCompositorInputHandlerImpl(WebCore::CCInputHandlerClient*);
7475
75  bool handleGestureFling(const WebGestureEvent&);
 76 enum EventDisposition { DidHandle, DidNotHandle, DropEvent };
 77 // This function processes the input event and determines the disposition, but does not make
 78 // any calls out to the WebCompositorInputHandlerClient. Some input types defer to helpers.
 79 EventDisposition handleInputEventInternal(const WebInputEvent&);
 80
 81 EventDisposition handleGestureFling(const WebGestureEvent&);
 82
 83 // Returns true if we actually had an active fling to cancel.
 84 bool cancelCurrentFling();
 85
 86 OwnPtr<WebCore::CCActiveGestureAnimation> m_wheelFlingAnimation;
 87 WebCore::IntPoint m_wheelFlingPoint; // Pointer position for the current fling.
7688
7789 WebCompositorInputHandlerClient* m_client;
7890 int m_identifier;

@@private:
8294 bool m_expectScrollUpdateEnd;
8395 bool m_expectPinchUpdateEnd;
8496#endif
85  bool m_scrollStarted;
 97 bool m_gestureScrollStarted;
8698
8799 static int s_nextAvailableIdentifier;
88100 static HashSet<WebCompositorInputHandlerImpl*>* s_compositors;

Source/WebKit/chromium/src/WebViewImpl.cpp

@@void WebViewImpl::applyScrollAndScale(const WebSize& scrollDelta, float pageScal
33013301 if (!mainFrameImpl() || !mainFrameImpl()->frameView())
33023302 return;
33033303
3304  if (pageScaleDelta == 1)
 3304 if (pageScaleDelta == 1) {
 3305 TRACE_EVENT_INSTANT2("webkit", "WebViewImpl::applyScrollAndScale::scrollBy", "x", scrollDelta.width, "y", scrollDelta.height);
33053306 mainFrameImpl()->frameView()->scrollBy(scrollDelta);
3306  else {
 3307 } else {
33073308 // The page scale changed, so apply a scale and scroll in a single
33083309 // operation. The old scroll offset (and passed-in delta) are
33093310 // in the old coordinate space, so we first need to multiply them

Source/WebKit/chromium/tests/WebCompositorInputHandlerImplTest.cpp

3434#include "cc/CCInputHandler.h"
3535#include "cc/CCSingleThreadProxy.h"
3636
 37#include <gmock/gmock.h>
3738#include <gtest/gtest.h>
3839#include <wtf/OwnPtr.h>
3940

@@class MockInputHandlerClient : public WebCore::CCInputHandlerClient {
4647 WTF_MAKE_NONCOPYABLE(MockInputHandlerClient);
4748public:
4849 MockInputHandlerClient()
49  : m_scrollStatus(ScrollStarted)
50  , m_pinchStarted(false)
51  , m_pinchEnded(false)
52  , m_pinchMagnification(0)
53  , m_scrollByCalled(false)
5450 {
5551 }
5652 virtual ~MockInputHandlerClient() { }
5753
58  void setScrollStatus(ScrollStatus status) { m_scrollStatus = status; }
5954
60  bool pinchStarted() const { return m_pinchStarted; }
61  bool pinchEnded() const { return m_pinchEnded; }
62  float pinchMaginifcation() const { return m_pinchMagnification; }
63  bool scrollByCalled() const { return m_scrollByCalled; }
 55 MOCK_METHOD0(pinchGestureBegin, void());
 56 MOCK_METHOD2(pinchGestureUpdate, void(float magnifyDelta, const WebCore::IntPoint& anchor));
 57 MOCK_METHOD0(pinchGestureEnd, void());
6458
65  void resetPinch()
66  {
67  m_pinchStarted = m_pinchEnded = m_scrollByCalled = false;
68  m_pinchMagnification = 0;
69  }
 59 MOCK_METHOD0(scheduleAnimation, void());
7060
71  virtual WebCore::CCActiveGestureAnimation* activeGestureAnimation() { return m_activeGestureAnimation.get(); }
72  virtual void setActiveGestureAnimation(PassOwnPtr<WebCore::CCActiveGestureAnimation> animation) { m_activeGestureAnimation = animation; }
 61 MOCK_METHOD2(scrollBegin, ScrollStatus(const WebCore::IntPoint&, WebCore::CCInputHandlerClient::ScrollInputType));
 62 MOCK_METHOD1(scrollBy, void(const WebCore::IntSize&));
 63 MOCK_METHOD0(scrollEnd, void());
7364
7465private:
75  virtual void setNeedsRedraw() OVERRIDE { }
76  virtual ScrollStatus scrollBegin(const WebCore::IntPoint&, WebCore::CCInputHandlerClient::ScrollInputType) OVERRIDE
77  {
78  return m_scrollStatus;
79  }
80  virtual void scrollBy(const WebCore::IntSize&) OVERRIDE { m_scrollByCalled = true; }
81  virtual void scrollEnd() OVERRIDE { }
82 
83  virtual void pinchGestureBegin() OVERRIDE
84  {
85  m_pinchStarted = true;
86  }
87  virtual void pinchGestureUpdate(float magnifyDelta, const WebCore::IntPoint& anchor) OVERRIDE
88  {
89  m_pinchMagnification = magnifyDelta;
90  }
91  virtual void pinchGestureEnd() OVERRIDE
92  {
93  m_pinchEnded = true;
94  }
9566 virtual void startPageScaleAnimation(const WebCore::IntSize& targetPosition,
9667 bool anchorPoint,
9768 float pageScale,
9869 double startTimeMs,
9970 double durationMs) OVERRIDE { }
10071
101  ScrollStatus m_scrollStatus;
102  bool m_pinchStarted;
103  bool m_pinchEnded;
104  float m_pinchMagnification;
105  bool m_scrollByCalled;
106 
107  OwnPtr<WebCore::CCActiveGestureAnimation> m_activeGestureAnimation;
 72 virtual WebCore::CCActiveGestureAnimation* activeGestureAnimation() OVERRIDE { return 0; }
 73 virtual void setActiveGestureAnimation(PassOwnPtr<WebCore::CCActiveGestureAnimation>) OVERRIDE { }
10874};
10975
11076class MockWebCompositorInputHandlerClient : public WebKit::WebCompositorInputHandlerClient {
11177 WTF_MAKE_NONCOPYABLE(MockWebCompositorInputHandlerClient);
11278public:
11379 MockWebCompositorInputHandlerClient()
114  : m_handled(false)
115  , m_sendToWidget(false)
 80 : WebKit::WebCompositorInputHandlerClient()
11681 {
11782 }
11883 virtual ~MockWebCompositorInputHandlerClient() { }
11984
120  void reset()
121  {
122  m_handled = false;
123  m_sendToWidget = false;
124  }
125 
126  bool handled() const { return m_handled; }
127  bool sendToWidget() const { return m_sendToWidget; }
128 
129 private:
130  virtual void willShutdown() OVERRIDE { }
131  virtual void didHandleInputEvent() OVERRIDE
132  {
133  m_handled = true;
134  }
135  virtual void didNotHandleInputEvent(bool sendToWidget) OVERRIDE
136  {
137  m_sendToWidget = sendToWidget;
138  }
139 
140  bool m_handled;
141  bool m_sendToWidget;
 85 MOCK_METHOD0(willShutdown, void());
 86 MOCK_METHOD0(didHandleInputEvent, void());
 87 MOCK_METHOD1(didNotHandleInputEvent, void(bool sendToWidget));
14288};
14389
14490TEST(WebCompositorInputHandlerImpl, fromIdentifier)
14591{
14692 WebKit::WebCompositor::initialize(0);
147 #ifndef NDEBUG
148  // WebCompositorInputHandler APIs can only be called from the compositor thread.
14993 WebCore::DebugScopedSetImplThread alwaysImplThread;
150 #endif
15194
15295 // Before creating any WebCompositorInputHandlers, lookups for any value should fail and not crash.
15396 EXPECT_EQ(0, WebCompositorInputHandler::fromIdentifier(2));

@@TEST(WebCompositorInputHandlerImpl, fromIdentifier)
167110
168111 // After the compositor is destroyed, its entry should be removed from the map.
169112 EXPECT_EQ(0, WebCompositorInputHandler::fromIdentifier(compositorIdentifier));
170 
171113 WebKit::WebCompositor::shutdown();
172114}
173115
174 TEST(WebCompositorInputHandlerImpl, gestureScroll)
175 {
176  WebKit::WebCompositor::initialize(0);
177 #ifndef NDEBUG
178  // WebCompositorInputHandler APIs can only be called from the compositor thread.
179  WebCore::DebugScopedSetImplThread alwaysImplThread;
180 #endif
 116class WebCompositorInputHandlerImplTest : public testing::Test {
 117public:
 118 WebCompositorInputHandlerImplTest()
 119 {
 120 WebKit::WebCompositor::initialize(0);
 121 m_inputHandler = WebCompositorInputHandlerImpl::create(&m_mockInputHandler);
 122 m_inputHandler->setClient(&m_mockClient);
 123 }
181124
182  MockInputHandlerClient mockInputHandler;
183  OwnPtr<WebCompositorInputHandlerImpl> inputHandler = WebCompositorInputHandlerImpl::create(&mockInputHandler);
184  MockWebCompositorInputHandlerClient mockClient;
185  inputHandler->setClient(&mockClient);
 125 ~WebCompositorInputHandlerImplTest()
 126 {
 127 m_inputHandler->setClient(0);
 128 m_inputHandler.clear();
 129 WebKit::WebCompositor::shutdown();
 130 }
186131
 132protected:
 133 MockInputHandlerClient m_mockInputHandler;
 134 OwnPtr<WebCompositorInputHandlerImpl> m_inputHandler;
 135 MockWebCompositorInputHandlerClient m_mockClient;
187136 WebKit::WebGestureEvent gesture;
188137
 138private:
 139 WebCore::DebugScopedSetImplThread m_alwaysImplThread;
 140};
 141
 142
 143TEST_F(WebCompositorInputHandlerImplTest, gestureScrollStarted)
 144{
 145 EXPECT_CALL(m_mockInputHandler, scrollBegin(testing::_, testing::_))
 146 .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollStarted));
 147 // We shouldn't send any events to the widget for this gesture.
 148 // Instead, we should get one didHandleInputEvent() call per handleInputEvent().
 149 EXPECT_CALL(m_mockClient, didNotHandleInputEvent(::testing::_)).Times(0);
 150
189151 gesture.type = WebKit::WebInputEvent::GestureScrollBegin;
190  inputHandler->handleInputEvent(gesture);
191  EXPECT_TRUE(mockClient.handled());
192  EXPECT_FALSE(mockClient.sendToWidget());
193  mockClient.reset();
 152 EXPECT_CALL(m_mockClient, didHandleInputEvent());
 153 m_inputHandler->handleInputEvent(gesture);
194154
195155 gesture.type = WebKit::WebInputEvent::GestureScrollUpdate;
196  gesture.deltaY = 40;
197  inputHandler->handleInputEvent(gesture);
198  EXPECT_TRUE(mockClient.handled());
199  EXPECT_FALSE(mockClient.sendToWidget());
200  mockClient.reset();
 156 gesture.deltaY = -40; // -Y means scroll down - i.e. in the +Y direction.
 157 EXPECT_CALL(m_mockInputHandler, scrollBy(testing::Property(&WebCore::IntSize::height, testing::Gt(0))));
 158 EXPECT_CALL(m_mockClient, didHandleInputEvent());
 159 m_inputHandler->handleInputEvent(gesture);
201160
202161 gesture.type = WebKit::WebInputEvent::GestureScrollEnd;
203162 gesture.deltaY = 0;
204  inputHandler->handleInputEvent(gesture);
205  EXPECT_TRUE(mockClient.handled());
206  EXPECT_FALSE(mockClient.sendToWidget());
207  mockClient.reset();
 163 EXPECT_CALL(m_mockInputHandler, scrollEnd());
 164 EXPECT_CALL(m_mockClient, didHandleInputEvent());
 165 m_inputHandler->handleInputEvent(gesture);
 166}
208167
209  mockInputHandler.setScrollStatus(WebCore::CCInputHandlerClient::ScrollFailed);
 168TEST_F(WebCompositorInputHandlerImplTest, gestureScrollFailed)
 169{
 170 EXPECT_CALL(m_mockInputHandler, scrollBegin(::testing::_, ::testing::_))
 171 .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollFailed));
 172 // We shouldn't handle any of these inputs ourselves.
 173 // Instead, we should get one didNotHandleInputEvent(true) call per handleInputEvent(),
 174 // indicating that while we couldn't scroll the widget still needs to see the event.
 175 EXPECT_CALL(m_mockClient, didHandleInputEvent()).Times(0);
210176
211177 gesture.type = WebKit::WebInputEvent::GestureScrollBegin;
212  inputHandler->handleInputEvent(gesture);
213  EXPECT_FALSE(mockClient.handled());
214  EXPECT_TRUE(mockClient.sendToWidget());
215  mockClient.reset();
 178 EXPECT_CALL(m_mockClient, didNotHandleInputEvent(true));
 179 m_inputHandler->handleInputEvent(gesture);
216180
217181 gesture.type = WebKit::WebInputEvent::GestureScrollUpdate;
218182 gesture.deltaY = 40;
219  inputHandler->handleInputEvent(gesture);
220  EXPECT_FALSE(mockClient.handled());
221  EXPECT_TRUE(mockClient.sendToWidget());
222  mockClient.reset();
 183 EXPECT_CALL(m_mockClient, didNotHandleInputEvent(true));
 184 m_inputHandler->handleInputEvent(gesture);
223185
224186 gesture.type = WebKit::WebInputEvent::GestureScrollEnd;
225187 gesture.deltaY = 0;
226  inputHandler->handleInputEvent(gesture);
227  EXPECT_FALSE(mockClient.handled());
228  EXPECT_TRUE(mockClient.sendToWidget());
229  mockClient.reset();
230 
231  inputHandler->setClient(0);
232 
233  WebKit::WebCompositor::shutdown();
 188 EXPECT_CALL(m_mockClient, didNotHandleInputEvent(true));
 189 m_inputHandler->handleInputEvent(gesture);
234190}
235191
236 TEST(WebCompositorInputHandlerImpl, gesturePinch)
 192TEST_F(WebCompositorInputHandlerImplTest, gestureScrollIgnored)
237193{
238  WebKit::WebCompositor::initialize(0);
239 #ifndef NDEBUG
240  // WebCompositorInputHandler APIs can only be called from the compositor thread.
241  WebCore::DebugScopedSetImplThread alwaysImplThread;
242 #endif
 194 EXPECT_CALL(m_mockInputHandler, scrollBegin(testing::_, testing::_))
 195 .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollIgnored));
243196
244  MockInputHandlerClient mockInputHandler;
245  OwnPtr<WebCompositorInputHandlerImpl> inputHandler = WebCompositorInputHandlerImpl::create(&mockInputHandler);
246  MockWebCompositorInputHandlerClient mockClient;
247  inputHandler->setClient(&mockClient);
 197 // We shouldn't handle any of these inputs ourselves.
 198 // Instead, we should get one didNotHandleInputEvent(false) call per handleInputEvent(),
 199 // indicating that we could determine that there's nothing that could scroll or otherwise
 200 // react to this gesture sequence and thus we should drop the whole gesture sequence on the floor.
 201 EXPECT_CALL(m_mockClient, didHandleInputEvent()).Times(0);
248202
249  WebKit::WebGestureEvent gesture;
 203 gesture.type = WebKit::WebInputEvent::GestureScrollBegin;
 204 EXPECT_CALL(m_mockClient, didNotHandleInputEvent(false));
 205 m_inputHandler->handleInputEvent(gesture);
 206}
250207
 208TEST_F(WebCompositorInputHandlerImplTest, gesturePinch)
 209{
 210 // We should handle the entire sequence ourselves.
 211 EXPECT_CALL(m_mockClient, didNotHandleInputEvent(::testing::_)).Times(0);
 212
251213 gesture.type = WebKit::WebInputEvent::GesturePinchBegin;
252  inputHandler->handleInputEvent(gesture);
253  EXPECT_TRUE(mockClient.handled());
254  EXPECT_FALSE(mockClient.sendToWidget());
255  EXPECT_TRUE(mockInputHandler.pinchStarted());
256  mockClient.reset();
257  mockInputHandler.resetPinch();
 214 EXPECT_CALL(m_mockInputHandler, pinchGestureBegin());
 215 EXPECT_CALL(m_mockClient, didHandleInputEvent());
 216 m_inputHandler->handleInputEvent(gesture);
258217
259218 gesture.type = WebKit::WebInputEvent::GesturePinchUpdate;
260219 gesture.deltaX = 1.5;
261  inputHandler->handleInputEvent(gesture);
262  EXPECT_TRUE(mockClient.handled());
263  EXPECT_FALSE(mockClient.sendToWidget());
264  EXPECT_FALSE(mockInputHandler.pinchEnded());
265  EXPECT_EQ(1.5, mockInputHandler.pinchMaginifcation());
266  mockClient.reset();
267  mockInputHandler.resetPinch();
 220 gesture.x = 7;
 221 gesture.y = 13;
 222 EXPECT_CALL(m_mockInputHandler, pinchGestureUpdate(1.5, WebCore::IntPoint(7, 13)));
 223 EXPECT_CALL(m_mockClient, didHandleInputEvent());
 224 m_inputHandler->handleInputEvent(gesture);
268225
269226 gesture.type = WebKit::WebInputEvent::GesturePinchUpdate;
270227 gesture.deltaX = 0.5;
271  inputHandler->handleInputEvent(gesture);
272  EXPECT_TRUE(mockClient.handled());
273  EXPECT_FALSE(mockClient.sendToWidget());
274  EXPECT_FALSE(mockInputHandler.pinchEnded());
275  EXPECT_EQ(0.5, mockInputHandler.pinchMaginifcation());
276  mockClient.reset();
277  mockInputHandler.resetPinch();
 228 gesture.x = 9;
 229 gesture.y = 6;
 230 EXPECT_CALL(m_mockInputHandler, pinchGestureUpdate(.5, WebCore::IntPoint(9, 6)));
 231 EXPECT_CALL(m_mockClient, didHandleInputEvent());
 232 m_inputHandler->handleInputEvent(gesture);
278233
279234 gesture.type = WebKit::WebInputEvent::GesturePinchEnd;
280  inputHandler->handleInputEvent(gesture);
281  EXPECT_TRUE(mockClient.handled());
282  EXPECT_FALSE(mockClient.sendToWidget());
283  EXPECT_TRUE(mockInputHandler.pinchEnded());
284  mockClient.reset();
285  mockInputHandler.resetPinch();
286 
287  inputHandler->setClient(0);
288 
289  WebKit::WebCompositor::shutdown();
 235 EXPECT_CALL(m_mockInputHandler, pinchGestureEnd());
 236 EXPECT_CALL(m_mockClient, didHandleInputEvent());
 237 m_inputHandler->handleInputEvent(gesture);
290238}
291239
292 TEST(WebCompositorInputHandlerImpl, gestureFling)
 240TEST_F(WebCompositorInputHandlerImplTest, gestureFlingStarted)
293241{
294  WebKit::WebCompositor::initialize(0);
 242 EXPECT_CALL(m_mockInputHandler, scrollBegin(testing::_, testing::_))
 243 .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollStarted));
 244
 245 // We should handle the entire sequence ourselves.
 246 EXPECT_CALL(m_mockClient, didNotHandleInputEvent(::testing::_)).Times(0);
 247
 248 gesture.type = WebKit::WebInputEvent::GestureFlingStart;
 249 gesture.deltaX = 10;
 250 EXPECT_CALL(m_mockClient, didHandleInputEvent());
 251 EXPECT_CALL(m_mockInputHandler, scheduleAnimation());
 252 m_inputHandler->handleInputEvent(gesture);
 253
 254 // Verify that a GestureFlingCancel during an animation cancels it.
 255 gesture.type = WebKit::WebInputEvent::GestureFlingCancel;
 256 EXPECT_CALL(m_mockClient, didHandleInputEvent());
 257 m_inputHandler->handleInputEvent(gesture);
 258}
295259
296  // WebCompositorInputHandler APIs can only be called from the compositor thread.
297  WebCore::DebugScopedSetImplThread alwaysImplThread;
 260TEST_F(WebCompositorInputHandlerImplTest, gestureFlingFailed)
 261{
 262 EXPECT_CALL(m_mockInputHandler, scrollBegin(testing::_, testing::_))
 263 .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollFailed));
298264
299  MockInputHandlerClient mockInputHandler;
300  OwnPtr<WebCompositorInputHandlerImpl> inputHandler = WebCompositorInputHandlerImpl::create(&mockInputHandler);
301  MockWebCompositorInputHandlerClient mockClient;
302  inputHandler->setClient(&mockClient);
 265 // We shouldn't handle any part of the sequence ourselves.
 266 EXPECT_CALL(m_mockClient, didHandleInputEvent()).Times(0);
303267
304  WebKit::WebGestureEvent gesture;
 268 gesture.type = WebKit::WebInputEvent::GestureFlingStart;
 269 EXPECT_CALL(m_mockClient, didNotHandleInputEvent(true));
 270 m_inputHandler->handleInputEvent(gesture);
305271
306  // Need scrollBegin before sending a scrollEnd event.
307  gesture.type = WebKit::WebInputEvent::GestureScrollBegin;
308  inputHandler->handleInputEvent(gesture);
309  EXPECT_TRUE(mockClient.handled());
310  EXPECT_FALSE(mockClient.sendToWidget());
311  mockClient.reset();
 272 // Even if we didn't start a fling ourselves, we still need to send the cancel event to the widget.
 273 gesture.type = WebKit::WebInputEvent::GestureFlingCancel;
 274 EXPECT_CALL(m_mockClient, didNotHandleInputEvent(true));
 275 m_inputHandler->handleInputEvent(gesture);
 276}
312277
313  gesture.type = WebKit::WebInputEvent::GestureScrollEnd;
314  gesture.deltaX = 2;
315  inputHandler->handleInputEvent(gesture);
316  EXPECT_TRUE(mockClient.handled());
317  EXPECT_FALSE(mockClient.sendToWidget());
318  ASSERT_TRUE(mockInputHandler.activeGestureAnimation());
319  EXPECT_FALSE(mockInputHandler.scrollByCalled());
320  mockInputHandler.activeGestureAnimation()->animate(0.001);
321  EXPECT_TRUE(mockInputHandler.scrollByCalled());
322  mockClient.reset();
323 
324  // Verify that a GestureTapDown during an animation cancels it.
325  gesture.type = WebKit::WebInputEvent::GestureTapDown;
326  inputHandler->handleInputEvent(gesture);
327  EXPECT_FALSE(mockInputHandler.activeGestureAnimation());
328  mockClient.reset();
329 
330  // Verify that a GestureTapDown when no animation is active is not handled.
331  gesture.type = WebKit::WebInputEvent::GestureTapDown;
332  inputHandler->handleInputEvent(gesture);
333  EXPECT_FALSE(mockClient.handled());
334  EXPECT_TRUE(mockClient.sendToWidget());
335 
336  inputHandler->setClient(0);
337  WebKit::WebCompositor::shutdown();
 278TEST_F(WebCompositorInputHandlerImplTest, gestureFlingAnimates)
 279{
 280 // We should handle the entire sequence ourselves.
 281 EXPECT_CALL(m_mockClient, didNotHandleInputEvent(::testing::_)).Times(0);
 282
 283 // On the fling start, we should schedule an animation but not actually start
 284 // scrolling.
 285 gesture.type = WebKit::WebInputEvent::GestureFlingStart;
 286 gesture.deltaX = -1000;
 287 EXPECT_CALL(m_mockClient, didHandleInputEvent());
 288 EXPECT_CALL(m_mockInputHandler, scheduleAnimation());
 289 EXPECT_CALL(m_mockInputHandler, scrollBegin(testing::_, testing::_))
 290 .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollStarted));
 291 m_inputHandler->handleInputEvent(gesture);
 292
 293 // The first animate call should let us pick up an animation start time, but we
 294 // shouldn't actually move anywhere just yet. The first frame after the fling start
 295 // will typically include the last scroll from the gesture that lead to the scroll
 296 // (either wheel or gesture scroll), so there should be no visible hitch.
 297 EXPECT_CALL(m_mockInputHandler, scheduleAnimation());
 298 EXPECT_CALL(m_mockInputHandler, scrollBegin(testing::_, testing::_)).Times(0);
 299 m_inputHandler->animate(10);
 300
 301 // The second call should start scrolling in the +X direction.
 302 EXPECT_CALL(m_mockInputHandler, scheduleAnimation());
 303 EXPECT_CALL(m_mockInputHandler, scrollBegin(testing::_, testing::_))
 304 .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollStarted));
 305 EXPECT_CALL(m_mockInputHandler, scrollBy(testing::Property(&WebCore::IntSize::width, testing::Gt(0))));
 306 EXPECT_CALL(m_mockInputHandler, scrollEnd());
 307 m_inputHandler->animate(10.1);
 308
 309 // Let's say on the third call we hit a non-scrollable region. We should abort the fling and not scroll.
 310 // FIXME: We also need to do some work to transfer the rest of this fling to the main thread.
 311 // Add tests for this once it's implemented.
 312 EXPECT_CALL(m_mockInputHandler, scheduleAnimation());
 313 EXPECT_CALL(m_mockInputHandler, scrollBegin(testing::_, testing::_))
 314 .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollIgnored));
 315 EXPECT_CALL(m_mockInputHandler, scrollBy(testing::_)).Times(0);
 316 EXPECT_CALL(m_mockInputHandler, scrollEnd()).Times(0);
 317 m_inputHandler->animate(10.2);
 318
 319 // Since we've aborted the fling, the next animation should be a no-op and should not result in another
 320 // frame being requested.
 321 EXPECT_CALL(m_mockInputHandler, scheduleAnimation()).Times(0);
 322 EXPECT_CALL(m_mockInputHandler, scrollBegin(testing::_, testing::_)).Times(0);
 323 m_inputHandler->animate(10.3);
338324}
339325
340326}