EventDispatcher.cpp [plain text]
#include "config.h"
#include "EventDispatcher.h"
#include "EventDispatcherMessages.h"
#include "WebEvent.h"
#include "WebEventConversion.h"
#include "WebPage.h"
#include "WebPageProxyMessages.h"
#include "WebProcess.h"
#include <WebCore/Page.h>
#include <WebCore/WheelEventTestTrigger.h>
#include <wtf/MainThread.h>
#include <wtf/RunLoop.h>
#if ENABLE(ASYNC_SCROLLING)
#include <WebCore/AsyncScrollingCoordinator.h>
#include <WebCore/ScrollingThread.h>
#include <WebCore/ThreadedScrollingTree.h>
#endif
using namespace WebCore;
namespace WebKit {
Ref<EventDispatcher> EventDispatcher::create()
{
return adoptRef(*new EventDispatcher);
}
EventDispatcher::EventDispatcher()
: m_queue(WorkQueue::create("com.apple.WebKit.EventDispatcher", WorkQueue::Type::Serial, WorkQueue::QOS::UserInteractive))
, m_recentWheelEventDeltaTracker(std::make_unique<WheelEventDeltaTracker>())
{
}
EventDispatcher::~EventDispatcher()
{
}
#if ENABLE(ASYNC_SCROLLING)
void EventDispatcher::addScrollingTreeForPage(WebPage* webPage)
{
MutexLocker locker(m_scrollingTreesMutex);
ASSERT(webPage->corePage()->scrollingCoordinator());
ASSERT(!m_scrollingTrees.contains(webPage->pageID()));
AsyncScrollingCoordinator& scrollingCoordinator = downcast<AsyncScrollingCoordinator>(*webPage->corePage()->scrollingCoordinator());
m_scrollingTrees.set(webPage->pageID(), downcast<ThreadedScrollingTree>(scrollingCoordinator.scrollingTree()));
}
void EventDispatcher::removeScrollingTreeForPage(WebPage* webPage)
{
MutexLocker locker(m_scrollingTreesMutex);
ASSERT(m_scrollingTrees.contains(webPage->pageID()));
m_scrollingTrees.remove(webPage->pageID());
}
#endif
void EventDispatcher::initializeConnection(IPC::Connection* connection)
{
connection->addWorkQueueMessageReceiver(Messages::EventDispatcher::messageReceiverName(), &m_queue.get(), this);
}
#if ENABLE(CSS_SCROLL_SNAP) || ENABLE(RUBBER_BANDING)
static void updateWheelEventTestTriggersIfNeeded(uint64_t pageID)
{
WebPage* webPage = WebProcess::singleton().webPage(pageID);
Page* page = webPage ? webPage->corePage() : nullptr;
if (!page || !page->expectsWheelEventTriggers())
return;
page->testTrigger()->deferTestsForReason(reinterpret_cast<WheelEventTestTrigger::ScrollableAreaIdentifier>(page), WheelEventTestTrigger::ScrollingThreadSyncNeeded);
}
#endif
void EventDispatcher::wheelEvent(uint64_t pageID, const WebWheelEvent& wheelEvent, bool canRubberBandAtLeft, bool canRubberBandAtRight, bool canRubberBandAtTop, bool canRubberBandAtBottom)
{
PlatformWheelEvent platformWheelEvent = platform(wheelEvent);
#if PLATFORM(COCOA)
switch (wheelEvent.phase()) {
case PlatformWheelEventPhaseBegan:
m_recentWheelEventDeltaTracker->beginTrackingDeltas();
break;
case PlatformWheelEventPhaseEnded:
m_recentWheelEventDeltaTracker->endTrackingDeltas();
break;
default:
break;
}
if (m_recentWheelEventDeltaTracker->isTrackingDeltas()) {
m_recentWheelEventDeltaTracker->recordWheelEventDelta(platformWheelEvent);
DominantScrollGestureDirection dominantDirection = DominantScrollGestureDirection::None;
dominantDirection = m_recentWheelEventDeltaTracker->dominantScrollGestureDirection();
if (dominantDirection == DominantScrollGestureDirection::Vertical && platformWheelEvent.deltaX())
platformWheelEvent = platformWheelEvent.copyIgnoringHorizontalDelta();
else if (dominantDirection == DominantScrollGestureDirection::Horizontal && platformWheelEvent.deltaY())
platformWheelEvent = platformWheelEvent.copyIgnoringVerticalDelta();
}
#endif
#if ENABLE(ASYNC_SCROLLING)
MutexLocker locker(m_scrollingTreesMutex);
if (RefPtr<ThreadedScrollingTree> scrollingTree = m_scrollingTrees.get(pageID)) {
if (platformWheelEvent.phase() == PlatformWheelEventPhaseBegan) {
ScrollingThread::dispatch([scrollingTree, canRubberBandAtLeft, canRubberBandAtRight, canRubberBandAtTop, canRubberBandAtBottom] {
scrollingTree->setCanRubberBandState(canRubberBandAtLeft, canRubberBandAtRight, canRubberBandAtTop, canRubberBandAtBottom);
});
}
ScrollingTree::EventResult result = scrollingTree->tryToHandleWheelEvent(platformWheelEvent);
#if ENABLE(CSS_SCROLL_SNAP) || ENABLE(RUBBER_BANDING)
if (result == ScrollingTree::DidHandleEvent)
updateWheelEventTestTriggersIfNeeded(pageID);
#endif
if (result == ScrollingTree::DidHandleEvent || result == ScrollingTree::DidNotHandleEvent) {
sendDidReceiveEvent(pageID, wheelEvent, result == ScrollingTree::DidHandleEvent);
return;
}
}
#else
UNUSED_PARAM(canRubberBandAtLeft);
UNUSED_PARAM(canRubberBandAtRight);
UNUSED_PARAM(canRubberBandAtTop);
UNUSED_PARAM(canRubberBandAtBottom);
#endif
RefPtr<EventDispatcher> eventDispatcher(this);
RunLoop::main().dispatch([eventDispatcher, pageID, wheelEvent] {
eventDispatcher->dispatchWheelEvent(pageID, wheelEvent);
});
}
#if ENABLE(IOS_TOUCH_EVENTS)
void EventDispatcher::clearQueuedTouchEventsForPage(const WebPage& webPage)
{
SpinLockHolder locker(&m_touchEventsLock);
m_touchEvents.remove(webPage.pageID());
}
void EventDispatcher::getQueuedTouchEventsForPage(const WebPage& webPage, TouchEventQueue& destinationQueue)
{
SpinLockHolder locker(&m_touchEventsLock);
destinationQueue = m_touchEvents.take(webPage.pageID());
}
void EventDispatcher::touchEvent(uint64_t pageID, const WebKit::WebTouchEvent& touchEvent)
{
bool updateListWasEmpty;
{
SpinLockHolder locker(&m_touchEventsLock);
updateListWasEmpty = m_touchEvents.isEmpty();
auto addResult = m_touchEvents.add(pageID, TouchEventQueue());
if (addResult.isNewEntry)
addResult.iterator->value.append(touchEvent);
else {
TouchEventQueue& queuedEvents = addResult.iterator->value;
ASSERT(!queuedEvents.isEmpty());
const WebTouchEvent& lastTouchEvent = queuedEvents.last();
WebEvent::Type type = lastTouchEvent.type();
if (type == WebEvent::TouchMove)
queuedEvents.last() = touchEvent;
else
queuedEvents.append(touchEvent);
}
}
if (updateListWasEmpty) {
RefPtr<EventDispatcher> eventDispatcher(this);
RunLoop::main().dispatch([eventDispatcher] {
eventDispatcher->dispatchTouchEvents();
});
}
}
void EventDispatcher::dispatchTouchEvents()
{
HashMap<uint64_t, TouchEventQueue> localCopy;
{
SpinLockHolder locker(&m_touchEventsLock);
localCopy.swap(m_touchEvents);
}
for (auto& slot : localCopy) {
if (WebPage* webPage = WebProcess::singleton().webPage(slot.key))
webPage->dispatchAsynchronousTouchEvents(slot.value);
}
}
#endif
void EventDispatcher::dispatchWheelEvent(uint64_t pageID, const WebWheelEvent& wheelEvent)
{
ASSERT(RunLoop::isMain());
WebPage* webPage = WebProcess::singleton().webPage(pageID);
if (!webPage)
return;
webPage->wheelEvent(wheelEvent);
}
#if ENABLE(ASYNC_SCROLLING)
void EventDispatcher::sendDidReceiveEvent(uint64_t pageID, const WebEvent& event, bool didHandleEvent)
{
WebProcess::singleton().parentProcessConnection()->send(Messages::WebPageProxy::DidReceiveEvent(static_cast<uint32_t>(event.type()), didHandleEvent), pageID);
}
#endif
}