1/*
2 * Copyright (C) 2011, 2014 Apple 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 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "EventDispatcher.h"
28
29#include "EventDispatcherMessages.h"
30#include "WebEvent.h"
31#include "WebEventConversion.h"
32#include "WebPage.h"
33#include "WebPageProxyMessages.h"
34#include "WebProcess.h"
35#include <WebCore/Page.h>
36#include <wtf/MainThread.h>
37#include <wtf/RunLoop.h>
38
39#if ENABLE(ASYNC_SCROLLING)
40#include <WebCore/AsyncScrollingCoordinator.h>
41#include <WebCore/ScrollingThread.h>
42#include <WebCore/ThreadedScrollingTree.h>
43#endif
44
45using namespace WebCore;
46
47namespace WebKit {
48
49PassRefPtr<EventDispatcher> EventDispatcher::create()
50{
51    return adoptRef(new EventDispatcher);
52}
53
54EventDispatcher::EventDispatcher()
55    : m_queue(WorkQueue::create("com.apple.WebKit.EventDispatcher", WorkQueue::QOS::UserInteractive))
56    , m_recentWheelEventDeltaTracker(std::make_unique<WheelEventDeltaTracker>())
57#if ENABLE(IOS_TOUCH_EVENTS)
58    , m_touchEventsLock(SPINLOCK_INITIALIZER)
59#endif
60{
61}
62
63EventDispatcher::~EventDispatcher()
64{
65}
66
67#if ENABLE(ASYNC_SCROLLING)
68void EventDispatcher::addScrollingTreeForPage(WebPage* webPage)
69{
70    MutexLocker locker(m_scrollingTreesMutex);
71
72    ASSERT(webPage->corePage()->scrollingCoordinator());
73    ASSERT(!m_scrollingTrees.contains(webPage->pageID()));
74
75    AsyncScrollingCoordinator* scrollingCoordinator = toAsyncScrollingCoordinator(webPage->corePage()->scrollingCoordinator());
76    m_scrollingTrees.set(webPage->pageID(), toThreadedScrollingTree(scrollingCoordinator->scrollingTree()));
77}
78
79void EventDispatcher::removeScrollingTreeForPage(WebPage* webPage)
80{
81    MutexLocker locker(m_scrollingTreesMutex);
82    ASSERT(m_scrollingTrees.contains(webPage->pageID()));
83
84    m_scrollingTrees.remove(webPage->pageID());
85}
86#endif
87
88void EventDispatcher::initializeConnection(IPC::Connection* connection)
89{
90    connection->addWorkQueueMessageReceiver(Messages::EventDispatcher::messageReceiverName(), m_queue.get(), this);
91}
92
93void EventDispatcher::wheelEvent(uint64_t pageID, const WebWheelEvent& wheelEvent, bool canRubberBandAtLeft, bool canRubberBandAtRight, bool canRubberBandAtTop, bool canRubberBandAtBottom)
94{
95    PlatformWheelEvent platformWheelEvent = platform(wheelEvent);
96
97#if PLATFORM(COCOA)
98    switch (wheelEvent.phase()) {
99    case PlatformWheelEventPhaseBegan:
100        m_recentWheelEventDeltaTracker->beginTrackingDeltas();
101        break;
102    case PlatformWheelEventPhaseEnded:
103        m_recentWheelEventDeltaTracker->endTrackingDeltas();
104        break;
105    default:
106        break;
107    }
108
109    if (m_recentWheelEventDeltaTracker->isTrackingDeltas()) {
110        m_recentWheelEventDeltaTracker->recordWheelEventDelta(platformWheelEvent);
111
112        DominantScrollGestureDirection dominantDirection = DominantScrollGestureDirection::None;
113        dominantDirection = m_recentWheelEventDeltaTracker->dominantScrollGestureDirection();
114
115        // Workaround for scrolling issues <rdar://problem/14758615>.
116        if (dominantDirection == DominantScrollGestureDirection::Vertical && platformWheelEvent.deltaX())
117            platformWheelEvent = platformWheelEvent.copyIgnoringHorizontalDelta();
118        else if (dominantDirection == DominantScrollGestureDirection::Horizontal && platformWheelEvent.deltaY())
119            platformWheelEvent = platformWheelEvent.copyIgnoringVerticalDelta();
120    }
121#endif
122
123#if ENABLE(ASYNC_SCROLLING)
124    MutexLocker locker(m_scrollingTreesMutex);
125    if (ThreadedScrollingTree* scrollingTree = m_scrollingTrees.get(pageID)) {
126        // FIXME: It's pretty horrible that we're updating the back/forward state here.
127        // WebCore should always know the current state and know when it changes so the
128        // scrolling tree can be notified.
129        // We only need to do this at the beginning of the gesture.
130        if (platformWheelEvent.phase() == PlatformWheelEventPhaseBegan)
131            ScrollingThread::dispatch(bind(&ThreadedScrollingTree::setCanRubberBandState, scrollingTree, canRubberBandAtLeft, canRubberBandAtRight, canRubberBandAtTop, canRubberBandAtBottom));
132
133        ScrollingTree::EventResult result = scrollingTree->tryToHandleWheelEvent(platformWheelEvent);
134        if (result == ScrollingTree::DidHandleEvent || result == ScrollingTree::DidNotHandleEvent) {
135            sendDidReceiveEvent(pageID, wheelEvent, result == ScrollingTree::DidHandleEvent);
136            return;
137        }
138    }
139#else
140    UNUSED_PARAM(canRubberBandAtLeft);
141    UNUSED_PARAM(canRubberBandAtRight);
142    UNUSED_PARAM(canRubberBandAtTop);
143    UNUSED_PARAM(canRubberBandAtBottom);
144#endif
145
146    RunLoop::main().dispatch(bind(&EventDispatcher::dispatchWheelEvent, this, pageID, wheelEvent));
147}
148
149#if ENABLE(IOS_TOUCH_EVENTS)
150void EventDispatcher::clearQueuedTouchEventsForPage(const WebPage& webPage)
151{
152    SpinLockHolder locker(&m_touchEventsLock);
153    m_touchEvents.remove(webPage.pageID());
154}
155
156void EventDispatcher::getQueuedTouchEventsForPage(const WebPage& webPage, TouchEventQueue& destinationQueue)
157{
158    SpinLockHolder locker(&m_touchEventsLock);
159    destinationQueue = m_touchEvents.take(webPage.pageID());
160}
161
162void EventDispatcher::touchEvent(uint64_t pageID, const WebKit::WebTouchEvent& touchEvent)
163{
164    bool updateListWasEmpty;
165    {
166        SpinLockHolder locker(&m_touchEventsLock);
167        updateListWasEmpty = m_touchEvents.isEmpty();
168        auto addResult = m_touchEvents.add(pageID, TouchEventQueue());
169        if (addResult.isNewEntry)
170            addResult.iterator->value.append(touchEvent);
171        else {
172            TouchEventQueue& queuedEvents = addResult.iterator->value;
173            ASSERT(!queuedEvents.isEmpty());
174            const WebTouchEvent& lastTouchEvent = queuedEvents.last();
175
176            // Coalesce touch move events.
177            WebEvent::Type type = lastTouchEvent.type();
178            if (type == WebEvent::TouchMove)
179                queuedEvents.last() = touchEvent;
180            else
181                queuedEvents.append(touchEvent);
182        }
183    }
184
185    if (updateListWasEmpty)
186        RunLoop::main().dispatch(bind(&EventDispatcher::dispatchTouchEvents, this));
187}
188
189void EventDispatcher::dispatchTouchEvents()
190{
191    HashMap<uint64_t, TouchEventQueue> localCopy;
192    {
193        SpinLockHolder locker(&m_touchEventsLock);
194        localCopy.swap(m_touchEvents);
195    }
196
197    for (auto& slot : localCopy) {
198        if (WebPage* webPage = WebProcess::shared().webPage(slot.key))
199            webPage->dispatchAsynchronousTouchEvents(slot.value);
200    }
201}
202#endif
203
204void EventDispatcher::dispatchWheelEvent(uint64_t pageID, const WebWheelEvent& wheelEvent)
205{
206    ASSERT(RunLoop::isMain());
207
208    WebPage* webPage = WebProcess::shared().webPage(pageID);
209    if (!webPage)
210        return;
211
212    webPage->wheelEvent(wheelEvent);
213}
214
215#if ENABLE(ASYNC_SCROLLING)
216void EventDispatcher::sendDidReceiveEvent(uint64_t pageID, const WebEvent& event, bool didHandleEvent)
217{
218    WebProcess::shared().parentProcessConnection()->send(Messages::WebPageProxy::DidReceiveEvent(static_cast<uint32_t>(event.type()), didHandleEvent), pageID);
219}
220#endif
221
222} // namespace WebKit
223