Bug 485101 - Implement panning feedback for touch enabled displays with win7. r+sr=smaug r=blassey.
authorJim Mathies <jmathies@mozilla.com>
Tue, 19 May 2009 21:05:59 -0500
changeset 28609 254c5cd4978b
parent 28608 48dd4339ea86
child 28610 401c215ae146
push id7143
push userjmathies@mozilla.com
push dateWed, 20 May 2009 02:06:52 +0000
treeherdermozilla-central@254c5cd4978b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersblassey
bugs485101
milestone1.9.2a1pre
Bug 485101 - Implement panning feedback for touch enabled displays with win7. r+sr=smaug r=blassey.
content/events/src/nsEventStateManager.cpp
content/events/src/nsEventStateManager.h
view/public/nsIScrollableView.h
view/src/nsScrollPortView.cpp
view/src/nsScrollPortView.h
widget/public/nsGUIEvent.h
widget/src/windows/nsWinGesture.cpp
widget/src/windows/nsWinGesture.h
widget/src/windows/nsWindow.cpp
--- a/content/events/src/nsEventStateManager.cpp
+++ b/content/events/src/nsEventStateManager.cpp
@@ -2874,37 +2874,38 @@ nsEventStateManager::SendPixelScrollEven
   event.delta = aPresContext->AppUnitsToIntCSSPixels(aEvent->delta * lineHeight);
 
   nsEventDispatcher::Dispatch(targetContent, aPresContext, &event, nsnull, aStatus);
 }
 
 nsresult
 nsEventStateManager::DoScrollText(nsPresContext* aPresContext,
                                   nsIFrame* aTargetFrame,
-                                  nsInputEvent* aEvent,
-                                  PRInt32 aNumLines,
-                                  PRBool aScrollHorizontal,
+                                  nsMouseScrollEvent* aMouseEvent,
                                   ScrollQuantity aScrollQuantity)
 {
   nsIScrollableView* scrollView = nsnull;
   nsIFrame* scrollFrame = aTargetFrame;
+  PRInt32 numLines = aMouseEvent->delta;
+  PRBool isHorizontal = aMouseEvent->scrollFlags & nsMouseScrollEvent::kIsHorizontal;
+  aMouseEvent->scrollOverflow = 0;
 
   // If the user recently scrolled with the mousewheel, then they probably want
   // to scroll the same view as before instead of the view under the cursor.
   // nsMouseWheelTransaction tracks the frame currently being scrolled with the
   // mousewheel. We consider the transaction ended when the mouse moves more than
   // "mousewheel.transaction.ignoremovedelay" milliseconds after the last scroll
   // operation, or any time the mouse moves 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) {
     nsIScrollableViewProvider* svp = do_QueryFrame(lastScrollFrame);
     if (svp && (scrollView = svp->GetScrollableView())) {
-      nsMouseWheelTransaction::UpdateTransaction(aNumLines, aScrollHorizontal);
+      nsMouseWheelTransaction::UpdateTransaction(numLines, isHorizontal);
       // When the scroll event will not scroll any views, UpdateTransaction
       // fired MozMouseScrollFailed event which is for automated testing.
       // In the event handler, the target frame might be destroyed.  Then,
       // we should not keep handling this scroll event.
       if (!nsMouseWheelTransaction::GetTargetFrame())
         return NS_OK;
     } else {
       nsMouseWheelTransaction::EndTransaction();
@@ -2923,29 +2924,29 @@ nsEventStateManager::DoScrollText(nsPres
     }
     if (!scrollView) {
       continue;
     }
 
     nsPresContext::ScrollbarStyles ss =
       nsLayoutUtils::ScrollbarStylesOfView(scrollView);
     if (NS_STYLE_OVERFLOW_HIDDEN ==
-        (aScrollHorizontal ? ss.mHorizontal : ss.mVertical)) {
+        (isHorizontal ? ss.mHorizontal : ss.mVertical)) {
       continue;
     }
 
     // Check if the scrollable view can be scrolled any further.
     nscoord lineHeight;
     scrollView->GetLineHeight(&lineHeight);
 
     if (lineHeight != 0) {
-      if (CanScrollOn(scrollView, aNumLines, aScrollHorizontal)) {
+      if (CanScrollOn(scrollView, numLines, isHorizontal)) {
         passToParent = PR_FALSE;
         nsMouseWheelTransaction::BeginTransaction(scrollFrame,
-                                                  aNumLines, aScrollHorizontal);
+                                                  numLines, isHorizontal);
       }
 
       // Comboboxes need special care.
       nsIComboboxControlFrame* comboBox = do_QueryFrame(scrollFrame);
       if (comboBox) {
         if (comboBox->IsDroppedDown()) {
           // Don't propagate to parent when drop down menu is active.
           if (passToParent) {
@@ -2968,58 +2969,76 @@ nsEventStateManager::DoScrollText(nsPres
     if (aScrollQuantity == eScrollByLine) {
       // Limit scrolling to be at most one page, but if possible, try to
       // just adjust the number of scrolled lines.
       nscoord lineHeight = 0;
       scrollView->GetLineHeight(&lineHeight);
       if (lineHeight) {
         nsSize pageScrollDistances(0, 0);
         scrollView->GetPageScrollDistances(&pageScrollDistances);
-        nscoord pageScroll = aScrollHorizontal ?
+        nscoord pageScroll = isHorizontal ?
           pageScrollDistances.width : pageScrollDistances.height;
 
-        if (PR_ABS(aNumLines) * lineHeight > pageScroll) {
+        if (PR_ABS(numLines) * lineHeight > pageScroll) {
           nscoord maxLines = (pageScroll / lineHeight);
           if (maxLines >= 1) {
-            aNumLines = ((aNumLines < 0) ? -1 : 1) * maxLines;
+            numLines = ((numLines < 0) ? -1 : 1) * maxLines;
           } else {
             aScrollQuantity = eScrollByPage;
           }
         }
       }
     }
 
     PRInt32 scrollX = 0;
-    PRInt32 scrollY = aNumLines;
+    PRInt32 scrollY = numLines;
 
     if (aScrollQuantity == eScrollByPage)
       scrollY = (scrollY > 0) ? 1 : -1;
       
-    if (aScrollHorizontal) {
+    if (isHorizontal) {
       scrollX = scrollY;
       scrollY = 0;
     }
     
-    if (aScrollQuantity == eScrollByPage)
-      scrollView->ScrollByPages(scrollX, scrollY, NS_VMREFRESH_SMOOTHSCROLL);
-    else if (aScrollQuantity == eScrollByPixel)
-      scrollView->ScrollByPixels(scrollX, scrollY, NS_VMREFRESH_DEFERRED);
+    PRBool noDefer = aMouseEvent->scrollFlags & nsMouseScrollEvent::kNoDefer;
+    PRInt32 overflowX = 0, overflowY = 0;
+    
+    if (aScrollQuantity == eScrollByPage) {
+      scrollView->ScrollByPages(scrollX, scrollY,
+        (noDefer ? NS_VMREFRESH_IMMEDIATE : NS_VMREFRESH_SMOOTHSCROLL));
+    }
+    else if (aScrollQuantity == eScrollByPixel) {
+      scrollView->ScrollByPixels(scrollX, scrollY, overflowX, overflowY,
+        (noDefer ? NS_VMREFRESH_IMMEDIATE : NS_VMREFRESH_DEFERRED));
+    }
+    else {
+      scrollView->ScrollByLinesWithOverflow(scrollX, scrollY, overflowX, overflowY,
+        (noDefer ? NS_VMREFRESH_IMMEDIATE : NS_VMREFRESH_SMOOTHSCROLL));
+    }
+    
+    if (isHorizontal)
+      aMouseEvent->scrollOverflow = overflowX;
     else
-      scrollView->ScrollByLines(scrollX, scrollY, NS_VMREFRESH_SMOOTHSCROLL);
-  }
+      aMouseEvent->scrollOverflow = overflowY;
+
+    return NS_OK;
+  }
+  
   if (passToParent) {
     nsresult rv;
     nsIFrame* newFrame = nsnull;
     nsCOMPtr<nsPresContext> newPresContext;
-    rv = GetParentScrollingView(aEvent, aPresContext, newFrame,
+    rv = GetParentScrollingView(aMouseEvent, aPresContext, newFrame,
                                 *getter_AddRefs(newPresContext));
     if (NS_SUCCEEDED(rv) && newFrame)
-      return DoScrollText(newPresContext, newFrame, aEvent, aNumLines,
-                          aScrollHorizontal, aScrollQuantity);
-  }
+      return DoScrollText(newPresContext, newFrame, aMouseEvent, aScrollQuantity);
+  }
+
+  aMouseEvent->scrollOverflow = numLines;
 
   return NS_OK;
 }
 
 nsresult
 nsEventStateManager::GetParentScrollingView(nsInputEvent *aEvent,
                                             nsPresContext* aPresContext,
                                             nsIFrame* &targetOuterFrame,
@@ -3270,49 +3289,33 @@ nsEventStateManager::PostHandleEvent(nsP
           if (action == MOUSE_SCROLL_N_LINES) {
             // We shouldn't scroll lines when a pixel scroll event will follow.
             action = -1;
           }
         }
 
         switch (action) {
         case MOUSE_SCROLL_N_LINES:
-          {
-            DoScrollText(presContext, aTargetFrame, msEvent, msEvent->delta,
-                         !!(msEvent->scrollFlags & nsMouseScrollEvent::kIsHorizontal),
-                         eScrollByLine);
-          }
+          DoScrollText(presContext, aTargetFrame, msEvent, eScrollByLine);
           break;
 
         case MOUSE_SCROLL_PAGE:
-          {
-            DoScrollText(presContext, aTargetFrame, msEvent, msEvent->delta,
-                         !!(msEvent->scrollFlags & nsMouseScrollEvent::kIsHorizontal),
-                         eScrollByPage);
-          }
+          DoScrollText(presContext, aTargetFrame, msEvent, eScrollByPage);
           break;
 
         case MOUSE_SCROLL_PIXELS:
-          {
-            DoScrollText(presContext, aTargetFrame, msEvent, msEvent->delta,
-                         !!(msEvent->scrollFlags & nsMouseScrollEvent::kIsHorizontal),
-                         eScrollByPixel);
-          }
+          DoScrollText(presContext, aTargetFrame, msEvent, eScrollByPixel);
           break;
 
         case MOUSE_SCROLL_HISTORY:
-          {
-            DoScrollHistory(msEvent->delta);
-          }
+          DoScrollHistory(msEvent->delta);
           break;
 
         case MOUSE_SCROLL_ZOOM:
-          {
-            DoScrollZoom(aTargetFrame, msEvent->delta);
-          }
+          DoScrollZoom(aTargetFrame, msEvent->delta);
           break;
 
         default:  // Including -1 (do nothing)
           break;
         }
         *aStatus = nsEventStatus_eConsumeNoDefault;
       }
     }
--- a/content/events/src/nsEventStateManager.h
+++ b/content/events/src/nsEventStateManager.h
@@ -307,19 +307,17 @@ protected:
                             nsEventStatus* aStatus);
   typedef enum {
     eScrollByPixel,
     eScrollByLine,
     eScrollByPage
   } ScrollQuantity;
   nsresult DoScrollText(nsPresContext* aPresContext,
                         nsIFrame* aTargetFrame,
-                        nsInputEvent* aEvent,
-                        PRInt32 aNumLines,
-                        PRBool aScrollHorizontal,
+                        nsMouseScrollEvent* aMouseEvent,
                         ScrollQuantity aScrollQuantity);
   void DoScrollHistory(PRInt32 direction);
   void DoScrollZoom(nsIFrame *aTargetFrame, PRInt32 adjustment);
   nsresult GetMarkupDocumentViewer(nsIMarkupDocumentViewer** aMv);
   nsresult ChangeTextSize(PRInt32 change);
   nsresult ChangeFullZoom(PRInt32 change);
   // end mousewheel functions
 
--- a/view/public/nsIScrollableView.h
+++ b/view/public/nsIScrollableView.h
@@ -142,16 +142,30 @@ public:
    * Prevents scrolling off the end of the view.
    * @param aNumLinesX number of lines to scroll the view horizontally
    * @param aNumLinesY number of lines to scroll the view vertically
    * @param aUpdateFlags indicate smooth or async scrolling
    * @return error status
    */
   NS_IMETHOD ScrollByLines(PRInt32 aNumLinesX, PRInt32 aNumLinexY,
                            PRUint32 aUpdateFlags = 0) = 0;
+  /**
+   * Identical to ScrollByLines while also returning overscroll values.
+   * @param aNumLinesX number of lines to scroll the view horizontally
+   * @param aNumLinesY number of lines to scroll the view vertically
+   * @param aOverflowX returns the number of pixels that could not
+   *                   be scrolled due to scroll bounds.
+   * @param aOverflowY returns the number of pixels that could not
+   *                   be scrolled due to scroll bounds.
+   * @param aUpdateFlags indicate smooth or async scrolling
+   * @return error status
+   */
+  NS_IMETHOD ScrollByLinesWithOverflow(PRInt32 aNumLinesX, PRInt32 aNumLinexY,
+                                       PRInt32& aOverflowX, PRInt32& aOverflowY,
+                                       PRUint32 aUpdateFlags = 0) = 0;
 
   /**
    * Get the desired size of a page scroll in each dimension.
    * ScrollByPages will scroll by independent multiples of these amounts
    * unless it hits the edge of the view.
    */
   NS_IMETHOD GetPageScrollDistances(nsSize *aDistances) = 0;
 
@@ -176,22 +190,27 @@ public:
    * @return error status
    */
   NS_IMETHOD ScrollByWhole(PRBool aTop, PRUint32 aUpdateFlags = 0) = 0;
 
   /**
    * Scroll the view left or right by aNumLinesX pixels.  Positive values move 
    * right.  Scroll the view up or down by aNumLinesY pixels.  Positive values
    * move down.  Prevents scrolling off the end of the view.
-   * @param aNumLinesX number of lines to scroll the view horizontally
-   * @param aNumLinesY number of lines to scroll the view vertically
-   * @param aUpdateFlags indicate smooth or async scrolling
+   * @param aNumPixelsX number of pixels to scroll the view horizontally
+   * @param aNumPixelsY number of pixels to scroll the view vertically
+   * @param aOverflowX returns the number of pixels that could not
+   *                   be scrolled due to scroll bounds.
+   * @param aOverflowY returns the number of pixels that could not
+   *                   be scrolled due to scroll bounds.
+   * @param aUpdateFlags indicate smooth, async, or immediate scrolling
    * @return error status
    */
   NS_IMETHOD ScrollByPixels(PRInt32 aNumPixelsX, PRInt32 aNumPixelsY,
+                            PRInt32& aOverflowX, PRInt32& aOverflowY,
                             PRUint32 aUpdateFlags = 0) = 0;
 
   /**
    * Check the view can scroll from current offset.
    * @param aHorizontal If checking to Left or to Right, true. Otherwise, false.
    * @param aForward    If checking to Right or Bottom, true. Otherwise, false.
    * @param aResult     If the view can scroll, true. Otherwise, false.
    * @return            error status
--- a/view/src/nsScrollPortView.cpp
+++ b/view/src/nsScrollPortView.cpp
@@ -203,17 +203,17 @@ static void ComputeVelocities(PRInt32 aC
 
   PRInt32 scale = NSIntPixelsToAppUnits(direction, aP2A);
   for (i = 0; i < SMOOTH_SCROLL_FRAMES; i++) {
     aVelocities[i*2] *= scale;
   }
 }
   
 static nsresult ClampScrollValues(nscoord& aX, nscoord& aY, nsScrollPortView* aThis) {
-  // make sure the new position in in bounds
+  // make sure the new position is in bounds
   nsView* scrolledView = aThis->GetScrolledView();
   if (!scrolledView) return NS_ERROR_FAILURE;
   
   nsRect scrolledRect;
   scrolledView->GetDimensions(scrolledRect);
   
   nsSize portSize;
   aThis->GetDimensions(portSize);
@@ -391,25 +391,76 @@ NS_IMETHODIMP nsScrollPortView::SetLineH
 }
 
 NS_IMETHODIMP nsScrollPortView::GetLineHeight(nscoord *aHeight)
 {
   *aHeight = mLineHeight;
   return NS_OK;
 }
 
-NS_IMETHODIMP nsScrollPortView::ScrollByLines(PRInt32 aNumLinesX, PRInt32 aNumLinesY,
+nsresult
+nsScrollPortView::CalcScrollOverflow(nscoord aX, nscoord aY,
+                                     PRInt32& aPixelOverflowX, PRInt32& aPixelOverflowY)
+{
+  // make sure the new position is in bounds
+  nsView* scrolledView = GetScrolledView();
+  if (!scrolledView) return NS_ERROR_FAILURE;
+  
+  nsRect scrolledRect;
+  scrolledView->GetDimensions(scrolledRect);
+  
+  nsSize portSize;
+  this->GetDimensions(portSize);
+  
+  nscoord maxX = scrolledRect.XMost() - portSize.width;
+  nscoord maxY = scrolledRect.YMost() - portSize.height;
+  
+  nsCOMPtr<nsIDeviceContext> dev;
+  mViewManager->GetDeviceContext(*getter_AddRefs(dev));
+  float p2a = (float)dev->AppUnitsPerDevPixel();
+
+  if (maxX != 0 && aX > maxX)
+    aPixelOverflowX = NSAppUnitsToIntPixels(aX - maxX, p2a);
+
+  if (maxY != 0 && aY > maxY)
+    aPixelOverflowY = NSAppUnitsToIntPixels(aY - maxY, p2a);
+
+  if (maxX != 0 && aX < scrolledRect.x)
+    aPixelOverflowX = NSAppUnitsToIntPixels(scrolledRect.x - aX, p2a);
+
+  if (maxY != 0 && aY < scrolledRect.y)
+    aPixelOverflowY = NSAppUnitsToIntPixels(scrolledRect.y - aY, p2a);
+  
+  return NS_OK;
+}
+
+NS_IMETHODIMP nsScrollPortView::ScrollByLines(PRInt32 aNumLinesX,
+                                              PRInt32 aNumLinesY,
                                               PRUint32 aUpdateFlags)
 {
   nscoord dx = mLineHeight*aNumLinesX;
   nscoord dy = mLineHeight*aNumLinesY;
 
   return ScrollTo(mDestinationX + dx, mDestinationY + dy, aUpdateFlags);
 }
 
+NS_IMETHODIMP nsScrollPortView::ScrollByLinesWithOverflow(PRInt32 aNumLinesX,
+                                                          PRInt32 aNumLinesY,
+                                                          PRInt32& aOverflowX,
+                                                          PRInt32& aOverflowY,
+                                                          PRUint32 aUpdateFlags)
+{
+  nscoord dx = mLineHeight*aNumLinesX;
+  nscoord dy = mLineHeight*aNumLinesY;
+
+  CalcScrollOverflow(mDestinationX + dx, mDestinationY + dy, aOverflowX, aOverflowY);
+
+  return ScrollTo(mDestinationX + dx, mDestinationY + dy, aUpdateFlags);
+}
+
 NS_IMETHODIMP nsScrollPortView::GetPageScrollDistances(nsSize *aDistances)
 {
   nsSize size;
   GetDimensions(size);
 
   // The page increment is the size of the page, minus the smaller of
   // 10% of the size or 2 lines.
   aDistances->width  = size.width  - PR_MIN(size.width  / 10, 2 * mLineHeight);
@@ -446,25 +497,29 @@ NS_IMETHODIMP nsScrollPortView::ScrollBy
 
   ScrollTo(mDestinationX, newPos, aUpdateFlags);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP nsScrollPortView::ScrollByPixels(PRInt32 aNumPixelsX,
                                                PRInt32 aNumPixelsY,
+                                               PRInt32& aOverflowX,
+                                               PRInt32& aOverflowY,
                                                PRUint32 aUpdateFlags)
 {
   nsCOMPtr<nsIDeviceContext> dev;
   mViewManager->GetDeviceContext(*getter_AddRefs(dev));
   PRInt32 p2a = dev->AppUnitsPerDevPixel(); 
 
   nscoord dx = NSIntPixelsToAppUnits(aNumPixelsX, p2a);
   nscoord dy = NSIntPixelsToAppUnits(aNumPixelsY, p2a);
 
+  CalcScrollOverflow(mDestinationX + dx, mDestinationY + dy, aOverflowX, aOverflowY);
+  
   return ScrollTo(mDestinationX + dx, mDestinationY + dy, aUpdateFlags);
 }
 
 NS_IMETHODIMP nsScrollPortView::CanScroll(PRBool aHorizontal,
                                           PRBool aForward,
                                           PRBool &aResult)
 {
   nscoord offset = aHorizontal ? mOffsetX : mOffsetY;
@@ -609,17 +664,17 @@ NS_IMETHODIMP nsScrollPortView::ScrollTo
   }
   
   PRInt32 xPixels = NSAppUnitsToIntPixels(aX, p2a);
   PRInt32 yPixels = NSAppUnitsToIntPixels(aY, p2a);
   
   aX = NSIntPixelsToAppUnits(xPixels, p2a);
   aY = NSIntPixelsToAppUnits(yPixels, p2a);
   
-  // do nothing if the we aren't scrolling.
+  // do nothing if we aren't scrolling.
   // this needs to be rechecked because of the clamping and
   // rounding
   if (aX == mOffsetX && aY == mOffsetY) {
     return NS_OK;
   }
 
   // figure out the diff by comparing old pos to new
   dxPx = NSAppUnitsToIntPixels(mOffsetX, p2a) - xPixels;
--- a/view/src/nsScrollPortView.h
+++ b/view/src/nsScrollPortView.h
@@ -68,21 +68,25 @@ public:
   NS_IMETHOD  GetScrollPosition(nscoord &aX, nscoord &aY) const;
   NS_IMETHOD  ScrollTo(nscoord aX, nscoord aY, PRUint32 aUpdateFlags);
   NS_IMETHOD  SetScrollProperties(PRUint32 aProperties);
   NS_IMETHOD  GetScrollProperties(PRUint32 *aProperties);
   NS_IMETHOD  SetLineHeight(nscoord aHeight);
   NS_IMETHOD  GetLineHeight(nscoord *aHeight);
   NS_IMETHOD  ScrollByLines(PRInt32 aNumLinesX, PRInt32 aNumLinesY,
                             PRUint32 aUpdateFlags = 0);
+  NS_IMETHOD  ScrollByLinesWithOverflow(PRInt32 aNumLinesX, PRInt32 aNumLinesY,
+                                        PRInt32& aOverflowX, PRInt32& aOverflowY,
+                                        PRUint32 aUpdateFlags = 0);
   NS_IMETHOD  GetPageScrollDistances(nsSize *aDistances);
   NS_IMETHOD  ScrollByPages(PRInt32 aNumPagesX, PRInt32 aNumPagesY,
                             PRUint32 aUpdateFlags = 0);
   NS_IMETHOD  ScrollByWhole(PRBool aTop, PRUint32 aUpdateFlags = 0);
   NS_IMETHOD  ScrollByPixels(PRInt32 aNumPixelsX, PRInt32 aNumPixelsY,
+                             PRInt32& aOverflowX, PRInt32& aOverflowY,
                              PRUint32 aUpdateFlags = 0);
   NS_IMETHOD  CanScroll(PRBool aHorizontal, PRBool aForward, PRBool &aResult);
 
   NS_IMETHOD_(nsIView*) View();
 
   NS_IMETHOD  AddScrollPositionListener(nsIScrollPositionListener* aListener);
   NS_IMETHOD  RemoveScrollPositionListener(nsIScrollPositionListener* aListener);
 
@@ -102,16 +106,17 @@ private:
   static void AsyncScrollCallback(nsITimer *aTimer, void* aSPV);
 
 protected:
   virtual ~nsScrollPortView();
 
   //private
   void Scroll(nsView *aScrolledView, nsPoint aTwipsDelta, nsIntPoint aPixDelta, nscoord aP2A);
   PRBool CannotBitBlt(nsView* aScrolledView);
+  nsresult CalcScrollOverflow(nscoord aX, nscoord aY, PRInt32& aOverflowX, PRInt32& aOverflowY);
 
   nscoord             mOffsetX, mOffsetY;
   nscoord             mDestinationX, mDestinationY;
   PRUint32            mScrollProperties;
   nscoord             mLineHeight;
   nsISupportsArray   *mListeners;
 };
 
--- a/widget/public/nsGUIEvent.h
+++ b/widget/public/nsGUIEvent.h
@@ -1056,31 +1056,34 @@ public:
                             // a fallback for pixel scroll events.
                             // These scroll events are used by things that can't
                             // be scrolled pixel-wise, like trees. You should
                             // ignore them when processing pixel scroll events
                             // to avoid double-processing the same scroll gesture.
                             // When kHasPixels is set, the event is guaranteed to
                             // be followed up by an event that contains pixel
                             // scrolling information.
-    kNoLines =      1 << 4  // Marks pixel scroll events that will not be
+    kNoLines =      1 << 4, // Marks pixel scroll events that will not be
                             // followed by a line scroll events. EventStateManager
                             // will compute the appropriate height/width based on
                             // view lineHeight and generate line scroll events
                             // as needed.
+    kNoDefer =      1 << 5  // For scrollable views, indicates scroll should not
+                            // occur asynchronously.
   };
 
   nsMouseScrollEvent(PRBool isTrusted, PRUint32 msg, nsIWidget *w)
     : nsMouseEvent_base(isTrusted, msg, w, NS_MOUSE_SCROLL_EVENT),
-      scrollFlags(0), delta(0)
+      scrollFlags(0), delta(0), scrollOverflow(0)
   {
   }
 
   PRInt32               scrollFlags;
   PRInt32               delta;
+  PRInt32               scrollOverflow;
 };
 
 class nsQueryContentEvent : public nsGUIEvent
 {
 public:
   nsQueryContentEvent(PRBool aIsTrusted, PRUint32 aMsg, nsIWidget *aWidget) :
     nsGUIEvent(aIsTrusted, aMsg, aWidget, NS_QUERY_CONTENT_EVENT),
     mSucceeded(PR_FALSE)
--- a/widget/src/windows/nsWinGesture.cpp
+++ b/widget/src/windows/nsWinGesture.cpp
@@ -47,70 +47,91 @@
 #include "nsIDOMSimpleGestureEvent.h"
 #include "nsGUIEvent.h"
 
 #ifndef M_PI
 #define M_PI 3.14159265358979323846
 #endif
 
 const PRUnichar nsWinGesture::kGestureLibraryName[] =  L"user32.dll";
+const PRUnichar nsWinGesture::kThemeLibraryName[] =  L"uxtheme.dll";
 HMODULE nsWinGesture::sLibraryHandle = nsnull;
 nsWinGesture::GetGestureInfoPtr nsWinGesture::getGestureInfo = nsnull;
 nsWinGesture::CloseGestureInfoHandlePtr nsWinGesture::closeGestureInfoHandle = nsnull;
 nsWinGesture::GetGestureExtraArgsPtr nsWinGesture::getGestureExtraArgs = nsnull;
 nsWinGesture::SetGestureConfigPtr nsWinGesture::setGestureConfig = nsnull;
 nsWinGesture::GetGestureConfigPtr nsWinGesture::getGestureConfig = nsnull;
+nsWinGesture::BeginPanningFeedbackPtr nsWinGesture::beginPanningFeedback = nsnull;
+nsWinGesture::EndPanningFeedbackPtr nsWinGesture::endPanningFeedback = nsnull;
+nsWinGesture::UpdatePanningFeedbackPtr nsWinGesture::updatePanningFeedback = nsnull;
 static PRBool gEnableSingleFingerPanEvents = PR_FALSE;
 
 nsWinGesture::nsWinGesture() :
-  mAvailable(PR_FALSE)
+  mFeedbackActive(PR_FALSE),
+  mXAxisFeedback(PR_FALSE),
+  mYAxisFeedback(PR_FALSE),
+  mPanActive(PR_FALSE),
+  mPanInertiaActive(PR_FALSE)
 {
   (void)InitLibrary();
+  mPixelScrollOverflow = 0;
 }
 
 nsWinGesture::~nsWinGesture()
 {
   ShutdownLibrary();
 }
 
 /* Load and shutdown */
 
 PRBool nsWinGesture::InitLibrary()
 {
 #ifdef WINCE
   return PR_FALSE;
 #else
   if (getGestureInfo) {
-    mAvailable = PR_TRUE;
     return PR_TRUE;
   } else if (sLibraryHandle) {
     return PR_FALSE;
   }
 
   sLibraryHandle = ::LoadLibraryW(kGestureLibraryName);
+  HMODULE hTheme = ::LoadLibraryW(kThemeLibraryName);
+
+  // gesture interfaces
   if (sLibraryHandle) {
     getGestureInfo = (GetGestureInfoPtr)GetProcAddress(sLibraryHandle, "GetGestureInfo");
     closeGestureInfoHandle = (CloseGestureInfoHandlePtr)GetProcAddress(sLibraryHandle, "CloseGestureInfoHandle");
     getGestureExtraArgs = (GetGestureExtraArgsPtr)GetProcAddress(sLibraryHandle, "GetGestureExtraArgs");
     setGestureConfig = (SetGestureConfigPtr)GetProcAddress(sLibraryHandle, "SetGestureConfig");
     getGestureConfig = (GetGestureConfigPtr)GetProcAddress(sLibraryHandle, "GetGestureConfig");
   }
 
   if (!getGestureInfo || !closeGestureInfoHandle || !getGestureExtraArgs ||
-      !setGestureConfig || !getGestureConfig) {
+    !setGestureConfig || !getGestureConfig) {
     getGestureInfo         = nsnull;
     closeGestureInfoHandle = nsnull;
     getGestureExtraArgs    = nsnull;
     setGestureConfig       = nsnull;
     getGestureConfig       = nsnull;
-    
     return PR_FALSE;
   }
 
-  mAvailable = PR_TRUE;
+  // panning feedback interfaces
+  if (hTheme) {
+    beginPanningFeedback = (BeginPanningFeedbackPtr)GetProcAddress(hTheme, "BeginPanningFeedback");
+    endPanningFeedback = (EndPanningFeedbackPtr)GetProcAddress(hTheme, "EndPanningFeedback");
+    updatePanningFeedback = (UpdatePanningFeedbackPtr)GetProcAddress(hTheme, "UpdatePanningFeedback");
+  }
+
+  if (!beginPanningFeedback || !endPanningFeedback || !updatePanningFeedback) {
+    beginPanningFeedback   = nsnull;
+    endPanningFeedback     = nsnull;
+    updatePanningFeedback  = nsnull;
+  }
 
   // Check to see if we want single finger gesture input. Only do this once
   // for the app so we don't have to look it up on every window create.
   nsCOMPtr<nsIPrefService> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
   if (prefs) {
     nsCOMPtr<nsIPrefBranch> prefBranch;
     prefs->GetBranch(0, getter_AddRefs(prefBranch));
     if (prefBranch) {
@@ -122,24 +143,25 @@ PRBool nsWinGesture::InitLibrary()
   }
 
   return PR_TRUE;
 #endif
 }
 
 void nsWinGesture::ShutdownLibrary()
 {
-  mAvailable = PR_FALSE;
+  getGestureInfo         = nsnull;
+  beginPanningFeedback   = nsnull;
 }
 
 #define GCOUNT 5
 
 PRBool nsWinGesture::InitWinGestureSupport(HWND hWnd)
 {
-  if (!mAvailable)
+  if (!getGestureInfo)
     return PR_FALSE;
 
   GESTURECONFIG config[GCOUNT];
 
   memset(&config, 0, sizeof(config));
 
   config[0].dwID = GID_ZOOM;
   config[0].dwWant = GC_ZOOM;
@@ -163,73 +185,97 @@ PRBool nsWinGesture::InitWinGestureSuppo
     config[2].dwBlock = GC_PAN_WITH_SINGLE_FINGER_VERTICALLY|
                         GC_PAN_WITH_SINGLE_FINGER_HORIZONTALLY;
   }
 
   config[3].dwWant = GC_TWOFINGERTAP;
   config[3].dwID = GID_TWOFINGERTAP;
   config[3].dwBlock = 0;
 
-  config[4].dwWant = GC_ROLLOVER;
-  config[4].dwID = GID_ROLLOVER;
+  config[4].dwWant = GC_PRESSANDTAP;
+  config[4].dwID = GID_PRESSANDTAP;
   config[4].dwBlock = 0;
 
   return SetGestureConfig(hWnd, GCOUNT, (PGESTURECONFIG)&config);
 }
 
 /* Helpers */
 
 PRBool nsWinGesture::IsAvailable()
 {
-  return mAvailable;
+  return getGestureInfo != nsnull;
 }
 
 PRBool nsWinGesture::GetGestureInfo(HGESTUREINFO hGestureInfo, PGESTUREINFO pGestureInfo)
 {
-  if (!mAvailable || !hGestureInfo || !pGestureInfo)
+  if (!getGestureInfo || !hGestureInfo || !pGestureInfo)
     return PR_FALSE;
 
   ZeroMemory(pGestureInfo, sizeof(GESTUREINFO));
   pGestureInfo->cbSize = sizeof(GESTUREINFO);
 
   return getGestureInfo(hGestureInfo, pGestureInfo);
 }
 
 PRBool nsWinGesture::CloseGestureInfoHandle(HGESTUREINFO hGestureInfo)
 {
-  if (!mAvailable || !hGestureInfo)
+  if (!getGestureInfo || !hGestureInfo)
     return PR_FALSE;
 
   return closeGestureInfoHandle(hGestureInfo);
 }
 
 PRBool nsWinGesture::GetGestureExtraArgs(HGESTUREINFO hGestureInfo, UINT cbExtraArgs, PBYTE pExtraArgs)
 {
-  if (!mAvailable || !hGestureInfo || !pExtraArgs)
+  if (!getGestureInfo || !hGestureInfo || !pExtraArgs)
     return PR_FALSE;
 
   return getGestureExtraArgs(hGestureInfo, cbExtraArgs, pExtraArgs);
 }
 
 PRBool nsWinGesture::SetGestureConfig(HWND hWnd, UINT cIDs, PGESTURECONFIG pGestureConfig)
 {
-  if (!mAvailable || !pGestureConfig)
+  if (!getGestureInfo || !pGestureConfig)
     return PR_FALSE;
 
   return setGestureConfig(hWnd, 0, cIDs, pGestureConfig, sizeof(GESTURECONFIG));
 }
 
 PRBool nsWinGesture::GetGestureConfig(HWND hWnd, DWORD dwFlags, PUINT pcIDs, PGESTURECONFIG pGestureConfig)
 {
-  if (!mAvailable || !pGestureConfig)
+  if (!getGestureInfo || !pGestureConfig)
     return PR_FALSE;
 
   return getGestureConfig(hWnd, 0, dwFlags, pcIDs, pGestureConfig, sizeof(GESTURECONFIG));
 }
 
+PRBool nsWinGesture::BeginPanningFeedback(HWND hWnd)
+{
+  if (!beginPanningFeedback)
+    return PR_FALSE;
+
+  return beginPanningFeedback(hWnd);
+}
+
+PRBool nsWinGesture::EndPanningFeedback(HWND hWnd)
+{
+  if (!beginPanningFeedback)
+    return PR_FALSE;
+
+  return endPanningFeedback(hWnd, TRUE);
+}
+
+PRBool nsWinGesture::UpdatePanningFeedback(HWND hWnd, LONG offsetX, LONG offsetY, BOOL fInInertia)
+{
+  if (!beginPanningFeedback)
+    return PR_FALSE;
+
+  return updatePanningFeedback(hWnd, offsetX, offsetY, fInInertia);
+}
+
 PRBool nsWinGesture::IsPanEvent(LPARAM lParam)
 {
   GESTUREINFO gi;
 
   ZeroMemory(&gi,sizeof(GESTUREINFO));
   gi.cbSize = sizeof(GESTUREINFO);
 
   BOOL result = GetGestureInfo((HGESTUREINFO)lParam, &gi);
@@ -336,19 +382,19 @@ nsWinGesture::ProcessGestureMessage(HWND
     case GID_TWOFINGERTAP:
     {
       // Normally maps to "restore" from whatever you may have recently changed. A simple
       // double click.
       evt.message = NS_SIMPLE_GESTURE_TAP;
     }
     break;
 
-    case GID_ROLLOVER:
+    case GID_PRESSANDTAP:
     {
-      // Two finger drum roll. Defaults to right click if it falls through.
+      // Two finger right click. Defaults to right click if it falls through.
       evt.message = NS_SIMPLE_GESTURE_PRESSTAP;
     }
     break;
   }
 
   return PR_TRUE;
 }
 
@@ -361,67 +407,198 @@ nsWinGesture::ProcessPanMessage(HWND hWn
   gi.cbSize = sizeof(GESTUREINFO);
 
   BOOL result = GetGestureInfo((HGESTUREINFO)lParam, &gi);
   if (!result)
     return PR_FALSE;
 
   // The coordinates of this event
   nsPointWin coord;
-  coord = gi.ptsLocation;
-  coord.ScreenToClient(hWnd);
+  coord = mPanRefPoint = gi.ptsLocation;
+  // We want screen coordinates in our local offsets as client coordinates will change
+  // when feedback is taking place. Gui events though require client coordinates. 
+  mPanRefPoint.ScreenToClient(hWnd);
 
   switch(gi.dwID)
   {
     case GID_BEGIN:
     case GID_END:
       // These should always fall through to DefWndProc
       return PR_FALSE;
       break;
 
     // Setup pixel scroll events for both axis
     case GID_PAN:
     {
       if (gi.dwFlags & GF_BEGIN) {
         mPanIntermediate = coord;
         mPixelScrollDelta = 0;
-      } else {
+        mPanActive = PR_TRUE;
+        mPanInertiaActive = PR_FALSE;
+      }
+      else {
+
+#ifdef DBG_jimm
+        PRInt32 deltaX = mPanIntermediate.x - coord.x;
+        PRInt32 deltaY = mPanIntermediate.y - coord.y;
+        printf("coordX=%d coordY=%d deltaX=%d deltaY=%d x:%d y:%d\n", coord.x,
+          coord.y, deltaX, deltaY, mXAxisFeedback, mYAxisFeedback);
+#endif
+
         mPixelScrollDelta.x = mPanIntermediate.x - coord.x;
         mPixelScrollDelta.y = mPanIntermediate.y - coord.y;
         mPanIntermediate = coord;
+
+        if (gi.dwFlags & GF_INERTIA)
+          mPanInertiaActive = PR_TRUE;
+
+        if (gi.dwFlags & GF_END) {
+          mPanActive = PR_FALSE;
+          mPanInertiaActive = PR_FALSE;
+          PanFeedbackFinalize(hWnd, PR_TRUE);
+        }
       }
     }
     break;
   }
   return PR_TRUE;
 }
 
+inline PRBool TestTransition(PRInt32 a, PRInt32 b)
+{
+  // If a is zero, overflow is zero, implying the cursor has moved back to the start position.
+  // If b is zero, cached overscroll is zero, implying feedback just begun. 
+  if (a == 0 || b == 0) return PR_TRUE;
+  // Test for different signs.
+  return (a < 0) == (b < 0);
+}
+
+void
+nsWinGesture::UpdatePanFeedbackX(HWND hWnd, nsMouseScrollEvent& evt, PRBool& endFeedback)
+{
+  // If scroll overflow was returned indicating we panned past the bounds of
+  // the scrollable view port, start feeback.
+  if (evt.scrollOverflow != 0) {
+    if (!mFeedbackActive) {
+      BeginPanningFeedback(hWnd);
+      mFeedbackActive = PR_TRUE;
+    }      
+    endFeedback = PR_FALSE;
+    mXAxisFeedback = PR_TRUE;
+    return;
+  }
+  
+  if (mXAxisFeedback) {
+    PRInt32 newOverflow = mPixelScrollOverflow.x - mPixelScrollDelta.x;
+
+    // Detect a reverse transition past the starting drag point. This tells us the user
+    // has panned all the way back so we can stop providing feedback for this axis.
+    if (!TestTransition(newOverflow, mPixelScrollOverflow.x) || newOverflow == 0)
+      return;
+
+    // Cache the total over scroll in pixels.
+    mPixelScrollOverflow.x = newOverflow;
+    endFeedback = PR_FALSE;
+  }
+}
+
+void
+nsWinGesture::UpdatePanFeedbackY(HWND hWnd, nsMouseScrollEvent& evt, PRBool& endFeedback)
+{
+  // If scroll overflow was returned indicating we panned past the bounds of
+  // the scrollable view port, start feeback.
+  if (evt.scrollOverflow != 0) {
+    if (!mFeedbackActive) {
+      BeginPanningFeedback(hWnd);
+      mFeedbackActive = PR_TRUE;
+    }
+    endFeedback = PR_FALSE;
+    mYAxisFeedback = PR_TRUE;
+    return;
+  }
+  
+  if (mYAxisFeedback) {
+    PRInt32 newOverflow = mPixelScrollOverflow.y - mPixelScrollDelta.y;
+
+    // Detect a reverse transition past the starting drag point. This tells us the user
+    // has panned all the way back so we can stop providing feedback for this axis.
+    if (!TestTransition(newOverflow, mPixelScrollOverflow.y) || newOverflow == 0)
+      return;
+
+    // Cache the total over scroll in pixels.
+    mPixelScrollOverflow.y = newOverflow;
+    endFeedback = PR_FALSE;
+  }
+}
+
+void
+nsWinGesture::PanFeedbackFinalize(HWND hWnd, PRBool endFeedback)
+{
+  if (!mFeedbackActive)
+    return;
+
+  if (endFeedback) {
+    mFeedbackActive = PR_FALSE;
+    mXAxisFeedback = PR_FALSE;
+    mYAxisFeedback = PR_FALSE;
+    mPixelScrollOverflow = 0;
+    EndPanningFeedback(hWnd);
+    return;
+  }
+
+  UpdatePanningFeedback(hWnd, mPixelScrollOverflow.x, mPixelScrollOverflow.y, mPanInertiaActive);
+}
+
 PRBool
 nsWinGesture::PanDeltaToPixelScrollX(nsMouseScrollEvent& evt)
 {
+  evt.delta = 0;
+  evt.scrollOverflow = 0;
+
+  // Don't scroll the view if we are currently at a bounds, or, if we are
+  // panning back from a max feedback position. This keeps the original drag point
+  // constant.
+  if (mXAxisFeedback)
+    return PR_FALSE;
+
   if (mPixelScrollDelta.x != 0)
   {
-    evt.scrollFlags = nsMouseScrollEvent::kIsHorizontal|nsMouseScrollEvent::kHasPixels|nsMouseScrollEvent::kNoLines;
+    evt.scrollFlags = nsMouseScrollEvent::kIsHorizontal|
+                      nsMouseScrollEvent::kHasPixels|
+                      nsMouseScrollEvent::kNoLines|
+                      nsMouseScrollEvent::kNoDefer;
     evt.delta = mPixelScrollDelta.x;
-    evt.refPoint.x = mPanIntermediate.x;
-    evt.refPoint.y = mPanIntermediate.y;
+    evt.refPoint.x = mPanRefPoint.x;
+    evt.refPoint.y = mPanRefPoint.y;
     return PR_TRUE;
   }
   return PR_FALSE;
 }
 
 PRBool
 nsWinGesture::PanDeltaToPixelScrollY(nsMouseScrollEvent& evt)
 {
+  evt.delta = 0;
+  evt.scrollOverflow = 0;
+
+  // Don't scroll the view if we are currently at a bounds, or, if we are
+  // panning back from a max feedback position. This keeps the original drag point
+  // constant.
+  if (mYAxisFeedback)
+    return PR_FALSE;
+
   if (mPixelScrollDelta.y != 0)
   {
-    evt.scrollFlags = nsMouseScrollEvent::kIsVertical|nsMouseScrollEvent::kHasPixels|nsMouseScrollEvent::kNoLines;
+    evt.scrollFlags = nsMouseScrollEvent::kIsVertical|
+                      nsMouseScrollEvent::kHasPixels|
+                      nsMouseScrollEvent::kNoLines|
+                      nsMouseScrollEvent::kNoDefer;
     evt.delta = mPixelScrollDelta.y;
-    evt.refPoint.x = mPanIntermediate.x;
-    evt.refPoint.y = mPanIntermediate.y;
+    evt.refPoint.x = mPanRefPoint.x;
+    evt.refPoint.y = mPanRefPoint.y;
     return PR_TRUE;
   }
   return PR_FALSE;
 }
 
 #endif /* WinGesture_cpp__ */
 
 
--- a/widget/src/windows/nsWinGesture.h
+++ b/widget/src/windows/nsWinGesture.h
@@ -39,17 +39,16 @@
 #ifndef WinGesture_h__
 #define WinGesture_h__
 
 #include "nsdefs.h"
 #include <winuser.h>
 #include "nsPoint.h"
 #include "nsGUIEvent.h"
 
-
 #if !defined(NTDDI_WIN7) ||  NTDDI_VERSION < NTDDI_WIN7
 
 DECLARE_HANDLE(HGESTUREINFO);
 
 /*
  * Gesture flags - GESTUREINFO.dwFlags
  */
 #define GF_BEGIN                        0x00000001
@@ -126,28 +125,28 @@ typedef struct tagGESTURENOTIFYSTRUCT {
 #define GC_PAN_WITH_SINGLE_FINGER_HORIZONTALLY      0x00000004
 #define GC_PAN_WITH_GUTTER                          0x00000008
 #define GC_PAN_WITH_INERTIA                         0x00000010
 
 #define GC_ROTATE                                   0x00000001
 
 #define GC_TWOFINGERTAP                             0x00000001
 
-#define GC_ROLLOVER                                 0x00000001
+#define GC_PRESSANDTAP                              0x00000001
 
 /*
  * Gesture IDs
  */
 #define GID_BEGIN                       1
 #define GID_END                         2
 #define GID_ZOOM                        3
 #define GID_PAN                         4
 #define GID_ROTATE                      5
 #define GID_TWOFINGERTAP                6
-#define GID_ROLLOVER                    7
+#define GID_PRESSANDTAP                 7
 
 // Maximum number of gestures that can be included
 // in a single call to SetGestureConfig / GetGestureConfig
 #define GESTURECONFIGMAXCOUNT           256
 
 // If specified, GetGestureConfig returns consolidated configuration
 // for the specified window and it's parent window chain
 #define GCF_INCLUDE_ANCESTORS           0x00000001
@@ -199,54 +198,72 @@ public:
   // Simple gesture process
   PRBool ProcessGestureMessage(HWND hWnd, WPARAM wParam, LPARAM lParam, nsSimpleGestureEvent& evt);
 
   // Pan processing
   PRBool IsPanEvent(LPARAM lParam);
   PRBool ProcessPanMessage(HWND hWnd, WPARAM wParam, LPARAM lParam);
   PRBool PanDeltaToPixelScrollX(nsMouseScrollEvent& evt);
   PRBool PanDeltaToPixelScrollY(nsMouseScrollEvent& evt);
+  void UpdatePanFeedbackX(HWND hWnd, nsMouseScrollEvent& evt, PRBool& endFeedback);
+  void UpdatePanFeedbackY(HWND hWnd, nsMouseScrollEvent& evt, PRBool& endFeedback);
+  void PanFeedbackFinalize(HWND hWnd, PRBool endFeedback);
   
 public:
   // Helpers
   PRBool GetGestureInfo(HGESTUREINFO hGestureInfo, PGESTUREINFO pGestureInfo);
   PRBool CloseGestureInfoHandle(HGESTUREINFO hGestureInfo);
   PRBool GetGestureExtraArgs(HGESTUREINFO hGestureInfo, UINT cbExtraArgs, PBYTE pExtraArgs);
   PRBool SetGestureConfig(HWND hWnd, UINT cIDs, PGESTURECONFIG pGestureConfig);
   PRBool GetGestureConfig(HWND hWnd, DWORD dwFlags, PUINT pcIDs, PGESTURECONFIG pGestureConfig);
+  PRBool BeginPanningFeedback(HWND hWnd);
+  PRBool EndPanningFeedback(HWND hWnd);
+  PRBool UpdatePanningFeedback(HWND hWnd, LONG offsetX, LONG offsetY, BOOL fInInertia);
 
 protected:
 
 private:
   // Function prototypes
   typedef BOOL (WINAPI * GetGestureInfoPtr)(HGESTUREINFO hGestureInfo, PGESTUREINFO pGestureInfo);
   typedef BOOL (WINAPI * CloseGestureInfoHandlePtr)(HGESTUREINFO hGestureInfo);
   typedef BOOL (WINAPI * GetGestureExtraArgsPtr)(HGESTUREINFO hGestureInfo, UINT cbExtraArgs, PBYTE pExtraArgs);
   typedef BOOL (WINAPI * SetGestureConfigPtr)(HWND hwnd, DWORD dwReserved, UINT cIDs, PGESTURECONFIG pGestureConfig, UINT cbSize);
   typedef BOOL (WINAPI * GetGestureConfigPtr)(HWND hwnd, DWORD dwReserved, DWORD dwFlags, PUINT pcIDs, PGESTURECONFIG pGestureConfig, UINT cbSize);
+  typedef BOOL (WINAPI * BeginPanningFeedbackPtr)(HWND hWnd);
+  typedef BOOL (WINAPI * EndPanningFeedbackPtr)(HWND hWnd, BOOL fAnimateBack);
+  typedef BOOL (WINAPI * UpdatePanningFeedbackPtr)(HWND hWnd, LONG offsetX, LONG offsetY, BOOL fInInertia);
 
   // Static function pointers
   static GetGestureInfoPtr getGestureInfo;
   static CloseGestureInfoHandlePtr closeGestureInfoHandle;
   static GetGestureExtraArgsPtr getGestureExtraArgs;
   static SetGestureConfigPtr setGestureConfig;
   static GetGestureConfigPtr getGestureConfig;
+  static BeginPanningFeedbackPtr beginPanningFeedback;
+  static EndPanningFeedbackPtr endPanningFeedback;
+  static UpdatePanningFeedbackPtr updatePanningFeedback;
 
   // Delay load info 
   PRBool InitLibrary();
   void ShutdownLibrary();
 
   static HMODULE sLibraryHandle;
   static const PRUnichar kGestureLibraryName[];
+  static const PRUnichar kThemeLibraryName[];
 
-  PRBool mAvailable;
-  
-  // Pan state
+  // Pan and feedback state
   nsPointWin mPanIntermediate;
+  nsPointWin mPanRefPoint;
   nsPointWin mPixelScrollDelta;
+  PRPackedBool mPanActive;
+  PRPackedBool mFeedbackActive;
+  PRPackedBool mXAxisFeedback;
+  PRPackedBool mYAxisFeedback;
+  PRPackedBool mPanInertiaActive;
+  nsPointWin mPixelScrollOverflow;
 
   // Zoom state
   double mZoomIntermediate;
 
   // Rotate state
   double mRotateIntermediate;
 };
 
--- a/widget/src/windows/nsWindow.cpp
+++ b/widget/src/windows/nsWindow.cpp
@@ -5216,23 +5216,28 @@ PRBool nsWindow::ProcessGestureMessage(W
 
     event.isShift   = IS_VK_DOWN(NS_VK_SHIFT);
     event.isControl = IS_VK_DOWN(NS_VK_CONTROL);
     event.isMeta    = PR_FALSE;
     event.isAlt     = IS_VK_DOWN(NS_VK_ALT);
     event.button    = 0;
     event.time      = ::GetMessageTime();
 
+    PRBool endFeedback = PR_TRUE;
+    
     if (mGesture.PanDeltaToPixelScrollX(event)) {
       DispatchEvent(&event, status);
     }
+    mGesture.UpdatePanFeedbackX(mWnd, event, endFeedback);
+    
     if (mGesture.PanDeltaToPixelScrollY(event)) {
       DispatchEvent(&event, status);
     }
-
+    mGesture.UpdatePanFeedbackY(mWnd, event, endFeedback);
+    mGesture.PanFeedbackFinalize(mWnd, endFeedback);
     mGesture.CloseGestureInfoHandle((HGESTUREINFO)lParam);
 
     return PR_TRUE;
   }
 
   // Other gestures translate into simple gesture events:
   nsSimpleGestureEvent event(PR_TRUE, 0, this, 0, 0.0);
   if ( !mGesture.ProcessGestureMessage(mWnd, wParam, lParam, event) ) {