Bug 878705 - Don't scroll when clicking inside the scrollbar track next to the scrollbar thumb. r=roc
authorMarkus Stange <mstange@themasta.com>
Tue, 11 Jun 2013 14:05:28 +0200
changeset 134634 1ac9367a6c23dcac1c14469eb18b79badf2017d1
parent 134633 d8425335f52f560ae2858f8c76e389084d06efe8
child 134635 8fe099852dc472aad700f6c3c1159b08fcecc151
push id24808
push userryanvm@gmail.com
push dateTue, 11 Jun 2013 19:01:06 +0000
treeherdermozilla-central@0acda90a6f6a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs878705
milestone24.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 878705 - Don't scroll when clicking inside the scrollbar track next to the scrollbar thumb. r=roc
layout/xul/base/src/nsSliderFrame.cpp
layout/xul/base/src/nsSliderFrame.h
--- a/layout/xul/base/src/nsSliderFrame.cpp
+++ b/layout/xul/base/src/nsSliderFrame.cpp
@@ -488,47 +488,32 @@ nsSliderFrame::HandleEvent(nsPresContext
 
       // set it
       SetCurrentThumbPosition(scrollbar, pos, false, true); // with snapping
     }
     break;
 
     case NS_TOUCH_END:
     case NS_MOUSE_BUTTON_UP:
-      if (aEvent->message == NS_TOUCH_END ||
-          static_cast<nsMouseEvent*>(aEvent)->button == nsMouseEvent::eLeftButton ||
-          (static_cast<nsMouseEvent*>(aEvent)->button == nsMouseEvent::eMiddleButton &&
-           gMiddlePref)) {
+      if (ShouldScrollForEvent(aEvent)) {
         // stop capturing
         AddListener();
         DragThumb(false);
         if (mChange) {
           StopRepeat();
           mChange = 0;
         }
         //we MUST call nsFrame HandleEvent for mouse ups to maintain the selection state and capture state.
         return nsFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
       }
     }
 
     //return nsFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
     return NS_OK;
-  } else if ((aEvent->message == NS_MOUSE_BUTTON_DOWN &&
-              static_cast<nsMouseEvent*>(aEvent)->button ==
-                nsMouseEvent::eLeftButton &&
-#ifdef XP_MACOSX
-              // On Mac the option key inverts the scroll-to-here preference.
-              (static_cast<nsMouseEvent*>(aEvent)->IsAlt() != GetScrollToClick())) ||
-#else
-              (static_cast<nsMouseEvent*>(aEvent)->IsShift() != GetScrollToClick())) ||
-#endif
-             (gMiddlePref && aEvent->message == NS_MOUSE_BUTTON_DOWN &&
-              static_cast<nsMouseEvent*>(aEvent)->button ==
-                nsMouseEvent::eMiddleButton) ||
-             (aEvent->message == NS_TOUCH_START && GetScrollToClick())) {
+  } else if (ShouldScrollToClickForEvent(aEvent)) {
     nsPoint eventPoint;
     if (!GetEventPoint(aEvent, eventPoint)) {
       return NS_OK;
     }
     nscoord pos = isHorizontal ? eventPoint.x : eventPoint.y;
 
     // adjust so that the middle of the thumb is placed under the click
     nsIFrame* thumbFrame = mFrames.FirstChild();
@@ -568,36 +553,32 @@ nsSliderFrame::HandleEvent(nsPresContext
 }
 
 // Helper function to collect the "scroll to click" metric. Beware of
 // caching this, users expect to be able to change the system preference
 // and see the browser change its behavior immediately.
 bool
 nsSliderFrame::GetScrollToClick()
 {
-  // if there is no parent scrollbar, check the movetoclick attribute. If set
-  // to true, always scroll to the click point. If false, never do this.
-  // Otherwise, the default is true on Mac and false on other platforms.
-  if (GetScrollbar() == this)
+  if (GetScrollbar() != this) {
+    return LookAndFeel::GetInt(LookAndFeel::eIntID_ScrollToClick, false);
+  }
+
+  if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::movetoclick,
+                            nsGkAtoms::_true, eCaseMatters)) {
+    return true;
+  }
+  if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::movetoclick,
+                            nsGkAtoms::_false, eCaseMatters)) {
+    return false;
+  }
+
 #ifdef XP_MACOSX
-    return !mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::movetoclick,
-                                   nsGkAtoms::_false, eCaseMatters);
- 
-  // if there was no scrollbar, always scroll on click
-  bool scrollToClick = false;
-  int32_t scrollToClickMetric;
-  nsresult rv = LookAndFeel::GetInt(LookAndFeel::eIntID_ScrollToClick,
-                                    &scrollToClickMetric);
-  if (NS_SUCCEEDED(rv) && scrollToClickMetric == 1)
-    scrollToClick = true;
-  return scrollToClick;
-
+  return true;
 #else
-    return mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::movetoclick,
-                                  nsGkAtoms::_true, eCaseMatters);
   return false;
 #endif
 }
 
 nsIFrame*
 nsSliderFrame::GetScrollbar()
 {
   // if we are in a scrollbar then return the scrollbar's content node
@@ -837,47 +818,34 @@ nsSliderFrame::StartDrag(nsIDOMEvent* aE
 {
 #ifdef DEBUG_SLIDER
   printf("Begin dragging\n");
 #endif
   if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled,
                             nsGkAtoms::_true, eCaseMatters))
     return NS_OK;
 
-  bool isHorizontal = IsHorizontal();
-  bool scrollToClick = false;
+  nsGUIEvent *event = static_cast<nsGUIEvent*>(aEvent->GetInternalNSEvent());
 
-  nsCOMPtr<nsIDOMMouseEvent> mouseEvent(do_QueryInterface(aEvent));
-  if (mouseEvent) {
-    uint16_t button = 0;
-    mouseEvent->GetButton(&button);
-    if (!(button == 0 || (button == 1 && gMiddlePref)))
-      return NS_OK;
-  
-#ifndef XP_MACOSX
-    // On Mac there's no scroll-to-here when clicking the thumb
-    mouseEvent->GetShiftKey(&scrollToClick);
-    if (button != 0) {
-      scrollToClick = true;
-    }
-#endif
+  if (!ShouldScrollForEvent(event)) {
+    return NS_OK;
   }
 
-  nsGUIEvent *event = static_cast<nsGUIEvent*>(aEvent->GetInternalNSEvent());
-
   nsPoint pt;
   if (!GetEventPoint(event, pt)) {
     return NS_OK;
   }
+  bool isHorizontal = IsHorizontal();
   nscoord pos = isHorizontal ? pt.x : pt.y;
 
-  // If shift click or middle button, first
-  // place the middle of the slider thumb under the click
+  // If we should scroll-to-click, first place the middle of the slider thumb
+  // under the mouse.
   nsCOMPtr<nsIContent> scrollbar;
   nscoord newpos = pos;
+  bool scrollToClick = ShouldScrollToClickForEvent(event);
   if (scrollToClick) {
     // adjust so that the middle of the thumb is placed under the click
     nsIFrame* thumbFrame = mFrames.FirstChild();
     if (!thumbFrame) {
       return NS_OK;
     }
     nsSize thumbSize = thumbFrame->GetSize();
     nscoord thumbLength = isHorizontal ? thumbSize.width : thumbSize.height;
@@ -964,34 +932,102 @@ nsSliderFrame::RemoveListener()
   nsIFrame* thumbFrame = mFrames.FirstChild();
   if (!thumbFrame)
     return;
 
   thumbFrame->GetContent()->
     RemoveSystemEventListener(NS_LITERAL_STRING("mousedown"), mMediator, false);
 }
 
+bool
+nsSliderFrame::ShouldScrollForEvent(nsGUIEvent* aEvent)
+{
+  switch (aEvent->message) {
+    case NS_TOUCH_START:
+    case NS_TOUCH_END:
+      return true;
+    case NS_MOUSE_BUTTON_DOWN:
+    case NS_MOUSE_BUTTON_UP: {
+      uint16_t button = static_cast<nsMouseEvent*>(aEvent)->button;
+      return (button == nsMouseEvent::eLeftButton) ||
+             (button == nsMouseEvent::eMiddleButton && gMiddlePref);
+    }
+    default:
+      return false;
+  }
+}
+
+bool
+nsSliderFrame::ShouldScrollToClickForEvent(nsGUIEvent* aEvent)
+{
+  if (!ShouldScrollForEvent(aEvent)) {
+    return false;
+  }
+
+  if (aEvent->message == NS_TOUCH_START) {
+    return GetScrollToClick();
+  }
+
+  if (aEvent->message != NS_MOUSE_BUTTON_DOWN) {
+    return false;
+  }
+
+#ifdef XP_MACOSX
+  // On Mac, clicking the scrollbar thumb should never scroll to click.
+  if (IsEventOverThumb(aEvent)) {
+    return false;
+  }
+#endif
+
+  nsMouseEvent* mouseEvent = static_cast<nsMouseEvent*>(aEvent);
+  if (mouseEvent->button == nsMouseEvent::eLeftButton) {
+#ifdef XP_MACOSX
+    bool invertPref = mouseEvent->IsAlt();
+#else
+    bool invertPref = mouseEvent->IsShift();
+#endif
+    return GetScrollToClick() != invertPref;
+  }
+
+  return true;
+}
+
+bool
+nsSliderFrame::IsEventOverThumb(nsGUIEvent* aEvent)
+{
+  nsIFrame* thumbFrame = mFrames.FirstChild();
+  if (!thumbFrame) {
+    return false;
+  }
+
+  nsPoint eventPoint;
+  if (!GetEventPoint(aEvent, eventPoint)) {
+    return false;
+  }
+
+  bool isHorizontal = IsHorizontal();
+  nsRect thumbRect = thumbFrame->GetRect();
+  nscoord eventPos = isHorizontal ? eventPoint.x : eventPoint.y;
+  nscoord thumbStart = isHorizontal ? thumbRect.x : thumbRect.y;
+  nscoord thumbEnd = isHorizontal ? thumbRect.XMost() : thumbRect.YMost();
+
+  return eventPos >= thumbStart && eventPos < thumbEnd;
+}
+
 NS_IMETHODIMP
 nsSliderFrame::HandlePress(nsPresContext* aPresContext,
                            nsGUIEvent*     aEvent,
                            nsEventStatus*  aEventStatus)
 {
-  if (aEvent->message == NS_TOUCH_START && GetScrollToClick()) {
+  if (!ShouldScrollForEvent(aEvent) || ShouldScrollToClickForEvent(aEvent)) {
     return NS_OK;
   }
 
-  if (aEvent->message == NS_MOUSE_BUTTON_DOWN) {
-#ifdef XP_MACOSX
-    // On Mac the option key inverts the scroll-to-here preference.
-    if (((nsMouseEvent *)aEvent)->IsAlt() != GetScrollToClick()) {
-#else
-    if (((nsMouseEvent *)aEvent)->IsShift() != GetScrollToClick()) {
-#endif
-      return NS_OK;
-    }
+  if (IsEventOverThumb(aEvent)) {
+    return NS_OK;
   }
 
   nsIFrame* thumbFrame = mFrames.FirstChild();
   if (!thumbFrame) // display:none?
     return NS_OK;
 
   if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled,
                             nsGkAtoms::_true, eCaseMatters))
--- a/layout/xul/base/src/nsSliderFrame.h
+++ b/layout/xul/base/src/nsSliderFrame.h
@@ -122,16 +122,19 @@ public:
   NS_IMETHOD HandleRelease(nsPresContext* aPresContext,
                            nsGUIEvent *    aEvent,
                            nsEventStatus*  aEventStatus) MOZ_OVERRIDE;
 
 private:
 
   bool GetScrollToClick();
   nsIFrame* GetScrollbar();
+  bool ShouldScrollForEvent(nsGUIEvent* aEvent);
+  bool ShouldScrollToClickForEvent(nsGUIEvent* aEvent);
+  bool IsEventOverThumb(nsGUIEvent* aEvent);
 
   void PageUpDown(nscoord change);
   void SetCurrentThumbPosition(nsIContent* aScrollbar, nscoord aNewPos, bool aIsSmooth,
                                bool aMaySnap);
   void SetCurrentPosition(nsIContent* aScrollbar, int32_t aNewPos, bool aIsSmooth);
   void SetCurrentPositionInternal(nsIContent* aScrollbar, int32_t pos,
                                   bool aIsSmooth);
   void CurrentPositionChanged();