diff --git a/browser/base/content/browser-gestureSupport.js b/browser/base/content/browser-gestureSupport.js index 7fe85568be3..d7cd75d9a4f 100644 --- a/browser/base/content/browser-gestureSupport.js +++ b/browser/base/content/browser-gestureSupport.js @@ -191,7 +191,19 @@ let gGestureSupport = { aEvent.allowedDirections |= isLTR ? aEvent.DIRECTION_RIGHT : aEvent.DIRECTION_LEFT; - gHistorySwipeAnimation.startAnimation(); + let isVerticalSwipe = false; + 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); this._doUpdate = function GS__doUpdate(aEvent) { gHistorySwipeAnimation.updateAnimation(aEvent.delta); @@ -541,10 +553,13 @@ let gHistorySwipeAnimation = { 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. @@ -553,6 +568,7 @@ let gHistorySwipeAnimation = { 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); } }, @@ -564,6 +580,7 @@ let gHistorySwipeAnimation = { 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; @@ -573,17 +590,32 @@ let gHistorySwipeAnimation = { /** * 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() { + startAnimation: function HSA_startAnimation(aIsVerticalSwipe) { + this._isVerticalSwipe = aIsVerticalSwipe; + if (this.isAnimationRunning()) { - gBrowser.stop(); - this._lastSwipeDir = "RELOAD"; // just ensure that != "" - this._canGoBack = this.canGoBack(); - this._canGoForward = this.canGoForward(); - this._handleFastSwiping(); + // 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(); + } } else { - this._historyIndex = gBrowser.webNavigation.sessionHistory.index; + this._startingIndex = gBrowser.webNavigation.sessionHistory.index; + this._historyIndex = this._startingIndex; this._canGoBack = this.canGoBack(); this._canGoForward = this.canGoForward(); if (this.active) { @@ -614,20 +646,29 @@ let gHistorySwipeAnimation = { if (!this.isAnimationRunning()) return; - if ((aVal >= 0 && this.isLTR) || - (aVal <= 0 && !this.isLTR)) { - if (aVal > 1) - aVal = 1; // Cap value to avoid sliding the page further than allowed. - + // 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 (this._canGoBack) this._prevBox.collapsed = false; - else + else { + tempDampValue = dampValue; 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); + this._positionBox(this._curBox, aVal / tempDampValue); // The forward page should be pushed offscreen all the way to the right. this._positionBox(this._nextBox, 1); @@ -643,13 +684,14 @@ let gHistorySwipeAnimation = { // 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); // aVal is negative + this._positionBox(this._nextBox, offset + aVal); } else { this._prevBox.collapsed = true; - this._positionBox(this._curBox, aVal); + this._positionBox(this._curBox, aVal / dampValue); } } }, @@ -666,13 +708,14 @@ let gHistorySwipeAnimation = { let browser = gBrowser.getBrowserForTab(aEvent.target); this._removeTrackedSnapshot(-1, browser); break; + case "DOMModalDialogClosed": + this.stopAnimation(); + break; case "pageshow": case "popstate": - if (this.isAnimationRunning()) { - if (aEvent.target != gBrowser.selectedBrowser.contentDocument) - break; - this.stopAnimation(); - } + if (aEvent.target != gBrowser.selectedBrowser.contentDocument) + break; + this.stopAnimation(); this._historyIndex = gBrowser.webNavigation.sessionHistory.index; break; case "pagehide": @@ -740,7 +783,7 @@ let gHistorySwipeAnimation = { * any. This will also result in the animation overlay to be torn down. */ swipeEndEventReceived: function HSA_swipeEndEventReceived() { - if (this._lastSwipeDir != "") + if (this._lastSwipeDir != "" && this._historyIndex != this._startingIndex) this._navigateToHistoryIndex(); else this.stopAnimation(); @@ -768,9 +811,10 @@ let gHistorySwipeAnimation = { * |this|. */ _navigateToHistoryIndex: function HSA__navigateToHistoryIndex() { - if (this._doesIndexExistInHistory(this._historyIndex)) { + if (this._doesIndexExistInHistory(this._historyIndex)) gBrowser.webNavigation.gotoIndex(this._historyIndex); - } + else + this.stopAnimation(); }, /** @@ -816,7 +860,9 @@ let gHistorySwipeAnimation = { "box"); this._container.appendChild(this._nextBox); - this._boxWidth = this._curBox.getBoundingClientRect().width; // cache width + // Cache width and height. + this._boxWidth = this._curBox.getBoundingClientRect().width; + this._boxHeight = this._curBox.getBoundingClientRect().height; }, /** @@ -830,6 +876,7 @@ let gHistorySwipeAnimation = { this._container.parentNode.removeChild(this._container); this._container = null; this._boxWidth = -1; + this._boxHeight = -1; }, /** @@ -857,7 +904,14 @@ let gHistorySwipeAnimation = { * The position (in X coordinates) to move the box element to. */ _positionBox: function HSA__positionBox(aBox, aPosition) { - aBox.style.transform = "translateX(" + this._boxWidth * aPosition + "px)"; + let transform = ""; + + if (this._isVerticalSwipe) + transform = "translateY(" + this._boxHeight * aPosition + "px)"; + else + transform = "translateX(" + this._boxWidth * aPosition + "px)"; + + aBox.style.transform = transform; }, /** @@ -996,12 +1050,17 @@ let gHistorySwipeAnimation = { return aBlob; let img = new Image(); - let url = URL.createObjectURL(aBlob); - img.onload = function() { - URL.revokeObjectURL(url); - }; - img.src = url; - return img; + let url = ""; + try { + url = URL.createObjectURL(aBlob); + img.onload = function() { + URL.revokeObjectURL(url); + }; + } + finally { + img.src = url; + return img; + } }, /** diff --git a/content/events/src/nsEventStateManager.cpp b/content/events/src/nsEventStateManager.cpp index a991a16fac5..9646ef5c99f 100644 --- a/content/events/src/nsEventStateManager.cpp +++ b/content/events/src/nsEventStateManager.cpp @@ -2668,6 +2668,12 @@ nsEventStateManager::ComputeScrollTarget(nsIFrame* aTargetFrame, 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; } } @@ -2733,7 +2739,14 @@ nsEventStateManager::ComputeScrollTarget(nsIFrame* aTargetFrame, aTargetFrame->PresContext()->FrameManager()->GetRootFrame()); aOptions = static_cast(aOptions & ~START_FROM_PARENT); - return newFrame ? ComputeScrollTarget(newFrame, aEvent, aOptions) : nullptr; + 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; } nsSize diff --git a/widget/cocoa/nsChildView.h b/widget/cocoa/nsChildView.h index eaa1f09f69e..46e658ed16f 100644 --- a/widget/cocoa/nsChildView.h +++ b/widget/cocoa/nsChildView.h @@ -272,7 +272,8 @@ typedef NSInteger NSEventGestureAxis; #ifdef __LP64__ // Support for fluid swipe tracking. - void (^mCancelSwipeAnimation)(); + BOOL* mCancelSwipeAnimation; + PRUint32 mCurrentSwipeDir; #endif // Whether this uses off-main-thread compositing. @@ -340,7 +341,8 @@ typedef NSInteger NSEventGestureAxis; // Support for fluid swipe tracking. #ifdef __LP64__ - (void)maybeTrackScrollEventAsSwipe:(NSEvent *)anEvent - scrollOverflow:(double)overflow; + scrollOverflowX:(double)overflowX + scrollOverflowY:(double)overflowY; #endif - (void)setUsingOMTCompositor:(BOOL)aUseOMTC; diff --git a/widget/cocoa/nsChildView.mm b/widget/cocoa/nsChildView.mm index 98af7673734..53385729757 100644 --- a/widget/cocoa/nsChildView.mm +++ b/widget/cocoa/nsChildView.mm @@ -2217,6 +2217,7 @@ NSEvent* gLastDragMouseDownEvent = nil; #ifdef __LP64__ mCancelSwipeAnimation = nil; + mCurrentSwipeDir = 0; #endif } @@ -3403,18 +3404,6 @@ NSEvent* gLastDragMouseDownEvent = nil; 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:(PRUint32)aAllowedDirections { @@ -3427,22 +3416,24 @@ NSEvent* gLastDragMouseDownEvent = nil; 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 horizontal 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 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 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 +// 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). - (void)maybeTrackScrollEventAsSwipe:(NSEvent *)anEvent - scrollOverflow:(double)overflow + scrollOverflowX:(double)overflowX + scrollOverflowY:(double)overflowY { 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 @@ -3451,65 +3442,111 @@ NSEvent* gLastDragMouseDownEvent = nil; if (![NSEvent isSwipeTrackingFromScrollEventsEnabled]) { return; } - if ([anEvent type] != NSScrollWheel) { + + // Verify that this is a scroll wheel event with proper phase to be tracked + // by the OS. + if ([anEvent type] != NSScrollWheel || [anEvent phase] == NSEventPhaseNone) { return; } // Only initiate tracking if the user has tried to scroll past the edge of - // 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) { + // 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) { return; } + CGFloat deltaX, deltaY; if ([anEvent hasPreciseScrollingDeltas]) { deltaX = [anEvent scrollingDeltaX]; deltaY = [anEvent scrollingDeltaY]; - } 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)) { + else { return; } - // If a swipe is currently being tracked kill it -- it's been interrupted by - // another gesture or legacy scroll wheel event. - [self cancelSwipeIfRunning]; + PRUint32 vDirs = (PRUint32)nsIDOMSimpleGestureEvent::DIRECTION_DOWN | + (PRUint32)nsIDOMSimpleGestureEvent::DIRECTION_UP; + PRUint32 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 = (PRUint32)nsIDOMSimpleGestureEvent::DIRECTION_RIGHT; + else + direction = (PRUint32)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 = (PRUint32)nsIDOMSimpleGestureEvent::DIRECTION_DOWN; + else + direction = (PRUint32)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; + } + + PRUint32 allowedDirections = 0; // 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. - PRUint32 allowedDirections = 0; bool shouldStartSwipe = [self sendSwipeEvent:anEvent - withKind:NS_SIMPLE_GESTURE_SWIPE_START - allowedDirections:&allowedDirections - direction:0 - delta:0.0]; + withKind:NS_SIMPLE_GESTURE_SWIPE_START + allowedDirections:&allowedDirections + direction:direction + delta:0.0]; if (!shouldStartSwipe) { return; } - double min = (allowedDirections & nsIDOMSimpleGestureEvent::DIRECTION_RIGHT) ? -1 : 0; - double max = (allowedDirections & nsIDOMSimpleGestureEvent::DIRECTION_LEFT) ? 1 : 0; + 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 animationCancelled = NO; - __block BOOL geckoSwipeEventSent = NO; + __block BOOL animationCanceled = 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 @@ -3525,67 +3562,72 @@ NSEvent* gLastDragMouseDownEvent = nil; // 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 + [anEvent trackSwipeEventWithOptions:NSEventSwipeTrackingLockDirection | + NSEventSwipeTrackingClampGestureAmount dampenAmountThresholdMin:min max:max - 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; + usingHandler:^(CGFloat gestureAmount, + NSEventPhase phase, + BOOL isComplete, + BOOL *stop) { + PRUint32 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]; } + mCurrentSwipeDir = 0; + return; + } - PRUint32 allowedDirectionsCopy = allowedDirections; + // Update animation overlay to match gestureAmount. + [self sendSwipeEvent:anEvent + withKind:NS_SIMPLE_GESTURE_SWIPE_UPDATE + allowedDirections:&allowedDirectionsCopy + direction:0.0 + delta:gestureAmount]; - // Update animation overlay to match 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. + + PRUint32 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. [self sendSwipeEvent:anEvent - withKind:NS_SIMPLE_GESTURE_SWIPE_UPDATE + withKind:NS_SIMPLE_GESTURE_SWIPE allowedDirections:&allowedDirectionsCopy - direction:0 - delta:gestureAmount]; + direction:directionCopy + delta:0.0]; + } - 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. - PRUint32 direction = gestureAmount > 0 ? - (PRUint32)nsIDOMSimpleGestureEvent::DIRECTION_LEFT : - (PRUint32)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 sendSwipeEndEvent:anEvent allowedDirections:allowedDirectionsCopy]; + mCurrentSwipeDir = 0; + mCancelSwipeAnimation = nil; + } + }]; - if (isComplete) { - [self cancelSwipeIfRunning]; - [self sendSwipeEndEvent:anEvent allowedDirections:allowedDirections]; - } - }]; - - mCancelSwipeAnimation = [^{ - animationCancelled = YES; - } copy]; + mCancelSwipeAnimation = &animationCanceled; } #endif // #ifdef __LP64__ @@ -3712,7 +3754,7 @@ NSEvent* gLastDragMouseDownEvent = nil; nsAutoRetainCocoaObject kungFuDeathGrip(self); NPCocoaEvent cocoaEvent; - + nsMouseEvent geckoEvent(true, NS_MOUSE_BUTTON_UP, mGeckoChild, nsMouseEvent::eReal); [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent]; if ([theEvent modifierFlags] & NSControlKeyMask) @@ -4148,12 +4190,14 @@ static int32_t RoundUp(double aDouble) } #ifdef __LP64__ - // 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) { + // 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)) { [self maybeTrackScrollEventAsSwipe:theEvent - scrollOverflow:wheelEvent.overflowDeltaX]; + scrollOverflowX:wheelEvent.overflowDeltaX + scrollOverflowY:wheelEvent.overflowDeltaY]; } #endif // #ifdef __LP64__ diff --git a/widget/nsGUIEvent.h b/widget/nsGUIEvent.h index 871e7f4ccaf..3173348d4a1 100644 --- a/widget/nsGUIEvent.h +++ b/widget/nsGUIEvent.h @@ -1306,7 +1306,8 @@ public: deltaMode(nsIDOMWheelEvent::DOM_DELTA_PIXEL), customizedByUserPrefs(false), isMomentum(false), isPixelOnlyDevice(false), lineOrPageDeltaX(0), lineOrPageDeltaY(0), scrollType(SCROLL_DEFAULT), - overflowDeltaX(0.0), overflowDeltaY(0.0) + overflowDeltaX(0.0), overflowDeltaY(0.0), + viewPortIsScrollTargetParent(false) { } @@ -1385,6 +1386,11 @@ public: // 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