Bug 605648 Support high resolution scrolling on Windows r=jimm+smaug
authorMasayuki Nakano <masayuki@d-toybox.com>
Tue, 17 May 2011 09:23:23 +0900
changeset 69589 0a1e7ec7e2684d21d19a017663c373b0518076c4
parent 69588 3e7a21049b6cef347c1b7307507cbaf2965f9b4a
child 69590 9968ed6b629a1b2c937e0cb1053a56c91f96c2d2
push id20034
push usermasayuki@d-toybox.com
push dateTue, 17 May 2011 00:25:17 +0000
treeherdermozilla-central@0a1e7ec7e268 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimm
bugs605648
milestone6.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 605648 Support high resolution scrolling on Windows r=jimm+smaug
content/events/src/nsEventStateManager.cpp
content/events/src/nsEventStateManager.h
widget/public/nsGUIEvent.h
widget/public/nsGUIEventIPC.h
widget/src/windows/nsWindow.cpp
widget/src/windows/nsWindow.h
widget/src/windows/nsWindowDefs.h
--- a/content/events/src/nsEventStateManager.cpp
+++ b/content/events/src/nsEventStateManager.cpp
@@ -1389,16 +1389,22 @@ nsEventStateManager::PreHandleEvent(nsPr
     break;
   case NS_QUERY_DOM_WIDGET_HITTEST:
     {
       // XXX remote event
       nsContentEventHandler handler(mPresContext);
       handler.OnQueryDOMWidgetHittest(static_cast<nsQueryContentEvent*>(aEvent));
     }
     break;
+  case NS_QUERY_SCROLL_TARGET_INFO:
+    {
+      DoQueryScrollTargetInfo(static_cast<nsQueryContentEvent*>(aEvent),
+                              aTargetFrame);
+      break;
+    }
   case NS_SELECTION_SET:
     {
       nsSelectionEvent *selectionEvent =
           static_cast<nsSelectionEvent*>(aEvent);
       if (IsTargetCrossProcess(selectionEvent)) {
         // Will not be handled locally, remote the event
         if (GetCrossProcessTarget()->SendSelectionEvent(*selectionEvent))
           selectionEvent->mSucceeded = PR_TRUE;
@@ -2559,17 +2565,18 @@ nsEventStateManager::SendPixelScrollEven
 
   nsEventDispatcher::Dispatch(targetContent, aPresContext, &event, nsnull, aStatus);
 }
 
 nsresult
 nsEventStateManager::DoScrollText(nsIFrame* aTargetFrame,
                                   nsMouseScrollEvent* aMouseEvent,
                                   nsIScrollableFrame::ScrollUnit aScrollQuantity,
-                                  PRBool aAllowScrollSpeedOverride)
+                                  PRBool aAllowScrollSpeedOverride,
+                                  nsQueryContentEvent* aQueryEvent)
 {
   nsIScrollableFrame* frameToScroll = 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
@@ -2638,16 +2645,29 @@ nsEventStateManager::DoScrollText(nsIFra
             nsMouseWheelTransaction::EndTransaction();
           }
         }
       }
     }
   }
 
   if (!passToParent && frameToScroll) {
+    if (aQueryEvent) {
+      nscoord appUnitsPerDevPixel =
+        aTargetFrame->PresContext()->AppUnitsPerDevPixel();
+      aQueryEvent->mReply.mLineHeight =
+        frameToScroll->GetLineScrollAmount().height / appUnitsPerDevPixel;
+      aQueryEvent->mReply.mPageHeight =
+        frameToScroll->GetPageScrollAmount().height / appUnitsPerDevPixel;
+      aQueryEvent->mReply.mPageWidth =
+        frameToScroll->GetPageScrollAmount().width / appUnitsPerDevPixel;
+      aQueryEvent->mSucceeded = PR_TRUE;
+      return NS_OK;
+    }
+
     if (aScrollQuantity == nsIScrollableFrame::LINES) {
       numLines =
         nsMouseWheelTransaction::AccelerateWheelDelta(numLines, isHorizontal,
                                                       aAllowScrollSpeedOverride,
                                                       &aScrollQuantity);
     }
 #ifdef DEBUG
     else {
@@ -2665,17 +2685,19 @@ nsEventStateManager::DoScrollText(nsIFra
     if (isHorizontal) {
       scrollX = scrollY;
       scrollY = 0;
     }
     
     nsIScrollableFrame::ScrollMode mode;
     if (aMouseEvent->scrollFlags & nsMouseScrollEvent::kNoDefer) {
       mode = nsIScrollableFrame::INSTANT;
-    } else if (aScrollQuantity != nsIScrollableFrame::DEVICE_PIXELS) {
+    } else if (aScrollQuantity != nsIScrollableFrame::DEVICE_PIXELS ||
+               (aMouseEvent->scrollFlags &
+                  nsMouseScrollEvent::kAllowSmoothScroll) != 0) {
       mode = nsIScrollableFrame::SMOOTH;
     } else {
       mode = nsIScrollableFrame::NORMAL;
     }
 
     nsIntPoint overflow;
     frameToScroll->ScrollBy(nsIntPoint(scrollX, scrollY), aScrollQuantity,
                             mode, &overflow);
@@ -2683,17 +2705,17 @@ nsEventStateManager::DoScrollText(nsIFra
     return NS_OK;
   }
   
   if (passToParent) {
     nsIFrame* newFrame = nsLayoutUtils::GetCrossDocParentFrame(
         aTargetFrame->PresContext()->FrameManager()->GetRootFrame());
     if (newFrame)
       return DoScrollText(newFrame, aMouseEvent, aScrollQuantity,
-                          aAllowScrollSpeedOverride);
+                          aAllowScrollSpeedOverride, aQueryEvent);
   }
 
   aMouseEvent->scrollOverflow = numLines;
 
   return NS_OK;
 }
 
 void
@@ -3046,25 +3068,25 @@ nsEventStateManager::PostHandleEvent(nsP
           nsContentUtils::GetBoolPref(sysNumLinesKey.get());
 
         if (useSysNumLines) {
           if (msEvent->scrollFlags & nsMouseScrollEvent::kIsFullPage)
             action = MOUSE_SCROLL_PAGE;
         }
 
         if (aEvent->message == NS_MOUSE_PIXEL_SCROLL) {
-          if (action == MOUSE_SCROLL_N_LINES ||
+          if (action == MOUSE_SCROLL_N_LINES || action == MOUSE_SCROLL_PAGE ||
               (msEvent->scrollFlags & nsMouseScrollEvent::kIsMomentum)) {
              action = MOUSE_SCROLL_PIXELS;
           } else {
             // Do not scroll pixels when zooming
             action = -1;
           }
         } else if (msEvent->scrollFlags & nsMouseScrollEvent::kHasPixels) {
-          if (action == MOUSE_SCROLL_N_LINES ||
+          if (useSysNumLines || action == MOUSE_SCROLL_N_LINES ||
               (msEvent->scrollFlags & nsMouseScrollEvent::kIsMomentum)) {
             // Don't scroll lines when a pixel scroll event will follow.
             // Also, don't do history scrolling or zooming for momentum scrolls.
             action = -1;
           }
         }
 
         switch (action) {
@@ -4642,16 +4664,30 @@ nsEventStateManager::DoContentCommandScr
   }
 
   // The caller may want synchronous scrolling.
   sf->ScrollBy(pt, scrollUnit, nsIScrollableFrame::INSTANT);
   return NS_OK;
 }
 
 void
+nsEventStateManager::DoQueryScrollTargetInfo(nsQueryContentEvent* aEvent,
+                                             nsIFrame* aTargetFrame)
+{
+  nsMouseScrollEvent* msEvent = aEvent->mInput.mMouseScrollEvent;
+  nsIScrollableFrame::ScrollUnit unit;
+  if (msEvent->scrollFlags & nsMouseScrollEvent::kIsFullPage) {
+    unit = nsIScrollableFrame::PAGES;
+  } else {
+    unit = nsIScrollableFrame::LINES;
+  }
+  DoScrollText(aTargetFrame, msEvent, unit, PR_FALSE, aEvent);
+}
+
+void
 nsEventStateManager::SetActiveManager(nsEventStateManager* aNewESM,
                                       nsIContent* aContent)
 {
   if (sActiveESM && aNewESM != sActiveESM) {
     sActiveESM->SetContentState(nsnull, NS_EVENT_STATE_ACTIVE);
   }
   sActiveESM = aNewESM;
   if (sActiveESM && aContent) {
--- a/content/events/src/nsEventStateManager.h
+++ b/content/events/src/nsEventStateManager.h
@@ -319,20 +319,29 @@ protected:
                            nsMouseScrollEvent* aEvent,
                            nsPresContext* aPresContext,
                            nsEventStatus* aStatus,
                            PRInt32 aNumLines);
   void SendPixelScrollEvent(nsIFrame* aTargetFrame,
                             nsMouseScrollEvent* aEvent,
                             nsPresContext* aPresContext,
                             nsEventStatus* aStatus);
+  /**
+   * @param aQueryEvent If you set vailid pointer for this, DoScrollText()
+   *                    computes the line-height and page size of current
+   *                    mouse wheel scroll target and sets it to the event.
+   *                    And then, this method does NOT scroll any scrollable
+   *                    elements.  I.e., you can just query the scroll target
+   *                    information.
+   */
   nsresult DoScrollText(nsIFrame* aTargetFrame,
                         nsMouseScrollEvent* aMouseEvent,
                         nsIScrollableFrame::ScrollUnit aScrollQuantity,
-                        PRBool aAllowScrollSpeedOverride);
+                        PRBool aAllowScrollSpeedOverride,
+                        nsQueryContentEvent* aQueryEvent = nsnull);
   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
 
   /*
@@ -392,16 +401,19 @@ protected:
    * BeginTrackingDragGesture). aEvent->widget must be
    * mCurrentTarget->GetNearestWidget().
    */
   void FillInEventFromGestureDown(nsMouseEvent* aEvent);
 
   nsresult DoContentCommandEvent(nsContentCommandEvent* aEvent);
   nsresult DoContentCommandScrollEvent(nsContentCommandEvent* aEvent);
 
+  void DoQueryScrollTargetInfo(nsQueryContentEvent* aEvent,
+                               nsIFrame* aTargetFrame);
+
   PRBool RemoteQueryContentEvent(nsEvent *aEvent);
   mozilla::dom::TabParent *GetCrossProcessTarget();
   PRBool IsTargetCrossProcess(nsGUIEvent *aEvent);
 
   PRInt32     mLockCursor;
 
   nsWeakFrame mCurrentTarget;
   nsCOMPtr<nsIContent> mCurrentTargetContent;
--- a/widget/public/nsGUIEvent.h
+++ b/widget/public/nsGUIEvent.h
@@ -410,16 +410,22 @@ class nsHashKey;
 // Query for the selection in the form of a nsITransferable.
 #define NS_QUERY_SELECTION_AS_TRANSFERABLE (NS_QUERY_CONTENT_EVENT_START + 7)
 // Query for character at a point.  This returns the character offset and its
 // rect.  The point is specified by nsEvent::refPoint.
 #define NS_QUERY_CHARACTER_AT_POINT     (NS_QUERY_CONTENT_EVENT_START + 8)
 // Query if the DOM element under nsEvent::refPoint belongs to our widget
 // or not.
 #define NS_QUERY_DOM_WIDGET_HITTEST     (NS_QUERY_CONTENT_EVENT_START + 9)
+// Query for some information about mouse wheel event's target
+// XXX This is used only for supporting high resolution mouse scroll on Windows
+//     and it's going to be reimplemented with another approach.  At that time,
+//     this even is going to be removed. Therefore,  DON'T use this event for
+//     other purpose.
+#define NS_QUERY_SCROLL_TARGET_INFO     (NS_QUERY_CONTENT_EVENT_START + 99)
 
 // Video events
 #ifdef MOZ_MEDIA
 #define NS_MEDIA_EVENT_START            3300
 #define NS_LOADSTART           (NS_MEDIA_EVENT_START)
 #define NS_PROGRESS            (NS_MEDIA_EVENT_START+1)
 #define NS_SUSPEND             (NS_MEDIA_EVENT_START+2)
 #define NS_EMPTIED             (NS_MEDIA_EVENT_START+3)
@@ -1183,19 +1189,21 @@ public:
                             // scrolling information.
     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.
-    kIsMomentum =   1 << 6  // Marks scroll events that aren't controlled by the
+    kIsMomentum =   1 << 6, // Marks scroll events that aren't controlled by the
                             // user but fire automatically as the result of a
                             // "momentum" scroll.
+    kAllowSmoothScroll = 1 << 7 // Allow smooth scroll for the pixel scroll
+                                // event.
   };
 
   nsMouseScrollEvent(PRBool isTrusted, PRUint32 msg, nsIWidget *w)
     : nsMouseEvent_base(isTrusted, msg, w, NS_MOUSE_SCROLL_EVENT),
       scrollFlags(0), delta(0), scrollOverflow(0)
   {
   }
 
@@ -1280,16 +1288,23 @@ public:
 
   void InitForQueryDOMWidgetHittest(nsIntPoint& aPoint)
   {
     NS_ASSERTION(message == NS_QUERY_DOM_WIDGET_HITTEST,
                  "wrong initializer is called");
     refPoint = aPoint;
   }
 
+  void InitForQueryScrollTargetInfo(nsMouseScrollEvent* aEvent)
+  {
+    NS_ASSERTION(message == NS_QUERY_SCROLL_TARGET_INFO,
+                 "wrong initializer is called");
+    mInput.mMouseScrollEvent = aEvent;
+  }
+
   PRUint32 GetSelectionStart(void) const
   {
     NS_ASSERTION(message == NS_QUERY_SELECTED_TEXT,
                  "not querying selection");
     return mReply.mOffset + (mReply.mReversed ? mReply.mString.Length() : 0);
   }
 
   PRUint32 GetSelectionEnd(void) const
@@ -1299,29 +1314,35 @@ public:
     return mReply.mOffset + (mReply.mReversed ? 0 : mReply.mString.Length());
   }
 
   PRBool mSucceeded;
   PRPackedBool mWasAsync;
   struct {
     PRUint32 mOffset;
     PRUint32 mLength;
+    // used by NS_QUERY_SCROLL_TARGET_INFO
+    nsMouseScrollEvent* mMouseScrollEvent;
   } mInput;
   struct {
     void* mContentsRoot;
     PRUint32 mOffset;
     nsString mString;
     nsIntRect mRect; // Finally, the coordinates is system coordinates.
     // The return widget has the caret. This is set at all query events.
     nsIWidget* mFocusedWidget;
     PRPackedBool mReversed; // true if selection is reversed (end < start)
     PRPackedBool mHasSelection; // true if the selection exists
     PRPackedBool mWidgetIsHit; // true if DOM element under mouse belongs to widget
     // used by NS_QUERY_SELECTION_AS_TRANSFERABLE
     nsCOMPtr<nsITransferable> mTransferable;
+    // used by NS_QUERY_SCROLL_TARGET_INFO
+    PRInt32 mLineHeight;
+    PRInt32 mPageWidth;
+    PRInt32 mPageHeight;
   } mReply;
 
   enum {
     NOT_FOUND = PR_UINT32_MAX
   };
 };
 
 class nsFocusEvent : public nsEvent
@@ -1615,25 +1636,17 @@ enum nsDragDropEventStatus {
 
 #define NS_IS_ACTIVATION_EVENT(evnt) \
        (((evnt)->message == NS_ACTIVATE) || \
         ((evnt)->message == NS_DEACTIVATE) || \
         ((evnt)->message == NS_PLUGIN_ACTIVATE) || \
         ((evnt)->message == NS_PLUGIN_FOCUS))
 
 #define NS_IS_QUERY_CONTENT_EVENT(evnt) \
-       (((evnt)->message == NS_QUERY_SELECTED_TEXT) || \
-        ((evnt)->message == NS_QUERY_TEXT_CONTENT) || \
-        ((evnt)->message == NS_QUERY_CARET_RECT) || \
-        ((evnt)->message == NS_QUERY_TEXT_RECT) || \
-        ((evnt)->message == NS_QUERY_EDITOR_RECT) || \
-        ((evnt)->message == NS_QUERY_CONTENT_STATE) || \
-        ((evnt)->message == NS_QUERY_SELECTION_AS_TRANSFERABLE) || \
-        ((evnt)->message == NS_QUERY_CHARACTER_AT_POINT) || \
-        ((evnt)->message == NS_QUERY_DOM_WIDGET_HITTEST))
+       ((evnt)->eventStructType == NS_QUERY_CONTENT_EVENT)
 
 #define NS_IS_SELECTION_EVENT(evnt) \
        (((evnt)->message == NS_SELECTION_SET))
 
 #define NS_IS_CONTENT_COMMAND_EVENT(evnt) \
        ((evnt)->eventStructType == NS_CONTENT_COMMAND_EVENT)
 
 #define NS_IS_PLUGIN_EVENT(evnt) \
@@ -1665,17 +1678,18 @@ enum nsDragDropEventStatus {
   (event)->flags &= ~NS_EVENT_FLAG_DISPATCHING; \
   (event)->flags |= NS_EVENT_DISPATCHED;
 
 // Be aware the query content events and the selection events are a part of IME
 // processing.  So, you shouldn't use NS_IS_IME_EVENT macro directly in most
 // cases, you should use NS_IS_IME_RELATED_EVENT instead.
 #define NS_IS_IME_RELATED_EVENT(evnt) \
   (NS_IS_IME_EVENT(evnt) || \
-   NS_IS_QUERY_CONTENT_EVENT(evnt) || \
+   (NS_IS_QUERY_CONTENT_EVENT(evnt) && \
+    evnt->message != NS_QUERY_SCROLL_TARGET_INFO) || \
    NS_IS_SELECTION_EVENT(evnt))
 
 /*
  * Virtual key bindings for keyboard events.
  * These come from nsIDOMKeyEvent.h, which is generated from MouseKeyEvent.idl.
  * Really, it would be better if we phased out the NS_VK symbols altogether
  * in favor of the DOM ones, but at least this way they'll be in sync.
  */
--- a/widget/public/nsGUIEventIPC.h
+++ b/widget/public/nsGUIEventIPC.h
@@ -105,16 +105,60 @@ struct ParamTraits<nsInputEvent>
            ReadParam(aMsg, aIter, &aResult->isShift) &&
            ReadParam(aMsg, aIter, &aResult->isControl) &&
            ReadParam(aMsg, aIter, &aResult->isAlt) &&
            ReadParam(aMsg, aIter, &aResult->isMeta);
   }
 };
 
 template<>
+struct ParamTraits<nsMouseEvent_base>
+{
+  typedef nsMouseEvent_base paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, static_cast<nsInputEvent>(aParam));
+    WriteParam(aMsg, aParam.button);
+    WriteParam(aMsg, aParam.pressure);
+    WriteParam(aMsg, aParam.inputSource);
+  }
+
+  static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
+  {
+    return ReadParam(aMsg, aIter, static_cast<nsInputEvent*>(aResult)) &&
+           ReadParam(aMsg, aIter, &aResult->button) &&
+           ReadParam(aMsg, aIter, &aResult->pressure) &&
+           ReadParam(aMsg, aIter, &aResult->inputSource);
+  }
+};
+
+template<>
+struct ParamTraits<nsMouseScrollEvent>
+{
+  typedef nsMouseScrollEvent paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, static_cast<nsMouseEvent_base>(aParam));
+    WriteParam(aMsg, aParam.scrollFlags);
+    WriteParam(aMsg, aParam.delta);
+    WriteParam(aMsg, aParam.scrollOverflow);
+  }
+
+  static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
+  {
+    return ReadParam(aMsg, aIter, static_cast<nsMouseEvent_base*>(aResult)) &&
+           ReadParam(aMsg, aIter, &aResult->scrollFlags) &&
+           ReadParam(aMsg, aIter, &aResult->delta) &&
+           ReadParam(aMsg, aIter, &aResult->scrollOverflow);
+  }
+};
+
+template<>
 struct ParamTraits<nsTextRangeStyle>
 {
   typedef nsTextRangeStyle paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
   {
     WriteParam(aMsg, aParam.mDefinedStyles);
     WriteParam(aMsg, aParam.mLineStyle);
@@ -230,37 +274,45 @@ struct ParamTraits<nsQueryContentEvent>
   typedef nsQueryContentEvent paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
   {
     WriteParam(aMsg, static_cast<nsGUIEvent>(aParam));
     WriteParam(aMsg, aParam.mSucceeded);
     WriteParam(aMsg, aParam.mInput.mOffset);
     WriteParam(aMsg, aParam.mInput.mLength);
+    WriteParam(aMsg, *aParam.mInput.mMouseScrollEvent);
     WriteParam(aMsg, aParam.mReply.mOffset);
     WriteParam(aMsg, aParam.mReply.mString);
     WriteParam(aMsg, aParam.mReply.mRect);
     WriteParam(aMsg, aParam.mReply.mReversed);
     WriteParam(aMsg, aParam.mReply.mHasSelection);
     WriteParam(aMsg, aParam.mReply.mWidgetIsHit);
+    WriteParam(aMsg, aParam.mReply.mLineHeight);
+    WriteParam(aMsg, aParam.mReply.mPageHeight);
+    WriteParam(aMsg, aParam.mReply.mPageWidth);
   }
 
   static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
   {
     aResult->mWasAsync = PR_TRUE;
     return ReadParam(aMsg, aIter, static_cast<nsGUIEvent*>(aResult)) &&
            ReadParam(aMsg, aIter, &aResult->mSucceeded) &&
            ReadParam(aMsg, aIter, &aResult->mInput.mOffset) &&
            ReadParam(aMsg, aIter, &aResult->mInput.mLength) &&
+           ReadParam(aMsg, aIter, aResult->mInput.mMouseScrollEvent) &&
            ReadParam(aMsg, aIter, &aResult->mReply.mOffset) &&
            ReadParam(aMsg, aIter, &aResult->mReply.mString) &&
            ReadParam(aMsg, aIter, &aResult->mReply.mRect) &&
            ReadParam(aMsg, aIter, &aResult->mReply.mReversed) &&
            ReadParam(aMsg, aIter, &aResult->mReply.mHasSelection) &&
-           ReadParam(aMsg, aIter, &aResult->mReply.mWidgetIsHit);
+           ReadParam(aMsg, aIter, &aResult->mReply.mWidgetIsHit) &&
+           ReadParam(aMsg, aIter, &aResult->mReply.mLineHeight) &&
+           ReadParam(aMsg, aIter, &aResult->mReply.mPageHeight) &&
+           ReadParam(aMsg, aIter, &aResult->mReply.mPageWidth);
   }
 };
 
 template<>
 struct ParamTraits<nsSelectionEvent>
 {
   typedef nsSelectionEvent paramType;
 
--- a/widget/src/windows/nsWindow.cpp
+++ b/widget/src/windows/nsWindow.cpp
@@ -293,16 +293,28 @@ LPFNLRESULTFROMOBJECT
 
 // Used in OOPP plugin focus processing.
 const PRUnichar* kOOPPPluginFocusEventId   = L"OOPP Plugin Focus Widget Event";
 PRUint32        nsWindow::sOOPPPluginFocusEvent   =
                   RegisterWindowMessageW(kOOPPPluginFocusEventId);
 
 MSG             nsWindow::sRedirectedKeyDown;
 
+PRBool          nsWindow::sNeedsToInitMouseWheelSettings = PR_TRUE;
+ULONG           nsWindow::sMouseWheelScrollLines  = 0;
+ULONG           nsWindow::sMouseWheelScrollChars  = 0;
+
+HWND            nsWindow::sLastMouseWheelWnd = NULL;
+PRInt32         nsWindow::sRemainingDeltaForScroll = 0;
+PRInt32         nsWindow::sRemainingDeltaForPixel = 0;
+PRBool          nsWindow::sLastMouseWheelDeltaIsPositive = PR_FALSE;
+PRBool          nsWindow::sLastMouseWheelOrientationIsVertical = PR_FALSE;
+PRBool          nsWindow::sLastMouseWheelUnitIsPage = PR_FALSE;
+PRUint32        nsWindow::sLastMouseWheelTime = 0;
+
 /**************************************************************
  *
  * SECTION: globals variables
  *
  **************************************************************/
 
 static const char *sScreenManagerContractID       = "@mozilla.org/gfx/screenmanager;1";
 
@@ -4519,18 +4531,16 @@ PRBool nsWindow::ProcessMessage(UINT msg
     if (ProcessMessageForPlugin(nativeMsg, aRetValue, callDefaultWndProc)) {
       return mWnd ? !callDefaultWndProc : PR_TRUE;
     }
   }
 
   PRBool result = PR_FALSE;    // call the default nsWindow proc
   *aRetValue = 0;
 
-  static PRBool getWheelInfo = PR_TRUE;
-
 #if MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_LONGHORN
   // Glass hit testing w/custom transparent margins
   LRESULT dwmHitResult;
   if (mCustomNonClient &&
       nsUXThemeData::CheckForCompositor() &&
       nsUXThemeData::dwmDwmDefWindowProcPtr(mWnd, msg, wParam, lParam, &dwmHitResult)) {
     *aRetValue = dwmHitResult;
     return PR_TRUE;
@@ -5182,17 +5192,22 @@ PRBool nsWindow::ProcessMessage(UINT msg
     case WM_WINDOWPOSCHANGED:
     {
       WINDOWPOS *wp = (LPWINDOWPOS)lParam;
       OnWindowPosChanged(wp, result);
     }
     break;
 
     case WM_SETTINGCHANGE:
-      getWheelInfo = PR_TRUE;
+      switch (wParam) {
+        case SPI_SETWHEELSCROLLLINES:
+        case SPI_SETWHEELSCROLLCHARS:
+          sNeedsToInitMouseWheelSettings = PR_TRUE;
+          break;
+      }
       break;
 
     case WM_INPUTLANGCHANGEREQUEST:
       *aRetValue = TRUE;
       result = PR_FALSE;
       break;
 
     case WM_INPUTLANGCHANGE:
@@ -5254,18 +5269,19 @@ PRBool nsWindow::ProcessMessage(UINT msg
   case WM_MOUSEHWHEEL:
     {
       // If OnMouseWheel returns true, the event was forwarded directly to another
       // mozilla window message handler (ProcessMessage). In this case the return
       // value of the forwarded event is in 'result' which we should return immediately.
       // If OnMouseWheel returns false, OnMouseWheel processed the event internally.
       // 'result' and 'aRetValue' will be set based on what we did with the event, so
       // we should fall through.
-      if (OnMouseWheel(msg, wParam, lParam, getWheelInfo, result, aRetValue))
+      if (OnMouseWheel(msg, wParam, lParam, result, aRetValue)) {
         return result;
+      }
     }
     break;
 
 #if MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_LONGHORN
   case WM_DWMCOMPOSITIONCHANGED:
     // First, update the compositor state to latest one. All other methods
     // should use same state as here for consistency painting.
     nsUXThemeData::CheckForCompositor(PR_TRUE);
@@ -6276,155 +6292,261 @@ PRUint16 nsWindow::GetMouseInputSource()
   LPARAM lParamExtraInfo = ::GetMessageExtraInfo();
   if ((lParamExtraInfo & TABLET_INK_SIGNATURE) == TABLET_INK_CHECK) {
     inputSource = (lParamExtraInfo & TABLET_INK_TOUCH) ?
                   PRUint16(nsIDOMNSMouseEvent::MOZ_SOURCE_TOUCH) : nsIDOMNSMouseEvent::MOZ_SOURCE_PEN;
   }
   return inputSource;
 }
 
+/* static */ void
+nsWindow::InitMouseWheelScrollData()
+{
+  if (!sNeedsToInitMouseWheelSettings) {
+    return;
+  }
+  sNeedsToInitMouseWheelSettings = PR_FALSE;
+  ResetRemainingWheelDelta();
+
+  if (!::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0,
+                              &sMouseWheelScrollLines, 0)) {
+    NS_WARNING("Failed to get SPI_GETWHEELSCROLLLINES");
+    sMouseWheelScrollLines = 3;
+  } else if (sMouseWheelScrollLines > WHEEL_DELTA) {
+    // sMouseWheelScrollLines usually equals 3 or 0 (for no scrolling)
+    // However, if sMouseWheelScrollLines > WHEEL_DELTA, we assume that
+    // the mouse driver wants a page scroll.  The docs state that
+    // sMouseWheelScrollLines should explicitly equal WHEEL_PAGESCROLL, but
+    // since some mouse drivers use an arbitrary large number instead,
+    // we have to handle that as well.
+    sMouseWheelScrollLines = WHEEL_PAGESCROLL;
+  }
+
+  if (!::SystemParametersInfo(SPI_GETWHEELSCROLLCHARS, 0,
+                              &sMouseWheelScrollChars, 0)) {
+    NS_ASSERTION(!nsUXThemeData::sIsVistaOrLater,
+                 "Failed to get SPI_GETWHEELSCROLLCHARS");
+    sMouseWheelScrollChars = 1;
+  } else if (sMouseWheelScrollChars > WHEEL_DELTA) {
+    // See the comments for the case sMouseWheelScrollLines > WHEEL_DELTA.
+    sMouseWheelScrollChars = WHEEL_PAGESCROLL;
+  }
+}
+
+/* static */
+void
+nsWindow::ResetRemainingWheelDelta()
+{
+  sRemainingDeltaForPixel = 0;
+  sRemainingDeltaForScroll = 0;
+  sLastMouseWheelWnd = NULL;
+}
+
+static PRInt32 RoundDelta(double aDelta)
+{
+  return aDelta >= 0 ? (PRInt32)NS_floor(aDelta) : (PRInt32)NS_ceil(aDelta);
+}
+
 /*
  * OnMouseWheel - mouse wheel event processing. This was originally embedded
  * within the message case block. If returning true result should be returned
  * immediately (no more processing).
  */
-PRBool nsWindow::OnMouseWheel(UINT msg, WPARAM wParam, LPARAM lParam, PRBool& getWheelInfo, PRBool& result, LRESULT *aRetValue)
-{
-  // Handle both flavors of mouse wheel events.
-  static int iDeltaPerLine, iDeltaPerChar;
-  static ULONG ulScrollLines, ulScrollChars = 1;
-  static int currentVDelta, currentHDelta;
-  static HWND currentWindow = 0;
-
-  PRBool isVertical = msg == WM_MOUSEWHEEL;
-
-  // Get mouse wheel metrics (but only once).
-  if (getWheelInfo) {
-    getWheelInfo = PR_FALSE;
-
-    SystemParametersInfo (SPI_GETWHEELSCROLLLINES, 0, &ulScrollLines, 0);
-
-    // ulScrollLines usually equals 3 or 0 (for no scrolling)
-    // WHEEL_DELTA equals 120, so iDeltaPerLine will be 40.
-
-    // However, if ulScrollLines > WHEEL_DELTA, we assume that
-    // the mouse driver wants a page scroll.  The docs state that
-    // ulScrollLines should explicitly equal WHEEL_PAGESCROLL, but
-    // since some mouse drivers use an arbitrary large number instead,
-    // we have to handle that as well.
-
-    iDeltaPerLine = 0;
-    if (ulScrollLines) {
-      if (ulScrollLines <= WHEEL_DELTA) {
-        iDeltaPerLine = WHEEL_DELTA / ulScrollLines;
-      } else {
-        ulScrollLines = WHEEL_PAGESCROLL;
-      }
-    }
-
-    if (!SystemParametersInfo(SPI_GETWHEELSCROLLCHARS, 0,
-                              &ulScrollChars, 0)) {
-      // Note that we may always fail to get the value before Win Vista.
-      ulScrollChars = 1;
-    }
-
-    iDeltaPerChar = 0;
-    if (ulScrollChars) {
-      if (ulScrollChars <= WHEEL_DELTA) {
-        iDeltaPerChar = WHEEL_DELTA / ulScrollChars;
-      } else {
-        ulScrollChars = WHEEL_PAGESCROLL;
-      }
-    }
-  }
-
-  if ((isVertical  && ulScrollLines != WHEEL_PAGESCROLL && !iDeltaPerLine) ||
-      (!isVertical && ulScrollChars != WHEEL_PAGESCROLL && !iDeltaPerChar))
-    return PR_FALSE; // break
+PRBool
+nsWindow::OnMouseWheel(UINT aMessage, WPARAM aWParam, LPARAM aLParam,
+                       PRBool& aHandled, LRESULT *aRetValue)
+{
+  InitMouseWheelScrollData();
+
+  PRBool isVertical = (aMessage == WM_MOUSEWHEEL);
+  if ((isVertical && sMouseWheelScrollLines == 0) ||
+      (!isVertical && sMouseWheelScrollChars == 0)) {
+    // XXX I think that we should dispatch mouse wheel events even if the
+    // operation will not scroll because the wheel operation really happened
+    // and web application may want to handle the event for non-scroll action.
+    ResetRemainingWheelDelta();
+    *aRetValue = isVertical ? TRUE : FALSE; // means we don't process it
+    aHandled = PR_FALSE;
+    return PR_FALSE;
+  }
 
   // The mousewheel event will be dispatched to the toplevel
   // window.  We need to give it to the child window.
   PRBool quit;
-  if (!HandleScrollingPlugins(msg, wParam, lParam, result, aRetValue, quit))
+  if (!HandleScrollingPlugins(aMessage, aWParam, aLParam,
+                              aHandled, aRetValue, quit)) {
+    ResetRemainingWheelDelta();
     return quit; // return immediately if it's not our window
-
-  // We should cancel the surplus delta if the current window is not
-  // same as previous.
-  if (currentWindow != mWnd) {
-    currentVDelta = 0;
-    currentHDelta = 0;
-    currentWindow = mWnd;
-  }
-
-  nsMouseScrollEvent scrollEvent(PR_TRUE, NS_MOUSE_SCROLL, this);
-  scrollEvent.delta = 0;
-  if (isVertical) {
-    scrollEvent.scrollFlags = nsMouseScrollEvent::kIsVertical;
-    if (ulScrollLines == WHEEL_PAGESCROLL) {
-      scrollEvent.scrollFlags |= nsMouseScrollEvent::kIsFullPage;
-      scrollEvent.delta = (((short) HIWORD (wParam)) > 0) ? -1 : 1;
-    } else {
-      currentVDelta -= (short) HIWORD (wParam);
-      if (PR_ABS(currentVDelta) >= iDeltaPerLine) {
-        scrollEvent.delta = currentVDelta / iDeltaPerLine;
-        currentVDelta %= iDeltaPerLine;
-      }
-    }
-  } else {
-    scrollEvent.scrollFlags = nsMouseScrollEvent::kIsHorizontal;
-    if (ulScrollChars == WHEEL_PAGESCROLL) {
-      scrollEvent.scrollFlags |= nsMouseScrollEvent::kIsFullPage;
-      scrollEvent.delta = (((short) HIWORD (wParam)) > 0) ? 1 : -1;
-    } else {
-      currentHDelta += (short) HIWORD (wParam);
-      if (PR_ABS(currentHDelta) >= iDeltaPerChar) {
-        scrollEvent.delta = currentHDelta / iDeltaPerChar;
-        currentHDelta %= iDeltaPerChar;
-      }
-    }
-  }
-
-  if (!scrollEvent.delta) {
-    // We store the wheel delta, and it will be used next wheel message, so,
-    // we consume this message actually.  We shouldn't call next wndproc.
-    result = PR_TRUE;
-    return PR_FALSE; // break
+ }
+
+  PRInt32 nativeDelta = (short)HIWORD(aWParam);
+  if (!nativeDelta) {
+    *aRetValue = isVertical ? TRUE : FALSE; // means we don't process it
+    aHandled = PR_FALSE;
+    ResetRemainingWheelDelta();
+    return PR_FALSE; // We cannot process this message
   }
 
   // The event may go to a plug-in which already dispatched this message.
   // Then, the event can cause deadlock.  We should unlock the sender here.
   ::ReplyMessage(isVertical ? 0 : TRUE);
 
+  PRBool isPageScroll =
+    ((isVertical && sMouseWheelScrollLines == WHEEL_PAGESCROLL) ||
+     (!isVertical && sMouseWheelScrollChars == WHEEL_PAGESCROLL));
+
+  // Discard the remaining delta if current wheel message and last one are
+  // received by different window or to scroll different direction or
+  // different unit scroll.  Furthermore, if the last event was too old.
+  PRUint32 now = PR_IntervalToMilliseconds(PR_IntervalNow());
+  if (sLastMouseWheelWnd &&
+      (sLastMouseWheelWnd != mWnd ||
+       sLastMouseWheelDeltaIsPositive != (nativeDelta > 0) ||
+       sLastMouseWheelOrientationIsVertical != isVertical ||
+       sLastMouseWheelUnitIsPage != isPageScroll ||
+       now - sLastMouseWheelTime > 1500)) {
+    ResetRemainingWheelDelta();
+  }
+  sLastMouseWheelWnd = mWnd;
+  sLastMouseWheelDeltaIsPositive = (nativeDelta > 0);
+  sLastMouseWheelOrientationIsVertical = isVertical;
+  sLastMouseWheelUnitIsPage = isPageScroll;
+  sLastMouseWheelTime = now;
+
+  nsMouseScrollEvent testEvent(PR_TRUE, NS_MOUSE_SCROLL, this);
+  InitEvent(testEvent);
+  testEvent.scrollFlags = isPageScroll ? nsMouseScrollEvent::kIsFullPage : 0;
+  testEvent.scrollFlags |= isVertical ? nsMouseScrollEvent::kIsVertical :
+                                        nsMouseScrollEvent::kIsHorizontal;
+  testEvent.delta = sLastMouseWheelDeltaIsPositive ? -1 : 1;
+  nsQueryContentEvent queryEvent(PR_TRUE, NS_QUERY_SCROLL_TARGET_INFO, this);
+  InitEvent(queryEvent);
+  queryEvent.InitForQueryScrollTargetInfo(&testEvent);
+  DispatchWindowEvent(&queryEvent);
+  // If the necessary interger isn't larger than 0, we should assume that
+  // the event failed for us.
+  if (queryEvent.mSucceeded) {
+    if (isPageScroll) {
+      if (isVertical) {
+        queryEvent.mSucceeded = (queryEvent.mReply.mPageHeight > 0);
+      } else {
+        queryEvent.mSucceeded = (queryEvent.mReply.mPageWidth > 0);
+      }
+    } else {
+      queryEvent.mSucceeded = (queryEvent.mReply.mLineHeight > 0);
+    }
+  }
+
+  *aRetValue = isVertical ? FALSE : TRUE; // means we process this message
+  nsModifierKeyState modKeyState;
+
+  // Our positive delta value means to bottom or right.
+  // But positive nativeDelta value means to top or right.
+  // Use orienter for computing our delta value.
+  PRInt32 orienter = isVertical ? -1 : 1;
+
   // Assume the Control key is down if the Elantech touchpad has sent the
   // mis-ordered WM_KEYDOWN/WM_MOUSEWHEEL messages.  (See the comment in
   // OnKeyUp.)
   PRBool isControl;
   if (mAssumeWheelIsZoomUntil &&
       static_cast<DWORD>(::GetMessageTime()) < mAssumeWheelIsZoomUntil) {
     isControl = PR_TRUE;
   } else {
-    isControl = IS_VK_DOWN(NS_VK_CONTROL);
-  }
-
-  scrollEvent.isShift   = IS_VK_DOWN(NS_VK_SHIFT);
-  scrollEvent.isControl = isControl;
-  scrollEvent.isMeta    = PR_FALSE;
-  scrollEvent.isAlt     = IS_VK_DOWN(NS_VK_ALT);
+    isControl = modKeyState.mIsControlDown;
+  }
+
+  nsMouseScrollEvent scrollEvent(PR_TRUE, NS_MOUSE_SCROLL, this);
   InitEvent(scrollEvent);
-  if (nsnull != mEventCallback) {
-    result = DispatchWindowEvent(&scrollEvent);
-  }
-  // Note that we should return zero if we process WM_MOUSEWHEEL.
-  // But if we process WM_MOUSEHWHEEL, we should return non-zero.
-
-  if (result)
-    *aRetValue = isVertical ? 0 : TRUE;
-  
-  return PR_FALSE; // break;
-} 
+  // If the query event failed, we cannot send pixel events.
+  scrollEvent.scrollFlags =
+    queryEvent.mSucceeded ? nsMouseScrollEvent::kHasPixels : 0;
+  scrollEvent.isShift     = modKeyState.mIsShiftDown;
+  scrollEvent.isControl   = isControl;
+  scrollEvent.isMeta      = PR_FALSE;
+  scrollEvent.isAlt       = modKeyState.mIsAltDown;
+
+  PRInt32 nativeDeltaForScroll = nativeDelta + sRemainingDeltaForScroll;
+
+  if (isPageScroll) {
+    scrollEvent.scrollFlags |= nsMouseScrollEvent::kIsFullPage;
+    if (isVertical) {
+      scrollEvent.scrollFlags |= nsMouseScrollEvent::kIsVertical;
+    } else {
+      scrollEvent.scrollFlags |= nsMouseScrollEvent::kIsHorizontal;
+    }
+    scrollEvent.delta = nativeDeltaForScroll * orienter / WHEEL_DELTA;
+    PRInt32 recomputedNativeDelta = scrollEvent.delta * orienter / WHEEL_DELTA;
+    sRemainingDeltaForScroll = nativeDeltaForScroll - recomputedNativeDelta;
+  } else {
+    double deltaPerUnit;
+    if (isVertical) {
+      scrollEvent.scrollFlags |= nsMouseScrollEvent::kIsVertical;
+      deltaPerUnit = (double)WHEEL_DELTA / sMouseWheelScrollLines;
+    } else {
+      scrollEvent.scrollFlags |= nsMouseScrollEvent::kIsHorizontal;
+      deltaPerUnit = (double)WHEEL_DELTA / sMouseWheelScrollChars;
+    }
+    scrollEvent.delta =
+      RoundDelta((double)nativeDeltaForScroll * orienter / deltaPerUnit);
+    PRInt32 recomputedNativeDelta =
+      (PRInt32)(scrollEvent.delta * orienter * deltaPerUnit);
+    sRemainingDeltaForScroll = nativeDeltaForScroll - recomputedNativeDelta;
+  }
+
+  if (scrollEvent.delta) {
+    aHandled = DispatchWindowEvent(&scrollEvent);
+    if (mOnDestroyCalled) {
+      ResetRemainingWheelDelta();
+      return PR_FALSE;
+    }
+  }
+
+  // If the query event failed, we cannot send pixel events.
+  if (!queryEvent.mSucceeded) {
+    sRemainingDeltaForPixel = 0;
+    return PR_FALSE;
+  }
+
+  nsMouseScrollEvent pixelEvent(PR_TRUE, NS_MOUSE_PIXEL_SCROLL, this);
+  InitEvent(pixelEvent);
+  pixelEvent.scrollFlags = nsMouseScrollEvent::kAllowSmoothScroll |
+    (scrollEvent.scrollFlags & ~nsMouseScrollEvent::kHasPixels);
+  pixelEvent.isShift     = modKeyState.mIsShiftDown;
+  pixelEvent.isControl   = modKeyState.mIsControlDown;
+  pixelEvent.isMeta      = PR_FALSE;
+  pixelEvent.isAlt       = modKeyState.mIsAltDown;
+
+  PRInt32 nativeDeltaForPixel = nativeDelta + sRemainingDeltaForPixel;
+
+  double deltaPerPixel;
+  if (isPageScroll) {
+    if (isVertical) {
+      deltaPerPixel = (double)WHEEL_DELTA / queryEvent.mReply.mPageHeight;
+    } else {
+      deltaPerPixel = (double)WHEEL_DELTA / queryEvent.mReply.mPageWidth;
+    }
+  } else {
+    if (isVertical) {
+      deltaPerPixel = (double)WHEEL_DELTA / sMouseWheelScrollLines;
+    } else {
+      deltaPerPixel = (double)WHEEL_DELTA / sMouseWheelScrollChars;
+    }
+    deltaPerPixel /= queryEvent.mReply.mLineHeight;
+  }
+  pixelEvent.delta =
+    RoundDelta((double)nativeDeltaForPixel * orienter / deltaPerPixel);
+  PRInt32 recomputedNativeDelta =
+    (PRInt32)(pixelEvent.delta * orienter * deltaPerPixel);
+  sRemainingDeltaForPixel = nativeDeltaForPixel - recomputedNativeDelta;
+  if (pixelEvent.delta != 0) {
+    aHandled = DispatchWindowEvent(&pixelEvent);
+  }
+  return PR_FALSE;
+}
 
 static PRBool
 StringCaseInsensitiveEquals(const PRUnichar* aChars1, const PRUint32 aNumChars1,
                             const PRUnichar* aChars2, const PRUint32 aNumChars2)
 {
   if (aNumChars1 != aNumChars2)
     return PR_FALSE;
 
--- a/widget/src/windows/nsWindow.h
+++ b/widget/src/windows/nsWindow.h
@@ -406,18 +406,18 @@ protected:
   PRBool                  OnGesture(WPARAM wParam, LPARAM lParam);
 #if MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_WIN7
   PRBool                  OnTouch(WPARAM wParam, LPARAM lParam);
 #endif
   PRBool                  OnHotKey(WPARAM wParam, LPARAM lParam);
   BOOL                    OnInputLangChange(HKL aHKL);
   PRBool                  OnPaint(HDC aDC, PRUint32 aNestingLevel);
   void                    OnWindowPosChanged(WINDOWPOS *wp, PRBool& aResult);
-  PRBool                  OnMouseWheel(UINT msg, WPARAM wParam, LPARAM lParam, 
-                                       PRBool& result, PRBool& getWheelInfo,
+  PRBool                  OnMouseWheel(UINT aMessage, WPARAM aWParam,
+                                       LPARAM aLParam, PRBool& aHandled,
                                        LRESULT *aRetValue);
   void                    OnWindowPosChanging(LPWINDOWPOS& info);
 
   /**
    * Function that registers when the user has been active (used for detecting
    * when the user is idle).
    */
   void                    UserActivity();
@@ -611,16 +611,30 @@ protected:
   static HINSTANCE      sAccLib;
   static LPFNLRESULTFROMOBJECT sLresultFromObject;
 #endif // ACCESSIBILITY
 
   // sRedirectedKeyDown is WM_KEYDOWN message or WM_SYSKEYDOWN message which
   // was reirected to SendInput() API by OnKeyDown().
   static MSG            sRedirectedKeyDown;
 
+  static PRBool sNeedsToInitMouseWheelSettings;
+  static ULONG sMouseWheelScrollLines;
+  static ULONG sMouseWheelScrollChars;
+  static void InitMouseWheelScrollData();
+
+  static HWND sLastMouseWheelWnd;
+  static PRInt32 sRemainingDeltaForScroll;
+  static PRInt32 sRemainingDeltaForPixel;
+  static PRBool sLastMouseWheelDeltaIsPositive;
+  static PRBool sLastMouseWheelOrientationIsVertical;
+  static PRBool sLastMouseWheelUnitIsPage;
+  static PRUint32 sLastMouseWheelTime; // in milliseconds
+  static void ResetRemainingWheelDelta();
+
   // If a window receives WM_KEYDOWN message or WM_SYSKEYDOWM message which is
   // redirected message, OnKeyDowm() prevents to dispatch NS_KEY_DOWN event
   // because it has been dispatched before the message was redirected.
   // However, in some cases, ProcessKeyDownMessage() doesn't call OnKeyDown().
   // Then, ProcessKeyDownMessage() needs to forget the redirected message and
   // remove WM_CHAR message or WM_SYSCHAR message for the redirected keydown
   // message.  AutoForgetRedirectedKeyDownMessage struct is a helper struct
   // for doing that.  This must be created in stack.
@@ -644,17 +658,16 @@ protected:
       // Foreget the redirected message
       nsWindow::ForgetRedirectedKeyDownMessage();
     }
 
     PRBool mCancel;
     nsRefPtr<nsWindow> mWindow;
     const MSG &mMsg;
   };
-
 };
 
 /**
  * A child window is a window with different style.
  */
 class ChildWindow : public nsWindow {
 
 public:
--- a/widget/src/windows/nsWindowDefs.h
+++ b/widget/src/windows/nsWindowDefs.h
@@ -85,16 +85,20 @@
 #ifndef WM_MOUSELEAVE
 #define WM_MOUSELEAVE                     0x02A3
 #endif
 
 #ifndef SPI_GETWHEELSCROLLCHARS
 #define SPI_GETWHEELSCROLLCHARS           0x006C
 #endif
 
+#ifndef SPI_SETWHEELSCROLLCHARS
+#define SPI_SETWHEELSCROLLCHARS           0x006D
+#endif
+
 #ifndef MAPVK_VSC_TO_VK
 #define MAPVK_VK_TO_VSC                   0
 #define MAPVK_VSC_TO_VK                   1
 #define MAPVK_VK_TO_CHAR                  2
 #endif
 
 // ConstrainPosition window positioning slop value
 #define kWindowPositionSlop               20