Bug 372047, support reverse direction scales, r=neil,josh
authorenndeakin@sympatico.ca
Mon, 09 Apr 2007 15:39:57 -0700
changeset 421 1b48057b36a3c183844ce9d701e77007bdc77cc2
parent 420 46555099a42b41f3b1a1926ac8a7b4110b815ffe
child 422 e9135b32f46e713ce5a22ed22b7db7f2661bb26c
push idunknown
push userunknown
push dateunknown
reviewersneil, josh
bugs372047
milestone1.9a4pre
Bug 372047, support reverse direction scales, r=neil,josh
layout/xul/base/src/nsSliderFrame.cpp
layout/xul/base/src/nsSliderFrame.h
toolkit/content/widgets/scale.xml
widget/src/cocoa/nsNativeThemeCocoa.h
widget/src/cocoa/nsNativeThemeCocoa.mm
xpfe/global/jar.mn
--- a/layout/xul/base/src/nsSliderFrame.cpp
+++ b/layout/xul/base/src/nsSliderFrame.cpp
@@ -393,31 +393,33 @@ nsSliderFrame::DoLayout(nsBoxLayoutState
         thumbcoord = thumbsize;
     }
   }
 
   ourmaxpos -= thumbcoord;
   if (float(maxpos) != 0)
     mRatio = float(ourmaxpos) / float(maxpos);
 
-  nscoord curpos = (curpospx - minpospx) * onePixel;
+  // in reverse mode, curpos is reversed such that lower values are to the
+  // right or bottom and increase leftwards or upwards. In this case, use the
+  // offset from the end instead of the beginning.
+  PRBool reverse = mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::dir,
+                                         nsGkAtoms::reverse, eCaseMatters);
+  nscoord pos = reverse ? (maxpospx - curpospx) : (curpospx - minpospx);
 
-  // set the thumbs y coord to be the current pos * the ratio.
-  nscoord pos = nscoord(float(curpos)*mRatio);
+  // set the thumb's coord to be the current pos * the ratio.
   nsRect thumbRect(clientRect.x, clientRect.y, thumbSize.width, thumbSize.height);
-
   if (isHorizontal)
-    thumbRect.x += pos;
+    thumbRect.x += nscoord(float(pos * onePixel) * mRatio);
   else
-    thumbRect.y += pos;
+    thumbRect.y += nscoord(float(pos * onePixel) * mRatio);
 
   nsRect oldThumbRect(thumbBox->GetRect());
   LayoutChildAt(aState, thumbBox, thumbRect);
 
-
   SyncLayout(aState);
 
 #ifdef DEBUG_SLIDER
   PRInt32 c = GetCurrentPosition(scrollbar);
   PRInt32 min = GetMinPosition(scrollbar);
   PRInt32 max = GetMaxPosition(scrollbar);
   printf("Current=%d, min=%d, max=%d\n", c, min, max);
 #endif
@@ -489,38 +491,36 @@ nsSliderFrame::HandleEvent(nsPresContext
                eventPoint.x > thumbSize.width +
                                 gSnapMultiplier * thumbSize.width)
              isMouseOutsideThumb = PR_TRUE;
          }
        }
        if (isMouseOutsideThumb)
        {
          // XXX see bug 81586
-         SetCurrentPosition(scrollbar, thumbFrame, (int) (mThumbStart / onePixel / mRatio), PR_FALSE);
+         SetCurrentPosition(scrollbar, (int) (mThumbStart / onePixel / mRatio), PR_FALSE);
          return NS_OK;
        }
 
-
        // convert to pixels
        nscoord pospx = pos/onePixel;
 
        // convert to our internal coordinate system
        pospx = nscoord(pospx/mRatio);
 
        // if snap="true", then the slider may only be set to min + (increment * x).
        // Otherwise, the slider may be set to any positive integer.
        if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::snap,
                                  nsGkAtoms::_true, eCaseMatters)) {
          PRInt32 increment = GetIncrement(scrollbar);
          pospx = NSToCoordRound(pospx / (float)increment) * increment;
        }
 
        // set it
-       SetCurrentPosition(scrollbar, thumbFrame, pospx, PR_FALSE);
-
+       SetCurrentPosition(scrollbar, pospx, PR_FALSE);
     }
     break;
 
     case NS_MOUSE_BUTTON_UP:
       if (NS_STATIC_CAST(nsMouseEvent*, aEvent)->button == nsMouseEvent::eLeftButton ||
           (NS_STATIC_CAST(nsMouseEvent*, aEvent)->button == nsMouseEvent::eMiddleButton &&
            gMiddlePref)) {
         // stop capturing
@@ -561,22 +561,21 @@ nsSliderFrame::HandleEvent(nsPresContext
     if (!thumbFrame) {
       return NS_OK;
     }
     nsSize thumbSize = thumbFrame->GetSize();
     nscoord thumbLength = isHorizontal ? thumbSize.width : thumbSize.height;
     thumbLength /= onePixel;
     pospx -= (thumbLength/2);
 
-
     // convert to our internal coordinate system
     pospx = nscoord(pospx/mRatio);
 
     // set it
-    SetCurrentPosition(scrollbar, thumbFrame, pospx, PR_FALSE);
+    SetCurrentPosition(scrollbar, pospx, PR_FALSE);
 
     DragThumb(PR_TRUE);
 
     if (isHorizontal)
       mThumbStart = thumbFrame->GetPosition().x;
     else
       mThumbStart = thumbFrame->GetPosition().y;
 
@@ -605,133 +604,160 @@ nsSliderFrame::GetScrollbar()
 
    if (scrollbar == nsnull)
        return this;
 
    return scrollbar->IsBoxFrame() ? scrollbar : this;
 }
 
 void
-nsSliderFrame::PageUpDown(nsIFrame* aThumbFrame, nscoord change)
+nsSliderFrame::PageUpDown(nscoord change)
 {
   // on a page up or down get our page increment. We get this by getting the scrollbar we are in and
   // asking it for the current position and the page increment. If we are not in a scrollbar we will
   // get the values from our own node.
   nsIBox* scrollbarBox = GetScrollbar();
   nsCOMPtr<nsIContent> scrollbar;
   scrollbar = GetContentOfBox(scrollbarBox);
 
+  if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::dir,
+                            nsGkAtoms::reverse, eCaseMatters))
+    change = -change;
+
   if (mScrollbarListener)
     mScrollbarListener->PagedUpDown(); // Let the listener decide our increment.
 
   nscoord pageIncrement = GetPageIncrement(scrollbar);
   PRInt32 curpos = GetCurrentPosition(scrollbar);
   PRInt32 minpos = GetMinPosition(scrollbar);
-  SetCurrentPosition(scrollbar, aThumbFrame, curpos - minpos + change*pageIncrement, PR_TRUE);
+  PRInt32 maxpos = GetMaxPosition(scrollbar);
+
+  // get the new position and make sure it is in bounds
+  PRInt32 newpos = curpos - minpos + change * pageIncrement;
+  if (newpos < minpos || maxpos < minpos)
+    newpos = minpos;
+  else if (newpos > maxpos)
+    newpos = maxpos;
+
+  SetCurrentPositionInternal(scrollbar, newpos, PR_TRUE);
 }
 
 // called when the current position changed and we need to update the thumb's location
 nsresult
 nsSliderFrame::CurrentPositionChanged(nsPresContext* aPresContext)
 {
   nsIBox* scrollbarBox = GetScrollbar();
   nsCOMPtr<nsIContent> scrollbar;
   scrollbar = GetContentOfBox(scrollbarBox);
 
   PRBool isHorizontal = IsHorizontal();
 
-    // get the current position
-    PRInt32 curpos = GetCurrentPosition(scrollbar);
+  // get the current position
+  PRInt32 curpospx = GetCurrentPosition(scrollbar);
 
-    // do nothing if the position did not change
-    if (mCurPos == curpos)
-        return NS_OK;
+  // do nothing if the position did not change
+  if (mCurPos == curpospx)
+      return NS_OK;
 
-    // get our current min and max position from our content node
-    PRInt32 minpos = GetMinPosition(scrollbar);
-    PRInt32 maxpos = GetMaxPosition(scrollbar);
+  // get our current min and max position from our content node
+  PRInt32 minpospx = GetMinPosition(scrollbar);
+  PRInt32 maxpospx = GetMaxPosition(scrollbar);
 
-    if (curpos < minpos || maxpos < minpos)
-      curpos = minpos;
-    else if (curpos > maxpos)
-      curpos = maxpos;
+  if (curpospx < minpospx || maxpospx < minpospx)
+     curpospx = minpospx;
+  else if (curpospx > maxpospx)
+     curpospx = maxpospx;
 
-    // convert to pixels
-    nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
-    nscoord curpospx = (curpos - minpos) * onePixel;
+  // get the thumb's rect
+  nsIFrame* thumbFrame = mFrames.FirstChild();
+  if (!thumbFrame)
+    return NS_OK; // The thumb may stream in asynchronously via XBL.
 
-    // get the thumb's rect
-    nsIFrame* thumbFrame = mFrames.FirstChild();
-    if (!thumbFrame)
-      return NS_OK; // The thumb may stream in asynchronously via XBL.
+  nsRect thumbRect = thumbFrame->GetRect();
 
-    nsRect thumbRect = thumbFrame->GetRect();
+  nsRect clientRect;
+  GetClientRect(clientRect);
+
+  // figure out the new rect
+  nsRect newThumbRect(thumbRect);
 
-    nsRect clientRect;
-    GetClientRect(clientRect);
-
-    // figure out the new rect
-    nsRect newThumbRect(thumbRect);
+  PRBool reverse = mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::dir,
+                                         nsGkAtoms::reverse, eCaseMatters);
+  nscoord pos = reverse ? (maxpospx - curpospx) : (curpospx - minpospx);
 
-    if (isHorizontal)
-       newThumbRect.x = clientRect.x + nscoord(float(curpospx)*mRatio);
-    else
-       newThumbRect.y = clientRect.y + nscoord(float(curpospx)*mRatio);
+  // convert to pixels
+  nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
+  if (isHorizontal)
+     newThumbRect.x = clientRect.x + nscoord(float(pos * onePixel) * mRatio);
+  else
+     newThumbRect.y = clientRect.y + nscoord(float(pos * onePixel) * mRatio);
 
-    // set the rect
-    thumbFrame->SetRect(newThumbRect);
+  // set the rect
+  thumbFrame->SetRect(newThumbRect);
 
-    // Figure out the union of the rect so we know what to redraw.
-    // Combine the old and new thumb overflow areas.
-    nsRect changeRect;
-    changeRect.UnionRect(thumbFrame->GetOverflowRect() + thumbRect.TopLeft(),
-                         thumbFrame->GetOverflowRect() + newThumbRect.TopLeft());
+  // Figure out the union of the rect so we know what to redraw.
+  // Combine the old and new thumb overflow areas.
+  nsRect changeRect;
+  changeRect.UnionRect(thumbFrame->GetOverflowRect() + thumbRect.TopLeft(),
+                       thumbFrame->GetOverflowRect() + newThumbRect.TopLeft());
 
-    // redraw just the change
-    Invalidate(changeRect, mRedrawImmediate);
+  // redraw just the change
+  Invalidate(changeRect, mRedrawImmediate);
     
-    if (mScrollbarListener)
-      mScrollbarListener->PositionChanged(aPresContext, mCurPos, curpos);
+  if (mScrollbarListener)
+    mScrollbarListener->PositionChanged(aPresContext, mCurPos, curpospx);
 
-    mCurPos = curpos;
+  mCurPos = curpospx;
 
-    return NS_OK;
+  return NS_OK;
 }
 
 static void UpdateAttribute(nsIContent* aScrollbar, nscoord aNewPos, PRBool aNotify, PRBool aIsSmooth) {
   nsAutoString str;
   str.AppendInt(aNewPos);
   
   if (aIsSmooth) {
     aScrollbar->SetAttr(kNameSpaceID_None, nsGkAtoms::smooth, NS_LITERAL_STRING("true"), PR_FALSE);
   }
   aScrollbar->SetAttr(kNameSpaceID_None, nsGkAtoms::curpos, str, aNotify);
   if (aIsSmooth) {
     aScrollbar->UnsetAttr(kNameSpaceID_None, nsGkAtoms::smooth, PR_FALSE);
   }
 }
 
 // newpos should be passed to this function as a position as if the minpos is 0.
-// That is, the minpos will be added to the position by this function.
+// That is, the minpos will be added to the position by this function. In a reverse
+// direction slider, the newpos should be the distance from the end.
 void
-nsSliderFrame::SetCurrentPosition(nsIContent* scrollbar, nsIFrame* aThumbFrame, nscoord newpos, PRBool aIsSmooth)
+nsSliderFrame::SetCurrentPosition(nsIContent* scrollbar, nscoord newpos, PRBool aIsSmooth)
 {
-
-   // get our current, min and max position from our content node
+   // get min and max position from our content node
   PRInt32 minpos = GetMinPosition(scrollbar);
   PRInt32 maxpos = GetMaxPosition(scrollbar);
 
-  newpos += minpos;
+  // in reverse direction sliders, flip the value so that it goes from
+  // right to left, or bottom to top.
+  if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::dir,
+                            nsGkAtoms::reverse, eCaseMatters))
+    newpos = maxpos - newpos;
+  else
+    newpos += minpos;
 
   // get the new position and make sure it is in bounds
   if (newpos < minpos || maxpos < minpos)
-      newpos = minpos;
+    newpos = minpos;
   else if (newpos > maxpos)
-      newpos = maxpos;
+    newpos = maxpos;
 
+  SetCurrentPositionInternal(scrollbar, newpos, aIsSmooth);
+}
+
+void
+nsSliderFrame::SetCurrentPositionInternal(nsIContent* scrollbar, nscoord newpos, PRBool aIsSmooth)
+{
   nsIBox* scrollbarBox = GetScrollbar();
   nsIScrollbarFrame* scrollbarFrame;
   CallQueryInterface(scrollbarBox, &scrollbarFrame);
 
   if (scrollbarFrame) {
     // See if we have a mediator.
     nsIScrollbarMediator* mediator = scrollbarFrame->GetScrollbarMediator();
     if (mediator) {
@@ -837,17 +863,17 @@ nsSliderFrame::MouseDown(nsIDOMEvent* aM
     // finally, convert to scrollbar's internal coordinate system
     pospx = nscoord(pospx/mRatio);
 
     nsIBox* scrollbarBox = GetScrollbar();
     nsCOMPtr<nsIContent> scrollbar;
     scrollbar = GetContentOfBox(scrollbarBox);
 
     // set it
-    SetCurrentPosition(scrollbar, thumbFrame, pospx, PR_FALSE);
+    SetCurrentPosition(scrollbar, pospx, PR_FALSE);
   }
 
   DragThumb(PR_TRUE);
 
   nsIFrame* thumbFrame = mFrames.FirstChild();
   if (!thumbFrame) {
     return NS_OK;
   }
@@ -967,17 +993,17 @@ nsSliderFrame::HandlePress(nsPresContext
                                                                     this);
   if (IsHorizontal() ? eventPoint.x < thumbRect.x 
                      : eventPoint.y < thumbRect.y)
     change = -1;
 
   mChange = change;
   DragThumb(PR_TRUE);
   mDestinationPoint = eventPoint;
-  PageUpDown(thumbFrame, change);
+  PageUpDown(change);
   
   nsRepeatService::GetInstance()->Start(mMediator);
   
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSliderFrame::HandleRelease(nsPresContext* aPresContext,
@@ -1085,17 +1111,17 @@ NS_IMETHODIMP_(void) nsSliderFrame::Noti
                 stop = PR_TRUE;
         }
     }
 
 
     if (stop) {
        nsRepeatService::GetInstance()->Stop();
     } else {
-      PageUpDown(thumbFrame, mChange);
+      PageUpDown(mChange);
     }
 }
 
 NS_INTERFACE_MAP_BEGIN(nsSliderMediator)
   NS_INTERFACE_MAP_ENTRY(nsIDOMMouseListener)
   NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITimerCallback)
 NS_INTERFACE_MAP_END
--- a/layout/xul/base/src/nsSliderFrame.h
+++ b/layout/xul/base/src/nsSliderFrame.h
@@ -212,18 +212,19 @@ public:
                            nsEventStatus*  aEventStatus);
 
   NS_IMETHOD_(void) Notify(nsITimer *timer);
  
 private:
 
   nsIBox* GetScrollbar();
 
-  void PageUpDown(nsIFrame* aThumbFrame, nscoord change);
-  void SetCurrentPosition(nsIContent* scrollbar, nsIFrame* aThumbFrame, nscoord pos, PRBool aIsSmooth);
+  void PageUpDown(nscoord change);
+  void SetCurrentPosition(nsIContent* scrollbar, nscoord pos, PRBool aIsSmooth);
+  void SetCurrentPositionInternal(nsIContent* scrollbar, nscoord pos, PRBool aIsSmooth);
   void DragThumb(PRBool aGrabMouseEvents);
   void AddListener();
   void RemoveListener();
   PRBool isDraggingThumb();
 
   float mRatio;
 
   nscoord mDragStart;
--- a/toolkit/content/widgets/scale.xml
+++ b/toolkit/content/widgets/scale.xml
@@ -21,17 +21,17 @@
   <binding id="scale"
            extends="chrome://global/content/bindings/general.xml#basecontrol">
     <resources>
       <stylesheet src="chrome://global/skin/scale.css"/>
     </resources>
 
     <content align="center" pack="center">
       <xul:slider anonid="slider" class="scale-slider" snap="true" flex="1"
-                  xbl:inherits="disabled,orient,curpos=value,minpos=min,maxpos=max,increment,pageincrement">
+                  xbl:inherits="disabled,orient,dir,curpos=value,minpos=min,maxpos=max,increment,pageincrement">
         <xul:thumb class="scale-thumb" xbl:inherits="disabled,orient"/>
       </xul:slider>
     </content>
     
     <implementation>
       <property name="value" onget="return this._getIntegerAttribute('curpos', 0);"
                              onset="return this._setIntegerAttribute('curpos', val);"/>
       <property name="min" onget="return this._getIntegerAttribute('minpos', 0);"
@@ -47,16 +47,28 @@
       <property name="_slider" readonly="true">
         <getter>
           if (!this._sliderElement)
             this._sliderElement = document.getAnonymousElementByAttribute(this, "anonid", "slider");
           return this._sliderElement;
         </getter>
       </property>
 
+      <constructor>
+        <![CDATA[
+          var value = parseInt(this.getAttribute("value"), 10);
+          if (!isNaN(value))
+            this.value = value;
+          else if (this.min > 0)
+            this.value = this.min;
+          else if (this.max < 0)
+            this.value = this.max;
+        ]]>
+      </constructor>
+
       <method name="_getIntegerAttribute">
         <parameter name="aAttr"/>
         <parameter name="aDefaultValue"/>
         <body>
           var value = this._slider.getAttribute(aAttr);
           var intvalue = parseInt(value, 10);
           if (!isNaN(intvalue))
             return intvalue;
@@ -129,50 +141,50 @@
         if (event.originalTarget != this._slider)
           return;
 
         switch (event.attrName) {
           case "curpos":
             this.setAttribute("value", event.newValue);
 
             var changeEvent = document.createEvent("Events");
-            changeEvent.initEvent("change", false, true);
+            changeEvent.initEvent("change", true, true);
             this.dispatchEvent(changeEvent);
             break;
 
           case "minpos":
             this.setAttribute("min", event.newValue);
             break;
 
           case "maxpos":
             this.setAttribute("max", event.newValue);
             break;
         }
       </handler>
 
       <handler event="keypress" keycode="VK_UP" preventdefault="true">
-        this.decrease();
+        return (this.dir == "reverse") ? this.increase() : this.decrease();
       </handler>
       <handler event="keypress" keycode="VK_LEFT" preventdefault="true">
-        this.decrease();
+        return (this.dir == "reverse") ? this.increase() : this.decrease();
       </handler>
       <handler event="keypress" keycode="VK_DOWN" preventdefault="true">
-        this.increase();
+        return (this.dir == "reverse") ? this.decrease() : this.increase();
       </handler>
       <handler event="keypress" keycode="VK_RIGHT" preventdefault="true">
-        this.increase();
+        return (this.dir == "reverse") ? this.decrease() : this.increase();
       </handler>
       <handler event="keypress" keycode="VK_PAGE_UP" preventdefault="true">
-        this.decreasePage();
+        return (this.dir == "reverse") ? this.increasePage() : this.decreasePage();
       </handler>
       <handler event="keypress" keycode="VK_PAGE_DOWN" preventdefault="true">
-        this.increasePage();
+        return (this.dir == "reverse") ? this.decreasePage() : this.increasePage();
       </handler>
       <handler event="keypress" keycode="VK_HOME" preventdefault="true">
-        this.value = this.min;
+        this.value = (this.dir == "reverse") ? this.max : this.min;
       </handler>
       <handler event="keypress" keycode="VK_END" preventdefault="true">
-        this.value = this.max;
+        this.value = (this.dir == "reverse") ? this.min : this.max;
       </handler>
     </handlers>
 
   </binding>
 </bindings>
--- a/widget/src/cocoa/nsNativeThemeCocoa.h
+++ b/widget/src/cocoa/nsNativeThemeCocoa.h
@@ -106,17 +106,18 @@ protected:
                     PRInt32 inValue);
   void DrawTab (CGContextRef context, const HIRect& inBoxRect,
                 PRBool inIsDisabled, PRBool inIsFrontmost, 
                 PRBool inIsHorizontal, PRBool inTabBottom,
                 PRInt32 inState);
   void DrawTabPanel (CGContextRef context, const HIRect& inBoxRect);
   void DrawScale (CGContextRef context, const HIRect& inBoxRect,
                   PRBool inIsDisabled, PRInt32 inState,
-                  PRBool inDirection, PRInt32 inCurrentValue,
+                  PRBool inDirection, PRBool inIsReverse,
+                  PRInt32 inCurrentValue,
                   PRInt32 inMinValue, PRInt32 inMaxValue);
   void DrawButton (CGContextRef context, ThemeButtonKind inKind,
                    const HIRect& inBoxRect, PRBool inIsDefault, 
                    PRBool inDisabled, ThemeButtonValue inValue,
                    ThemeButtonAdornment inAdornment, PRInt32 inState);
   void DrawSpinButtons (CGContextRef context, ThemeButtonKind inKind,
                         const HIRect& inBoxRect,
                         PRBool inDisabled, ThemeDrawState inDrawState,
--- a/widget/src/cocoa/nsNativeThemeCocoa.mm
+++ b/widget/src/cocoa/nsNativeThemeCocoa.mm
@@ -242,30 +242,33 @@ nsNativeThemeCocoa::DrawTabPanel(CGConte
 
   HIThemeDrawTabPane(&inBoxRect, &tpdi, cgContext, HITHEME_ORIENTATION);
 }
 
 
 void
 nsNativeThemeCocoa::DrawScale(CGContextRef cgContext, const HIRect& inBoxRect,
                               PRBool inIsDisabled, PRInt32 inState,
-                              PRBool inIsVertical, PRInt32 inCurrentValue,
+                              PRBool inIsVertical, PRBool inIsReverse,
+                              PRInt32 inCurrentValue,
                               PRInt32 inMinValue, PRInt32 inMaxValue)
 {
   HIThemeTrackDrawInfo tdi;
 
   tdi.version = 0;
   tdi.kind = kThemeMediumSlider;
   tdi.bounds = inBoxRect;
   tdi.min = inMinValue;
   tdi.max = inMaxValue;
   tdi.value = inCurrentValue;
   tdi.attributes = kThemeTrackShowThumb;
   if (!inIsVertical)
     tdi.attributes |= kThemeTrackHorizontal;
+  if (inIsReverse)
+    tdi.attributes |= kThemeTrackRightToLeft;
   if (inState & NS_EVENT_STATE_FOCUS)
     tdi.attributes |= kThemeTrackHasFocus;
   tdi.enableState = inIsDisabled ? kThemeTrackDisabled : kThemeTrackActive;
   tdi.trackInfo.slider.thumbDir = kThemeThumbPlain;
   tdi.trackInfo.slider.pressState = 0;
 
   HIThemeDrawTrack(&tdi, NULL, cgContext, HITHEME_ORIENTATION);
 }
@@ -605,18 +608,21 @@ nsNativeThemeCocoa::DrawWidgetBackground
     case NS_THEME_SCALE_HORIZONTAL:
     case NS_THEME_SCALE_VERTICAL: {
       PRInt32 curpos = CheckIntAttr(aFrame, nsWidgetAtoms::curpos);
       PRInt32 minpos = CheckIntAttr(aFrame, nsWidgetAtoms::minpos);
       PRInt32 maxpos = CheckIntAttr(aFrame, nsWidgetAtoms::maxpos);
       if (!maxpos)
         maxpos = 100;
 
+      PRBool reverse = aFrame->GetContent()->
+        AttrValueIs(kNameSpaceID_None, nsWidgetAtoms::dir,
+                    NS_LITERAL_STRING("reverse"), eCaseMatters);
       DrawScale(cgContext, macRect, IsDisabled(aFrame), eventState,
-                (aWidgetType == NS_THEME_SCALE_VERTICAL),
+                (aWidgetType == NS_THEME_SCALE_VERTICAL), reverse,
                 curpos, minpos, maxpos);
     }
       break;
 
     case NS_THEME_SCALE_THUMB_HORIZONTAL:
     case NS_THEME_SCALE_THUMB_VERTICAL:
       // do nothing, drawn by scale
       break;
--- a/xpfe/global/jar.mn
+++ b/xpfe/global/jar.mn
@@ -49,17 +49,17 @@ toolkit.jar:
         content/global/bindings/menu.xml            (resources/content/bindings/menu.xml)
         content/global/bindings/menulist.xml        (resources/content/bindings/menulist.xml)
         content/global/bindings/numberbox.xml       (/toolkit/content/widgets/numberbox.xml)
         content/global/bindings/popup.xml           (resources/content/bindings/popup.xml)
         content/global/bindings/progressmeter.xml   (resources/content/bindings/progressmeter.xml)
         content/global/bindings/radio.xml           (resources/content/bindings/radio.xml)
         content/global/bindings/scrollbar.xml       (resources/content/bindings/scrollbar.xml)
         content/global/bindings/nativescrollbar.xml (resources/content/bindings/nativescrollbar.xml)
-        content/global/bindings/scale.xml           (resources/content/bindings/scale.xml)
+        content/global/bindings/scale.xml           (/toolkit/content/widgets/scale.xml)
         content/global/bindings/scrollbox.xml       (resources/content/bindings/scrollbox.xml)
         content/global/bindings/splitter.xml        (resources/content/bindings/splitter.xml)
         content/global/bindings/spinbuttons.xml     (/toolkit/content/widgets/spinbuttons.xml)
         content/global/bindings/stringbundle.xml    (resources/content/bindings/stringbundle.xml)
         content/global/bindings/tabbox.xml          (resources/content/bindings/tabbox.xml)
         content/global/bindings/tabbrowser.xml      (resources/content/bindings/tabbrowser.xml)
         content/global/bindings/text.xml            (resources/content/bindings/text.xml)
         content/global/bindings/textbox.xml         (resources/content/bindings/textbox.xml)