2 * Copyright (C) 2011, 2014-2015 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "EventDispatcher.h"
29 #include "EventDispatcherMessages.h"
31 #include "WebEventConversion.h"
33 #include "WebPageProxyMessages.h"
34 #include "WebProcess.h"
35 #include <WebCore/Page.h>
36 #include <WebCore/WheelEventTestTrigger.h>
37 #include <wtf/MainThread.h>
38 #include <wtf/RunLoop.h>
40 #if ENABLE(ASYNC_SCROLLING)
41 #include <WebCore/AsyncScrollingCoordinator.h>
42 #include <WebCore/ScrollingThread.h>
43 #include <WebCore/ThreadedScrollingTree.h>
46 using namespace WebCore;
50 PassRefPtr<EventDispatcher> EventDispatcher::create()
52 return adoptRef(new EventDispatcher);
55 EventDispatcher::EventDispatcher()
56 : m_queue(WorkQueue::create("com.apple.WebKit.EventDispatcher", WorkQueue::Type::Serial, WorkQueue::QOS::UserInteractive))
57 , m_recentWheelEventDeltaTracker(std::make_unique<WheelEventDeltaTracker>())
61 EventDispatcher::~EventDispatcher()
65 #if ENABLE(ASYNC_SCROLLING)
66 void EventDispatcher::addScrollingTreeForPage(WebPage* webPage)
68 MutexLocker locker(m_scrollingTreesMutex);
70 ASSERT(webPage->corePage()->scrollingCoordinator());
71 ASSERT(!m_scrollingTrees.contains(webPage->pageID()));
73 AsyncScrollingCoordinator& scrollingCoordinator = downcast<AsyncScrollingCoordinator>(*webPage->corePage()->scrollingCoordinator());
74 m_scrollingTrees.set(webPage->pageID(), downcast<ThreadedScrollingTree>(scrollingCoordinator.scrollingTree()));
77 void EventDispatcher::removeScrollingTreeForPage(WebPage* webPage)
79 MutexLocker locker(m_scrollingTreesMutex);
80 ASSERT(m_scrollingTrees.contains(webPage->pageID()));
82 m_scrollingTrees.remove(webPage->pageID());
86 void EventDispatcher::initializeConnection(IPC::Connection* connection)
88 connection->addWorkQueueMessageReceiver(Messages::EventDispatcher::messageReceiverName(), &m_queue.get(), this);
91 static void updateWheelEventTestTriggersIfNeeded(uint64_t pageID)
93 WebPage* webPage = WebProcess::singleton().webPage(pageID);
94 Page* page = webPage ? webPage->corePage() : nullptr;
96 if (!page || !page->expectsWheelEventTriggers())
99 page->testTrigger()->deferTestsForReason(reinterpret_cast<WheelEventTestTrigger::ScrollableAreaIdentifier>(page), WheelEventTestTrigger::ScrollingThreadSyncNeeded);
102 void EventDispatcher::wheelEvent(uint64_t pageID, const WebWheelEvent& wheelEvent, bool canRubberBandAtLeft, bool canRubberBandAtRight, bool canRubberBandAtTop, bool canRubberBandAtBottom)
104 PlatformWheelEvent platformWheelEvent = platform(wheelEvent);
107 switch (wheelEvent.phase()) {
108 case PlatformWheelEventPhaseBegan:
109 m_recentWheelEventDeltaTracker->beginTrackingDeltas();
111 case PlatformWheelEventPhaseEnded:
112 m_recentWheelEventDeltaTracker->endTrackingDeltas();
118 if (m_recentWheelEventDeltaTracker->isTrackingDeltas()) {
119 m_recentWheelEventDeltaTracker->recordWheelEventDelta(platformWheelEvent);
121 DominantScrollGestureDirection dominantDirection = DominantScrollGestureDirection::None;
122 dominantDirection = m_recentWheelEventDeltaTracker->dominantScrollGestureDirection();
124 // Workaround for scrolling issues <rdar://problem/14758615>.
125 if (dominantDirection == DominantScrollGestureDirection::Vertical && platformWheelEvent.deltaX())
126 platformWheelEvent = platformWheelEvent.copyIgnoringHorizontalDelta();
127 else if (dominantDirection == DominantScrollGestureDirection::Horizontal && platformWheelEvent.deltaY())
128 platformWheelEvent = platformWheelEvent.copyIgnoringVerticalDelta();
132 #if ENABLE(ASYNC_SCROLLING)
133 MutexLocker locker(m_scrollingTreesMutex);
134 if (RefPtr<ThreadedScrollingTree> scrollingTree = m_scrollingTrees.get(pageID)) {
135 // FIXME: It's pretty horrible that we're updating the back/forward state here.
136 // WebCore should always know the current state and know when it changes so the
137 // scrolling tree can be notified.
138 // We only need to do this at the beginning of the gesture.
139 if (platformWheelEvent.phase() == PlatformWheelEventPhaseBegan) {
140 ScrollingThread::dispatch([scrollingTree, canRubberBandAtLeft, canRubberBandAtRight, canRubberBandAtTop, canRubberBandAtBottom] {
141 scrollingTree->setCanRubberBandState(canRubberBandAtLeft, canRubberBandAtRight, canRubberBandAtTop, canRubberBandAtBottom);
145 ScrollingTree::EventResult result = scrollingTree->tryToHandleWheelEvent(platformWheelEvent);
147 #if ENABLE(CSS_SCROLL_SNAP) || ENABLE(RUBBER_BANDING)
148 if (result == ScrollingTree::DidHandleEvent)
149 updateWheelEventTestTriggersIfNeeded(pageID);
152 if (result == ScrollingTree::DidHandleEvent || result == ScrollingTree::DidNotHandleEvent) {
153 sendDidReceiveEvent(pageID, wheelEvent, result == ScrollingTree::DidHandleEvent);
158 UNUSED_PARAM(canRubberBandAtLeft);
159 UNUSED_PARAM(canRubberBandAtRight);
160 UNUSED_PARAM(canRubberBandAtTop);
161 UNUSED_PARAM(canRubberBandAtBottom);
164 RefPtr<EventDispatcher> eventDispatcher(this);
165 RunLoop::main().dispatch([eventDispatcher, pageID, wheelEvent] {
166 eventDispatcher->dispatchWheelEvent(pageID, wheelEvent);
170 #if ENABLE(IOS_TOUCH_EVENTS)
171 void EventDispatcher::clearQueuedTouchEventsForPage(const WebPage& webPage)
173 SpinLockHolder locker(&m_touchEventsLock);
174 m_touchEvents.remove(webPage.pageID());
177 void EventDispatcher::getQueuedTouchEventsForPage(const WebPage& webPage, TouchEventQueue& destinationQueue)
179 SpinLockHolder locker(&m_touchEventsLock);
180 destinationQueue = m_touchEvents.take(webPage.pageID());
183 void EventDispatcher::touchEvent(uint64_t pageID, const WebKit::WebTouchEvent& touchEvent)
185 bool updateListWasEmpty;
187 SpinLockHolder locker(&m_touchEventsLock);
188 updateListWasEmpty = m_touchEvents.isEmpty();
189 auto addResult = m_touchEvents.add(pageID, TouchEventQueue());
190 if (addResult.isNewEntry)
191 addResult.iterator->value.append(touchEvent);
193 TouchEventQueue& queuedEvents = addResult.iterator->value;
194 ASSERT(!queuedEvents.isEmpty());
195 const WebTouchEvent& lastTouchEvent = queuedEvents.last();
197 // Coalesce touch move events.
198 WebEvent::Type type = lastTouchEvent.type();
199 if (type == WebEvent::TouchMove)
200 queuedEvents.last() = touchEvent;
202 queuedEvents.append(touchEvent);
206 if (updateListWasEmpty) {
207 RefPtr<EventDispatcher> eventDispatcher(this);
208 RunLoop::main().dispatch([eventDispatcher] {
209 eventDispatcher->dispatchTouchEvents();
214 void EventDispatcher::dispatchTouchEvents()
216 HashMap<uint64_t, TouchEventQueue> localCopy;
218 SpinLockHolder locker(&m_touchEventsLock);
219 localCopy.swap(m_touchEvents);
222 for (auto& slot : localCopy) {
223 if (WebPage* webPage = WebProcess::singleton().webPage(slot.key))
224 webPage->dispatchAsynchronousTouchEvents(slot.value);
229 void EventDispatcher::dispatchWheelEvent(uint64_t pageID, const WebWheelEvent& wheelEvent)
231 ASSERT(RunLoop::isMain());
233 WebPage* webPage = WebProcess::singleton().webPage(pageID);
237 webPage->wheelEvent(wheelEvent);
240 #if ENABLE(ASYNC_SCROLLING)
241 void EventDispatcher::sendDidReceiveEvent(uint64_t pageID, const WebEvent& event, bool didHandleEvent)
243 WebProcess::singleton().parentProcessConnection()->send(Messages::WebPageProxy::DidReceiveEvent(static_cast<uint32_t>(event.type()), didHandleEvent), pageID);
247 } // namespace WebKit