b=726443 rework last event time calculation to avoid problems with gtk_get_current_event_time in nested loops r=roc
authorKarl Tomlinson <karlt+@karlt.net>
Tue, 14 Feb 2012 09:19:59 +1300
changeset 86742 7d542384106f630dfbe78c57ed1f3bca7fd3eb10
parent 86741 9c6893026006b9ebcd09197d8b96228d2b22667d
child 86743 f28f16d674c95fdbe2ecb1f8dcff61ded1dbe404
push id6014
push userktomlinson@mozilla.com
push dateMon, 13 Feb 2012 21:11:46 +0000
treeherdermozilla-inbound@7d542384106f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs726443
milestone13.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
b=726443 rework last event time calculation to avoid problems with gtk_get_current_event_time in nested loops r=roc
widget/gtk2/nsDragService.cpp
widget/gtk2/nsWindow.cpp
widget/gtk2/nsWindow.h
--- a/widget/gtk2/nsDragService.cpp
+++ b/widget/gtk2/nsDragService.cpp
@@ -326,17 +326,17 @@ nsDragService::InvokeDragSession(nsIDOMN
     // Create a fake event for the drag so we can pass the time (so to speak).
     // If we don't do this, then, when the timestamp for the pending button
     // release event is used for the ungrab, the ungrab can fail due to the
     // timestamp being _earlier_ than CurrentTime.
     GdkEvent event;
     memset(&event, 0, sizeof(GdkEvent));
     event.type = GDK_BUTTON_PRESS;
     event.button.window = mHiddenWidget->window;
-    event.button.time = nsWindow::GetCurrentEventTime();
+    event.button.time = nsWindow::GetLastUserInputTime();
 
     // start our drag.
     GdkDragContext *context = gtk_drag_begin(mHiddenWidget,
                                              sourceList,
                                              action,
                                              1,
                                              &event);
 
--- a/widget/gtk2/nsWindow.cpp
+++ b/widget/gtk2/nsWindow.cpp
@@ -277,32 +277,24 @@ static bool gdk_keyboard_get_modmap_mask
                                             PRUint32* aCapsLockMask,
                                             PRUint32* aNumLockMask,
                                             PRUint32* aScrollLockMask);
 #endif /* MOZ_X11 */
 
 /* initialization static functions */
 static nsresult    initialize_prefs        (void);
 
-static void
-UpdateLastInputEventTime()
-{
-  nsCOMPtr<nsIdleService> idleService = do_GetService("@mozilla.org/widget/idleservice;1");
-  if (idleService) {
-    idleService->ResetIdleTimeOut();
-  }
-}
-
 // this is the last window that had a drag event happen on it.
 nsWindow *nsWindow::sLastDragMotionWindow = NULL;
 bool nsWindow::sIsDraggingOutOf = false;
 
 // Time of the last button release event. We use it to detect when the
 // drag ended before we could properly setup drag and drop.
 guint32   nsWindow::sLastButtonReleaseTime = 0;
+static guint32 sLastUserInputTime = GDK_CURRENT_TIME;
 static guint32 sRetryGrabTime;
 
 static NS_DEFINE_IID(kCDragServiceCID,  NS_DRAGSERVICE_CID);
 
 // The window from which the focus manager asks us to dispatch key events.
 static nsWindow         *gFocusWindow          = NULL;
 static bool              gBlockActivateEvent   = false;
 static bool              gGlobalsInitialized   = false;
@@ -372,16 +364,39 @@ GetBitmapStride(PRInt32 width)
 {
 #if defined(MOZ_X11) || defined(MOZ_WIDGET_GTK2)
   return (width+7)/8;
 #else
   return cairo_format_stride_for_width(CAIRO_FORMAT_A1, width);
 #endif
 }
 
+static inline bool TimestampIsNewerThan(guint32 a, guint32 b)
+{
+    // Timestamps are just the least significant bits of a monotonically
+    // increasing function, and so the use of unsigned overflow arithmetic.
+    return a - b <= G_MAXUINT32/2;
+}
+
+static void
+UpdateLastInputEventTime(void *aGdkEvent)
+{
+    nsCOMPtr<nsIdleService> idleService =
+        do_GetService("@mozilla.org/widget/idleservice;1");
+    if (idleService) {
+        idleService->ResetIdleTimeOut();
+    }
+
+    guint timestamp = gdk_event_get_time(static_cast<GdkEvent*>(aGdkEvent));
+    if (timestamp == GDK_CURRENT_TIME)
+        return;
+
+    sLastUserInputTime = timestamp;
+}
+
 nsWindow::nsWindow()
 {
     mIsTopLevel       = false;
     mIsDestroyed      = false;
     mNeedsResize      = false;
     mNeedsMove        = false;
     mListenForResizes = false;
     mIsShown          = false;
@@ -1422,37 +1437,30 @@ SetUserTimeAndStartupIDForActivatedWindo
     sn_launchee_context_unref(ctx);
     sn_display_unref(snd);
 #endif
 
     GTKToolkit->SetDesktopStartupID(EmptyCString());
 }
 
 /* static */ guint32
-nsWindow::GetCurrentEventTime()
-{
-    static guint32 sLastCurrentEventTime = GDK_CURRENT_TIME;
-
-    guint32 timestamp = gtk_get_current_event_time();
-
-    if (timestamp == GDK_CURRENT_TIME) {
-        timestamp = gdk_x11_display_get_user_time(gdk_display_get_default());
-
-        // The user_time is not updated on all user events, so check that we
-        // haven't returned a more recent timestamp.  If so, use the more
-        // recent timestamp to ensure that subsequent requests will override
-        // previous requests.  Timestamps are just the least significant bits
-        // of a monotonically increasing function, and so the use of unsigned
-        // overflow arithmetic.
-        if (sLastCurrentEventTime != GDK_CURRENT_TIME &&
-            sLastCurrentEventTime - timestamp <= G_MAXUINT32/2)
-            return sLastCurrentEventTime;
+nsWindow::GetLastUserInputTime()
+{
+    // gdk_x11_display_get_user_time tracks button and key presses,
+    // DESKTOP_STARTUP_ID used to start the app, drop events from external
+    // drags, WM_DELETE_WINDOW delete events, but not usually mouse motion nor
+    // button and key releases.  Therefore use the most recent of
+    // gdk_x11_display_get_user_time and the last time that we have seen.
+    guint32 timestamp =
+            gdk_x11_display_get_user_time(gdk_display_get_default());
+    if (sLastUserInputTime != GDK_CURRENT_TIME &&
+        TimestampIsNewerThan(sLastUserInputTime, timestamp)) {
+        return sLastUserInputTime;
     }       
 
-    sLastCurrentEventTime = timestamp;
     return timestamp;
 }
 
 NS_IMETHODIMP
 nsWindow::SetFocus(bool aRaise)
 {
     // Make sure that our owning widget has focus.  If it doesn't try to
     // grab it.  Note that we don't set our focus flag in this case.
@@ -1899,17 +1907,17 @@ nsWindow::CaptureMouse(bool aCapture)
         return NS_OK;
 
     GtkWidget *widget = GetMozContainerWidget();
     if (!widget)
         return NS_ERROR_FAILURE;
 
     if (aCapture) {
         gtk_grab_add(widget);
-        GrabPointer(GetCurrentEventTime());
+        GrabPointer(GetLastUserInputTime());
     }
     else {
         ReleaseGrabs();
         gtk_grab_remove(widget);
     }
 
     return NS_OK;
 }
@@ -1931,17 +1939,17 @@ nsWindow::CaptureRollupEvents(nsIRollupL
     if (aDoCapture) {
         gConsumeRollupEvent = aConsumeRollupEvent;
         gRollupListener = aListener;
         gRollupWindow = do_GetWeakReference(static_cast<nsIWidget*>
                                                        (this));
         // real grab is only done when there is no dragging
         if (!nsWindow::DragInProgress()) {
             gtk_grab_add(widget);
-            GrabPointer(GetCurrentEventTime());
+            GrabPointer(GetLastUserInputTime());
         }
     }
     else {
         if (!nsWindow::DragInProgress()) {
             ReleaseGrabs();
         }
         // There may not have been a drag in process when aDoCapture was set,
         // so make sure to remove any added grab.  This is a no-op if the grab
@@ -5721,48 +5729,48 @@ GetFirstNSWindowForGDKWindow(GdkWindow *
         }
     }
     return window;
 }
 
 static gboolean
 motion_notify_event_cb(GtkWidget *widget, GdkEventMotion *event)
 {
-    UpdateLastInputEventTime();
+    UpdateLastInputEventTime(event);
 
     nsWindow *window = GetFirstNSWindowForGDKWindow(event->window);
     if (!window)
         return FALSE;
 
     window->OnMotionNotifyEvent(widget, event);
 
 #ifdef HAVE_GTK_MOTION_HINTS
     gdk_event_request_motions(event);
 #endif
     return TRUE;
 }
 
 static gboolean
 button_press_event_cb(GtkWidget *widget, GdkEventButton *event)
 {
-    UpdateLastInputEventTime();
+    UpdateLastInputEventTime(event);
 
     nsWindow *window = GetFirstNSWindowForGDKWindow(event->window);
     if (!window)
         return FALSE;
 
     window->OnButtonPressEvent(widget, event);
 
     return TRUE;
 }
 
 static gboolean
 button_release_event_cb(GtkWidget *widget, GdkEventButton *event)
 {
-    UpdateLastInputEventTime();
+    UpdateLastInputEventTime(event);
 
     nsWindow *window = GetFirstNSWindowForGDKWindow(event->window);
     if (!window)
         return FALSE;
 
     window->OnButtonReleaseEvent(widget, event);
 
     return TRUE;
@@ -5958,17 +5966,17 @@ plugin_client_message_filter(GdkXEvent *
 }
 #endif /* MOZ_X11 */
 
 static gboolean
 key_press_event_cb(GtkWidget *widget, GdkEventKey *event)
 {
     LOG(("key_press_event_cb\n"));
 
-    UpdateLastInputEventTime();
+    UpdateLastInputEventTime(event);
 
     // find the window with focus and dispatch this event to that widget
     nsWindow *window = get_window_for_gtk_widget(widget);
     if (!window)
         return FALSE;
 
     nsRefPtr<nsWindow> focusWindow = gFocusWindow ? gFocusWindow : window;
 
@@ -6001,17 +6009,17 @@ key_press_event_cb(GtkWidget *widget, Gd
     return focusWindow->OnKeyPressEvent(widget, event);
 }
 
 static gboolean
 key_release_event_cb(GtkWidget *widget, GdkEventKey *event)
 {
     LOG(("key_release_event_cb\n"));
 
-    UpdateLastInputEventTime();
+    UpdateLastInputEventTime(event);
 
     // find the window with focus and dispatch this event to that widget
     nsWindow *window = get_window_for_gtk_widget(widget);
     if (!window)
         return FALSE;
 
     nsRefPtr<nsWindow> focusWindow = gFocusWindow ? gFocusWindow : window;
 
--- a/widget/gtk2/nsWindow.h
+++ b/widget/gtk2/nsWindow.h
@@ -187,21 +187,20 @@ public:
     NS_IMETHOD         GetAttention(PRInt32 aCycleCount);
 
     virtual bool       HasPendingInputEvent();
 
     NS_IMETHOD         MakeFullScreen(bool aFullScreen);
     NS_IMETHOD         HideWindowChrome(bool aShouldHide);
 
     /**
-     * GetCurrentEventTime guesses a timestamp for the most recent user input
-     * event (when the event is not available).  This is intended for pointer
-     * grab or focus requests, for example.
+     * GetLastUserInputTime returns a timestamp for the most recent user input
+     * event.  This is intended for pointer grab requests (including drags).
      */
-    static guint32     GetCurrentEventTime();
+    static guint32     GetLastUserInputTime();
 
     // utility method, -1 if no change should be made, otherwise returns a
     // value that can be passed to gdk_window_set_decorations
     gint               ConvertBorderStyles(nsBorderStyle aStyle);
 
     // event callbacks
 #if defined(MOZ_WIDGET_GTK2)
     gboolean           OnExposeEvent(GdkEventExpose *aEvent);