Bug 668953 - [10.7] Support new back/forward gestures in OSX Lion; r=smichaud
authorBenoit Girard <b56girard@gmail.com>
Fri, 05 Aug 2011 14:36:51 -0400
changeset 73981 1ca50e8b3d3780837065b31bae3fc75cc7ef3da5
parent 73980 ac7e11df24ae296e60e619bce92ad52264ba14f8
child 73982 fd30726145d5db2f40e82e81c656402cc336ed81
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
reviewerssmichaud
bugs668953
milestone8.0a1
Bug 668953 - [10.7] Support new back/forward gestures in OSX Lion; r=smichaud
content/events/src/nsEventStateManager.cpp
widget/src/cocoa/Makefile.in
widget/src/cocoa/nsCocoaFeatures.h
widget/src/cocoa/nsCocoaFeatures.mm
--- a/content/events/src/nsEventStateManager.cpp
+++ b/content/events/src/nsEventStateManager.cpp
@@ -136,16 +136,20 @@
 #include "mozilla/Services.h"
 #include "mozAutoDocUpdate.h"
 #include "nsHTMLLabelElement.h"
 
 #include "mozilla/Preferences.h"
 
 #ifdef XP_MACOSX
 #import <ApplicationServices/ApplicationServices.h>
+
+#ifdef MOZ_WIDGET_COCOA
+#include "nsCocoaFeatures.h"
+#endif 
 #endif
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 //#define DEBUG_DOCSHELL_FOCUS
 
 #define NS_USER_INTERACTION_INTERVAL 5000 // ms
@@ -368,25 +372,30 @@ public:
   // Be careful, UpdateTransaction may fire a DOM event, therefore, the target
   // frame might be destroyed in the event handler.
   static PRBool UpdateTransaction(PRInt32 aNumLines,
                                   PRBool aScrollHorizontal);
   static void EndTransaction();
   static void OnEvent(nsEvent* aEvent);
   static void Shutdown();
   static PRUint32 GetTimeoutTime();
+  static PRUint32 GetGestureTimeoutTime();
   static PRInt32 AccelerateWheelDelta(PRInt32 aScrollLines,
                    PRBool aIsHorizontal, PRBool aAllowScrollSpeedOverride,
                    nsIScrollableFrame::ScrollUnit *aScrollQuantity,
                    PRBool aLimitToMaxOnePageScroll = PR_TRUE);
   static PRBool IsAccelerationEnabled();
 
   enum {
     kScrollSeriesTimeout = 80
   };
+#ifdef MOZ_WIDGET_COCOA
+  static PRBool GetGestureTriggered();
+  static void SetGestureTriggered();
+#endif
 protected:
   static nsIntPoint GetScreenPoint(nsGUIEvent* aEvent);
   static void OnFailToScrollTarget();
   static void OnTimeout(nsITimer *aTimer, void *aClosure);
   static void SetTimeout();
   static PRUint32 GetIgnoreMoveDelayTime();
   static PRInt32 GetAccelerationStart();
   static PRInt32 GetAccelerationFactor();
@@ -397,23 +406,29 @@ protected:
                    PRBool aIsHorizontal,
                    nsIScrollableFrame::ScrollUnit *aScrollQuantity);
 
   static nsWeakFrame sTargetFrame;
   static PRUint32    sTime;        // in milliseconds
   static PRUint32    sMouseMoved;  // in milliseconds
   static nsITimer*   sTimer;
   static PRInt32     sScrollSeriesCounter;
+#ifdef MOZ_WIDGET_COCOA
+  static PRUint32    sGestureTriggered; // in milliseconds
+#endif
 };
 
 nsWeakFrame nsMouseWheelTransaction::sTargetFrame(nsnull);
 PRUint32    nsMouseWheelTransaction::sTime        = 0;
 PRUint32    nsMouseWheelTransaction::sMouseMoved  = 0;
 nsITimer*   nsMouseWheelTransaction::sTimer       = nsnull;
 PRInt32     nsMouseWheelTransaction::sScrollSeriesCounter = 0;
+#ifdef MOZ_WIDGET_COCOA
+PRUint32      nsMouseWheelTransaction::sGestureTriggered = 0;
+#endif
 
 static PRBool
 OutOfTime(PRUint32 aBaseTime, PRUint32 aThreshold)
 {
   PRUint32 now = PR_IntervalToMilliseconds(PR_IntervalNow());
   return (now - aBaseTime > aThreshold);
 }
 
@@ -482,18 +497,39 @@ nsMouseWheelTransaction::UpdateTransacti
 
 void
 nsMouseWheelTransaction::EndTransaction()
 {
   if (sTimer)
     sTimer->Cancel();
   sTargetFrame = nsnull;
   sScrollSeriesCounter = 0;
+#ifdef MOZ_WIDGET_COCOA
+  sGestureTriggered = 0;
+#endif
 }
 
+#ifdef MOZ_WIDGET_COCOA
+void
+nsMouseWheelTransaction::SetGestureTriggered() {
+  sGestureTriggered = PR_IntervalToMilliseconds(PR_IntervalNow());
+}
+
+PRBool
+nsMouseWheelTransaction::GetGestureTriggered() {
+  if (sGestureTriggered != 0 &&
+      OutOfTime(sGestureTriggered, GetGestureTimeoutTime())) {
+    // Start accepting new gestures
+    sGestureTriggered = 0;
+  }
+
+  return sGestureTriggered != 0;
+}
+#endif
+
 void
 nsMouseWheelTransaction::OnEvent(nsEvent* aEvent)
 {
   if (!sTargetFrame)
     return;
 
   if (OutOfTime(sTime, GetTimeoutTime())) {
     // Even if the scroll event which is handled after timeout, but onTimeout
@@ -630,16 +666,22 @@ nsIntPoint
 nsMouseWheelTransaction::GetScreenPoint(nsGUIEvent* aEvent)
 {
   NS_ASSERTION(aEvent, "aEvent is null");
   NS_ASSERTION(aEvent->widget, "aEvent-widget is null");
   return aEvent->refPoint + aEvent->widget->WidgetToScreenOffset();
 }
 
 PRUint32
+nsMouseWheelTransaction::GetGestureTimeoutTime()
+{
+  return Preferences::GetUint("mousewheel.transaction.gesturetimeout", 300);
+}
+
+PRUint32
 nsMouseWheelTransaction::GetTimeoutTime()
 {
   return Preferences::GetUint("mousewheel.transaction.timeout", 1500);
 }
 
 PRUint32
 nsMouseWheelTransaction::GetIgnoreMoveDelayTime()
 {
@@ -2792,16 +2834,32 @@ nsEventStateManager::DoScrollText(nsIFra
             passToParent = PR_TRUE;
             nsMouseWheelTransaction::EndTransaction();
           }
         }
       }
     }
   }
 
+#ifdef MOZ_WIDGET_COCOA
+  // On lion scroll will trigger back/forward at the edge of the page
+  if (isHorizontal && passToParent && nsCocoaFeatures::OnLionOrLater()) {
+    if (!nsMouseWheelTransaction::GetGestureTriggered()) {
+      if (numLines > 4 || numLines < -4) {
+        DoScrollHistory(-numLines);
+        nsMouseWheelTransaction::SetGestureTriggered();
+        return NS_OK;
+      }
+    } else {
+      // Extend the gesture in progress
+      nsMouseWheelTransaction::SetGestureTriggered();
+    }
+  }
+#endif
+
   if (!passToParent && frameToScroll) {
     if (aScrollQuantity == nsIScrollableFrame::LINES) {
       // When this is called for querying the scroll target information,
       // we shouldn't limit the scrolling amount to less one page.
       // Otherwise, we shouldn't scroll more one page at once.
       numLines =
         nsMouseWheelTransaction::AccelerateWheelDelta(numLines, isHorizontal,
                                                       aAllowScrollSpeedOverride,
--- a/widget/src/cocoa/Makefile.in
+++ b/widget/src/cocoa/Makefile.in
@@ -52,31 +52,33 @@ GRE_MODULE	= 1
 LIBXUL_LIBRARY = 1
 
 
 
 EXPORTS = \
 		mozView.h \
 		nsChangeObserver.h \
 		nsCocoaUtils.h \
+		nsCocoaFeatures.h \
 		$(NULL)
 
 CMMSRCS = \
   nsBidiKeyboard.mm \
   nsClipboard.mm \
   nsMenuX.mm \
   nsMenuBarX.mm \
   nsMenuItemX.mm \
   nsMenuItemIconX.mm \
   nsMenuUtilsX.mm \
   nsMenuGroupOwnerX.mm \
   nsFilePicker.mm \
   nsDragService.mm \
   nsToolkit.mm \
   nsAppShell.mm \
+  nsCocoaFeatures.mm \
   nsCocoaUtils.mm \
   nsCocoaWindow.mm \
   nsChildView.mm \
   nsWindowMap.mm \
   nsWidgetFactory.mm \
   nsCursorManager.mm \
   nsMacCursor.mm \
   nsScreenCocoa.mm \
new file mode 100644
--- /dev/null
+++ b/widget/src/cocoa/nsCocoaFeatures.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is 
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Benoit Girard <bgirard@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nsCocoaFeatures_h_
+#define nsCocoaFeatures_h_
+
+#include "prtypes.h"
+
+class nsCocoaFeatures {
+public:
+  static PRInt32 OSXVersion();
+
+  static PRBool OnSnowLeopardOrLater();  
+  static PRBool OnLionOrLater();  
+private:
+  static PRInt32 mOSXVersion;
+};
+#endif // nsCocoaFeatures_h_
new file mode 100644
--- /dev/null
+++ b/widget/src/cocoa/nsCocoaFeatures.mm
@@ -0,0 +1,81 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is thebes gfx code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Benoit Girard <bgirard@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#define MAC_OS_X_VERSION_MASK     0x0000FFFF // Not supported
+#define MAC_OS_X_VERSION_10_4_HEX 0x00001040 // Not supported
+#define MAC_OS_X_VERSION_10_5_HEX 0x00001050
+#define MAC_OS_X_VERSION_10_6_HEX 0x00001060
+#define MAC_OS_X_VERSION_10_7_HEX 0x00001070
+
+// This API will not work for OS X 10.10, see Gestalt.h.
+
+#include "nsCocoaFeatures.h"
+#include "nsDebug.h"
+
+#import <Cocoa/Cocoa.h>
+
+PRInt32 nsCocoaFeatures::mOSXVersion = 0;
+
+/* static */ PRInt32
+nsCocoaFeatures::OSXVersion()
+{
+    if (!mOSXVersion) {
+        // minor version is not accurate, use gestaltSystemVersionMajor, 
+        // gestaltSystemVersionMinor, gestaltSystemVersionBugFix for these
+        OSErr err = ::Gestalt(gestaltSystemVersion, reinterpret_cast<SInt32*>(&mOSXVersion));
+        if (err != noErr) {
+            // This should probably be changed when our minimum version changes
+            NS_ERROR("Couldn't determine OS X version, assuming 10.5");
+            mOSXVersion = MAC_OS_X_VERSION_10_5_HEX;
+        }
+        mOSXVersion &= MAC_OS_X_VERSION_MASK;
+    }
+    return mOSXVersion;
+}
+
+/* static */ PRBool
+nsCocoaFeatures::OnSnowLeopardOrLater()
+{
+    return (OSXVersion() >= MAC_OS_X_VERSION_10_6_HEX);
+}
+
+/* static */ PRBool
+nsCocoaFeatures::OnLionOrLater()
+{
+    return (OSXVersion() >= MAC_OS_X_VERSION_10_7_HEX);
+}
+