layout/xul/nsSliderFrame.cpp

Thu, 15 Jan 2015 15:59:08 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:59:08 +0100
branch
TOR_BUG_9701
changeset 10
ac0c01689b40
permissions
-rw-r--r--

Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 //
     7 // Eric Vaughan
     8 // Netscape Communications
     9 //
    10 // See documentation in associated header file
    11 //
    13 #include "nsSliderFrame.h"
    14 #include "nsStyleContext.h"
    15 #include "nsPresContext.h"
    16 #include "nsIContent.h"
    17 #include "nsCOMPtr.h"
    18 #include "nsNameSpaceManager.h"
    19 #include "nsGkAtoms.h"
    20 #include "nsHTMLParts.h"
    21 #include "nsIPresShell.h"
    22 #include "nsCSSRendering.h"
    23 #include "nsIDOMMouseEvent.h"
    24 #include "nsScrollbarButtonFrame.h"
    25 #include "nsISliderListener.h"
    26 #include "nsIScrollbarMediator.h"
    27 #include "nsScrollbarFrame.h"
    28 #include "nsRepeatService.h"
    29 #include "nsBoxLayoutState.h"
    30 #include "nsSprocketLayout.h"
    31 #include "nsIServiceManager.h"
    32 #include "nsContentUtils.h"
    33 #include "nsLayoutUtils.h"
    34 #include "nsDisplayList.h"
    35 #include "mozilla/Preferences.h"
    36 #include "mozilla/LookAndFeel.h"
    37 #include "mozilla/MouseEvents.h"
    38 #include <algorithm>
    40 using namespace mozilla;
    42 bool nsSliderFrame::gMiddlePref = false;
    43 int32_t nsSliderFrame::gSnapMultiplier;
    45 // Turn this on if you want to debug slider frames.
    46 #undef DEBUG_SLIDER
    48 static already_AddRefed<nsIContent>
    49 GetContentOfBox(nsIFrame *aBox)
    50 {
    51   nsCOMPtr<nsIContent> content = aBox->GetContent();
    52   return content.forget();
    53 }
    55 nsIFrame*
    56 NS_NewSliderFrame (nsIPresShell* aPresShell, nsStyleContext* aContext)
    57 {
    58   return new (aPresShell) nsSliderFrame(aPresShell, aContext);
    59 }
    61 NS_IMPL_FRAMEARENA_HELPERS(nsSliderFrame)
    63 nsSliderFrame::nsSliderFrame(nsIPresShell* aPresShell, nsStyleContext* aContext):
    64   nsBoxFrame(aPresShell, aContext),
    65   mCurPos(0),
    66   mChange(0),
    67   mUserChanged(false)
    68 {
    69 }
    71 // stop timer
    72 nsSliderFrame::~nsSliderFrame()
    73 {
    74 }
    76 void
    77 nsSliderFrame::Init(nsIContent*      aContent,
    78                     nsIFrame*        aParent,
    79                     nsIFrame*        aPrevInFlow)
    80 {
    81   nsBoxFrame::Init(aContent, aParent, aPrevInFlow);
    83   static bool gotPrefs = false;
    84   if (!gotPrefs) {
    85     gotPrefs = true;
    87     gMiddlePref = Preferences::GetBool("middlemouse.scrollbarPosition");
    88     gSnapMultiplier = Preferences::GetInt("slider.snapMultiplier");
    89   }
    91   mCurPos = GetCurrentPosition(aContent);
    92 }
    94 nsresult
    95 nsSliderFrame::RemoveFrame(ChildListID     aListID,
    96                            nsIFrame*       aOldFrame)
    97 {
    98   nsresult rv = nsBoxFrame::RemoveFrame(aListID, aOldFrame);
    99   if (mFrames.IsEmpty())
   100     RemoveListener();
   102   return rv;
   103 }
   105 nsresult
   106 nsSliderFrame::InsertFrames(ChildListID     aListID,
   107                             nsIFrame*       aPrevFrame,
   108                             nsFrameList&    aFrameList)
   109 {
   110   bool wasEmpty = mFrames.IsEmpty();
   111   nsresult rv = nsBoxFrame::InsertFrames(aListID, aPrevFrame, aFrameList);
   112   if (wasEmpty)
   113     AddListener();
   115   return rv;
   116 }
   118 nsresult
   119 nsSliderFrame::AppendFrames(ChildListID     aListID,
   120                             nsFrameList&    aFrameList)
   121 {
   122   // if we have no children and on was added then make sure we add the
   123   // listener
   124   bool wasEmpty = mFrames.IsEmpty();
   125   nsresult rv = nsBoxFrame::AppendFrames(aListID, aFrameList);
   126   if (wasEmpty)
   127     AddListener();
   129   return rv;
   130 }
   132 int32_t
   133 nsSliderFrame::GetCurrentPosition(nsIContent* content)
   134 {
   135   return GetIntegerAttribute(content, nsGkAtoms::curpos, 0);
   136 }
   138 int32_t
   139 nsSliderFrame::GetMinPosition(nsIContent* content)
   140 {
   141   return GetIntegerAttribute(content, nsGkAtoms::minpos, 0);
   142 }
   144 int32_t
   145 nsSliderFrame::GetMaxPosition(nsIContent* content)
   146 {
   147   return GetIntegerAttribute(content, nsGkAtoms::maxpos, 100);
   148 }
   150 int32_t
   151 nsSliderFrame::GetIncrement(nsIContent* content)
   152 {
   153   return GetIntegerAttribute(content, nsGkAtoms::increment, 1);
   154 }
   157 int32_t
   158 nsSliderFrame::GetPageIncrement(nsIContent* content)
   159 {
   160   return GetIntegerAttribute(content, nsGkAtoms::pageincrement, 10);
   161 }
   163 int32_t
   164 nsSliderFrame::GetIntegerAttribute(nsIContent* content, nsIAtom* atom, int32_t defaultValue)
   165 {
   166     nsAutoString value;
   167     content->GetAttr(kNameSpaceID_None, atom, value);
   168     if (!value.IsEmpty()) {
   169       nsresult error;
   171       // convert it to an integer
   172       defaultValue = value.ToInteger(&error);
   173     }
   175     return defaultValue;
   176 }
   178 class nsValueChangedRunnable : public nsRunnable
   179 {
   180 public:
   181   nsValueChangedRunnable(nsISliderListener* aListener,
   182                          nsIAtom* aWhich,
   183                          int32_t aValue,
   184                          bool aUserChanged)
   185   : mListener(aListener), mWhich(aWhich),
   186     mValue(aValue), mUserChanged(aUserChanged)
   187   {}
   189   NS_IMETHODIMP Run()
   190   {
   191     return mListener->ValueChanged(nsDependentAtomString(mWhich),
   192                                    mValue, mUserChanged);
   193   }
   195   nsCOMPtr<nsISliderListener> mListener;
   196   nsCOMPtr<nsIAtom> mWhich;
   197   int32_t mValue;
   198   bool mUserChanged;
   199 };
   201 class nsDragStateChangedRunnable : public nsRunnable
   202 {
   203 public:
   204   nsDragStateChangedRunnable(nsISliderListener* aListener,
   205                              bool aDragBeginning)
   206   : mListener(aListener),
   207     mDragBeginning(aDragBeginning)
   208   {}
   210   NS_IMETHODIMP Run()
   211   {
   212     return mListener->DragStateChanged(mDragBeginning);
   213   }
   215   nsCOMPtr<nsISliderListener> mListener;
   216   bool mDragBeginning;
   217 };
   219 nsresult
   220 nsSliderFrame::AttributeChanged(int32_t aNameSpaceID,
   221                                 nsIAtom* aAttribute,
   222                                 int32_t aModType)
   223 {
   224   nsresult rv = nsBoxFrame::AttributeChanged(aNameSpaceID, aAttribute,
   225                                              aModType);
   226   // if the current position changes
   227   if (aAttribute == nsGkAtoms::curpos) {
   228      CurrentPositionChanged();
   229   } else if (aAttribute == nsGkAtoms::minpos ||
   230              aAttribute == nsGkAtoms::maxpos) {
   231       // bounds check it.
   233       nsIFrame* scrollbarBox = GetScrollbar();
   234       nsCOMPtr<nsIContent> scrollbar;
   235       scrollbar = GetContentOfBox(scrollbarBox);
   236       int32_t current = GetCurrentPosition(scrollbar);
   237       int32_t min = GetMinPosition(scrollbar);
   238       int32_t max = GetMaxPosition(scrollbar);
   240       // inform the parent <scale> that the minimum or maximum changed
   241       nsIFrame* parent = GetParent();
   242       if (parent) {
   243         nsCOMPtr<nsISliderListener> sliderListener = do_QueryInterface(parent->GetContent());
   244         if (sliderListener) {
   245           nsContentUtils::AddScriptRunner(
   246             new nsValueChangedRunnable(sliderListener, aAttribute,
   247                                        aAttribute == nsGkAtoms::minpos ? min : max, false));
   248         }
   249       }
   251       if (current < min || current > max)
   252       {
   253         if (current < min || max < min)
   254             current = min;
   255         else if (current > max)
   256             current = max;
   258         // set the new position and notify observers
   259         nsScrollbarFrame* scrollbarFrame = do_QueryFrame(scrollbarBox);
   260         if (scrollbarFrame) {
   261           nsIScrollbarMediator* mediator = scrollbarFrame->GetScrollbarMediator();
   262           if (mediator) {
   263             mediator->PositionChanged(scrollbarFrame, GetCurrentPosition(scrollbar), current);
   264           }
   265         }
   267         nsContentUtils::AddScriptRunner(
   268           new nsSetAttrRunnable(scrollbar, nsGkAtoms::curpos, current));
   269       }
   270   }
   272   if (aAttribute == nsGkAtoms::minpos ||
   273       aAttribute == nsGkAtoms::maxpos ||
   274       aAttribute == nsGkAtoms::pageincrement ||
   275       aAttribute == nsGkAtoms::increment) {
   277       PresContext()->PresShell()->
   278         FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
   279   }
   281   return rv;
   282 }
   284 void
   285 nsSliderFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
   286                                 const nsRect&           aDirtyRect,
   287                                 const nsDisplayListSet& aLists)
   288 {
   289   if (aBuilder->IsForEventDelivery() && isDraggingThumb()) {
   290     // This is EVIL, we shouldn't be messing with event delivery just to get
   291     // thumb mouse drag events to arrive at the slider!
   292     aLists.Outlines()->AppendNewToTop(new (aBuilder)
   293       nsDisplayEventReceiver(aBuilder, this));
   294     return;
   295   }
   297   nsBoxFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
   298 }
   300 void
   301 nsSliderFrame::BuildDisplayListForChildren(nsDisplayListBuilder*   aBuilder,
   302                                            const nsRect&           aDirtyRect,
   303                                            const nsDisplayListSet& aLists)
   304 {
   305   // if we are too small to have a thumb don't paint it.
   306   nsIFrame* thumb = GetChildBox();
   308   if (thumb) {
   309     nsRect thumbRect(thumb->GetRect());
   310     nsMargin m;
   311     thumb->GetMargin(m);
   312     thumbRect.Inflate(m);
   314     nsRect crect;
   315     GetClientRect(crect);
   317     if (crect.width < thumbRect.width || crect.height < thumbRect.height)
   318       return;
   319   }
   321   nsBoxFrame::BuildDisplayListForChildren(aBuilder, aDirtyRect, aLists);
   322 }
   324 NS_IMETHODIMP
   325 nsSliderFrame::DoLayout(nsBoxLayoutState& aState)
   326 {
   327   // get the thumb should be our only child
   328   nsIFrame* thumbBox = GetChildBox();
   330   if (!thumbBox) {
   331     SyncLayout(aState);
   332     return NS_OK;
   333   }
   335   EnsureOrient();
   337 #ifdef DEBUG_LAYOUT
   338   if (mState & NS_STATE_DEBUG_WAS_SET) {
   339       if (mState & NS_STATE_SET_TO_DEBUG)
   340           SetDebug(aState, true);
   341       else
   342           SetDebug(aState, false);
   343   }
   344 #endif
   346   // get the content area inside our borders
   347   nsRect clientRect;
   348   GetClientRect(clientRect);
   350   // get the scrollbar
   351   nsIFrame* scrollbarBox = GetScrollbar();
   352   nsCOMPtr<nsIContent> scrollbar;
   353   scrollbar = GetContentOfBox(scrollbarBox);
   355   // get the thumb's pref size
   356   nsSize thumbSize = thumbBox->GetPrefSize(aState);
   358   if (IsHorizontal())
   359     thumbSize.height = clientRect.height;
   360   else
   361     thumbSize.width = clientRect.width;
   363   int32_t curPos = GetCurrentPosition(scrollbar);
   364   int32_t minPos = GetMinPosition(scrollbar);
   365   int32_t maxPos = GetMaxPosition(scrollbar);
   366   int32_t pageIncrement = GetPageIncrement(scrollbar);
   368   maxPos = std::max(minPos, maxPos);
   369   curPos = clamped(curPos, minPos, maxPos);
   371   nscoord& availableLength = IsHorizontal() ? clientRect.width : clientRect.height;
   372   nscoord& thumbLength = IsHorizontal() ? thumbSize.width : thumbSize.height;
   374   if ((pageIncrement + maxPos - minPos) > 0 && thumbBox->GetFlex(aState) > 0) {
   375     float ratio = float(pageIncrement) / float(maxPos - minPos + pageIncrement);
   376     thumbLength = std::max(thumbLength, NSToCoordRound(availableLength * ratio));
   377   }
   379   // Round the thumb's length to device pixels.
   380   nsPresContext* presContext = PresContext();
   381   thumbLength = presContext->DevPixelsToAppUnits(
   382                   presContext->AppUnitsToDevPixels(thumbLength));
   384   // mRatio translates the thumb position in app units to the value.
   385   mRatio = (minPos != maxPos) ? float(availableLength - thumbLength) / float(maxPos - minPos) : 1;
   387   // in reverse mode, curpos is reversed such that lower values are to the
   388   // right or bottom and increase leftwards or upwards. In this case, use the
   389   // offset from the end instead of the beginning.
   390   bool reverse = mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::dir,
   391                                          nsGkAtoms::reverse, eCaseMatters);
   392   nscoord pos = reverse ? (maxPos - curPos) : (curPos - minPos);
   394   // set the thumb's coord to be the current pos * the ratio.
   395   nsRect thumbRect(clientRect.x, clientRect.y, thumbSize.width, thumbSize.height);
   396   int32_t& thumbPos = (IsHorizontal() ? thumbRect.x : thumbRect.y);
   397   thumbPos += NSToCoordRound(pos * mRatio);
   399   nsRect oldThumbRect(thumbBox->GetRect());
   400   LayoutChildAt(aState, thumbBox, thumbRect);
   402   SyncLayout(aState);
   404   // Redraw only if thumb changed size.
   405   if (!oldThumbRect.IsEqualInterior(thumbRect))
   406     Redraw(aState);
   408   return NS_OK;
   409 }
   412 nsresult
   413 nsSliderFrame::HandleEvent(nsPresContext* aPresContext,
   414                            WidgetGUIEvent* aEvent,
   415                            nsEventStatus* aEventStatus)
   416 {
   417   NS_ENSURE_ARG_POINTER(aEventStatus);
   419   // If a web page calls event.preventDefault() we still want to
   420   // scroll when scroll arrow is clicked. See bug 511075.
   421   if (!mContent->IsInNativeAnonymousSubtree() &&
   422       nsEventStatus_eConsumeNoDefault == *aEventStatus) {
   423     return NS_OK;
   424   }
   426   nsIFrame* scrollbarBox = GetScrollbar();
   427   nsCOMPtr<nsIContent> scrollbar;
   428   scrollbar = GetContentOfBox(scrollbarBox);
   429   bool isHorizontal = IsHorizontal();
   431   if (isDraggingThumb())
   432   {
   433     switch (aEvent->message) {
   434     case NS_TOUCH_MOVE:
   435     case NS_MOUSE_MOVE: {
   436       nsPoint eventPoint;
   437       if (!GetEventPoint(aEvent, eventPoint)) {
   438         break;
   439       }
   440       if (mChange) {
   441         // We're in the process of moving the thumb to the mouse,
   442         // but the mouse just moved.  Make sure to update our
   443         // destination point.
   444         mDestinationPoint = eventPoint;
   445         StopRepeat();
   446         StartRepeat();
   447         break;
   448       }
   450       nscoord pos = isHorizontal ? eventPoint.x : eventPoint.y;
   452       nsIFrame* thumbFrame = mFrames.FirstChild();
   453       if (!thumbFrame) {
   454         return NS_OK;
   455       }
   457       // take our current position and subtract the start location
   458       pos -= mDragStart;
   459       bool isMouseOutsideThumb = false;
   460       if (gSnapMultiplier) {
   461         nsSize thumbSize = thumbFrame->GetSize();
   462         if (isHorizontal) {
   463           // horizontal scrollbar - check if mouse is above or below thumb
   464           // XXXbz what about looking at the .y of the thumb's rect?  Is that
   465           // always zero here?
   466           if (eventPoint.y < -gSnapMultiplier * thumbSize.height ||
   467               eventPoint.y > thumbSize.height +
   468                                gSnapMultiplier * thumbSize.height)
   469             isMouseOutsideThumb = true;
   470         }
   471         else {
   472           // vertical scrollbar - check if mouse is left or right of thumb
   473           if (eventPoint.x < -gSnapMultiplier * thumbSize.width ||
   474               eventPoint.x > thumbSize.width +
   475                                gSnapMultiplier * thumbSize.width)
   476             isMouseOutsideThumb = true;
   477         }
   478       }
   479       if (aEvent->eventStructType == NS_TOUCH_EVENT) {
   480         *aEventStatus = nsEventStatus_eConsumeNoDefault;
   481       }
   482       if (isMouseOutsideThumb)
   483       {
   484         SetCurrentThumbPosition(scrollbar, mThumbStart, false, false);
   485         return NS_OK;
   486       }
   488       // set it
   489       SetCurrentThumbPosition(scrollbar, pos, false, true); // with snapping
   490     }
   491     break;
   493     case NS_TOUCH_END:
   494     case NS_MOUSE_BUTTON_UP:
   495       if (ShouldScrollForEvent(aEvent)) {
   496         // stop capturing
   497         AddListener();
   498         DragThumb(false);
   499         if (mChange) {
   500           StopRepeat();
   501           mChange = 0;
   502         }
   503         //we MUST call nsFrame HandleEvent for mouse ups to maintain the selection state and capture state.
   504         return nsFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
   505       }
   506     }
   508     //return nsFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
   509     return NS_OK;
   510   } else if (ShouldScrollToClickForEvent(aEvent)) {
   511     nsPoint eventPoint;
   512     if (!GetEventPoint(aEvent, eventPoint)) {
   513       return NS_OK;
   514     }
   515     nscoord pos = isHorizontal ? eventPoint.x : eventPoint.y;
   517     // adjust so that the middle of the thumb is placed under the click
   518     nsIFrame* thumbFrame = mFrames.FirstChild();
   519     if (!thumbFrame) {
   520       return NS_OK;
   521     }
   522     nsSize thumbSize = thumbFrame->GetSize();
   523     nscoord thumbLength = isHorizontal ? thumbSize.width : thumbSize.height;
   525     // set it
   526     nsWeakFrame weakFrame(this);
   527     // should aMaySnap be true here?
   528     SetCurrentThumbPosition(scrollbar, pos - thumbLength/2, false, false);
   529     NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK);
   531     DragThumb(true);
   532     if (aEvent->eventStructType == NS_TOUCH_EVENT) {
   533       *aEventStatus = nsEventStatus_eConsumeNoDefault;
   534     }
   536     if (isHorizontal)
   537       mThumbStart = thumbFrame->GetPosition().x;
   538     else
   539       mThumbStart = thumbFrame->GetPosition().y;
   541     mDragStart = pos - mThumbStart;
   542   }
   544   // XXX hack until handle release is actually called in nsframe.
   545 //  if (aEvent->message == NS_MOUSE_EXIT_SYNTH || aEvent->message == NS_MOUSE_RIGHT_BUTTON_UP || aEvent->message == NS_MOUSE_LEFT_BUTTON_UP)
   546   //   HandleRelease(aPresContext, aEvent, aEventStatus);
   548   if (aEvent->message == NS_MOUSE_EXIT_SYNTH && mChange)
   549      HandleRelease(aPresContext, aEvent, aEventStatus);
   551   return nsFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
   552 }
   554 // Helper function to collect the "scroll to click" metric. Beware of
   555 // caching this, users expect to be able to change the system preference
   556 // and see the browser change its behavior immediately.
   557 bool
   558 nsSliderFrame::GetScrollToClick()
   559 {
   560   if (GetScrollbar() != this) {
   561     return LookAndFeel::GetInt(LookAndFeel::eIntID_ScrollToClick, false);
   562   }
   564   if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::movetoclick,
   565                             nsGkAtoms::_true, eCaseMatters)) {
   566     return true;
   567   }
   568   if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::movetoclick,
   569                             nsGkAtoms::_false, eCaseMatters)) {
   570     return false;
   571   }
   573 #ifdef XP_MACOSX
   574   return true;
   575 #else
   576   return false;
   577 #endif
   578 }
   580 nsIFrame*
   581 nsSliderFrame::GetScrollbar()
   582 {
   583   // if we are in a scrollbar then return the scrollbar's content node
   584   // if we are not then return ours.
   585    nsIFrame* scrollbar;
   586    nsScrollbarButtonFrame::GetParentWithTag(nsGkAtoms::scrollbar, this, scrollbar);
   588    if (scrollbar == nullptr)
   589        return this;
   591    return scrollbar->IsBoxFrame() ? scrollbar : this;
   592 }
   594 void
   595 nsSliderFrame::PageUpDown(nscoord change)
   596 {
   597   // on a page up or down get our page increment. We get this by getting the scrollbar we are in and
   598   // asking it for the current position and the page increment. If we are not in a scrollbar we will
   599   // get the values from our own node.
   600   nsIFrame* scrollbarBox = GetScrollbar();
   601   nsCOMPtr<nsIContent> scrollbar;
   602   scrollbar = GetContentOfBox(scrollbarBox);
   604   if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::dir,
   605                             nsGkAtoms::reverse, eCaseMatters))
   606     change = -change;
   608   nscoord pageIncrement = GetPageIncrement(scrollbar);
   609   int32_t curpos = GetCurrentPosition(scrollbar);
   610   int32_t minpos = GetMinPosition(scrollbar);
   611   int32_t maxpos = GetMaxPosition(scrollbar);
   613   // get the new position and make sure it is in bounds
   614   int32_t newpos = curpos + change * pageIncrement;
   615   if (newpos < minpos || maxpos < minpos)
   616     newpos = minpos;
   617   else if (newpos > maxpos)
   618     newpos = maxpos;
   620   SetCurrentPositionInternal(scrollbar, newpos, true);
   621 }
   623 // called when the current position changed and we need to update the thumb's location
   624 void
   625 nsSliderFrame::CurrentPositionChanged()
   626 {
   627   nsIFrame* scrollbarBox = GetScrollbar();
   628   nsCOMPtr<nsIContent> scrollbar;
   629   scrollbar = GetContentOfBox(scrollbarBox);
   631   // get the current position
   632   int32_t curPos = GetCurrentPosition(scrollbar);
   634   // do nothing if the position did not change
   635   if (mCurPos == curPos)
   636     return;
   638   // get our current min and max position from our content node
   639   int32_t minPos = GetMinPosition(scrollbar);
   640   int32_t maxPos = GetMaxPosition(scrollbar);
   642   maxPos = std::max(minPos, maxPos);
   643   curPos = clamped(curPos, minPos, maxPos);
   645   // get the thumb's rect
   646   nsIFrame* thumbFrame = mFrames.FirstChild();
   647   if (!thumbFrame)
   648     return; // The thumb may stream in asynchronously via XBL.
   650   nsRect thumbRect = thumbFrame->GetRect();
   652   nsRect clientRect;
   653   GetClientRect(clientRect);
   655   // figure out the new rect
   656   nsRect newThumbRect(thumbRect);
   658   bool reverse = mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::dir,
   659                                          nsGkAtoms::reverse, eCaseMatters);
   660   nscoord pos = reverse ? (maxPos - curPos) : (curPos - minPos);
   662   if (IsHorizontal())
   663      newThumbRect.x = clientRect.x + NSToCoordRound(pos * mRatio);
   664   else
   665      newThumbRect.y = clientRect.y + NSToCoordRound(pos * mRatio);
   667 #ifdef MOZ_WIDGET_GONK
   668   // avoid putting the scroll thumb at subpixel positions which cause needless invalidations
   669   nscoord appUnitsPerPixel = PresContext()->AppUnitsPerDevPixel();
   670   newThumbRect = newThumbRect.ToNearestPixels(appUnitsPerPixel).ToAppUnits(appUnitsPerPixel);
   671 #endif
   672   // set the rect
   673   thumbFrame->SetRect(newThumbRect);
   675   // Request a repaint of the scrollbar
   676   SchedulePaint();
   678   mCurPos = curPos;
   680   // inform the parent <scale> if it exists that the value changed
   681   nsIFrame* parent = GetParent();
   682   if (parent) {
   683     nsCOMPtr<nsISliderListener> sliderListener = do_QueryInterface(parent->GetContent());
   684     if (sliderListener) {
   685       nsContentUtils::AddScriptRunner(
   686         new nsValueChangedRunnable(sliderListener, nsGkAtoms::curpos, mCurPos, mUserChanged));
   687     }
   688   }
   689 }
   691 static void UpdateAttribute(nsIContent* aScrollbar, nscoord aNewPos, bool aNotify, bool aIsSmooth) {
   692   nsAutoString str;
   693   str.AppendInt(aNewPos);
   695   if (aIsSmooth) {
   696     aScrollbar->SetAttr(kNameSpaceID_None, nsGkAtoms::smooth, NS_LITERAL_STRING("true"), false);
   697   }
   698   aScrollbar->SetAttr(kNameSpaceID_None, nsGkAtoms::curpos, str, aNotify);
   699   if (aIsSmooth) {
   700     aScrollbar->UnsetAttr(kNameSpaceID_None, nsGkAtoms::smooth, false);
   701   }
   702 }
   704 // Use this function when you want to set the scroll position via the position
   705 // of the scrollbar thumb, e.g. when dragging the slider. This function scrolls
   706 // the content in such a way that thumbRect.x/.y becomes aNewThumbPos.
   707 void
   708 nsSliderFrame::SetCurrentThumbPosition(nsIContent* aScrollbar, nscoord aNewThumbPos,
   709                                        bool aIsSmooth, bool aMaySnap)
   710 {
   711   nsRect crect;
   712   GetClientRect(crect);
   713   nscoord offset = IsHorizontal() ? crect.x : crect.y;
   714   int32_t newPos = NSToIntRound((aNewThumbPos - offset) / mRatio);
   716   if (aMaySnap && mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::snap,
   717                                         nsGkAtoms::_true, eCaseMatters)) {
   718     // If snap="true", then the slider may only be set to min + (increment * x).
   719     // Otherwise, the slider may be set to any positive integer.
   720     int32_t increment = GetIncrement(aScrollbar);
   721     newPos = NSToIntRound(newPos / float(increment)) * increment;
   722   }
   724   SetCurrentPosition(aScrollbar, newPos, aIsSmooth);
   725 }
   727 // Use this function when you know the target scroll position of the scrolled content.
   728 // aNewPos should be passed to this function as a position as if the minpos is 0.
   729 // That is, the minpos will be added to the position by this function. In a reverse
   730 // direction slider, the newpos should be the distance from the end.
   731 void
   732 nsSliderFrame::SetCurrentPosition(nsIContent* aScrollbar, int32_t aNewPos,
   733                                   bool aIsSmooth)
   734 {
   735    // get min and max position from our content node
   736   int32_t minpos = GetMinPosition(aScrollbar);
   737   int32_t maxpos = GetMaxPosition(aScrollbar);
   739   // in reverse direction sliders, flip the value so that it goes from
   740   // right to left, or bottom to top.
   741   if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::dir,
   742                             nsGkAtoms::reverse, eCaseMatters))
   743     aNewPos = maxpos - aNewPos;
   744   else
   745     aNewPos += minpos;
   747   // get the new position and make sure it is in bounds
   748   if (aNewPos < minpos || maxpos < minpos)
   749     aNewPos = minpos;
   750   else if (aNewPos > maxpos)
   751     aNewPos = maxpos;
   753   SetCurrentPositionInternal(aScrollbar, aNewPos, aIsSmooth);
   754 }
   756 void
   757 nsSliderFrame::SetCurrentPositionInternal(nsIContent* aScrollbar, int32_t aNewPos,
   758                                           bool aIsSmooth)
   759 {
   760   nsCOMPtr<nsIContent> scrollbar = aScrollbar;
   761   nsIFrame* scrollbarBox = GetScrollbar();
   763   mUserChanged = true;
   765   nsScrollbarFrame* scrollbarFrame = do_QueryFrame(scrollbarBox);
   766   if (scrollbarFrame) {
   767     // See if we have a mediator.
   768     nsIScrollbarMediator* mediator = scrollbarFrame->GetScrollbarMediator();
   769     if (mediator) {
   770       nsRefPtr<nsPresContext> context = PresContext();
   771       nsCOMPtr<nsIContent> content = GetContent();
   772       mediator->PositionChanged(scrollbarFrame, GetCurrentPosition(scrollbar), aNewPos);
   773       // 'mediator' might be dangling now...
   774       UpdateAttribute(scrollbar, aNewPos, false, aIsSmooth);
   775       nsIFrame* frame = content->GetPrimaryFrame();
   776       if (frame && frame->GetType() == nsGkAtoms::sliderFrame) {
   777         static_cast<nsSliderFrame*>(frame)->CurrentPositionChanged();
   778       }
   779       mUserChanged = false;
   780       return;
   781     }
   782   }
   784   UpdateAttribute(scrollbar, aNewPos, true, aIsSmooth);
   785   mUserChanged = false;
   787 #ifdef DEBUG_SLIDER
   788   printf("Current Pos=%d\n",aNewPos);
   789 #endif
   791 }
   793 nsIAtom*
   794 nsSliderFrame::GetType() const
   795 {
   796   return nsGkAtoms::sliderFrame;
   797 }
   799 nsresult
   800 nsSliderFrame::SetInitialChildList(ChildListID     aListID,
   801                                    nsFrameList&    aChildList)
   802 {
   803   nsresult r = nsBoxFrame::SetInitialChildList(aListID, aChildList);
   805   AddListener();
   807   return r;
   808 }
   810 nsresult
   811 nsSliderMediator::HandleEvent(nsIDOMEvent* aEvent)
   812 {
   813   // Only process the event if the thumb is not being dragged.
   814   if (mSlider && !mSlider->isDraggingThumb())
   815     return mSlider->StartDrag(aEvent);
   817   return NS_OK;
   818 }
   820 nsresult
   821 nsSliderFrame::StartDrag(nsIDOMEvent* aEvent)
   822 {
   823 #ifdef DEBUG_SLIDER
   824   printf("Begin dragging\n");
   825 #endif
   826   if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled,
   827                             nsGkAtoms::_true, eCaseMatters))
   828     return NS_OK;
   830   WidgetGUIEvent* event = aEvent->GetInternalNSEvent()->AsGUIEvent();
   832   if (!ShouldScrollForEvent(event)) {
   833     return NS_OK;
   834   }
   836   nsPoint pt;
   837   if (!GetEventPoint(event, pt)) {
   838     return NS_OK;
   839   }
   840   bool isHorizontal = IsHorizontal();
   841   nscoord pos = isHorizontal ? pt.x : pt.y;
   843   // If we should scroll-to-click, first place the middle of the slider thumb
   844   // under the mouse.
   845   nsCOMPtr<nsIContent> scrollbar;
   846   nscoord newpos = pos;
   847   bool scrollToClick = ShouldScrollToClickForEvent(event);
   848   if (scrollToClick) {
   849     // adjust so that the middle of the thumb is placed under the click
   850     nsIFrame* thumbFrame = mFrames.FirstChild();
   851     if (!thumbFrame) {
   852       return NS_OK;
   853     }
   854     nsSize thumbSize = thumbFrame->GetSize();
   855     nscoord thumbLength = isHorizontal ? thumbSize.width : thumbSize.height;
   857     newpos -= (thumbLength/2);
   859     nsIFrame* scrollbarBox = GetScrollbar();
   860     scrollbar = GetContentOfBox(scrollbarBox);
   861   }
   863   DragThumb(true);
   865   if (scrollToClick) {
   866     // should aMaySnap be true here?
   867     SetCurrentThumbPosition(scrollbar, newpos, false, false);
   868   }
   870   nsIFrame* thumbFrame = mFrames.FirstChild();
   871   if (!thumbFrame) {
   872     return NS_OK;
   873   }
   875   if (isHorizontal)
   876     mThumbStart = thumbFrame->GetPosition().x;
   877   else
   878     mThumbStart = thumbFrame->GetPosition().y;
   880   mDragStart = pos - mThumbStart;
   882 #ifdef DEBUG_SLIDER
   883   printf("Pressed mDragStart=%d\n",mDragStart);
   884 #endif
   886   return NS_OK;
   887 }
   889 void
   890 nsSliderFrame::DragThumb(bool aGrabMouseEvents)
   891 {
   892   // inform the parent <scale> that a drag is beginning or ending
   893   nsIFrame* parent = GetParent();
   894   if (parent) {
   895     nsCOMPtr<nsISliderListener> sliderListener = do_QueryInterface(parent->GetContent());
   896     if (sliderListener) {
   897       nsContentUtils::AddScriptRunner(
   898         new nsDragStateChangedRunnable(sliderListener, aGrabMouseEvents));
   899     }
   900   }
   902   nsIPresShell::SetCapturingContent(aGrabMouseEvents ? GetContent() : nullptr,
   903                                     aGrabMouseEvents ? CAPTURE_IGNOREALLOWED : 0);
   904 }
   906 bool
   907 nsSliderFrame::isDraggingThumb()
   908 {
   909   return (nsIPresShell::GetCapturingContent() == GetContent());
   910 }
   912 void
   913 nsSliderFrame::AddListener()
   914 {
   915   if (!mMediator) {
   916     mMediator = new nsSliderMediator(this);
   917   }
   919   nsIFrame* thumbFrame = mFrames.FirstChild();
   920   if (!thumbFrame) {
   921     return;
   922   }
   923   thumbFrame->GetContent()->
   924     AddSystemEventListener(NS_LITERAL_STRING("mousedown"), mMediator,
   925                            false, false);
   926   thumbFrame->GetContent()->
   927     AddSystemEventListener(NS_LITERAL_STRING("touchstart"), mMediator,
   928                            false, false);
   929 }
   931 void
   932 nsSliderFrame::RemoveListener()
   933 {
   934   NS_ASSERTION(mMediator, "No listener was ever added!!");
   936   nsIFrame* thumbFrame = mFrames.FirstChild();
   937   if (!thumbFrame)
   938     return;
   940   thumbFrame->GetContent()->
   941     RemoveSystemEventListener(NS_LITERAL_STRING("mousedown"), mMediator, false);
   942 }
   944 bool
   945 nsSliderFrame::ShouldScrollForEvent(WidgetGUIEvent* aEvent)
   946 {
   947   switch (aEvent->message) {
   948     case NS_TOUCH_START:
   949     case NS_TOUCH_END:
   950       return true;
   951     case NS_MOUSE_BUTTON_DOWN:
   952     case NS_MOUSE_BUTTON_UP: {
   953       uint16_t button = aEvent->AsMouseEvent()->button;
   954       return (button == WidgetMouseEvent::eLeftButton) ||
   955              (button == WidgetMouseEvent::eMiddleButton && gMiddlePref);
   956     }
   957     default:
   958       return false;
   959   }
   960 }
   962 bool
   963 nsSliderFrame::ShouldScrollToClickForEvent(WidgetGUIEvent* aEvent)
   964 {
   965   if (!ShouldScrollForEvent(aEvent)) {
   966     return false;
   967   }
   969   if (aEvent->message == NS_TOUCH_START) {
   970     return GetScrollToClick();
   971   }
   973   if (aEvent->message != NS_MOUSE_BUTTON_DOWN) {
   974     return false;
   975   }
   977 #ifdef XP_MACOSX
   978   // On Mac, clicking the scrollbar thumb should never scroll to click.
   979   if (IsEventOverThumb(aEvent)) {
   980     return false;
   981   }
   982 #endif
   984   WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
   985   if (mouseEvent->button == WidgetMouseEvent::eLeftButton) {
   986 #ifdef XP_MACOSX
   987     bool invertPref = mouseEvent->IsAlt();
   988 #else
   989     bool invertPref = mouseEvent->IsShift();
   990 #endif
   991     return GetScrollToClick() != invertPref;
   992   }
   994   return true;
   995 }
   997 bool
   998 nsSliderFrame::IsEventOverThumb(WidgetGUIEvent* aEvent)
   999 {
  1000   nsIFrame* thumbFrame = mFrames.FirstChild();
  1001   if (!thumbFrame) {
  1002     return false;
  1005   nsPoint eventPoint;
  1006   if (!GetEventPoint(aEvent, eventPoint)) {
  1007     return false;
  1010   bool isHorizontal = IsHorizontal();
  1011   nsRect thumbRect = thumbFrame->GetRect();
  1012   nscoord eventPos = isHorizontal ? eventPoint.x : eventPoint.y;
  1013   nscoord thumbStart = isHorizontal ? thumbRect.x : thumbRect.y;
  1014   nscoord thumbEnd = isHorizontal ? thumbRect.XMost() : thumbRect.YMost();
  1016   return eventPos >= thumbStart && eventPos < thumbEnd;
  1019 NS_IMETHODIMP
  1020 nsSliderFrame::HandlePress(nsPresContext* aPresContext,
  1021                            WidgetGUIEvent* aEvent,
  1022                            nsEventStatus* aEventStatus)
  1024   if (!ShouldScrollForEvent(aEvent) || ShouldScrollToClickForEvent(aEvent)) {
  1025     return NS_OK;
  1028   if (IsEventOverThumb(aEvent)) {
  1029     return NS_OK;
  1032   nsIFrame* thumbFrame = mFrames.FirstChild();
  1033   if (!thumbFrame) // display:none?
  1034     return NS_OK;
  1036   if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled,
  1037                             nsGkAtoms::_true, eCaseMatters))
  1038     return NS_OK;
  1040   nsRect thumbRect = thumbFrame->GetRect();
  1042   nscoord change = 1;
  1043   nsPoint eventPoint;
  1044   if (!GetEventPoint(aEvent, eventPoint)) {
  1045     return NS_OK;
  1047   if (IsHorizontal() ? eventPoint.x < thumbRect.x 
  1048                      : eventPoint.y < thumbRect.y)
  1049     change = -1;
  1051   mChange = change;
  1052   DragThumb(true);
  1053   mDestinationPoint = eventPoint;
  1054   StartRepeat();
  1055   PageUpDown(change);
  1056   return NS_OK;
  1059 NS_IMETHODIMP
  1060 nsSliderFrame::HandleRelease(nsPresContext* aPresContext,
  1061                              WidgetGUIEvent* aEvent,
  1062                              nsEventStatus* aEventStatus)
  1064   StopRepeat();
  1066   return NS_OK;
  1069 void
  1070 nsSliderFrame::DestroyFrom(nsIFrame* aDestructRoot)
  1072   // tell our mediator if we have one we are gone.
  1073   if (mMediator) {
  1074     mMediator->SetSlider(nullptr);
  1075     mMediator = nullptr;
  1077   StopRepeat();
  1079   // call base class Destroy()
  1080   nsBoxFrame::DestroyFrom(aDestructRoot);
  1083 nsSize
  1084 nsSliderFrame::GetPrefSize(nsBoxLayoutState& aState)
  1086   EnsureOrient();
  1087   return nsBoxFrame::GetPrefSize(aState);
  1090 nsSize
  1091 nsSliderFrame::GetMinSize(nsBoxLayoutState& aState)
  1093   EnsureOrient();
  1095   // our min size is just our borders and padding
  1096   return nsBox::GetMinSize(aState);
  1099 nsSize
  1100 nsSliderFrame::GetMaxSize(nsBoxLayoutState& aState)
  1102   EnsureOrient();
  1103   return nsBoxFrame::GetMaxSize(aState);
  1106 void
  1107 nsSliderFrame::EnsureOrient()
  1109   nsIFrame* scrollbarBox = GetScrollbar();
  1111   bool isHorizontal = (scrollbarBox->GetStateBits() & NS_STATE_IS_HORIZONTAL) != 0;
  1112   if (isHorizontal)
  1113       mState |= NS_STATE_IS_HORIZONTAL;
  1114   else
  1115       mState &= ~NS_STATE_IS_HORIZONTAL;
  1119 void nsSliderFrame::Notify(void)
  1121     bool stop = false;
  1123     nsIFrame* thumbFrame = mFrames.FirstChild();
  1124     if (!thumbFrame) {
  1125       StopRepeat();
  1126       return;
  1128     nsRect thumbRect = thumbFrame->GetRect();
  1130     bool isHorizontal = IsHorizontal();
  1132     // See if the thumb has moved past our destination point.
  1133     // if it has we want to stop.
  1134     if (isHorizontal) {
  1135         if (mChange < 0) {
  1136             if (thumbRect.x < mDestinationPoint.x)
  1137                 stop = true;
  1138         } else {
  1139             if (thumbRect.x + thumbRect.width > mDestinationPoint.x)
  1140                 stop = true;
  1142     } else {
  1143          if (mChange < 0) {
  1144             if (thumbRect.y < mDestinationPoint.y)
  1145                 stop = true;
  1146         } else {
  1147             if (thumbRect.y + thumbRect.height > mDestinationPoint.y)
  1148                 stop = true;
  1153     if (stop) {
  1154       StopRepeat();
  1155     } else {
  1156       PageUpDown(mChange);
  1160 NS_IMPL_ISUPPORTS(nsSliderMediator,
  1161                   nsIDOMEventListener)

mercurial