Title: [230817] trunk/Source/WebKit
Revision
230817
Author
bb...@apple.com
Date
2018-04-19 13:37:31 -0700 (Thu, 19 Apr 2018)

Log Message

Web Automation: simulated mouse interactions should not be done until associated DOM events have been dispatched
https://bugs.webkit.org/show_bug.cgi?id=184462
<rdar://problem/39323336>

Reviewed by Carlos Garcia Campos and Tim Horton.

Covered by existing layout tests and actions endpoints in WebDriver test suite.

In preparation for implementing the W3C WebDriver command "Perform Actions", we need a way to
know when a simulated mouse event has been fully processed by WebProcess and it is okay to continue
to dispatch more simulated events.

This patch makes mouse events go through a queue as they are delivered to WebPageProxy. The approach
is very similar to how key events are handled. In the key event case, lots of WebEvents can come out
of typing one keystroke, so these need to be queued up and retired one by one when the WebProcess has
finished handling each event. In some mouse event cases---particularly fake mouse moves---there can
also be more than one mouse event waiting to be handled by WebProcess.

In the past, these queued mouse events were tracked with several member variables as different
use cases emerged. These are all replaced with ordinary deque operations, such as peeking or
checking the queue length.

* Platform/Logging.h: Add logging channel for mouse events.
* UIProcess/Automation/WebAutomationSession.cpp:
(WebKit::AutomationCommandError::toProtocolString): Add type-safe helper class for command errors.
In future patches we can hide knowledge of how this is sent over the protocol by relying more on
the convenience constructors and .toProtocolString() method.

(WebKit::WebAutomationSession::willShowJavaScriptDialog):
This section needs adjustments. Since performMouseInteraction now depends on key events being processed
prior to returning from the command, we need to abort any key event callbacks that are pending if an
alert pops up as a result of sending a mousedown event. Any mouse events that are still queued will
be handled when the alert is dismissed and the nested run loop exits.

(WebKit::WebAutomationSession::mouseEventsFlushedForPage):
(WebKit::WebAutomationSession::keyboardEventsFlushedForPage):
Modernize this a bit. Don't spread knowledge about how commands are sent back out into event handling code.
Our wrapper callbacks in performXXXInteraction handle the protocol-specific details of the response.

(WebKit::WebAutomationSession::performMouseInteraction):
Add code similar to performKeyboardInteractions so that the command doesn't finish until the mouse
event has been fully handled. Unlike keyboards, sometimes mouse interactions don't turn into WebEvents
so we also need to handle the case where there is nothing to be waited on because hit testing did
not return a target to deliver the event to.

(WebKit::WebAutomationSession::performKeyboardInteractions):
Modernize a little bit to use generic callbacks rather than protocol-generated callbacks in the
event waiting/handling code. Now it matches the types used for the mouse event case.

* UIProcess/Automation/WebAutomationSession.h:
(WebKit::AutomationCommandError::AutomationCommandError):
Add a helper struct to hold an enumerated error name and an optional free-form error message.

* UIProcess/WebPageProxy.h:
* UIProcess/WebPageProxy.cpp:
(WebKit::webMouseEventTypeString):
(WebKit::webKeyboardEventTypeString):
(WebKit::WebPageProxy::handleMouseEvent):
(WebKit::WebPageProxy::processNextQueuedMouseEvent):
Split the old method into handleMouseEvent (called by other code) and processNextQueuedMouseEvent.
The latter sends the next mouse event to WebProcess, and can be triggered in didReceiveEvent
if there are more mouse events to be sent to WebProcess.

(WebKit::WebPageProxy::isProcessingMouseEvents const): Added.
(WebKit::WebPageProxy::currentlyProcessedMouseDownEvent): Reimplemented on top of the deque.
(WebKit::WebPageProxy::didReceiveEvent):
Unify the code paths for different mouse event types to all use the deque. They also will
notify the automation session if there are no more mouse events to send (i.e., interaction is over).

(WebKit::WebPageProxy::resetStateAfterProcessExited): Add handling for new map.

Modified Paths

Diff

Modified: trunk/Source/WebKit/ChangeLog (230816 => 230817)


--- trunk/Source/WebKit/ChangeLog	2018-04-19 20:25:55 UTC (rev 230816)
+++ trunk/Source/WebKit/ChangeLog	2018-04-19 20:37:31 UTC (rev 230817)
@@ -1,3 +1,76 @@
+2018-04-19  Brian Burg  <bb...@apple.com>
+
+        Web Automation: simulated mouse interactions should not be done until associated DOM events have been dispatched
+        https://bugs.webkit.org/show_bug.cgi?id=184462
+        <rdar://problem/39323336>
+
+        Reviewed by Carlos Garcia Campos and Tim Horton.
+
+        Covered by existing layout tests and actions endpoints in WebDriver test suite.
+
+        In preparation for implementing the W3C WebDriver command "Perform Actions", we need a way to
+        know when a simulated mouse event has been fully processed by WebProcess and it is okay to continue
+        to dispatch more simulated events.
+
+        This patch makes mouse events go through a queue as they are delivered to WebPageProxy. The approach
+        is very similar to how key events are handled. In the key event case, lots of WebEvents can come out
+        of typing one keystroke, so these need to be queued up and retired one by one when the WebProcess has
+        finished handling each event. In some mouse event cases---particularly fake mouse moves---there can
+        also be more than one mouse event waiting to be handled by WebProcess.
+
+        In the past, these queued mouse events were tracked with several member variables as different
+        use cases emerged. These are all replaced with ordinary deque operations, such as peeking or
+        checking the queue length.
+
+        * Platform/Logging.h: Add logging channel for mouse events.
+        * UIProcess/Automation/WebAutomationSession.cpp:
+        (WebKit::AutomationCommandError::toProtocolString): Add type-safe helper class for command errors.
+        In future patches we can hide knowledge of how this is sent over the protocol by relying more on
+        the convenience constructors and .toProtocolString() method.
+
+        (WebKit::WebAutomationSession::willShowJavaScriptDialog):
+        This section needs adjustments. Since performMouseInteraction now depends on key events being processed
+        prior to returning from the command, we need to abort any key event callbacks that are pending if an
+        alert pops up as a result of sending a mousedown event. Any mouse events that are still queued will
+        be handled when the alert is dismissed and the nested run loop exits.
+
+        (WebKit::WebAutomationSession::mouseEventsFlushedForPage):
+        (WebKit::WebAutomationSession::keyboardEventsFlushedForPage):
+        Modernize this a bit. Don't spread knowledge about how commands are sent back out into event handling code.
+        Our wrapper callbacks in performXXXInteraction handle the protocol-specific details of the response.
+
+        (WebKit::WebAutomationSession::performMouseInteraction):
+        Add code similar to performKeyboardInteractions so that the command doesn't finish until the mouse
+        event has been fully handled. Unlike keyboards, sometimes mouse interactions don't turn into WebEvents
+        so we also need to handle the case where there is nothing to be waited on because hit testing did
+        not return a target to deliver the event to.
+
+        (WebKit::WebAutomationSession::performKeyboardInteractions):
+        Modernize a little bit to use generic callbacks rather than protocol-generated callbacks in the
+        event waiting/handling code. Now it matches the types used for the mouse event case.
+
+        * UIProcess/Automation/WebAutomationSession.h:
+        (WebKit::AutomationCommandError::AutomationCommandError):
+        Add a helper struct to hold an enumerated error name and an optional free-form error message.
+
+        * UIProcess/WebPageProxy.h:
+        * UIProcess/WebPageProxy.cpp:
+        (WebKit::webMouseEventTypeString):
+        (WebKit::webKeyboardEventTypeString):
+        (WebKit::WebPageProxy::handleMouseEvent):
+        (WebKit::WebPageProxy::processNextQueuedMouseEvent):
+        Split the old method into handleMouseEvent (called by other code) and processNextQueuedMouseEvent.
+        The latter sends the next mouse event to WebProcess, and can be triggered in didReceiveEvent
+        if there are more mouse events to be sent to WebProcess.
+
+        (WebKit::WebPageProxy::isProcessingMouseEvents const): Added.
+        (WebKit::WebPageProxy::currentlyProcessedMouseDownEvent): Reimplemented on top of the deque.
+        (WebKit::WebPageProxy::didReceiveEvent):
+        Unify the code paths for different mouse event types to all use the deque. They also will
+        notify the automation session if there are no more mouse events to send (i.e., interaction is over).
+
+        (WebKit::WebPageProxy::resetStateAfterProcessExited): Add handling for new map.
+
 2018-04-19  Andy Estes  <aes...@apple.com>
 
         [iOS] Implement find-in-page in the new WKPDFView

Modified: trunk/Source/WebKit/Platform/Logging.h (230816 => 230817)


--- trunk/Source/WebKit/Platform/Logging.h	2018-04-19 20:25:55 UTC (rev 230816)
+++ trunk/Source/WebKit/Platform/Logging.h	2018-04-19 20:37:31 UTC (rev 230817)
@@ -51,6 +51,7 @@
     M(KeyHandling) \
     M(Layers) \
     M(Loading) \
+    M(MouseHandling) \
     M(Network) \
     M(NetworkCache) \
     M(NetworkCacheSpeculativePreloading) \

Modified: trunk/Source/WebKit/UIProcess/Automation/WebAutomationSession.cpp (230816 => 230817)


--- trunk/Source/WebKit/UIProcess/Automation/WebAutomationSession.cpp	2018-04-19 20:25:55 UTC (rev 230816)
+++ trunk/Source/WebKit/UIProcess/Automation/WebAutomationSession.cpp	2018-04-19 20:37:31 UTC (rev 230817)
@@ -53,6 +53,15 @@
 
 namespace WebKit {
 
+String AutomationCommandError::toProtocolString()
+{
+    String protocolErrorName = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(type);
+    if (!message.has_value())
+        return protocolErrorName;
+
+    return makeString(protocolErrorName, errorNameAndDetailsSeparator, message.value());
+}
+    
 // ยง8. Sessions
 // https://www.w3.org/TR/webdriver/#dfn-session-page-load-timeout
 static const Seconds defaultPageLoadTimeout = 300_s;
@@ -583,6 +592,20 @@
                 callback->sendFailure(unexpectedAlertOpenError);
             }
         }
+        
+        if (!m_pendingMouseEventsFlushedCallbacksPerPage.isEmpty()) {
+            for (auto key : copyToVector(m_pendingMouseEventsFlushedCallbacksPerPage.keys())) {
+                auto callback = m_pendingMouseEventsFlushedCallbacksPerPage.take(key);
+                callback(std::nullopt);
+            }
+        }
+
+        if (!m_pendingKeyboardEventsFlushedCallbacksPerPage.isEmpty()) {
+            for (auto key : copyToVector(m_pendingKeyboardEventsFlushedCallbacksPerPage.keys())) {
+                auto callback = m_pendingKeyboardEventsFlushedCallbacksPerPage.take(key);
+                callback(std::nullopt);
+            }
+        }
     });
 }
     
@@ -698,13 +721,23 @@
         callback->sendSuccess(JSON::Object::create());
 }
 
+void WebAutomationSession::mouseEventsFlushedForPage(const WebPageProxy& page)
+{
+    if (auto callback = m_pendingMouseEventsFlushedCallbacksPerPage.take(page.pageID())) {
+        if (m_pendingMouseEventsFlushedCallbacksPerPage.isEmpty())
+            m_simulatingUserInteraction = false;
+
+        callback(std::nullopt);
+    }
+}
+
 void WebAutomationSession::keyboardEventsFlushedForPage(const WebPageProxy& page)
 {
     if (auto callback = m_pendingKeyboardEventsFlushedCallbacksPerPage.take(page.pageID())) {
-        callback->sendSuccess(JSON::Object::create());
-
         if (m_pendingKeyboardEventsFlushedCallbacksPerPage.isEmpty())
             m_simulatingUserInteraction = false;
+
+        callback(std::nullopt);
     }
 }
 
@@ -1398,12 +1431,34 @@
         if (!parsedButton)
             ASYNC_FAIL_WITH_PREDEFINED_ERROR_AND_DETAILS(InvalidParameter, "The parameter 'button' is invalid.");
 
+        auto mouseEventsFlushedCallback = [protectedThis = WTFMove(protectedThis), callback = WTFMove(callback), page = page.copyRef(), x, y](std::optional<AutomationCommandError> error) {
+            if (error)
+                callback->sendFailure(error.value().toProtocolString());
+            else {
+                callback->sendSuccess(Inspector::Protocol::Automation::Point::create()
+                    .setX(x)
+                    .setY(y - page->topContentInset())
+                    .release());
+            }
+        };
+
+        auto& callbackInMap = m_pendingMouseEventsFlushedCallbacksPerPage.add(page->pageID(), nullptr).iterator->value;
+        if (callbackInMap)
+            callbackInMap(AUTOMATION_COMMAND_ERROR_WITH_NAME(Timeout));
+        callbackInMap = WTFMove(mouseEventsFlushedCallback);
+
+        // This is cleared when all mouse events are flushed.
+        m_simulatingUserInteraction = true;
+
         platformSimulateMouseInteraction(page, viewPosition, parsedInteraction.value(), parsedButton.value(), keyModifiers);
-
-        callback->sendSuccess(Inspector::Protocol::Automation::Point::create()
-            .setX(x)
-            .setY(y - page->topContentInset())
-            .release());
+        
+        // If the event location was previously clipped and does not hit test anything in the window, then it will not be processed.
+        // For compatibility with pre-W3C driver implementations, don't make this a hard error; just do nothing silently.
+        // In W3C-only code paths, we can reject any pointer actions whose coordinates are outside the viewport rect.
+        if (callbackInMap && !page->isProcessingMouseEvents()) {
+            auto callbackToCancel = m_pendingMouseEventsFlushedCallbacksPerPage.take(page->pageID());
+            callbackToCancel(std::nullopt);
+        }
     });
 #endif // USE(APPKIT) || PLATFORM(GTK)
 }
@@ -1473,10 +1528,17 @@
     if (!actionsToPerform.size())
         ASYNC_FAIL_WITH_PREDEFINED_ERROR_AND_DETAILS(InternalError, "No actions to perform.");
 
+    auto keyboardEventsFlushedCallback = [protectedThis = makeRef(*this), callback = WTFMove(callback), page = makeRef(*page)](std::optional<AutomationCommandError> error) {
+        if (error)
+            callback->sendFailure(error.value().toProtocolString());
+        else
+            callback->sendSuccess();
+    };
+
     auto& callbackInMap = m_pendingKeyboardEventsFlushedCallbacksPerPage.add(page->pageID(), nullptr).iterator->value;
     if (callbackInMap)
-        callbackInMap->sendFailure(STRING_FOR_PREDEFINED_ERROR_NAME(Timeout));
-    callbackInMap = WTFMove(callback);
+        callbackInMap(AUTOMATION_COMMAND_ERROR_WITH_NAME(Timeout));
+    callbackInMap = WTFMove(keyboardEventsFlushedCallback);
 
     // This is cleared when all keyboard events are flushed.
     m_simulatingUserInteraction = true;

Modified: trunk/Source/WebKit/UIProcess/Automation/WebAutomationSession.h (230816 => 230817)


--- trunk/Source/WebKit/UIProcess/Automation/WebAutomationSession.h	2018-04-19 20:25:55 UTC (rev 230816)
+++ trunk/Source/WebKit/UIProcess/Automation/WebAutomationSession.h	2018-04-19 20:37:31 UTC (rev 230817)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016, 2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2016-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -76,6 +76,21 @@
 class WebPageProxy;
 class WebProcessPool;
 
+struct AutomationCommandError {
+public:
+    Inspector::Protocol::Automation::ErrorMessage type;
+    std::optional<String> message { std::nullopt };
+    
+    AutomationCommandError(Inspector::Protocol::Automation::ErrorMessage type)
+        : type(type) { }
+
+    AutomationCommandError(Inspector::Protocol::Automation::ErrorMessage type, const String& message)
+        : type(type)
+        , message(message) { }
+    
+    String toProtocolString();
+};
+
 class WebAutomationSession final : public API::ObjectImpl<API::Object::Type::AutomationSession>, public IPC::MessageReceiver
 #if ENABLE(REMOTE_INSPECTOR)
     , public Inspector::RemoteAutomationTarget
@@ -98,6 +113,7 @@
     void documentLoadedForFrame(const WebFrameProxy&);
     void inspectorFrontendLoaded(const WebPageProxy&);
     void keyboardEventsFlushedForPage(const WebPageProxy&);
+    void mouseEventsFlushedForPage(const WebPageProxy&);
     void willClosePage(const WebPageProxy&);
     void handleRunOpenPanel(const WebPageProxy&, const WebFrameProxy&, const API::OpenPanelParameters&, WebOpenPanelResultListenerProxy&);
     void willShowJavaScriptDialog(WebPageProxy&);
@@ -235,7 +251,8 @@
     HashMap<uint64_t, RefPtr<Inspector::BackendDispatcher::CallbackBase>> m_pendingNormalNavigationInBrowsingContextCallbacksPerFrame;
     HashMap<uint64_t, RefPtr<Inspector::BackendDispatcher::CallbackBase>> m_pendingEagerNavigationInBrowsingContextCallbacksPerFrame;
     HashMap<uint64_t, RefPtr<Inspector::BackendDispatcher::CallbackBase>> m_pendingInspectorCallbacksPerPage;
-    HashMap<uint64_t, RefPtr<Inspector::BackendDispatcher::CallbackBase>> m_pendingKeyboardEventsFlushedCallbacksPerPage;
+    HashMap<uint64_t, Function<void(std::optional<AutomationCommandError>)>> m_pendingKeyboardEventsFlushedCallbacksPerPage;
+    HashMap<uint64_t, Function<void(std::optional<AutomationCommandError>)>> m_pendingMouseEventsFlushedCallbacksPerPage;
 
     uint64_t m_nextEvaluateJavaScriptCallbackID { 1 };
     HashMap<uint64_t, RefPtr<Inspector::AutomationBackendDispatcherHandler::EvaluateJavaScriptFunctionCallback>> m_evaluateJavaScriptFunctionCallbacks;

Modified: trunk/Source/WebKit/UIProcess/Automation/WebAutomationSessionMacros.h (230816 => 230817)


--- trunk/Source/WebKit/UIProcess/Automation/WebAutomationSessionMacros.h	2018-04-19 20:25:55 UTC (rev 230816)
+++ trunk/Source/WebKit/UIProcess/Automation/WebAutomationSessionMacros.h	2018-04-19 20:37:31 UTC (rev 230817)
@@ -38,6 +38,8 @@
 #define STRING_FOR_PREDEFINED_ERROR_MESSAGE(errorMessage) Inspector::Protocol::AutomationHelpers::getEnumConstantValue(VALIDATED_ERROR_MESSAGE(errorMessage))
 #define STRING_FOR_PREDEFINED_ERROR_MESSAGE_AND_DETAILS(errorMessage, detailsString) makeString(Inspector::Protocol::AutomationHelpers::getEnumConstantValue(VALIDATED_ERROR_MESSAGE(errorMessage)), errorNameAndDetailsSeparator, detailsString)
 
+#define AUTOMATION_COMMAND_ERROR_WITH_NAME(errorName) AutomationCommandError(Inspector::Protocol::Automation::ErrorMessage::errorName)
+
 // Convenience macros for filling in the error string of synchronous commands in bailout branches.
 #define SYNC_FAIL_WITH_PREDEFINED_ERROR(errorName) \
 do { \

Modified: trunk/Source/WebKit/UIProcess/WebPageProxy.cpp (230816 => 230817)


--- trunk/Source/WebKit/UIProcess/WebPageProxy.cpp	2018-04-19 20:25:55 UTC (rev 230816)
+++ trunk/Source/WebKit/UIProcess/WebPageProxy.cpp	2018-04-19 20:37:31 UTC (rev 230817)
@@ -293,21 +293,38 @@
 }
 
 #if !LOG_DISABLED
+static const char* webMouseEventTypeString(WebEvent::Type type)
+{
+    switch (type) {
+    case WebEvent::MouseDown:
+        return "MouseDown";
+    case WebEvent::MouseUp:
+        return "MouseUp";
+    case WebEvent::MouseMove:
+        return "MouseMove";
+    case WebEvent::MouseForceChanged:
+        return "MouseForceChanged";
+    case WebEvent::MouseForceDown:
+        return "MouseForceDown";
+    case WebEvent::MouseForceUp:
+        return "MouseForceUp";
+    default:
+        ASSERT_NOT_REACHED();
+        return "<unknown>";
+    }
+}
+
 static const char* webKeyboardEventTypeString(WebEvent::Type type)
 {
     switch (type) {
     case WebEvent::KeyDown:
         return "KeyDown";
-    
     case WebEvent::KeyUp:
         return "KeyUp";
-    
     case WebEvent::RawKeyDown:
         return "RawKeyDown";
-    
     case WebEvent::Char:
         return "Char";
-    
     default:
         ASSERT_NOT_REACHED();
         return "<unknown>";
@@ -1896,6 +1913,21 @@
     if (!isValid())
         return;
 
+    LOG(MouseHandling, "UIProcess: enqueued mouse event %s (queue size %zu)", webMouseEventTypeString(event.type()), m_mouseEventQueue.size());
+    m_mouseEventQueue.append(event);
+    if (m_mouseEventQueue.size() == 1) // Otherwise, called from DidReceiveEvent message handler.
+        processNextQueuedMouseEvent();
+}
+    
+void WebPageProxy::processNextQueuedMouseEvent()
+{
+    if (!isValid())
+        return;
+
+    ASSERT(!m_mouseEventQueue.isEmpty());
+
+    const NativeWebMouseEvent& event = m_mouseEventQueue.first();
+    
     if (m_pageClient.windowIsFrontWindowUnderMouse(event))
         setToolTip(String());
 
@@ -1902,22 +1934,8 @@
     // NOTE: This does not start the responsiveness timer because mouse move should not indicate interaction.
     if (event.type() != WebEvent::MouseMove)
         m_process->responsivenessTimer().start();
-    else {
-        if (m_processingMouseMoveEvent) {
-            m_nextMouseMoveEvent = std::make_unique<NativeWebMouseEvent>(event);
-            return;
-        }
 
-        m_processingMouseMoveEvent = true;
-    }
-
-    // <https://bugs.webkit.org/show_bug.cgi?id=57904> We need to keep track of the mouse down event in the case where we
-    // display a popup menu for select elements. When the user changes the selected item,
-    // we fake a mouse up event by using this stored down event. This event gets cleared
-    // when the mouse up message is received from WebProcess.
-    if (event.type() == WebEvent::MouseDown)
-        m_currentlyProcessedMouseDownEvent = std::make_unique<NativeWebMouseEvent>(event);
-
+    LOG(MouseHandling, "UIProcess: sent mouse event %s (queue size %zu)", webMouseEventTypeString(event.type()), m_mouseEventQueue.size());
     m_process->send(Messages::WebPage::MouseEvent(event), m_pageID);
 }
 
@@ -4833,9 +4851,26 @@
     m_process->send(Messages::WebPage::SetTextForActivePopupMenu(index), m_pageID);
 }
 
+bool WebPageProxy::isProcessingMouseEvents() const
+{
+    return !m_mouseEventQueue.isEmpty();
+}
+
 NativeWebMouseEvent* WebPageProxy::currentlyProcessedMouseDownEvent()
 {
-    return m_currentlyProcessedMouseDownEvent.get();
+    // <https://bugs.webkit.org/show_bug.cgi?id=57904> We need to keep track of the mouse down event in the case where we
+    // display a popup menu for select elements. When the user changes the selected item, we fake a mouseup event by
+    // using this stored mousedown event and changing the event type. This trickery happens when WebProcess handles
+    // a mousedown event that runs the default handler for HTMLSelectElement, so the triggering mousedown must be the first event.
+
+    if (m_mouseEventQueue.isEmpty())
+        return nullptr;
+    
+    auto& event = m_mouseEventQueue.first();
+    if (event.type() != WebEvent::Type::MouseDown)
+        return nullptr;
+
+    return &event;
 }
 
 void WebPageProxy::postMessageToInjectedBundle(const String& messageName, API::Object* messageBody)
@@ -5243,20 +5278,27 @@
     switch (type) {
     case WebEvent::NoType:
         break;
-    case WebEvent::MouseMove:
-        m_processingMouseMoveEvent = false;
-        if (m_nextMouseMoveEvent)
-            handleMouseEvent(*std::exchange(m_nextMouseMoveEvent, nullptr));
-        break;
-    case WebEvent::MouseDown:
-        break;
-    case WebEvent::MouseUp:
-        m_currentlyProcessedMouseDownEvent = nullptr;
-        break;
     case WebEvent::MouseForceChanged:
     case WebEvent::MouseForceDown:
     case WebEvent::MouseForceUp:
+    case WebEvent::MouseMove:
+    case WebEvent::MouseDown:
+    case WebEvent::MouseUp: {
+        LOG(MouseHandling, "WebPageProxy::didReceiveEvent: %s (queue size %zu)", webMouseEventTypeString(type), m_mouseEventQueue.size());
+
+        // Retire the last sent event now that WebProcess is done handling it.
+        MESSAGE_CHECK(!m_mouseEventQueue.isEmpty());
+        NativeWebMouseEvent event = m_mouseEventQueue.takeFirst();
+        MESSAGE_CHECK(type == event.type());
+
+        if (!m_mouseEventQueue.isEmpty()) {
+            LOG(MouseHandling, " UIProcess: handling a queued mouse event from didReceiveEvent");
+            processNextQueuedMouseEvent();
+        } else if (auto* automationSession = process().processPool().automationSession())
+            automationSession->mouseEventsFlushedForPage(*this);
+
         break;
+    }
 
     case WebEvent::Wheel: {
         MESSAGE_CHECK(!m_currentlyProcessedWheelEvents.isEmpty());
@@ -5285,7 +5327,8 @@
 
         MESSAGE_CHECK(type == event.type());
 
-        if (!m_keyEventQueue.isEmpty()) {
+        bool canProcessMoreKeyEvents = !m_keyEventQueue.isEmpty();
+        if (canProcessMoreKeyEvents) {
             LOG(KeyHandling, " UI process: sent keyEvent from didReceiveEvent");
             m_process->send(Messages::WebPage::KeyEvent(m_keyEventQueue.first()), m_pageID);
         }
@@ -5299,7 +5342,7 @@
             m_uiClient->didNotHandleKeyEvent(this, event);
 
         // Notify the session after -[NSApp sendEvent:] has a crack at turning the event into an action.
-        if (m_keyEventQueue.isEmpty()) {
+        if (!canProcessMoreKeyEvents) {
             if (auto* automationSession = process().processPool().automationSession())
                 automationSession->keyboardEventsFlushedForPage(*this);
         }
@@ -5903,15 +5946,10 @@
     m_pendingLearnOrIgnoreWordMessageCount = 0;
 
     // Can't expect DidReceiveEvent notifications from a crashed web process.
+    m_mouseEventQueue.clear();
     m_keyEventQueue.clear();
     m_wheelEventQueue.clear();
     m_currentlyProcessedWheelEvents.clear();
-
-    m_nextMouseMoveEvent = nullptr;
-    m_currentlyProcessedMouseDownEvent = nullptr;
-
-    m_processingMouseMoveEvent = false;
-
 #if ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS)
     m_touchEventQueue.clear();
 #endif

Modified: trunk/Source/WebKit/UIProcess/WebPageProxy.h (230816 => 230817)


--- trunk/Source/WebKit/UIProcess/WebPageProxy.h	2018-04-19 20:25:55 UTC (rev 230816)
+++ trunk/Source/WebKit/UIProcess/WebPageProxy.h	2018-04-19 20:37:31 UTC (rev 230817)
@@ -686,7 +686,10 @@
     void setBackgroundColor(const WebCore::Color& color) { m_backgroundColor = color; }
 #endif
 
+    bool isProcessingMouseEvents() const;
+    void processNextQueuedMouseEvent();
     void handleMouseEvent(const NativeWebMouseEvent&);
+
     void handleWheelEvent(const NativeWebWheelEvent&);
     void handleKeyboardEvent(const NativeWebKeyboardEvent&);
 
@@ -1939,6 +1942,7 @@
     WebCore::ResourceRequest m_decidePolicyForResponseRequest;
     bool m_shouldSuppressAppLinksInNextNavigationPolicyDecision { false };
 
+    Deque<NativeWebMouseEvent> m_mouseEventQueue;
     Deque<NativeWebKeyboardEvent> m_keyEventQueue;
     Deque<NativeWebWheelEvent> m_wheelEventQueue;
     Deque<std::unique_ptr<Vector<NativeWebWheelEvent>>> m_currentlyProcessedWheelEvents;
@@ -1946,10 +1950,6 @@
     Deque<NativeWebGestureEvent> m_gestureEventQueue;
 #endif
 
-    bool m_processingMouseMoveEvent { false };
-    std::unique_ptr<NativeWebMouseEvent> m_nextMouseMoveEvent;
-    std::unique_ptr<NativeWebMouseEvent> m_currentlyProcessedMouseDownEvent;
-
 #if ENABLE(TOUCH_EVENTS)
     struct TouchEventTracking {
         WebCore::TrackingType touchForceChangedTracking { WebCore::TrackingType::NotTracking };
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to