Bug 1321069 - Redirect the end event of a long-tap sequence back to the content window. r=karlt, a=RyanVM
authorKartikaya Gupta <kgupta@mozilla.com>
Wed, 30 May 2018 09:49:23 -0400
changeset 473635 9008c3afe6b1c6eee770aa2b853685e0ea89de8e
parent 473634 ebdb496585ab89d5698ff18be74be35c50fe6ff8
child 473636 0e891abf73fe0397e6422e7fc1e7f9e0ec46b4a1
push id1728
push userjlund@mozilla.com
push dateMon, 18 Jun 2018 21:12:27 +0000
treeherdermozilla-release@c296fde26f5f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskarlt, RyanVM
bugs1321069
milestone61.0
Bug 1321069 - Redirect the end event of a long-tap sequence back to the content window. r=karlt, a=RyanVM In the case of a long-tap touch sequence, a new popup window (the contextmenu) is spawned while the sequence is ongoing. The touch-end of the sequence ends up getting delivered to the popup window, instead of the original content window, and that causes the touch-handling machinery state in the content window to get out of sync with reality. This patch detects this scenario and redirects the touch events on the popup window back to the original content window. MozReview-Commit-ID: L2vvKLlogRA
widget/gtk/nsWindow.cpp
widget/gtk/nsWindow.h
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -3487,21 +3487,51 @@ nsWindow::OnDragDataReceivedEvent(GtkWid
     LOGDRAG(("nsWindow::OnDragDataReceived(%p)\n", (void*)this));
 
     RefPtr<nsDragService> dragService = nsDragService::GetInstance();
     dragService->
         TargetDataReceived(aWidget, aDragContext, aX, aY,
                            aSelectionData, aInfo, aTime);
 }
 
+nsWindow*
+nsWindow::GetTransientForWindowIfPopup()
+{
+    if (mWindowType != eWindowType_popup) {
+        return nullptr;
+    }
+    GtkWindow* toplevel = gtk_window_get_transient_for(GTK_WINDOW(mShell));
+    if (toplevel) {
+        return get_window_for_gtk_widget(GTK_WIDGET(toplevel));
+    }
+    return nullptr;
+}
+
+bool
+nsWindow::IsHandlingTouchSequence(GdkEventSequence* aSequence)
+{
+    return mHandleTouchEvent && mTouches.Contains(aSequence);
+}
+
 #if GTK_CHECK_VERSION(3,4,0)
 gboolean
 nsWindow::OnTouchEvent(GdkEventTouch* aEvent)
 {
     if (!mHandleTouchEvent) {
+        // If a popup window was spawned (e.g. as the result of a long-press)
+        // and touch events got diverted to that window within a touch sequence,
+        // ensure the touch event gets sent to the original window instead. We
+        // keep the checks here very conservative so that we only redirect
+        // events in this specific scenario.
+        nsWindow* targetWindow = GetTransientForWindowIfPopup();
+        if (targetWindow &&
+            targetWindow->IsHandlingTouchSequence(aEvent->sequence)) {
+            return targetWindow->OnTouchEvent(aEvent);
+        }
+
         return FALSE;
     }
 
     EventMessage msg;
     switch (aEvent->type) {
     case GDK_TOUCH_BEGIN:
         msg = eTouchStart;
         break;
@@ -4819,22 +4849,26 @@ nsWindow::GrabPointer(guint32 aTime)
 
     if (!mIsX11Display) {
         // Don't to the grab on Wayland as it causes a regression
         // from Bug 1377084.
         return;
     }
 
     gint retval;
+    // Note that we need GDK_TOUCH_MASK below to work around a GDK/X11 bug that
+    // causes touch events that would normally be received by this client on
+    // other windows to be discarded during the grab.
     retval = gdk_pointer_grab(mGdkWindow, TRUE,
                               (GdkEventMask)(GDK_BUTTON_PRESS_MASK |
                                              GDK_BUTTON_RELEASE_MASK |
                                              GDK_ENTER_NOTIFY_MASK |
                                              GDK_LEAVE_NOTIFY_MASK |
-                                             GDK_POINTER_MOTION_MASK),
+                                             GDK_POINTER_MOTION_MASK |
+                                             GDK_TOUCH_MASK),
                               (GdkWindow *)nullptr, nullptr, aTime);
 
     if (retval == GDK_GRAB_NOT_VIEWABLE) {
         LOG(("GrabPointer: window not viewable; will retry\n"));
         mRetryPointerGrab = true;
     } else if (retval != GDK_GRAB_SUCCESS) {
         LOG(("GrabPointer: pointer grab failed: %i\n", retval));
         // A failed grab indicates that another app has grabbed the pointer.
--- a/widget/gtk/nsWindow.h
+++ b/widget/gtk/nsWindow.h
@@ -476,16 +476,19 @@ private:
                                    GdkWindow** aWindow, gint* aButton,
                                    gint* aRootX, gint* aRootY);
     void               ClearCachedResources();
     nsIWidgetListener* GetListener();
     bool               IsComposited() const;
 
     void               UpdateClientOffsetForCSDWindow();
 
+    nsWindow*          GetTransientForWindowIfPopup();
+    bool               IsHandlingTouchSequence(GdkEventSequence* aSequence);
+
 #ifdef MOZ_X11
     typedef enum { GTK_WIDGET_COMPOSIDED_DEFAULT = 0,
                    GTK_WIDGET_COMPOSIDED_DISABLED = 1,
                    GTK_WIDGET_COMPOSIDED_ENABLED = 2
     } WindowComposeRequest;
 
     void                SetCompositorHint(WindowComposeRequest aState);
 #endif