b=500081 use a timestamp when grabbing the pointer and generate timestamps for drags in the same way r=roc
authorKarl Tomlinson <karlt+@karlt.net>
Thu, 02 Feb 2012 15:12:26 +1300
changeset 86280 050334f9128c16d072990c3b88cf5145dddcd0cd
parent 86279 ca19aff687a18efe997887d52785acda04764008
child 86281 08b3c9d38e326f88a3df78e4f2852a89ae505cdc
push id22008
push userbmo@edmorley.co.uk
push dateTue, 07 Feb 2012 10:32:58 +0000
treeherdermozilla-central@2b61af9d18ee [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs500081
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=500081 use a timestamp when grabbing the pointer and generate timestamps for drags in the same way r=roc
widget/gtk2/nsDragService.cpp
widget/gtk2/nsWindow.cpp
widget/gtk2/nsWindow.h
--- a/widget/gtk2/nsDragService.cpp
+++ b/widget/gtk2/nsDragService.cpp
@@ -318,27 +318,25 @@ nsDragService::InvokeDragSession(nsIDOMN
 
     if (aActionType & DRAGDROP_ACTION_COPY)
         action = (GdkDragAction)(action | GDK_ACTION_COPY);
     if (aActionType & DRAGDROP_ACTION_MOVE)
         action = (GdkDragAction)(action | GDK_ACTION_MOVE);
     if (aActionType & DRAGDROP_ACTION_LINK)
         action = (GdkDragAction)(action | GDK_ACTION_LINK);
 
-    // Create a fake event for the drag so we can pass the time
-    // (so to speak.)  If we don't do this the drag can end as a
-    // result of a button release that is actually _earlier_ than
-    // CurrentTime.  So we use the time on the last button press
-    // event, as that will always be older than the button release
-    // that ends any drag.
+    // 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::sLastButtonPressTime;
+    event.button.time = nsWindow::GetCurrentEventTime();
 
     // start our drag.
     GdkDragContext *context = gtk_drag_begin(mHiddenWidget,
                                              sourceList,
                                              action,
                                              1,
                                              &event);
 
--- a/widget/gtk2/nsWindow.cpp
+++ b/widget/gtk2/nsWindow.cpp
@@ -290,22 +290,20 @@ UpdateLastInputEventTime()
     idleService->ResetIdleTimeOut();
   }
 }
 
 // this is the last window that had a drag event happen on it.
 nsWindow *nsWindow::sLastDragMotionWindow = NULL;
 bool nsWindow::sIsDraggingOutOf = false;
 
-// This is the time of the last button press event.  The drag service
-// uses it as the time to start drags.
-guint32   nsWindow::sLastButtonPressTime = 0;
 // 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 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;
 static bool              gRaiseWindows         = true;
@@ -1423,16 +1421,41 @@ 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;
+    }       
+
+    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.
 
     LOGFOCUS(("  SetFocus %d [%p]\n", aRaise, (void *)this));
 
@@ -1876,17 +1899,17 @@ nsWindow::CaptureMouse(bool aCapture)
         return NS_OK;
 
     GtkWidget *widget = GetMozContainerWidget();
     if (!widget)
         return NS_ERROR_FAILURE;
 
     if (aCapture) {
         gtk_grab_add(widget);
-        GrabPointer();
+        GrabPointer(GetCurrentEventTime());
     }
     else {
         ReleaseGrabs();
         gtk_grab_remove(widget);
     }
 
     return NS_OK;
 }
@@ -1908,17 +1931,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();
+            GrabPointer(GetCurrentEventTime());
         }
     }
     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
@@ -2734,18 +2757,17 @@ nsWindow::OnButtonPressEvent(GtkWidget *
     GdkEvent *peekedEvent = gdk_event_peek();
     if (peekedEvent) {
         GdkEventType type = peekedEvent->any.type;
         gdk_event_free(peekedEvent);
         if (type == GDK_2BUTTON_PRESS || type == GDK_3BUTTON_PRESS)
             return;
     }
 
-    // Always save the time of this event
-    sLastButtonPressTime = aEvent->time;
+    // We haven't received the corresponding release event yet.
     sLastButtonReleaseTime = 0;
 
     nsWindow *containerWindow = GetContainerWindow();
     if (!gFocusWindow && containerWindow) {
         containerWindow->DispatchActivateEvent();
     }
 
     // check to see if we should rollup
@@ -4489,17 +4511,17 @@ nsWindow::GetSafeWindowSize(nsIntSize aS
     }
     return result;
 }
 
 void
 nsWindow::EnsureGrabs(void)
 {
     if (mRetryPointerGrab)
-        GrabPointer();
+        GrabPointer(sRetryGrabTime);
 }
 
 void
 nsWindow::SetTransparencyMode(nsTransparencyMode aMode)
 {
     if (!mShell) {
         // Pass the request to the toplevel window
         GtkWidget *topWidget = nsnull;
@@ -4878,17 +4900,17 @@ nsWindow::UpdateTranslucentWindowAlphaIn
 
     if (!mNeedsShow) {
         ApplyTransparencyBitmap();
     }
     return NS_OK;
 }
 
 void
-nsWindow::GrabPointer(void)
+nsWindow::GrabPointer(guint32 aTime)
 {
     LOG(("GrabPointer %d\n", mRetryPointerGrab));
 
     mRetryPointerGrab = false;
 
     // If the window isn't visible, just set the flag to retry the
     // grab.  When this window becomes visible, the grab will be
     // retried.
@@ -4906,21 +4928,22 @@ nsWindow::GrabPointer(void)
                               (GdkEventMask)(GDK_BUTTON_PRESS_MASK |
                                              GDK_BUTTON_RELEASE_MASK |
                                              GDK_ENTER_NOTIFY_MASK |
                                              GDK_LEAVE_NOTIFY_MASK |
 #ifdef HAVE_GTK_MOTION_HINTS
                                              GDK_POINTER_MOTION_HINT_MASK |
 #endif
                                              GDK_POINTER_MOTION_MASK),
-                              (GdkWindow *)NULL, NULL, GDK_CURRENT_TIME);
+                              (GdkWindow *)NULL, NULL, aTime);
 
     if (retval == GDK_GRAB_NOT_VIEWABLE) {
         LOG(("GrabPointer: window not viewable; will retry\n"));
         mRetryPointerGrab = true;
+        sRetryGrabTime = aTime;
     } 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.
         // Check for rollup now, because, without the grab, we likely won't
         // get subsequent button press events.
         check_for_rollup(0, 0, false, true);
     }
 }
--- a/widget/gtk2/nsWindow.h
+++ b/widget/gtk2/nsWindow.h
@@ -186,16 +186,23 @@ public:
                                            bool aConsumeRollupEvent);
     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.
+     */
+    static guint32     GetCurrentEventTime();
+
     // 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);
 #else
@@ -270,17 +277,17 @@ private:
                                     PRInt32 aHeight,
                                     bool    aRepaint);
 
     void               NativeShow  (bool    aAction);
     void               SetHasMappedToplevel(bool aState);
     nsIntSize          GetSafeWindowSize(nsIntSize aSize);
 
     void               EnsureGrabs  (void);
-    void               GrabPointer  (void);
+    void               GrabPointer  (guint32 aTime);
     void               ReleaseGrabs (void);
 
 public:
     enum PluginType {
         PluginType_NONE = 0,   /* do not have any plugin */
         PluginType_XEMBED,     /* the plugin support xembed */
         PluginType_NONXEMBED   /* the plugin does not support xembed */
     };