~ubuntu-branches/ubuntu/raring/qtwebkit-source/raring-proposed

« back to all changes in this revision

Viewing changes to Source/WebKit/chromium/src/WebCompositorInputHandlerImpl.cpp

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2013-02-18 14:24:18 UTC
  • Revision ID: package-import@ubuntu.com-20130218142418-eon0jmjg3nj438uy
Tags: upstream-2.3
ImportĀ upstreamĀ versionĀ 2.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2011 Google Inc. All rights reserved.
 
3
 *
 
4
 * Redistribution and use in source and binary forms, with or without
 
5
 * modification, are permitted provided that the following conditions
 
6
 * are met:
 
7
 *
 
8
 * 1.  Redistributions of source code must retain the above copyright
 
9
 *     notice, this list of conditions and the following disclaimer.
 
10
 * 2.  Redistributions in binary form must reproduce the above copyright
 
11
 *     notice, this list of conditions and the following disclaimer in the
 
12
 *     documentation and/or other materials provided with the distribution.
 
13
 *
 
14
 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
 
15
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 
16
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 
17
 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
 
18
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 
19
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 
20
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 
21
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 
22
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 
23
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
24
 */
 
25
 
 
26
#include "config.h"
 
27
 
 
28
#include "WebCompositorInputHandlerImpl.h"
 
29
 
 
30
#include "TraceEvent.h"
 
31
#include "WebCompositorInputHandlerClient.h"
 
32
#include "WebInputEvent.h"
 
33
#include <public/Platform.h>
 
34
#include <public/WebInputHandlerClient.h>
 
35
#include <wtf/PassOwnPtr.h>
 
36
#include <wtf/ThreadingPrimitives.h>
 
37
 
 
38
using namespace WebCore;
 
39
 
 
40
namespace WebKit {
 
41
 
 
42
// These statics may only be accessed from the compositor thread.
 
43
int WebCompositorInputHandlerImpl::s_nextAvailableIdentifier = 1;
 
44
HashSet<WebCompositorInputHandlerImpl*>* WebCompositorInputHandlerImpl::s_compositors = 0;
 
45
 
 
46
WebCompositorInputHandler* WebCompositorInputHandler::fromIdentifier(int identifier)
 
47
{
 
48
    return WebCompositorInputHandlerImpl::fromIdentifier(identifier);
 
49
}
 
50
 
 
51
WebCompositorInputHandler* WebCompositorInputHandlerImpl::fromIdentifier(int identifier)
 
52
{
 
53
 
 
54
    if (!s_compositors)
 
55
        return 0;
 
56
 
 
57
    for (HashSet<WebCompositorInputHandlerImpl*>::iterator it = s_compositors->begin(); it != s_compositors->end(); ++it) {
 
58
        if ((*it)->identifier() == identifier)
 
59
            return *it;
 
60
    }
 
61
    return 0;
 
62
}
 
63
 
 
64
WebCompositorInputHandlerImpl::WebCompositorInputHandlerImpl()
 
65
    : m_client(0)
 
66
    , m_identifier(s_nextAvailableIdentifier++)
 
67
    , m_inputHandlerClient(0)
 
68
#ifndef NDEBUG
 
69
    , m_expectScrollUpdateEnd(false)
 
70
    , m_expectPinchUpdateEnd(false)
 
71
#endif
 
72
    , m_gestureScrollOnImplThread(false)
 
73
    , m_gesturePinchOnImplThread(false)
 
74
{
 
75
}
 
76
 
 
77
WebCompositorInputHandlerImpl::~WebCompositorInputHandlerImpl()
 
78
{
 
79
    if (m_client)
 
80
        m_client->willShutdown();
 
81
 
 
82
    ASSERT(s_compositors);
 
83
    s_compositors->remove(this);
 
84
    if (!s_compositors->size()) {
 
85
        delete s_compositors;
 
86
        s_compositors = 0;
 
87
    }
 
88
}
 
89
 
 
90
void WebCompositorInputHandlerImpl::setClient(WebCompositorInputHandlerClient* client)
 
91
{
 
92
    // It's valid to set a new client if we've never had one or to clear the client, but it's not valid to change from having one client to a different one.
 
93
    ASSERT(!m_client || !client);
 
94
    m_client = client;
 
95
}
 
96
 
 
97
void WebCompositorInputHandlerImpl::handleInputEvent(const WebInputEvent& event)
 
98
{
 
99
    ASSERT(m_client);
 
100
 
 
101
    WebCompositorInputHandlerImpl::EventDisposition disposition = handleInputEventInternal(event);
 
102
    switch (disposition) {
 
103
    case DidHandle:
 
104
        m_client->didHandleInputEvent();
 
105
        break;
 
106
    case DidNotHandle:
 
107
        m_client->didNotHandleInputEvent(true /* sendToWidget */);
 
108
        break;
 
109
    case DropEvent:
 
110
        m_client->didNotHandleInputEvent(false /* sendToWidget */);
 
111
        break;
 
112
    }
 
113
}
 
114
 
 
115
WebCompositorInputHandlerImpl::EventDisposition WebCompositorInputHandlerImpl::handleInputEventInternal(const WebInputEvent& event)
 
116
{
 
117
    if (event.type == WebInputEvent::MouseWheel) {
 
118
        const WebMouseWheelEvent& wheelEvent = *static_cast<const WebMouseWheelEvent*>(&event);
 
119
        WebInputHandlerClient::ScrollStatus scrollStatus = m_inputHandlerClient->scrollBegin(WebPoint(wheelEvent.x, wheelEvent.y), WebInputHandlerClient::ScrollInputTypeWheel);
 
120
        switch (scrollStatus) {
 
121
        case WebInputHandlerClient::ScrollStatusStarted: {
 
122
            TRACE_EVENT_INSTANT2("webkit", "WebCompositorInputHandlerImpl::handleInput wheel scroll", "deltaX", -wheelEvent.deltaX, "deltaY", -wheelEvent.deltaY);
 
123
            bool didScroll = m_inputHandlerClient->scrollByIfPossible(WebPoint(wheelEvent.x, wheelEvent.y), IntSize(-wheelEvent.deltaX, -wheelEvent.deltaY));
 
124
            m_inputHandlerClient->scrollEnd();
 
125
            return didScroll ? DidHandle : DropEvent;
 
126
        }
 
127
        case WebInputHandlerClient::ScrollStatusIgnored:
 
128
            // FIXME: This should be DropEvent, but in cases where we fail to properly sync scrollability it's safer to send the
 
129
            // event to the main thread. Change back to DropEvent once we have synchronization bugs sorted out.
 
130
            return DidNotHandle; 
 
131
        case WebInputHandlerClient::ScrollStatusOnMainThread:
 
132
            return DidNotHandle;
 
133
        }
 
134
    } else if (event.type == WebInputEvent::GestureScrollBegin) {
 
135
        ASSERT(!m_gestureScrollOnImplThread);
 
136
        ASSERT(!m_expectScrollUpdateEnd);
 
137
#ifndef NDEBUG
 
138
        m_expectScrollUpdateEnd = true;
 
139
#endif
 
140
        const WebGestureEvent& gestureEvent = *static_cast<const WebGestureEvent*>(&event);
 
141
        WebInputHandlerClient::ScrollStatus scrollStatus = m_inputHandlerClient->scrollBegin(WebPoint(gestureEvent.x, gestureEvent.y), WebInputHandlerClient::ScrollInputTypeGesture);
 
142
        switch (scrollStatus) {
 
143
        case WebInputHandlerClient::ScrollStatusStarted:
 
144
            m_gestureScrollOnImplThread = true;
 
145
            return DidHandle;
 
146
        case WebInputHandlerClient::ScrollStatusOnMainThread:
 
147
            return DidNotHandle;
 
148
        case WebInputHandlerClient::ScrollStatusIgnored:
 
149
            return DropEvent;
 
150
        }
 
151
    } else if (event.type == WebInputEvent::GestureScrollUpdate) {
 
152
        ASSERT(m_expectScrollUpdateEnd);
 
153
 
 
154
        if (!m_gestureScrollOnImplThread && !m_gesturePinchOnImplThread)
 
155
            return DidNotHandle;
 
156
 
 
157
        const WebGestureEvent& gestureEvent = *static_cast<const WebGestureEvent*>(&event);
 
158
        bool didScroll = m_inputHandlerClient->scrollByIfPossible(WebPoint(gestureEvent.x, gestureEvent.y),
 
159
            IntSize(-gestureEvent.data.scrollUpdate.deltaX, -gestureEvent.data.scrollUpdate.deltaY));
 
160
        return didScroll ? DidHandle : DropEvent;
 
161
    } else if (event.type == WebInputEvent::GestureScrollEnd) {
 
162
        ASSERT(m_expectScrollUpdateEnd);
 
163
#ifndef NDEBUG
 
164
        m_expectScrollUpdateEnd = false;
 
165
#endif
 
166
        if (!m_gestureScrollOnImplThread)
 
167
            return DidNotHandle;
 
168
 
 
169
        m_inputHandlerClient->scrollEnd();
 
170
        m_gestureScrollOnImplThread = false;
 
171
        return DidHandle;
 
172
    } else if (event.type == WebInputEvent::GesturePinchBegin) {
 
173
        ASSERT(!m_expectPinchUpdateEnd);
 
174
#ifndef NDEBUG
 
175
        m_expectPinchUpdateEnd = true;
 
176
#endif
 
177
        m_inputHandlerClient->pinchGestureBegin();
 
178
        m_gesturePinchOnImplThread = true;
 
179
        return DidHandle;
 
180
    } else if (event.type == WebInputEvent::GesturePinchEnd) {
 
181
        ASSERT(m_expectPinchUpdateEnd);
 
182
#ifndef NDEBUG
 
183
        m_expectPinchUpdateEnd = false;
 
184
#endif
 
185
        m_gesturePinchOnImplThread = false;
 
186
        m_inputHandlerClient->pinchGestureEnd();
 
187
        return DidHandle;
 
188
    } else if (event.type == WebInputEvent::GesturePinchUpdate) {
 
189
        ASSERT(m_expectPinchUpdateEnd);
 
190
        const WebGestureEvent& gestureEvent = *static_cast<const WebGestureEvent*>(&event);
 
191
        m_inputHandlerClient->pinchGestureUpdate(gestureEvent.data.pinchUpdate.scale, WebPoint(gestureEvent.x, gestureEvent.y));
 
192
        return DidHandle;
 
193
    } else if (event.type == WebInputEvent::GestureFlingStart) {
 
194
        const WebGestureEvent& gestureEvent = *static_cast<const WebGestureEvent*>(&event);
 
195
        return handleGestureFling(gestureEvent);
 
196
    } else if (event.type == WebInputEvent::GestureFlingCancel) {
 
197
        if (cancelCurrentFling())
 
198
            return DidHandle;
 
199
    } else if (WebInputEvent::isKeyboardEventType(event.type)) {
 
200
         cancelCurrentFling();
 
201
    }
 
202
 
 
203
    return DidNotHandle;
 
204
}
 
205
 
 
206
WebCompositorInputHandlerImpl::EventDisposition WebCompositorInputHandlerImpl::handleGestureFling(const WebGestureEvent& gestureEvent)
 
207
{
 
208
    WebInputHandlerClient::ScrollStatus scrollStatus = m_inputHandlerClient->scrollBegin(WebPoint(gestureEvent.x, gestureEvent.y), WebInputHandlerClient::ScrollInputTypeGesture);
 
209
    switch (scrollStatus) {
 
210
    case WebInputHandlerClient::ScrollStatusStarted: {
 
211
        if (gestureEvent.data.flingStart.sourceDevice == WebGestureEvent::Touchpad)
 
212
            m_inputHandlerClient->scrollEnd();
 
213
        m_flingCurve = adoptPtr(Platform::current()->createFlingAnimationCurve(gestureEvent.data.flingStart.sourceDevice, WebFloatPoint(gestureEvent.data.flingStart.velocityX, gestureEvent.data.flingStart.velocityY), WebSize()));
 
214
        TRACE_EVENT_ASYNC_BEGIN0("webkit", "WebCompositorInputHandlerImpl::handleGestureFling::started", this);
 
215
        m_flingParameters.delta = WebFloatPoint(gestureEvent.data.flingStart.velocityX, gestureEvent.data.flingStart.velocityY);
 
216
        m_flingParameters.point = WebPoint(gestureEvent.x, gestureEvent.y);
 
217
        m_flingParameters.globalPoint = WebPoint(gestureEvent.globalX, gestureEvent.globalY);
 
218
        m_flingParameters.modifiers = gestureEvent.modifiers;
 
219
        m_flingParameters.sourceDevice = gestureEvent.data.flingStart.sourceDevice;
 
220
        m_inputHandlerClient->scheduleAnimation();
 
221
        return DidHandle;
 
222
    }
 
223
    case WebInputHandlerClient::ScrollStatusOnMainThread: {
 
224
        TRACE_EVENT_INSTANT0("webkit", "WebCompositorInputHandlerImpl::handleGestureFling::scrollOnMainThread");
 
225
        return DidNotHandle;
 
226
    }
 
227
    case WebInputHandlerClient::ScrollStatusIgnored: {
 
228
        TRACE_EVENT_INSTANT0("webkit", "WebCompositorInputHandlerImpl::handleGestureFling::ignored");
 
229
        if (gestureEvent.data.flingStart.sourceDevice == WebGestureEvent::Touchpad) {
 
230
            // We still pass the curve to the main thread if there's nothing scrollable, in case something
 
231
            // registers a handler before the curve is over.
 
232
            return DidNotHandle;
 
233
        }
 
234
        return DropEvent;
 
235
    }
 
236
    }
 
237
    return DidNotHandle;
 
238
}
 
239
 
 
240
void WebCompositorInputHandlerImpl::bindToClient(WebInputHandlerClient* client)
 
241
{
 
242
    ASSERT(!m_inputHandlerClient);
 
243
 
 
244
    TRACE_EVENT_INSTANT0("webkit", "WebCompositorInputHandlerImpl::bindToClient");
 
245
    if (!s_compositors)
 
246
        s_compositors = new HashSet<WebCompositorInputHandlerImpl*>;
 
247
    s_compositors->add(this);
 
248
 
 
249
    m_inputHandlerClient = client;
 
250
}
 
251
 
 
252
void WebCompositorInputHandlerImpl::animate(double monotonicTime)
 
253
{
 
254
    if (!m_flingCurve)
 
255
        return;
 
256
 
 
257
    if (!m_flingParameters.startTime) {
 
258
        m_flingParameters.startTime = monotonicTime;
 
259
        m_inputHandlerClient->scheduleAnimation();
 
260
        return;
 
261
    }
 
262
 
 
263
    if (m_flingCurve->apply(monotonicTime - m_flingParameters.startTime, this))
 
264
        m_inputHandlerClient->scheduleAnimation();
 
265
    else {
 
266
        TRACE_EVENT_INSTANT0("webkit", "WebCompositorInputHandlerImpl::animate::flingOver");
 
267
        cancelCurrentFling();
 
268
    }
 
269
}
 
270
 
 
271
bool WebCompositorInputHandlerImpl::cancelCurrentFling()
 
272
{
 
273
    bool hadFlingAnimation = m_flingCurve;
 
274
    if (hadFlingAnimation && m_flingParameters.sourceDevice == WebGestureEvent::Touchscreen) {
 
275
        m_inputHandlerClient->scrollEnd();
 
276
        TRACE_EVENT_ASYNC_END0("webkit", "WebCompositorInputHandlerImpl::handleGestureFling::started", this);
 
277
    }
 
278
 
 
279
    TRACE_EVENT_INSTANT1("webkit", "WebCompositorInputHandlerImpl::cancelCurrentFling", "hadFlingAnimation", hadFlingAnimation);
 
280
    m_flingCurve.clear();
 
281
    m_flingParameters = WebActiveWheelFlingParameters();
 
282
    return hadFlingAnimation;
 
283
}
 
284
 
 
285
bool WebCompositorInputHandlerImpl::touchpadFlingScroll(const WebPoint& increment)
 
286
{
 
287
    WebMouseWheelEvent syntheticWheel;
 
288
    syntheticWheel.type = WebInputEvent::MouseWheel;
 
289
    syntheticWheel.deltaX = increment.x;
 
290
    syntheticWheel.deltaY = increment.y;
 
291
    syntheticWheel.hasPreciseScrollingDeltas = true;
 
292
    syntheticWheel.x = m_flingParameters.point.x;
 
293
    syntheticWheel.y = m_flingParameters.point.y;
 
294
    syntheticWheel.globalX = m_flingParameters.globalPoint.x;
 
295
    syntheticWheel.globalY = m_flingParameters.globalPoint.y;
 
296
    syntheticWheel.modifiers = m_flingParameters.modifiers;
 
297
 
 
298
    WebCompositorInputHandlerImpl::EventDisposition disposition = handleInputEventInternal(syntheticWheel);
 
299
    switch (disposition) {
 
300
    case DidHandle:
 
301
        return true;
 
302
    case DropEvent:
 
303
        break;
 
304
    case DidNotHandle:
 
305
        TRACE_EVENT_INSTANT0("webkit", "WebCompositorInputHandlerImpl::scrollBy::AbortFling");
 
306
        // If we got a DidNotHandle, that means we need to deliver wheels on the main thread.
 
307
        // In this case we need to schedule a commit and transfer the fling curve over to the main
 
308
        // thread and run the rest of the wheels from there.
 
309
        // This can happen when flinging a page that contains a scrollable subarea that we can't
 
310
        // scroll on the thread if the fling starts outside the subarea but then is flung "under" the
 
311
        // pointer.
 
312
        m_client->transferActiveWheelFlingAnimation(m_flingParameters);
 
313
        cancelCurrentFling();
 
314
        break;
 
315
    }
 
316
 
 
317
    return false;
 
318
}
 
319
 
 
320
void WebCompositorInputHandlerImpl::scrollBy(const WebPoint& increment)
 
321
{
 
322
    if (increment == WebPoint())
 
323
        return;
 
324
 
 
325
    TRACE_EVENT2("webkit", "WebCompositorInputHandlerImpl::scrollBy", "x", increment.x, "y", increment.y);
 
326
 
 
327
    bool didScroll = false;
 
328
 
 
329
    switch (m_flingParameters.sourceDevice) {
 
330
    case WebGestureEvent::Touchpad:
 
331
        didScroll = touchpadFlingScroll(increment);
 
332
        break;
 
333
    case WebGestureEvent::Touchscreen:
 
334
        didScroll = m_inputHandlerClient->scrollByIfPossible(m_flingParameters.point, IntSize(-increment.x, -increment.y));
 
335
        break;
 
336
    }
 
337
 
 
338
    if (didScroll) {
 
339
        m_flingParameters.cumulativeScroll.width += increment.x;
 
340
        m_flingParameters.cumulativeScroll.height += increment.y;
 
341
    }
 
342
}
 
343
 
 
344
}