Web Automation: simulated mouse interactions should not be done until associated...
authorbburg@apple.com <bburg@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 18 Apr 2018 00:46:06 +0000 (00:46 +0000)
committerbburg@apple.com <bburg@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 18 Apr 2018 00:46:06 +0000 (00:46 +0000)
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.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@230743 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebKit/ChangeLog
Source/WebKit/Platform/Logging.h
Source/WebKit/UIProcess/Automation/WebAutomationSession.cpp
Source/WebKit/UIProcess/Automation/WebAutomationSession.h
Source/WebKit/UIProcess/Automation/WebAutomationSessionMacros.h
Source/WebKit/UIProcess/WebPageProxy.cpp
Source/WebKit/UIProcess/WebPageProxy.h

index 629781c..6a5cb4b 100644 (file)
@@ -1,3 +1,76 @@
+2018-04-11  Brian Burg  <bburg@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-17  Adrian Perez de Castro  <aperez@igalia.com>
 
         [GTK][WPE] Build failure due to presence of Avahi's <dns_sd.h> header
index 45566ac..f55855c 100644 (file)
@@ -51,6 +51,7 @@ extern "C" {
     M(KeyHandling) \
     M(Layers) \
     M(Loading) \
+    M(MouseHandling) \
     M(Network) \
     M(NetworkCache) \
     M(NetworkCacheSpeculativePreloading) \
index 485d755..2b811b2 100644 (file)
@@ -53,6 +53,15 @@ using namespace Inspector;
 
 namespace WebKit {
 
+String AutomationCommandError::toProtocolString() const
+{
+    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 @@ void WebAutomationSession::willShowJavaScriptDialog(WebPageProxy& page)
                 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 @@ void WebAutomationSession::inspectorFrontendLoaded(const WebPageProxy& page)
         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 @@ void WebAutomationSession::performMouseInteraction(const String& handle, const J
         if (!parsedButton)
             ASYNC_FAIL_WITH_PREDEFINED_ERROR_AND_DETAILS(InvalidParameter, "The parameter 'button' is invalid.");
 
-        platformSimulateMouseInteraction(page, viewPosition, parsedInteraction.value(), parsedButton.value(), keyModifiers);
+        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);
 
-        callback->sendSuccess(Inspector::Protocol::Automation::Point::create()
-            .setX(x)
-            .setY(y - page->topContentInset())
-            .release());
+        // This is cleared when all mouse events are flushed.
+        m_simulatingUserInteraction = true;
+
+        platformSimulateMouseInteraction(page, viewPosition, parsedInteraction.value(), parsedButton.value(), keyModifiers);
+        
+        // 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 @@ void WebAutomationSession::performKeyboardInteractions(const String& handle, con
     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;
index 5706782..24e81e7 100644 (file)
@@ -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 WebOpenPanelResultListenerProxy;
 class WebPageProxy;
 class WebProcessPool;
 
+struct AutomationCommandError {
+public:
+    AutomationCommandError(Inspector::Protocol::Automation::ErrorMessage type)
+        : type(type) { }
+
+    AutomationCommandError(Inspector::Protocol::Automation::ErrorMessage type, const String& message)
+        : type(type)
+        , message(message) { }
+    
+    String toProtocolString() const;
+
+    Inspector::Protocol::Automation::ErrorMessage type;
+    std::optional<String> message;
+};
+
 class WebAutomationSession final : public API::ObjectImpl<API::Object::Type::AutomationSession>, public IPC::MessageReceiver
 #if ENABLE(REMOTE_INSPECTOR)
     , public Inspector::RemoteAutomationTarget
@@ -98,6 +113,7 @@ public:
     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 @@ private:
     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;
index 2c5db19..17e9a37 100644 (file)
@@ -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 { \
index 84ce249..0e02763 100644 (file)
@@ -291,21 +291,32 @@ ExceededDatabaseQuotaRecords::Record* ExceededDatabaseQuotaRecords::next()
 }
 
 #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";
+    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>";
@@ -1898,28 +1909,28 @@ void WebPageProxy::handleMouseEvent(const NativeWebMouseEvent& event)
     if (!isValid())
         return;
 
+    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());
 
     // 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, " UI process: sent mouseEvent from handleMouseEvent");
     m_process->send(Messages::WebPage::MouseEvent(event), m_pageID);
 }
 
@@ -4804,9 +4815,26 @@ void WebPageProxy::setTextFromItemForPopupMenu(WebPopupMenuProxy*, int32_t index
     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)
@@ -5215,15 +5243,24 @@ void WebPageProxy::didReceiveEvent(uint32_t opaqueType, bool handled)
     case WebEvent::NoType:
         break;
     case WebEvent::MouseMove:
-        m_processingMouseMoveEvent = false;
-        if (m_nextMouseMoveEvent)
-            handleMouseEvent(*std::exchange(m_nextMouseMoveEvent, nullptr));
-        break;
     case WebEvent::MouseDown:
+    case WebEvent::MouseUp: {
+        LOG(MouseHandling, "WebPageProxy::didReceiveEvent: %s (queue empty %d)", webMouseEventTypeString(type), m_mouseEventQueue.isEmpty());
+
+        // 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, " UI process: handling a queued mouse event from didReceiveEvent");
+            processNextQueuedMouseEvent();
+        } else if (auto* automationSession = process().processPool().automationSession())
+            automationSession->mouseEventsFlushedForPage(*this);
+
         break;
-    case WebEvent::MouseUp:
-        m_currentlyProcessedMouseDownEvent = nullptr;
-        break;
+    }
+
     case WebEvent::MouseForceChanged:
     case WebEvent::MouseForceDown:
     case WebEvent::MouseForceUp:
@@ -5256,7 +5293,8 @@ void WebPageProxy::didReceiveEvent(uint32_t opaqueType, bool handled)
 
         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);
         }
@@ -5270,7 +5308,7 @@ void WebPageProxy::didReceiveEvent(uint32_t opaqueType, bool handled)
             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);
         }
@@ -5874,15 +5912,10 @@ void WebPageProxy::resetStateAfterProcessExited(ProcessTerminationReason termina
     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
index a1d231a..d3618d2 100644 (file)
@@ -684,7 +684,10 @@ public:
     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&);
 
@@ -1932,6 +1935,7 @@ private:
     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;
@@ -1939,10 +1943,6 @@ private:
     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 };