Revert 366e6a39d71a from bug 673875 for causing bug 907275. r=smichaud, a=bajaj
authorStephen Pohl <spohl.mozilla.bugs@gmail.com>
Wed, 21 Aug 2013 12:22:03 -0400
changeset 153870 e6b7411e9b1f4f1a2f5b5466d91c2ff6f88b5ed9
parent 153869 7be7bbac0064fe4dea3f0a607b320bd51971460f
child 153871 e56e99a1ce5fb0487f20f7444fbc4754f40cf7a7
push id2859
push userakeybl@mozilla.com
push dateMon, 16 Sep 2013 19:14:59 +0000
treeherdermozilla-beta@87d3c51cd2bf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmichaud, bajaj
bugs673875, 907275
milestone25.0a2
Revert 366e6a39d71a from bug 673875 for causing bug 907275. r=smichaud, a=bajaj
browser/base/content/browser-gestureSupport.js
content/events/src/nsEventStateManager.cpp
widget/cocoa/nsChildView.h
widget/cocoa/nsChildView.mm
widget/nsGUIEvent.h
--- a/browser/base/content/browser-gestureSupport.js
+++ b/browser/base/content/browser-gestureSupport.js
@@ -190,31 +190,17 @@ let gGestureSupport = {
 
     if (canGoBack)
       aEvent.allowedDirections |= isLTR ? aEvent.DIRECTION_LEFT :
                                           aEvent.DIRECTION_RIGHT;
     if (canGoForward)
       aEvent.allowedDirections |= isLTR ? aEvent.DIRECTION_RIGHT :
                                           aEvent.DIRECTION_LEFT;
 
-    let isVerticalSwipe = false;
-    if (gHistorySwipeAnimation.active) {
-      if (aEvent.direction == aEvent.DIRECTION_UP) {
-        isVerticalSwipe = true;
-        // Force a synchronous scroll to the top of the page.
-        content.scrollTo(content.scrollX, 0);
-      }
-      else if (aEvent.direction == aEvent.DIRECTION_DOWN) {
-        isVerticalSwipe = true;
-        // Force a synchronous scroll to the bottom of the page.
-        content.scrollTo(content.scrollX, content.scrollMaxY);
-      }
-    }
-
-    gHistorySwipeAnimation.startAnimation(isVerticalSwipe);
+    gHistorySwipeAnimation.startAnimation();
 
     this._doUpdate = function GS__doUpdate(aEvent) {
       gHistorySwipeAnimation.updateAnimation(aEvent.delta);
     };
 
     this._doEnd = function GS__doEnd(aEvent) {
       gHistorySwipeAnimation.swipeEndEventReceived();
 
@@ -558,79 +544,59 @@ let gHistorySwipeAnimation = {
   init: function HSA_init() {
     if (!this._isSupported())
       return;
 
     this.active = false;
     this.isLTR = document.documentElement.mozMatchesSelector(
                                             ":-moz-locale-dir(ltr)");
     this._trackedSnapshots = [];
-    this._startingIndex = -1;
     this._historyIndex = -1;
     this._boxWidth = -1;
-    this._boxHeight = -1;
     this._maxSnapshots = this._getMaxSnapshots();
     this._lastSwipeDir = "";
-    this._isVerticalSwipe = false;
 
     // We only want to activate history swipe animations if we store snapshots.
     // If we don't store any, we handle horizontal swipes without animations.
     if (this._maxSnapshots > 0) {
       this.active = true;
       gBrowser.addEventListener("pagehide", this, false);
       gBrowser.addEventListener("pageshow", this, false);
       gBrowser.addEventListener("popstate", this, false);
-      gBrowser.addEventListener("DOMModalDialogClosed", this, false);
       gBrowser.tabContainer.addEventListener("TabClose", this, false);
     }
   },
 
   /**
    * Uninitializes the support for history swipe animations.
    */
   uninit: function HSA_uninit() {
     gBrowser.removeEventListener("pagehide", this, false);
     gBrowser.removeEventListener("pageshow", this, false);
     gBrowser.removeEventListener("popstate", this, false);
-    gBrowser.removeEventListener("DOMModalDialogClosed", this, false);
     gBrowser.tabContainer.removeEventListener("TabClose", this, false);
 
     this.active = false;
     this.isLTR = false;
   },
 
   /**
    * Starts the swipe animation and handles fast swiping (i.e. a swipe animation
    * is already in progress when a new one is initiated).
-   *
-   * @param aIsVerticalSwipe
-   *        Whether we're dealing with a vertical swipe or not.
    */
-  startAnimation: function HSA_startAnimation(aIsVerticalSwipe) {
-    this._isVerticalSwipe = aIsVerticalSwipe;
-
+  startAnimation: function HSA_startAnimation() {
     if (this.isAnimationRunning()) {
-      // If this is a horizontal scroll, or if this is a vertical scroll that
-      // was started while a horizontal scroll was still running, handle it as
-      // as a fast swipe. In the case of the latter scenario, this allows us to
-      // start the vertical animation without first loading the final page, or
-      // taking another snapshot. If vertical scrolls are initiated repeatedly
-      // without prior horizontal scroll we skip this and restart the animation
-      // from 0.
-      if (!this._isVerticalSwipe || this._lastSwipeDir != "") {
-        gBrowser.stop();
-        this._lastSwipeDir = "RELOAD"; // just ensure that != ""
-        this._canGoBack = this.canGoBack();
-        this._canGoForward = this.canGoForward();
-        this._handleFastSwiping();
-      }
+      gBrowser.stop();
+      this._lastSwipeDir = "RELOAD"; // just ensure that != ""
+      this._canGoBack = this.canGoBack();
+      this._canGoForward = this.canGoForward();
+      this._handleFastSwiping();
     }
     else {
-      this._startingIndex = gBrowser.webNavigation.sessionHistory.index;
-      this._historyIndex = this._startingIndex;
+      this._historyIndex = gBrowser.webNavigation.sessionHistory.index;
       this._canGoBack = this.canGoBack();
       this._canGoForward = this.canGoForward();
       if (this.active) {
         this._takeSnapshot();
         this._installPrevAndNextSnapshots();
         this._addBoxes();
         this._lastSwipeDir = "";
       }
@@ -651,86 +617,75 @@ let gHistorySwipeAnimation = {
    * @param aVal
    *        A floating point value that represents the progress of the
    *        swipe gesture.
    */
   updateAnimation: function HSA_updateAnimation(aVal) {
     if (!this.isAnimationRunning())
       return;
 
-    // We use the following value to decrease the bounce effect when scrolling
-    // to the top or bottom of the page, or when swiping back/forward past the
-    // browsing history. This value was determined experimentally.
-    let dampValue = 4;
-    if (this._isVerticalSwipe) {
-      this._prevBox.collapsed = true;
-      this._nextBox.collapsed = true;
-      this._positionBox(this._curBox, -1 * aVal / dampValue);
-    }
-    else if ((aVal >= 0 && this.isLTR) ||
-             (aVal <= 0 && !this.isLTR)) {
-      let tempDampValue = 1;
+    if ((aVal >= 0 && this.isLTR) ||
+        (aVal <= 0 && !this.isLTR)) {
+      if (aVal > 1)
+        aVal = 1; // Cap value to avoid sliding the page further than allowed.
+
       if (this._canGoBack)
         this._prevBox.collapsed = false;
-      else {
-        tempDampValue = dampValue;
+      else
         this._prevBox.collapsed = true;
-      }
 
       // The current page is pushed to the right (LTR) or left (RTL),
       // the intention is to go back.
       // If there is a page to go back to, it should show in the background.
-      this._positionBox(this._curBox, aVal / tempDampValue);
+      this._positionBox(this._curBox, aVal);
 
       // The forward page should be pushed offscreen all the way to the right.
       this._positionBox(this._nextBox, 1);
     }
     else {
       if (aVal < -1)
         aVal = -1; // Cap value to avoid sliding the page further than allowed.
 
       // The intention is to go forward. If there is a page to go forward to,
       // it should slide in from the right (LTR) or left (RTL).
       // Otherwise, the current page should slide to the left (LTR) or
       // right (RTL) and the backdrop should appear in the background.
       // For the backdrop to be visible in that case, the previous page needs
       // to be hidden (if it exists).
       if (this._canGoForward) {
-        this._nextBox.collapsed = false;
         let offset = this.isLTR ? 1 : -1;
         this._positionBox(this._curBox, 0);
-        this._positionBox(this._nextBox, offset + aVal);
+        this._positionBox(this._nextBox, offset + aVal); // aVal is negative
       }
       else {
         this._prevBox.collapsed = true;
-        this._positionBox(this._curBox, aVal / dampValue);
+        this._positionBox(this._curBox, aVal);
       }
     }
   },
 
   /**
    * Event handler for events relevant to the history swipe animation.
    *
    * @param aEvent
    *        An event to process.
    */
   handleEvent: function HSA_handleEvent(aEvent) {
     switch (aEvent.type) {
       case "TabClose":
         let browser = gBrowser.getBrowserForTab(aEvent.target);
         this._removeTrackedSnapshot(-1, browser);
         break;
-      case "DOMModalDialogClosed":
-        this.stopAnimation();
-        break;
       case "pageshow":
       case "popstate":
-        if (aEvent.target != gBrowser.selectedBrowser.contentDocument)
-          break;
-        this.stopAnimation();
+        if (this.isAnimationRunning()) {
+          if (aEvent.target != gBrowser.selectedBrowser.contentDocument)
+            break;
+          this.stopAnimation();
+        }
         this._historyIndex = gBrowser.webNavigation.sessionHistory.index;
         break;
       case "pagehide":
         if (aEvent.target == gBrowser.selectedBrowser.contentDocument) {
           // Take a snapshot of a page whenever it's about to be navigated away
           // from.
           this._takeSnapshot();
         }
@@ -788,17 +743,17 @@ let gHistorySwipeAnimation = {
   },
 
   /**
    * Used to notify the history swipe animation that the OS sent a swipe end
    * event and that we should navigate to the page that the user swiped to, if
    * any. This will also result in the animation overlay to be torn down.
    */
   swipeEndEventReceived: function HSA_swipeEndEventReceived() {
-    if (this._lastSwipeDir != "" && this._historyIndex != this._startingIndex)
+    if (this._lastSwipeDir != "")
       this._navigateToHistoryIndex();
     else
       this.stopAnimation();
   },
 
   /**
    * Checks whether a particular index exists in the browser history or not.
    *
@@ -816,20 +771,19 @@ let gHistorySwipeAnimation = {
     return true;
   },
 
   /**
    * Navigates to the index in history that is currently being tracked by
    * |this|.
    */
   _navigateToHistoryIndex: function HSA__navigateToHistoryIndex() {
-    if (this._doesIndexExistInHistory(this._historyIndex))
+    if (this._doesIndexExistInHistory(this._historyIndex)) {
       gBrowser.webNavigation.gotoIndex(this._historyIndex);
-    else
-      this.stopAnimation();
+    }
   },
 
   /**
    * Checks to see if history swipe animations are supported by this
    * platform/configuration.
    *
    * return true if supported, false otherwise.
    */
@@ -865,33 +819,30 @@ let gHistorySwipeAnimation = {
     this._curBox = this._createElement("historySwipeAnimationCurrentPage",
                                        "box");
     this._container.appendChild(this._curBox);
 
     this._nextBox = this._createElement("historySwipeAnimationNextPage",
                                         "box");
     this._container.appendChild(this._nextBox);
 
-    // Cache width and height.
-    this._boxWidth = this._curBox.getBoundingClientRect().width;
-    this._boxHeight = this._curBox.getBoundingClientRect().height;
+    this._boxWidth = this._curBox.getBoundingClientRect().width; // cache width
   },
 
   /**
    * Removes the boxes.
    */
   _removeBoxes: function HSA__removeBoxes() {
     this._curBox = null;
     this._prevBox = null;
     this._nextBox = null;
     if (this._container)
       this._container.parentNode.removeChild(this._container);
     this._container = null;
     this._boxWidth = -1;
-    this._boxHeight = -1;
   },
 
   /**
    * Creates an element with a given identifier and tag name.
    *
    * @param aID
    *        An identifier to create the element with.
    * @param aTagName
@@ -909,24 +860,17 @@ let gHistorySwipeAnimation = {
    * Moves a given box to a given X coordinate position.
    *
    * @param aBox
    *        The box element to position.
    * @param aPosition
    *        The position (in X coordinates) to move the box element to.
    */
   _positionBox: function HSA__positionBox(aBox, aPosition) {
-    let transform = "";
-
-    if (this._isVerticalSwipe)
-      transform = "translateY(" + this._boxHeight * aPosition + "px)";
-    else
-      transform = "translateX(" + this._boxWidth * aPosition + "px)";
-
-    aBox.style.transform = transform;
+    aBox.style.transform = "translateX(" + this._boxWidth * aPosition + "px)";
   },
 
   /**
    * Takes a snapshot of the page the browser is currently on.
    */
   _takeSnapshot: function HSA__takeSnapshot() {
     if ((this._maxSnapshots < 1) ||
         (gBrowser.webNavigation.sessionHistory.index < 0))
@@ -1055,27 +999,22 @@ let gHistorySwipeAnimation = {
     if (!aBlob)
       return null;
 
     // Return aBlob if it's still a canvas and not a compressed blob yet.
     if (aBlob instanceof HTMLCanvasElement)
       return aBlob;
 
     let img = new Image();
-    let url = "";
-    try {
-      url = URL.createObjectURL(aBlob);
-      img.onload = function() {
-        URL.revokeObjectURL(url);
-      };
-    }
-    finally {
-      img.src = url;
-      return img;
-    }
+    let url = URL.createObjectURL(aBlob);
+    img.onload = function() {
+      URL.revokeObjectURL(url);
+    };
+    img.src = url;
+    return img;
   },
 
   /**
    * Sets the snapshot of the current page to the snapshot passed as parameter,
    * or to the one previously stored for the current index in history if the
    * parameter is null.
    *
    * @param aCanvas
--- a/content/events/src/nsEventStateManager.cpp
+++ b/content/events/src/nsEventStateManager.cpp
@@ -2668,22 +2668,16 @@ nsEventStateManager::ComputeScrollTarget
     // out of the frame, or when more than "mousewheel.transaction.timeout"
     // milliseconds have passed after the last operation, even if the mouse
     // hasn't moved.
     nsIFrame* lastScrollFrame = nsMouseWheelTransaction::GetTargetFrame();
     if (lastScrollFrame) {
       nsIScrollableFrame* frameToScroll =
         lastScrollFrame->GetScrollTargetFrame();
       if (frameToScroll) {
-        nsIFrame* activeRootFrame = nsLayoutUtils::GetActiveScrolledRootFor(
-                                      lastScrollFrame, nullptr);
-        if (!nsLayoutUtils::GetCrossDocParentFrame(activeRootFrame)) {
-          // Record the fact that the scroll occurred on the top-level page.
-          aEvent->viewPortIsScrollTargetParent = true;
-        }
         return frameToScroll;
       }
     }
   }
 
   // If the event doesn't cause scroll actually, we cannot find scroll target
   // because we check if the event can cause scroll actually on each found
   // scrollable frame.
@@ -2739,24 +2733,17 @@ nsEventStateManager::ComputeScrollTarget
       return frameToScroll;
     }
   }
 
   nsIFrame* newFrame = nsLayoutUtils::GetCrossDocParentFrame(
       aTargetFrame->PresContext()->FrameManager()->GetRootFrame());
   aOptions =
     static_cast<ComputeScrollTargetOptions>(aOptions & ~START_FROM_PARENT);
-  if (newFrame) {
-    return ComputeScrollTarget(newFrame, aEvent, aOptions);
-  }
-
-  // Record the fact that the scroll occurred past the bounds of the top-level
-  // page.
-  aEvent->viewPortIsScrollTargetParent = true;
-  return nullptr;
+  return newFrame ? ComputeScrollTarget(newFrame, aEvent, aOptions) : nullptr;
 }
 
 nsSize
 nsEventStateManager::GetScrollAmount(nsPresContext* aPresContext,
                                      widget::WheelEvent* aEvent,
                                      nsIScrollableFrame* aScrollableFrame)
 {
   MOZ_ASSERT(aPresContext);
--- a/widget/cocoa/nsChildView.h
+++ b/widget/cocoa/nsChildView.h
@@ -281,18 +281,17 @@ typedef NSInteger NSEventGestureAxis;
   float mCumulativeMagnification;
   float mCumulativeRotation;
 
   BOOL mDidForceRefreshOpenGL;
   BOOL mWaitingForPaint;
 
 #ifdef __LP64__
   // Support for fluid swipe tracking.
-  BOOL* mCancelSwipeAnimation;
-  uint32_t mCurrentSwipeDir;
+  void (^mCancelSwipeAnimation)();
 #endif
 
   // Whether this uses off-main-thread compositing.
   BOOL mUsingOMTCompositor;
 
   // The mask image that's used when painting into the titlebar using basic
   // CGContext painting (i.e. non-accelerated).
   CGImageRef mTopLeftCornerMask;
@@ -353,18 +352,17 @@ typedef NSInteger NSEventGestureAxis;
 - (void)endGestureWithEvent:(NSEvent *)anEvent;
 
 // Helper function for Lion smart magnify events
 + (BOOL)isLionSmartMagnifyEvent:(NSEvent*)anEvent;
 
 // Support for fluid swipe tracking.
 #ifdef __LP64__
 - (void)maybeTrackScrollEventAsSwipe:(NSEvent *)anEvent
-                     scrollOverflowX:(double)overflowX
-                     scrollOverflowY:(double)overflowY;
+                      scrollOverflow:(double)overflow;
 #endif
 
 - (void)setUsingOMTCompositor:(BOOL)aUseOMTC;
 @end
 
 class ChildViewMouseTracker {
 
 public:
--- a/widget/cocoa/nsChildView.mm
+++ b/widget/cocoa/nsChildView.mm
@@ -2789,17 +2789,16 @@ NSEvent* gLastDragMouseDownEvent = nil;
     // we call it in drawRect:inContext:, when we know that a draw is in
     // progress.
     mDidForceRefreshOpenGL = NO;
 
     [self setFocusRingType:NSFocusRingTypeNone];
 
 #ifdef __LP64__
     mCancelSwipeAnimation = nil;
-    mCurrentSwipeDir = 0;
 #endif
 
     mTopLeftCornerMask = NULL;
   }
 
   // register for things we'll take from other applications
   [ChildView registerViewForDraggedTypes:self];
 
@@ -4056,240 +4055,202 @@ NSEvent* gLastDragMouseDownEvent = nil;
   nsSimpleGestureEvent geckoEvent(true, aMsg, mGeckoChild, aDirection, aDelta);
   geckoEvent.allowedDirections = *aAllowedDirections;
   [self convertCocoaMouseEvent:aEvent toGeckoEvent:&geckoEvent];
   bool eventCancelled = mGeckoChild->DispatchWindowEvent(geckoEvent);
   *aAllowedDirections = geckoEvent.allowedDirections;
   return eventCancelled; // event cancelled == swipe should start
 }
 
+- (void)cancelSwipeIfRunning
+{
+  // Clear gesture state.
+  mGestureState = eGestureState_None;
+
+  if (mCancelSwipeAnimation) {
+    mCancelSwipeAnimation();
+    [mCancelSwipeAnimation release];
+    mCancelSwipeAnimation = nil;
+  }
+}
+
 - (void)sendSwipeEndEvent:(NSEvent *)anEvent
         allowedDirections:(uint32_t)aAllowedDirections
 {
     // Tear down animation overlay by sending a swipe end event.
     uint32_t allowedDirectionsCopy = aAllowedDirections;
     [self sendSwipeEvent:anEvent
                 withKind:NS_SIMPLE_GESTURE_SWIPE_END
        allowedDirections:&allowedDirectionsCopy
                direction:0
                    delta:0.0];
 }
 
-// Support fluid swipe tracking on OS X 10.7 and higher. We must be careful
-// to only invoke this support on a two-finger gesture that really
+// Support fluid swipe tracking on OS X 10.7 and higher.  We must be careful
+// to only invoke this support on a horizontal two-finger gesture that really
 // is a swipe (and not a scroll) -- in other words, the app is responsible
-// for deciding which is which. But once the decision is made, the OS tracks
+// for deciding which is which.  But once the decision is made, the OS tracks
 // the swipe until it has finished, and decides whether or not it succeeded.
-// A horizontal swipe has the same functionality as the Back and Forward
-// buttons.
-// This method is partly based on Apple sample code available at
-// developer.apple.com/library/mac/#releasenotes/Cocoa/AppKitOlderNotes.html
-// (under Fluid Swipe Tracking API).
+// A swipe has the same functionality as the Back and Forward buttons.  For
+// now swipe animation is unsupported (e.g. no bounces).  This method is
+// partly based on Apple sample code available at
+// http://developer.apple.com/library/mac/#releasenotes/Cocoa/AppKit.html
 - (void)maybeTrackScrollEventAsSwipe:(NSEvent *)anEvent
-                     scrollOverflowX:(double)overflowX
-                     scrollOverflowY:(double)overflowY
+                      scrollOverflow:(double)overflow
 {
   if (!nsCocoaFeatures::OnLionOrLater()) {
     return;
   }
 
   // This method checks whether the AppleEnableSwipeNavigateWithScrolls global
   // preference is set.  If it isn't, fluid swipe tracking is disabled, and a
   // horizontal two-finger gesture is always a scroll (even in Safari).  This
   // preference can't (currently) be set from the Preferences UI -- only using
   // 'defaults write'.
   if (![NSEvent isSwipeTrackingFromScrollEventsEnabled]) {
     return;
   }
 
-  // Verify that this is a scroll wheel event with proper phase to be tracked
-  // by the OS.
-  if ([anEvent type] != NSScrollWheel || [anEvent phase] == NSEventPhaseNone) {
+  if ([anEvent type] != NSScrollWheel) {
     return;
   }
 
   // Only initiate tracking if the user has tried to scroll past the edge of
-  // the current page (as indicated by 'overflowX' or 'overflowY' being
-  // non-zero).  Gecko only sets nsMouseScrollEvent.scrollOverflow when it's
-  // processing NS_MOUSE_PIXEL_SCROLL events (not NS_MOUSE_SCROLL events).
-  if (overflowX == 0.0 && overflowY == 0.0) {
+  // the current page (as indicated by 'overflow' being non-zero).  Gecko only
+  // sets nsMouseScrollEvent.scrollOverflow when it's processing
+  // NS_MOUSE_PIXEL_SCROLL events (not NS_MOUSE_SCROLL events).
+  // nsMouseScrollEvent.scrollOverflow only indicates left or right overflow
+  // for horizontal NS_MOUSE_PIXEL_SCROLL events.
+  if (!overflow) {
+    return;
+  }
+
+  // Only initiate tracking for gestures that have just begun -- otherwise a
+  // scroll to one side of the page can have a swipe tacked on to it.
+  if ([anEvent phase] != NSEventPhaseBegan) {
     return;
   }
 
   CGFloat deltaX, deltaY;
   if ([anEvent hasPreciseScrollingDeltas]) {
     deltaX = [anEvent scrollingDeltaX];
     deltaY = [anEvent scrollingDeltaY];
-  }
-  else {
+  } else {
+    deltaX = [anEvent deltaX];
+    deltaY = [anEvent deltaY];
+  }
+  // Only initiate tracking for events whose horizontal element is at least
+  // eight times larger than its vertical element.  This minimizes performance
+  // problems with vertical scrolls (by minimizing the possibility that they'll
+  // be misinterpreted as horizontal swipes), while still tolerating a small
+  // vertical element to a true horizontal swipe.  The number '8' was arrived
+  // at by trial and error.
+  if ((deltaX == 0) || (fabs(deltaX) <= fabs(deltaY) * 8)) {
     return;
   }
 
-  uint32_t vDirs = (uint32_t)nsIDOMSimpleGestureEvent::DIRECTION_DOWN |
-                   (uint32_t)nsIDOMSimpleGestureEvent::DIRECTION_UP;
-  uint32_t direction = 0;
-  // Only initiate horizontal tracking for events whose horizontal element is
-  // at least eight times larger than its vertical element. This minimizes
-  // performance problems with vertical scrolls (by minimizing the possibility
-  // that they'll be misinterpreted as horizontal swipes), while still
-  // tolerating a small vertical element to a true horizontal swipe.  The number
-  // '8' was arrived at by trial and error.
-  if (overflowX != 0.0 && deltaX != 0.0 &&
-      fabsf(deltaX) > fabsf(deltaY) * 8) {
-    // Only initiate horizontal tracking for gestures that have just begun --
-    // otherwise a scroll to one side of the page can have a swipe tacked on
-    // to it.
-    if ([anEvent phase] != NSEventPhaseBegan)
-      return;
-
-    if (deltaX < 0.0)
-      direction = (uint32_t)nsIDOMSimpleGestureEvent::DIRECTION_RIGHT;
-    else
-      direction = (uint32_t)nsIDOMSimpleGestureEvent::DIRECTION_LEFT;
-  }
-  // Only initiate vertical tracking for events whose vertical element is
-  // at least two times larger than its horizontal element. This minimizes
-  // performance problems. The number '2' was arrived at by trial and error.
-  else if (overflowY != 0.0 && deltaY != 0.0 &&
-           fabsf(deltaY) > fabsf(deltaX) * 2) {
-    if (deltaY < 0.0)
-      direction = (uint32_t)nsIDOMSimpleGestureEvent::DIRECTION_DOWN;
-    else
-      direction = (uint32_t)nsIDOMSimpleGestureEvent::DIRECTION_UP;
-
-    if ((mCurrentSwipeDir & vDirs) && (mCurrentSwipeDir != direction)) {
-      // If a swipe is currently being tracked kill it -- it's been interrupted
-      // by another gesture event.
-      if (mCancelSwipeAnimation && *mCancelSwipeAnimation == NO) {
-        *mCancelSwipeAnimation = YES;
-        mCancelSwipeAnimation = nil;
-        [self sendSwipeEndEvent:anEvent allowedDirections:0];
-      }
-      return;
-    }
-  }
-  else {
-    return;
-  }
-
-  // Track the direction we're going in.
-  mCurrentSwipeDir = direction;
-
-  // If a swipe is currently being tracked kill it -- it's been interrupted
-  // by another gesture event.
-  if (mCancelSwipeAnimation && *mCancelSwipeAnimation == NO) {
-    *mCancelSwipeAnimation = YES;
-    mCancelSwipeAnimation = nil;
-  }
-
-  uint32_t allowedDirections = 0;
+  // If a swipe is currently being tracked kill it -- it's been interrupted by
+  // another gesture or legacy scroll wheel event.
+  [self cancelSwipeIfRunning];
+
   // We're ready to start the animation. Tell Gecko about it, and at the same
   // time ask it if it really wants to start an animation for this event.
   // This event also reports back the directions that we can swipe in.
+  uint32_t allowedDirections = 0;
   bool shouldStartSwipe = [self sendSwipeEvent:anEvent
-                                      withKind:NS_SIMPLE_GESTURE_SWIPE_START
-                             allowedDirections:&allowedDirections
-                                     direction:direction
-                                         delta:0.0];
+                                        withKind:NS_SIMPLE_GESTURE_SWIPE_START
+                               allowedDirections:&allowedDirections
+                                       direction:0
+                                           delta:0.0];
 
   if (!shouldStartSwipe) {
     return;
   }
 
-  CGFloat min = 0.0;
-  CGFloat max = 0.0;
-  if (!(direction & vDirs)) {
-    min = (allowedDirections & nsIDOMSimpleGestureEvent::DIRECTION_RIGHT) ?
-          -1.0 : 0.0;
-    max = (allowedDirections & nsIDOMSimpleGestureEvent::DIRECTION_LEFT) ?
-          1.0 : 0.0;
-  }
-
-  __block BOOL animationCanceled = NO;
+  double min = (allowedDirections & nsIDOMSimpleGestureEvent::DIRECTION_RIGHT) ? -1 : 0;
+  double max = (allowedDirections & nsIDOMSimpleGestureEvent::DIRECTION_LEFT) ? 1 : 0;
+
+  __block BOOL animationCancelled = NO;
+  __block BOOL geckoSwipeEventSent = NO;
   // At this point, anEvent is the first scroll wheel event in a two-finger
   // horizontal gesture that we've decided to treat as a swipe.  When we call
   // [NSEvent trackSwipeEventWithOptions:...], the OS interprets all
   // subsequent scroll wheel events that are part of this gesture as a swipe,
   // and stops sending them to us.  The OS calls the trackingHandler "block"
   // multiple times, asynchronously (sometimes after [NSEvent
   // maybeTrackScrollEventAsSwipe:...] has returned).  The OS determines when
   // the gesture has finished, and whether or not it was "successful" -- this
   // information is passed to trackingHandler.  We must be careful to only
   // call [NSEvent maybeTrackScrollEventAsSwipe:...] on a "real" swipe --
   // otherwise two-finger scrolling performance will suffer significantly.
   // Note that we use anEvent inside the block. This extends the lifetime of
   // the anEvent object because it's retained by the block, see bug 682445.
   // The block will release it when the block goes away at the end of the
   // animation, or when the animation is canceled.
-  [anEvent trackSwipeEventWithOptions:NSEventSwipeTrackingLockDirection |
-                                      NSEventSwipeTrackingClampGestureAmount
+  [anEvent trackSwipeEventWithOptions:NSEventSwipeTrackingLockDirection
              dampenAmountThresholdMin:min
                                   max:max
-                         usingHandler:^(CGFloat gestureAmount,
-                                        NSEventPhase phase,
-                                        BOOL isComplete,
-                                        BOOL *stop) {
-    uint32_t allowedDirectionsCopy = allowedDirections;
-    // Since this tracking handler can be called asynchronously, mGeckoChild
-    // might have become NULL here (our child widget might have been
-    // destroyed).
-    // Checking for gestureAmount == 0.0 also works around bug 770626, which
-    // happens when DispatchWindowEvent() triggers a modal dialog, which spins
-    // the event loop and confuses the OS. This results in several re-entrant
-    // calls to this handler.
-    if (animationCanceled || !mGeckoChild || gestureAmount == 0.0) {
-      *stop = YES;
-      animationCanceled = YES;
-      if (gestureAmount == 0.0 ||
-          ((direction & vDirs) && (direction != mCurrentSwipeDir))) {
-        if (mCancelSwipeAnimation)
-          *mCancelSwipeAnimation = YES;
-        mCancelSwipeAnimation = nil;
-        [self sendSwipeEndEvent:anEvent
-              allowedDirections:allowedDirectionsCopy];
+                         usingHandler:^(CGFloat gestureAmount, NSEventPhase phase, BOOL isComplete, BOOL *stop) {
+      // Since this tracking handler can be called asynchronously, mGeckoChild
+      // might have become NULL here (our child widget might have been
+      // destroyed).
+      if (animationCancelled || !mGeckoChild) {
+        *stop = YES;
+        return;
       }
-      mCurrentSwipeDir = 0;
-      return;
-    }
-
-    // Update animation overlay to match gestureAmount.
-    [self sendSwipeEvent:anEvent
-                withKind:NS_SIMPLE_GESTURE_SWIPE_UPDATE
-       allowedDirections:&allowedDirectionsCopy
-               direction:0.0
-                   delta:gestureAmount];
-
-    if (phase == NSEventPhaseEnded) {
-      // The result of the swipe is now known, so the main event can be sent.
-      // The animation might continue even after this event was sent, so
-      // don't tear down the animation overlay yet.
-
-      uint32_t directionCopy = direction;
-
-      // gestureAmount is documented to be '-1', '0' or '1' when isComplete
-      // is TRUE, but the docs don't say anything about its value at other
-      // times.  However, tests show that, when phase == NSEventPhaseEnded,
-      // gestureAmount is negative when it will be '-1' at isComplete, and
-      // positive when it will be '1'.  And phase is never equal to
-      // NSEventPhaseEnded when gestureAmount will be '0' at isComplete.
+
+      uint32_t allowedDirectionsCopy = allowedDirections;
+
+      // Update animation overlay to match gestureAmount.
       [self sendSwipeEvent:anEvent
-                  withKind:NS_SIMPLE_GESTURE_SWIPE
+                  withKind:NS_SIMPLE_GESTURE_SWIPE_UPDATE
          allowedDirections:&allowedDirectionsCopy
-                 direction:directionCopy
-                     delta:0.0];
-    }
-
-    if (isComplete) {
-      [self sendSwipeEndEvent:anEvent allowedDirections:allowedDirectionsCopy];
-      mCurrentSwipeDir = 0;
-      mCancelSwipeAnimation = nil;
-    }
-  }];
-
-  mCancelSwipeAnimation = &animationCanceled;
+                 direction:0
+                     delta:gestureAmount];
+
+      if (phase == NSEventPhaseEnded && !geckoSwipeEventSent) {
+        // The result of the swipe is now known, so the main event can be sent.
+        // The animation might continue even after this event was sent, so
+        // don't tear down the animation overlay yet.
+        // gestureAmount is documented to be '-1', '0' or '1' when isComplete
+        // is TRUE, but the docs don't say anything about its value at other
+        // times.  However, tests show that, when phase == NSEventPhaseEnded,
+        // gestureAmount is negative when it will be '-1' at isComplete, and
+        // positive when it will be '1'.  And phase is never equal to
+        // NSEventPhaseEnded when gestureAmount will be '0' at isComplete.
+        uint32_t direction = gestureAmount > 0 ?
+          (uint32_t)nsIDOMSimpleGestureEvent::DIRECTION_LEFT :
+          (uint32_t)nsIDOMSimpleGestureEvent::DIRECTION_RIGHT;
+        // If DispatchWindowEvent() does something to trigger a modal dialog
+        // (which spins the event loop), the OS gets confused and makes
+        // several re-entrant calls to this handler, all of which have
+        // 'phase' set to NSEventPhaseEnded.  Unless we do something about
+        // it, this results in an equal number of re-entrant calls to
+        // DispatchWindowEvent(), and to our modal-event handling code.
+        // Probably because of bug 478703, this really messes things up,
+        // and requires a force quit to get out of.  We avoid this by
+        // avoiding re-entrant calls to DispatchWindowEvent().  See bug
+        // 770626.
+        geckoSwipeEventSent = YES;
+        [self sendSwipeEvent:anEvent
+                    withKind:NS_SIMPLE_GESTURE_SWIPE
+           allowedDirections:&allowedDirectionsCopy
+                  direction:direction
+                       delta:0.0];
+      }
+
+      if (isComplete) {
+        [self cancelSwipeIfRunning];
+        [self sendSwipeEndEvent:anEvent allowedDirections:allowedDirections];
+      }
+    }];
+
+  mCancelSwipeAnimation = [^{
+    animationCancelled = YES;
+  } copy];
 }
 #endif // #ifdef __LP64__
 
 - (void)setUsingOMTCompositor:(BOOL)aUseOMTC
 {
   mUsingOMTCompositor = aUseOMTC;
 }
 
@@ -4406,17 +4367,17 @@ NSEvent* gLastDragMouseDownEvent = nil;
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   if (!mGeckoChild || mBlockedLastMouseDown)
     return;
 
   nsAutoRetainCocoaObject kungFuDeathGrip(self);
 
   NPCocoaEvent cocoaEvent;
-
+	
   nsMouseEvent geckoEvent(true, NS_MOUSE_BUTTON_UP, mGeckoChild, nsMouseEvent::eReal);
   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
   if ([theEvent modifierFlags] & NSControlKeyMask)
     geckoEvent.button = nsMouseEvent::eRightButton;
   else
     geckoEvent.button = nsMouseEvent::eLeftButton;
 
   // Create event for use by plugins.
@@ -4856,24 +4817,22 @@ static int32_t RoundUp(double aDouble)
   }
 
   mGeckoChild->DispatchWindowEvent(wheelEvent);
   if (!mGeckoChild) {
     return;
   }
 
 #ifdef __LP64__
-  // overflowDeltaX and overflowDeltaY tell us when the user has tried to
-  // scroll past the edge of a page (in those cases it's non-zero).
-  if ((wheelEvent.deltaMode == nsIDOMWheelEvent::DOM_DELTA_PIXEL) &&
-      (wheelEvent.viewPortIsScrollTargetParent) &&
-      (wheelEvent.deltaX != 0.0 || wheelEvent.deltaY != 0.0)) {
+  // overflowDeltaX tells us when the user has tried to scroll past the edge
+  // of a page to the left or the right (in those cases it's non-zero).
+  if (wheelEvent.deltaMode == nsIDOMWheelEvent::DOM_DELTA_PIXEL &&
+      wheelEvent.deltaX != 0.0) {
     [self maybeTrackScrollEventAsSwipe:theEvent
-                       scrollOverflowX:wheelEvent.overflowDeltaX
-                       scrollOverflowY:wheelEvent.overflowDeltaY];
+                        scrollOverflow:wheelEvent.overflowDeltaX];
   }
 #endif // #ifdef __LP64__
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 -(NSMenu*)menuForEvent:(NSEvent*)theEvent
 {
--- a/widget/nsGUIEvent.h
+++ b/widget/nsGUIEvent.h
@@ -1341,18 +1341,17 @@ private:
 
 public:
   WheelEvent(bool aIsTrusted, uint32_t aMessage, nsIWidget* aWidget) :
     nsMouseEvent_base(aIsTrusted, aMessage, aWidget, NS_WHEEL_EVENT),
     deltaX(0.0), deltaY(0.0), deltaZ(0.0),
     deltaMode(nsIDOMWheelEvent::DOM_DELTA_PIXEL),
     customizedByUserPrefs(false), isMomentum(false), isPixelOnlyDevice(false),
     lineOrPageDeltaX(0), lineOrPageDeltaY(0), scrollType(SCROLL_DEFAULT),
-    overflowDeltaX(0.0), overflowDeltaY(0.0),
-    viewPortIsScrollTargetParent(false)
+    overflowDeltaX(0.0), overflowDeltaY(0.0)
   {
   }
 
   // NOTE: deltaX, deltaY and deltaZ may be customized by
   //       mousewheel.*.delta_multiplier_* prefs which are applied by
   //       nsEventStateManager.  So, after widget dispatches this event,
   //       these delta values may have different values than before.
   double deltaX;
@@ -1421,21 +1420,16 @@ public:
   // not used by scroll are set.
   // NOTE: deltaX, deltaY and deltaZ may be modified by nsEventStateManager.
   //       However, overflowDeltaX and overflowDeltaY indicate unused original
   //       delta values which are not applied the delta_multiplier prefs.
   //       So, if widget wanted to know the actual direction to be scrolled,
   //       it would need to check the deltaX and deltaY.
   double overflowDeltaX;
   double overflowDeltaY;
-
-  // Whether or not the parent of the currently scrolled frame is the ViewPort.
-  // This is false in situations when an element on the page is being scrolled
-  // (such as a text field), but true when the 'page' is being scrolled.
-  bool viewPortIsScrollTargetParent;
 };
 
 } // namespace widget
 } // namespace mozilla
 
 /*
  * Gesture Notify Event:
  *