/* * Copyright (C) 2011, 2014-2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #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 #include #include #include #if ENABLE(ASYNC_SCROLLING) #include #include #include #endif using namespace WebCore; namespace WebKit { Ref EventDispatcher::create() { return adoptRef(*new EventDispatcher); } EventDispatcher::EventDispatcher() : m_queue(WorkQueue::create("com.apple.WebKit.EventDispatcher", WorkQueue::Type::Serial, WorkQueue::QOS::UserInteractive)) , m_recentWheelEventDeltaFilter(WheelEventDeltaFilter::create()) { } EventDispatcher::~EventDispatcher() { } #if ENABLE(ASYNC_SCROLLING) void EventDispatcher::addScrollingTreeForPage(WebPage* webPage) { LockHolder locker(m_scrollingTreesMutex); ASSERT(webPage->corePage()->scrollingCoordinator()); ASSERT(!m_scrollingTrees.contains(webPage->pageID())); AsyncScrollingCoordinator& scrollingCoordinator = downcast(*webPage->corePage()->scrollingCoordinator()); m_scrollingTrees.set(webPage->pageID(), downcast(scrollingCoordinator.scrollingTree())); } void EventDispatcher::removeScrollingTreeForPage(WebPage* webPage) { LockHolder 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); } 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_recentWheelEventDeltaFilter->beginFilteringDeltas(); break; case PlatformWheelEventPhaseEnded: m_recentWheelEventDeltaFilter->endFilteringDeltas(); break; default: break; } if (m_recentWheelEventDeltaFilter->isFilteringDeltas()) { m_recentWheelEventDeltaFilter->updateFromDelta(FloatSize(platformWheelEvent.deltaX(), platformWheelEvent.deltaY())); FloatSize filteredDelta = m_recentWheelEventDeltaFilter->filteredDelta(); platformWheelEvent = platformWheelEvent.copyWithDeltas(filteredDelta.width(), filteredDelta.height()); } #endif #if ENABLE(ASYNC_SCROLLING) LockHolder locker(m_scrollingTreesMutex); if (RefPtr scrollingTree = m_scrollingTrees.get(pageID)) { // FIXME: It's pretty horrible that we're updating the back/forward state here. // WebCore should always know the current state and know when it changes so the // scrolling tree can be notified. // We only need to do this at the beginning of the gesture. if (platformWheelEvent.phase() == PlatformWheelEventPhaseBegan) { ScrollingThread::dispatch([scrollingTree, canRubberBandAtLeft, canRubberBandAtRight, canRubberBandAtTop, canRubberBandAtBottom] { scrollingTree->setCanRubberBandState(canRubberBandAtLeft, canRubberBandAtRight, canRubberBandAtTop, canRubberBandAtBottom); }); } ScrollingTree::EventResult result = scrollingTree->tryToHandleWheelEvent(platformWheelEvent); 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 = this; RunLoop::main().dispatch([eventDispatcher, pageID, wheelEvent] { eventDispatcher->dispatchWheelEvent(pageID, wheelEvent); }); } #if ENABLE(MAC_GESTURE_EVENTS) || ENABLE(QT_GESTURE_EVENTS) void EventDispatcher::gestureEvent(uint64_t pageID, const WebKit::WebGestureEvent& gestureEvent) { RefPtr eventDispatcher = this; RunLoop::main().dispatch([eventDispatcher, pageID, gestureEvent] { eventDispatcher->dispatchGestureEvent(pageID, gestureEvent); }); } #endif #if ENABLE(IOS_TOUCH_EVENTS) void EventDispatcher::clearQueuedTouchEventsForPage(const WebPage& webPage) { LockHolder locker(&m_touchEventsLock); m_touchEvents.remove(webPage.pageID()); } void EventDispatcher::getQueuedTouchEventsForPage(const WebPage& webPage, TouchEventQueue& destinationQueue) { LockHolder locker(&m_touchEventsLock); destinationQueue = m_touchEvents.take(webPage.pageID()); } void EventDispatcher::touchEvent(uint64_t pageID, const WebKit::WebTouchEvent& touchEvent) { bool updateListWasEmpty; { LockHolder 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(); // Coalesce touch move events. WebEvent::Type type = lastTouchEvent.type(); if (type == WebEvent::TouchMove) queuedEvents.last() = touchEvent; else queuedEvents.append(touchEvent); } } if (updateListWasEmpty) { RefPtr eventDispatcher = this; RunLoop::main().dispatch([eventDispatcher] { eventDispatcher->dispatchTouchEvents(); }); } } void EventDispatcher::dispatchTouchEvents() { HashMap localCopy; { LockHolder 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(MAC_GESTURE_EVENTS) || ENABLE(QT_GESTURE_EVENTS) void EventDispatcher::dispatchGestureEvent(uint64_t pageID, const WebGestureEvent& gestureEvent) { ASSERT(RunLoop::isMain()); WebPage* webPage = WebProcess::singleton().webPage(pageID); if (!webPage) return; webPage->gestureEvent(gestureEvent); } #endif #if ENABLE(ASYNC_SCROLLING) void EventDispatcher::sendDidReceiveEvent(uint64_t pageID, const WebEvent& event, bool didHandleEvent) { WebProcess::singleton().parentProcessConnection()->send(Messages::WebPageProxy::DidReceiveEvent(static_cast(event.type()), didHandleEvent), pageID); } #endif } // namespace WebKit