1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
9 // Netscape Communications
11 // See documentation in associated header file
14 #include "nsSliderFrame.h"
16 #include "mozilla/ComputedStyle.h"
17 #include "nsPresContext.h"
18 #include "nsIContent.h"
20 #include "nsNameSpaceManager.h"
21 #include "nsGkAtoms.h"
22 #include "nsHTMLParts.h"
23 #include "nsCSSRendering.h"
24 #include "nsScrollbarButtonFrame.h"
25 #include "nsIScrollableFrame.h"
26 #include "nsIScrollbarMediator.h"
27 #include "nsISupportsImpl.h"
28 #include "nsScrollbarFrame.h"
29 #include "nsRepeatService.h"
30 #include "nsBoxLayoutState.h"
31 #include "nsSprocketLayout.h"
32 #include "nsIServiceManager.h"
33 #include "nsContentUtils.h"
34 #include "nsLayoutUtils.h"
35 #include "nsDisplayList.h"
36 #include "nsRefreshDriver.h" // for nsAPostRefreshObserver
37 #include "nsSVGIntegrationUtils.h"
38 #include "mozilla/Assertions.h" // for MOZ_ASSERT
39 #include "mozilla/LookAndFeel.h"
40 #include "mozilla/MouseEvents.h"
41 #include "mozilla/Preferences.h"
42 #include "mozilla/PresShell.h"
43 #include "mozilla/Telemetry.h"
44 #include "mozilla/dom/Event.h"
45 #include "mozilla/layers/APZCCallbackHelper.h"
46 #include "mozilla/layers/AsyncDragMetrics.h"
47 #include "mozilla/layers/InputAPZContext.h"
50 using namespace mozilla
;
51 using mozilla::dom::Event
;
52 using mozilla::layers::APZCCallbackHelper
;
53 using mozilla::layers::AsyncDragMetrics
;
54 using mozilla::layers::InputAPZContext
;
55 using mozilla::layers::ScrollbarData
;
56 using mozilla::layers::ScrollDirection
;
58 bool nsSliderFrame::gMiddlePref
= false;
59 int32_t nsSliderFrame::gSnapMultiplier
;
61 // Turn this on if you want to debug slider frames.
64 static already_AddRefed
<nsIContent
> GetContentOfBox(nsIFrame
* aBox
) {
65 nsCOMPtr
<nsIContent
> content
= aBox
->GetContent();
66 return content
.forget();
69 nsIFrame
* NS_NewSliderFrame(PresShell
* aPresShell
, ComputedStyle
* aStyle
) {
70 return new (aPresShell
) nsSliderFrame(aStyle
, aPresShell
->GetPresContext());
73 NS_IMPL_FRAMEARENA_HELPERS(nsSliderFrame
)
75 NS_QUERYFRAME_HEAD(nsSliderFrame
)
76 NS_QUERYFRAME_ENTRY(nsSliderFrame
)
77 NS_QUERYFRAME_TAIL_INHERITING(nsBoxFrame
)
79 nsSliderFrame::nsSliderFrame(ComputedStyle
* aStyle
, nsPresContext
* aPresContext
)
80 : nsBoxFrame(aStyle
, aPresContext
, kClassID
),
88 mScrollingWithAPZ(false),
89 mSuppressionActive(false) {}
92 nsSliderFrame::~nsSliderFrame() {
93 if (mSuppressionActive
) {
94 if (mozilla::PresShell
* presShell
= PresShell()) {
95 presShell
->SuppressDisplayport(false);
100 void nsSliderFrame::Init(nsIContent
* aContent
, nsContainerFrame
* aParent
,
101 nsIFrame
* aPrevInFlow
) {
102 nsBoxFrame::Init(aContent
, aParent
, aPrevInFlow
);
104 static bool gotPrefs
= false;
108 gMiddlePref
= Preferences::GetBool("middlemouse.scrollbarPosition");
109 gSnapMultiplier
= Preferences::GetInt("slider.snapMultiplier");
112 mCurPos
= GetCurrentPosition(aContent
);
115 void nsSliderFrame::RemoveFrame(ChildListID aListID
, nsIFrame
* aOldFrame
) {
116 nsBoxFrame::RemoveFrame(aListID
, aOldFrame
);
117 if (mFrames
.IsEmpty()) RemoveListener();
120 void nsSliderFrame::InsertFrames(ChildListID aListID
, nsIFrame
* aPrevFrame
,
121 const nsLineList::iterator
* aPrevFrameLine
,
122 nsFrameList
& aFrameList
) {
123 bool wasEmpty
= mFrames
.IsEmpty();
124 nsBoxFrame::InsertFrames(aListID
, aPrevFrame
, aPrevFrameLine
, aFrameList
);
125 if (wasEmpty
) AddListener();
128 void nsSliderFrame::AppendFrames(ChildListID aListID
, nsFrameList
& aFrameList
) {
129 // if we have no children and on was added then make sure we add the
131 bool wasEmpty
= mFrames
.IsEmpty();
132 nsBoxFrame::AppendFrames(aListID
, aFrameList
);
133 if (wasEmpty
) AddListener();
136 int32_t nsSliderFrame::GetCurrentPosition(nsIContent
* content
) {
137 return GetIntegerAttribute(content
, nsGkAtoms::curpos
, 0);
140 int32_t nsSliderFrame::GetMinPosition(nsIContent
* content
) {
141 return GetIntegerAttribute(content
, nsGkAtoms::minpos
, 0);
144 int32_t nsSliderFrame::GetMaxPosition(nsIContent
* content
) {
145 return GetIntegerAttribute(content
, nsGkAtoms::maxpos
, 100);
148 int32_t nsSliderFrame::GetIncrement(nsIContent
* content
) {
149 return GetIntegerAttribute(content
, nsGkAtoms::increment
, 1);
152 int32_t nsSliderFrame::GetPageIncrement(nsIContent
* content
) {
153 return GetIntegerAttribute(content
, nsGkAtoms::pageincrement
, 10);
156 int32_t nsSliderFrame::GetIntegerAttribute(nsIContent
* content
, nsAtom
* atom
,
157 int32_t defaultValue
) {
159 if (content
->IsElement()) {
160 content
->AsElement()->GetAttr(kNameSpaceID_None
, atom
, value
);
162 if (!value
.IsEmpty()) {
165 // convert it to an integer
166 defaultValue
= value
.ToInteger(&error
);
172 nsresult
nsSliderFrame::AttributeChanged(int32_t aNameSpaceID
,
173 nsAtom
* aAttribute
, int32_t aModType
) {
175 nsBoxFrame::AttributeChanged(aNameSpaceID
, aAttribute
, aModType
);
176 // if the current position changes
177 if (aAttribute
== nsGkAtoms::curpos
) {
178 CurrentPositionChanged();
179 } else if (aAttribute
== nsGkAtoms::minpos
||
180 aAttribute
== nsGkAtoms::maxpos
) {
183 nsIFrame
* scrollbarBox
= GetScrollbar();
184 nsCOMPtr
<nsIContent
> scrollbar
= GetContentOfBox(scrollbarBox
);
185 int32_t current
= GetCurrentPosition(scrollbar
);
186 int32_t min
= GetMinPosition(scrollbar
);
187 int32_t max
= GetMaxPosition(scrollbar
);
189 if (current
< min
|| current
> max
) {
190 int32_t direction
= 0;
191 if (current
< min
|| max
< min
) {
194 } else if (current
> max
) {
199 // set the new position and notify observers
200 nsScrollbarFrame
* scrollbarFrame
= do_QueryFrame(scrollbarBox
);
201 if (scrollbarFrame
) {
202 nsIScrollbarMediator
* mediator
= scrollbarFrame
->GetScrollbarMediator();
203 scrollbarFrame
->SetIncrementToWhole(direction
);
205 mediator
->ScrollByWhole(scrollbarFrame
, direction
,
206 nsIScrollbarMediator::ENABLE_SNAP
);
209 // 'this' might be destroyed here
211 nsContentUtils::AddScriptRunner(new nsSetAttrRunnable(
212 scrollbar
->AsElement(), nsGkAtoms::curpos
, current
));
216 if (aAttribute
== nsGkAtoms::minpos
|| aAttribute
== nsGkAtoms::maxpos
||
217 aAttribute
== nsGkAtoms::pageincrement
||
218 aAttribute
== nsGkAtoms::increment
) {
219 PresShell()->FrameNeedsReflow(this, IntrinsicDirty::StyleChange
,
226 void nsSliderFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
227 const nsDisplayListSet
& aLists
) {
228 if (aBuilder
->IsForEventDelivery() && isDraggingThumb()) {
229 // This is EVIL, we shouldn't be messing with event delivery just to get
230 // thumb mouse drag events to arrive at the slider!
231 aLists
.Outlines()->AppendNewToTop
<nsDisplayEventReceiver
>(aBuilder
, this);
235 nsBoxFrame::BuildDisplayList(aBuilder
, aLists
);
238 static bool UsesCustomScrollbarMediator(nsIFrame
* scrollbarBox
) {
239 if (nsScrollbarFrame
* scrollbarFrame
= do_QueryFrame(scrollbarBox
)) {
240 if (nsIScrollbarMediator
* mediator
=
241 scrollbarFrame
->GetScrollbarMediator()) {
242 nsIScrollableFrame
* scrollFrame
= do_QueryFrame(mediator
);
243 // The scrollbar mediator is not the scroll frame.
244 // That means this scroll frame has a custom scrollbar mediator.
253 void nsSliderFrame::BuildDisplayListForChildren(
254 nsDisplayListBuilder
* aBuilder
, const nsDisplayListSet
& aLists
) {
255 // if we are too small to have a thumb don't paint it.
256 nsIFrame
* thumb
= nsBox::GetChildXULBox(this);
259 nsRect
thumbRect(thumb
->GetRect());
261 thumb
->GetXULMargin(m
);
262 thumbRect
.Inflate(m
);
265 GetXULClientRect(sliderTrack
);
267 if (sliderTrack
.width
< thumbRect
.width
||
268 sliderTrack
.height
< thumbRect
.height
)
271 // If this scrollbar is the scrollbar of an actively scrolled scroll frame,
272 // layerize the scrollbar thumb, wrap it in its own ContainerLayer and
273 // attach scrolling information to it.
274 // We do this here and not in the thumb's nsBoxFrame::BuildDisplayList so
275 // that the event region that gets created for the thumb is included in
276 // the nsDisplayOwnLayer contents.
278 const mozilla::layers::ScrollableLayerGuid::ViewID scrollTargetId
=
279 aBuilder
->GetCurrentScrollbarTarget();
280 const bool thumbGetsLayer
=
281 (scrollTargetId
!= layers::ScrollableLayerGuid::NULL_SCROLL_ID
);
283 if (thumbGetsLayer
) {
284 const Maybe
<ScrollDirection
> scrollDirection
=
285 aBuilder
->GetCurrentScrollbarDirection();
286 MOZ_ASSERT(scrollDirection
.isSome());
287 const bool isHorizontal
=
288 *scrollDirection
== ScrollDirection::eHorizontal
;
289 const float appUnitsPerCss
= float(AppUnitsPerCSSPixel());
290 const CSSCoord thumbLength
= NSAppUnitsToFloatPixels(
291 isHorizontal
? thumbRect
.width
: thumbRect
.height
, appUnitsPerCss
);
293 nsIFrame
* scrollbarBox
= GetScrollbar();
294 bool isAsyncDraggable
= !UsesCustomScrollbarMediator(scrollbarBox
);
296 nsPoint scrollPortOrigin
;
297 if (nsIScrollableFrame
* scrollFrame
=
298 do_QueryFrame(scrollbarBox
->GetParent())) {
299 scrollPortOrigin
= scrollFrame
->GetScrollPortRect().TopLeft();
301 isAsyncDraggable
= false;
304 // This rect is the range in which the scroll thumb can slide in.
305 sliderTrack
= sliderTrack
+ GetRect().TopLeft() +
306 scrollbarBox
->GetPosition() - scrollPortOrigin
;
307 const CSSCoord sliderTrackStart
= NSAppUnitsToFloatPixels(
308 isHorizontal
? sliderTrack
.x
: sliderTrack
.y
, appUnitsPerCss
);
309 const CSSCoord sliderTrackLength
= NSAppUnitsToFloatPixels(
310 isHorizontal
? sliderTrack
.width
: sliderTrack
.height
,
312 const CSSCoord thumbStart
= NSAppUnitsToFloatPixels(
313 isHorizontal
? thumbRect
.x
: thumbRect
.y
, appUnitsPerCss
);
315 const nsRect overflow
= thumb
->GetVisualOverflowRectRelativeToParent();
316 nsSize refSize
= aBuilder
->RootReferenceFrame()->GetSize();
317 const gfxSize scale
= nsLayoutUtils::GetTransformToAncestorScale(thumb
);
318 if (scale
.width
!= 0 && scale
.height
!= 0) {
319 refSize
.width
/= scale
.width
;
320 refSize
.height
/= scale
.height
;
322 nsRect dirty
= aBuilder
->GetVisibleRect().Intersect(thumbRect
);
323 dirty
= nsLayoutUtils::ComputePartialPrerenderArea(
324 aBuilder
->GetVisibleRect(), overflow
, refSize
);
326 nsDisplayListBuilder::AutoBuildingDisplayList
buildingDisplayList(
327 aBuilder
, this, dirty
, dirty
);
329 // Clip the thumb layer to the slider track. This is necessary to ensure
330 // FrameLayerBuilder is able to merge content before and after the
331 // scrollframe into the same layer (otherwise it thinks the thumb could
332 // potentially move anywhere within the existing clip).
333 DisplayListClipState::AutoSaveRestore
thumbClipState(aBuilder
);
334 thumbClipState
.ClipContainingBlockDescendants(
335 GetRectRelativeToSelf() + aBuilder
->ToReferenceFrame(this));
337 // Have the thumb's container layer capture the current clip, so
338 // it doesn't apply to the thumb's contents. This allows the contents
339 // to be fully rendered even if they're partially or fully offscreen,
340 // so async scrolling can still bring it into view.
341 DisplayListClipState::AutoSaveRestore
thumbContentsClipState(aBuilder
);
342 thumbContentsClipState
.Clear();
344 nsDisplayListBuilder::AutoContainerASRTracker
contASRTracker(aBuilder
);
345 nsDisplayListCollection
tempLists(aBuilder
);
346 nsBoxFrame::BuildDisplayListForChildren(aBuilder
, tempLists
);
348 // This is a bit of a hack. Collect up all descendant display items
349 // and merge them into a single Content() list.
350 nsDisplayList masterList
;
351 masterList
.AppendToTop(tempLists
.BorderBackground());
352 masterList
.AppendToTop(tempLists
.BlockBorderBackgrounds());
353 masterList
.AppendToTop(tempLists
.Floats());
354 masterList
.AppendToTop(tempLists
.Content());
355 masterList
.AppendToTop(tempLists
.PositionedDescendants());
356 masterList
.AppendToTop(tempLists
.Outlines());
358 // Restore the saved clip so it applies to the thumb container layer.
359 thumbContentsClipState
.Restore();
361 // Wrap the list to make it its own layer.
362 const ActiveScrolledRoot
* ownLayerASR
= contASRTracker
.GetContainerASR();
363 aLists
.Content()->AppendNewToTop
<nsDisplayOwnLayer
>(
364 aBuilder
, this, &masterList
, ownLayerASR
,
365 nsDisplayOwnLayerFlags::None
,
366 ScrollbarData::CreateForThumb(*scrollDirection
, GetThumbRatio(),
367 thumbStart
, thumbLength
,
368 isAsyncDraggable
, sliderTrackStart
,
369 sliderTrackLength
, scrollTargetId
),
370 true, false, nsDisplayOwnLayer::OwnLayerForScrollThumb
);
376 nsBoxFrame::BuildDisplayListForChildren(aBuilder
, aLists
);
380 nsSliderFrame::DoXULLayout(nsBoxLayoutState
& aState
) {
381 // get the thumb should be our only child
382 nsIFrame
* thumbBox
= nsBox::GetChildXULBox(this);
391 // get the content area inside our borders
393 GetXULClientRect(clientRect
);
396 nsIFrame
* scrollbarBox
= GetScrollbar();
397 nsCOMPtr
<nsIContent
> scrollbar
= GetContentOfBox(scrollbarBox
);
399 // get the thumb's pref size
400 nsSize thumbSize
= thumbBox
->GetXULPrefSize(aState
);
402 if (IsXULHorizontal())
403 thumbSize
.height
= clientRect
.height
;
405 thumbSize
.width
= clientRect
.width
;
407 int32_t curPos
= GetCurrentPosition(scrollbar
);
408 int32_t minPos
= GetMinPosition(scrollbar
);
409 int32_t maxPos
= GetMaxPosition(scrollbar
);
410 int32_t pageIncrement
= GetPageIncrement(scrollbar
);
412 maxPos
= std::max(minPos
, maxPos
);
413 curPos
= clamped(curPos
, minPos
, maxPos
);
415 nscoord
& availableLength
=
416 IsXULHorizontal() ? clientRect
.width
: clientRect
.height
;
417 nscoord
& thumbLength
= IsXULHorizontal() ? thumbSize
.width
: thumbSize
.height
;
419 if ((pageIncrement
+ maxPos
- minPos
) > 0 && thumbBox
->GetXULFlex() > 0) {
420 float ratio
= float(pageIncrement
) / float(maxPos
- minPos
+ pageIncrement
);
422 std::max(thumbLength
, NSToCoordRound(availableLength
* ratio
));
425 // Round the thumb's length to device pixels.
426 nsPresContext
* presContext
= PresContext();
427 thumbLength
= presContext
->DevPixelsToAppUnits(
428 presContext
->AppUnitsToDevPixels(thumbLength
));
430 // mRatio translates the thumb position in app units to the value.
431 mRatio
= (minPos
!= maxPos
)
432 ? float(availableLength
- thumbLength
) / float(maxPos
- minPos
)
435 // in reverse mode, curpos is reversed such that lower values are to the
436 // right or bottom and increase leftwards or upwards. In this case, use the
437 // offset from the end instead of the beginning.
438 bool reverse
= mContent
->AsElement()->AttrValueIs(
439 kNameSpaceID_None
, nsGkAtoms::dir
, nsGkAtoms::reverse
, eCaseMatters
);
440 nscoord pos
= reverse
? (maxPos
- curPos
) : (curPos
- minPos
);
442 // set the thumb's coord to be the current pos * the ratio.
443 nsRect
thumbRect(clientRect
.x
, clientRect
.y
, thumbSize
.width
,
445 int32_t& thumbPos
= (IsXULHorizontal() ? thumbRect
.x
: thumbRect
.y
);
446 thumbPos
+= NSToCoordRound(pos
* mRatio
);
448 nsRect
oldThumbRect(thumbBox
->GetRect());
449 LayoutChildAt(aState
, thumbBox
, thumbRect
);
453 // Redraw only if thumb changed size.
454 if (!oldThumbRect
.IsEqualInterior(thumbRect
)) XULRedraw(aState
);
459 nsresult
nsSliderFrame::HandleEvent(nsPresContext
* aPresContext
,
460 WidgetGUIEvent
* aEvent
,
461 nsEventStatus
* aEventStatus
) {
462 NS_ENSURE_ARG_POINTER(aEventStatus
);
464 if (mAPZDragInitiated
&&
465 *mAPZDragInitiated
== InputAPZContext::GetInputBlockId() &&
466 aEvent
->mMessage
== eMouseDown
) {
467 // If we get the mousedown after the APZ notification, then immediately
468 // switch into the state corresponding to an APZ thumb-drag. Note that
469 // we can't just do this in AsyncScrollbarDragInitiated() directly because
470 // the handling for this mousedown event in the presShell will reset the
471 // capturing content which makes isDraggingThumb() return false. We check
472 // the input block here to make sure that we correctly handle any ordering
473 // of {eMouseDown arriving, AsyncScrollbarDragInitiated() being called}.
474 mAPZDragInitiated
= Nothing();
476 mScrollingWithAPZ
= true;
480 // If a web page calls event.preventDefault() we still want to
481 // scroll when scroll arrow is clicked. See bug 511075.
482 if (!mContent
->IsInNativeAnonymousSubtree() &&
483 nsEventStatus_eConsumeNoDefault
== *aEventStatus
) {
487 if (!mDragFinished
&& !isDraggingThumb()) {
492 nsIFrame
* scrollbarBox
= GetScrollbar();
493 nsCOMPtr
<nsIContent
> scrollbar
;
494 scrollbar
= GetContentOfBox(scrollbarBox
);
495 bool isHorizontal
= IsXULHorizontal();
497 if (isDraggingThumb()) {
498 switch (aEvent
->mMessage
) {
501 if (mScrollingWithAPZ
) {
505 if (!GetEventPoint(aEvent
, eventPoint
)) {
509 // On Linux the destination point is determined by the initial click
510 // on the scrollbar track and doesn't change until the mouse button
512 #ifndef MOZ_WIDGET_GTK
513 // On the other platforms we need to update the destination point now.
514 mDestinationPoint
= eventPoint
;
521 nscoord pos
= isHorizontal
? eventPoint
.x
: eventPoint
.y
;
523 nsIFrame
* thumbFrame
= mFrames
.FirstChild();
528 // take our current position and subtract the start location
530 bool isMouseOutsideThumb
= false;
531 if (gSnapMultiplier
) {
532 nsSize thumbSize
= thumbFrame
->GetSize();
534 // horizontal scrollbar - check if mouse is above or below thumb
535 // XXXbz what about looking at the .y of the thumb's rect? Is that
537 if (eventPoint
.y
< -gSnapMultiplier
* thumbSize
.height
||
539 thumbSize
.height
+ gSnapMultiplier
* thumbSize
.height
)
540 isMouseOutsideThumb
= true;
542 // vertical scrollbar - check if mouse is left or right of thumb
543 if (eventPoint
.x
< -gSnapMultiplier
* thumbSize
.width
||
545 thumbSize
.width
+ gSnapMultiplier
* thumbSize
.width
)
546 isMouseOutsideThumb
= true;
549 if (aEvent
->mClass
== eTouchEventClass
) {
550 *aEventStatus
= nsEventStatus_eConsumeNoDefault
;
552 if (isMouseOutsideThumb
) {
553 SetCurrentThumbPosition(scrollbar
, mThumbStart
, false, false);
558 SetCurrentThumbPosition(scrollbar
, pos
, false, true); // with snapping
563 if (ShouldScrollForEvent(aEvent
)) {
565 // we MUST call nsFrame HandleEvent for mouse ups to maintain the
566 // selection state and capture state.
567 return nsFrame::HandleEvent(aPresContext
, aEvent
, aEventStatus
);
575 // return nsFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
577 } else if (ShouldScrollToClickForEvent(aEvent
)) {
579 if (!GetEventPoint(aEvent
, eventPoint
)) {
582 nscoord pos
= isHorizontal
? eventPoint
.x
: eventPoint
.y
;
584 // adjust so that the middle of the thumb is placed under the click
585 nsIFrame
* thumbFrame
= mFrames
.FirstChild();
589 nsSize thumbSize
= thumbFrame
->GetSize();
590 nscoord thumbLength
= isHorizontal
? thumbSize
.width
: thumbSize
.height
;
593 AutoWeakFrame
weakFrame(this);
594 // should aMaySnap be true here?
595 SetCurrentThumbPosition(scrollbar
, pos
- thumbLength
/ 2, false, false);
596 NS_ENSURE_TRUE(weakFrame
.IsAlive(), NS_OK
);
600 #ifdef MOZ_WIDGET_GTK
601 RefPtr
<Element
> thumb
= thumbFrame
->GetContent()->AsElement();
602 thumb
->SetAttr(kNameSpaceID_None
, nsGkAtoms::active
,
603 NS_LITERAL_STRING("true"), true);
606 if (aEvent
->mClass
== eTouchEventClass
) {
607 *aEventStatus
= nsEventStatus_eConsumeNoDefault
;
611 mThumbStart
= thumbFrame
->GetPosition().x
;
613 mThumbStart
= thumbFrame
->GetPosition().y
;
615 mDragStart
= pos
- mThumbStart
;
617 #ifdef MOZ_WIDGET_GTK
618 else if (ShouldScrollForEvent(aEvent
) && aEvent
->mClass
== eMouseEventClass
&&
619 aEvent
->AsMouseEvent()->mButton
== MouseButton::eRight
) {
620 // HandlePress and HandleRelease are usually called via
621 // nsFrame::HandleEvent, but only for the left mouse button.
622 if (aEvent
->mMessage
== eMouseDown
) {
623 HandlePress(aPresContext
, aEvent
, aEventStatus
);
624 } else if (aEvent
->mMessage
== eMouseUp
) {
625 HandleRelease(aPresContext
, aEvent
, aEventStatus
);
632 // XXX hack until handle release is actually called in nsframe.
633 // if (aEvent->mMessage == eMouseOut ||
634 // aEvent->mMessage == NS_MOUSE_RIGHT_BUTTON_UP ||
635 // aEvent->mMessage == NS_MOUSE_LEFT_BUTTON_UP) {
636 // HandleRelease(aPresContext, aEvent, aEventStatus);
639 if (aEvent
->mMessage
== eMouseOut
&& mChange
)
640 HandleRelease(aPresContext
, aEvent
, aEventStatus
);
642 return nsFrame::HandleEvent(aPresContext
, aEvent
, aEventStatus
);
645 // Helper function to collect the "scroll to click" metric. Beware of
646 // caching this, users expect to be able to change the system preference
647 // and see the browser change its behavior immediately.
648 bool nsSliderFrame::GetScrollToClick() {
649 if (GetScrollbar() != this) {
650 return LookAndFeel::GetInt(LookAndFeel::eIntID_ScrollToClick
, false);
653 if (mContent
->AsElement()->AttrValueIs(kNameSpaceID_None
,
654 nsGkAtoms::movetoclick
,
655 nsGkAtoms::_true
, eCaseMatters
)) {
658 if (mContent
->AsElement()->AttrValueIs(kNameSpaceID_None
,
659 nsGkAtoms::movetoclick
,
660 nsGkAtoms::_false
, eCaseMatters
)) {
671 nsIFrame
* nsSliderFrame::GetScrollbar() {
672 // if we are in a scrollbar then return the scrollbar's content node
673 // if we are not then return ours.
675 nsScrollbarButtonFrame::GetParentWithTag(nsGkAtoms::scrollbar
, this,
678 if (scrollbar
== nullptr) return this;
680 return scrollbar
->IsXULBoxFrame() ? scrollbar
: this;
683 void nsSliderFrame::PageUpDown(nscoord change
) {
684 // on a page up or down get our page increment. We get this by getting the
685 // scrollbar we are in and asking it for the current position and the page
686 // increment. If we are not in a scrollbar we will get the values from our own
688 nsIFrame
* scrollbarBox
= GetScrollbar();
689 nsCOMPtr
<nsIContent
> scrollbar
;
690 scrollbar
= GetContentOfBox(scrollbarBox
);
692 nscoord pageIncrement
= GetPageIncrement(scrollbar
);
693 int32_t curpos
= GetCurrentPosition(scrollbar
);
694 int32_t minpos
= GetMinPosition(scrollbar
);
695 int32_t maxpos
= GetMaxPosition(scrollbar
);
697 // get the new position and make sure it is in bounds
698 int32_t newpos
= curpos
+ change
* pageIncrement
;
699 if (newpos
< minpos
|| maxpos
< minpos
)
701 else if (newpos
> maxpos
)
704 SetCurrentPositionInternal(scrollbar
, newpos
, true);
707 // called when the current position changed and we need to update the thumb's
709 void nsSliderFrame::CurrentPositionChanged() {
710 nsIFrame
* scrollbarBox
= GetScrollbar();
711 nsCOMPtr
<nsIContent
> scrollbar
= GetContentOfBox(scrollbarBox
);
713 // get the current position
714 int32_t curPos
= GetCurrentPosition(scrollbar
);
716 // do nothing if the position did not change
717 if (mCurPos
== curPos
) return;
719 // get our current min and max position from our content node
720 int32_t minPos
= GetMinPosition(scrollbar
);
721 int32_t maxPos
= GetMaxPosition(scrollbar
);
723 maxPos
= std::max(minPos
, maxPos
);
724 curPos
= clamped(curPos
, minPos
, maxPos
);
726 // get the thumb's rect
727 nsIFrame
* thumbFrame
= mFrames
.FirstChild();
728 if (!thumbFrame
) return; // The thumb may stream in asynchronously via XBL.
730 nsRect thumbRect
= thumbFrame
->GetRect();
733 GetXULClientRect(clientRect
);
735 // figure out the new rect
736 nsRect
newThumbRect(thumbRect
);
738 bool reverse
= mContent
->AsElement()->AttrValueIs(
739 kNameSpaceID_None
, nsGkAtoms::dir
, nsGkAtoms::reverse
, eCaseMatters
);
740 nscoord pos
= reverse
? (maxPos
- curPos
) : (curPos
- minPos
);
742 if (IsXULHorizontal())
743 newThumbRect
.x
= clientRect
.x
+ NSToCoordRound(pos
* mRatio
);
745 newThumbRect
.y
= clientRect
.y
+ NSToCoordRound(pos
* mRatio
);
747 // avoid putting the scroll thumb at subpixel positions which cause needless
749 nscoord appUnitsPerPixel
= PresContext()->AppUnitsPerDevPixel();
750 nsPoint snappedThumbLocation
=
751 ToAppUnits(newThumbRect
.TopLeft().ToNearestPixels(appUnitsPerPixel
),
753 if (IsXULHorizontal()) {
754 newThumbRect
.x
= snappedThumbLocation
.x
;
756 newThumbRect
.y
= snappedThumbLocation
.y
;
760 thumbFrame
->SetRect(newThumbRect
);
762 // Request a repaint of the scrollbar
763 nsScrollbarFrame
* scrollbarFrame
= do_QueryFrame(scrollbarBox
);
764 nsIScrollbarMediator
* mediator
=
765 scrollbarFrame
? scrollbarFrame
->GetScrollbarMediator() : nullptr;
766 if (!mediator
|| !mediator
->ShouldSuppressScrollbarRepaints()) {
773 static void UpdateAttribute(Element
* aScrollbar
, nscoord aNewPos
, bool aNotify
,
776 str
.AppendInt(aNewPos
);
779 aScrollbar
->SetAttr(kNameSpaceID_None
, nsGkAtoms::smooth
,
780 NS_LITERAL_STRING("true"), false);
782 aScrollbar
->SetAttr(kNameSpaceID_None
, nsGkAtoms::curpos
, str
, aNotify
);
784 aScrollbar
->UnsetAttr(kNameSpaceID_None
, nsGkAtoms::smooth
, false);
788 // Use this function when you want to set the scroll position via the position
789 // of the scrollbar thumb, e.g. when dragging the slider. This function scrolls
790 // the content in such a way that thumbRect.x/.y becomes aNewThumbPos.
791 void nsSliderFrame::SetCurrentThumbPosition(nsIContent
* aScrollbar
,
792 nscoord aNewThumbPos
,
793 bool aIsSmooth
, bool aMaySnap
) {
795 GetXULClientRect(crect
);
796 nscoord offset
= IsXULHorizontal() ? crect
.x
: crect
.y
;
797 int32_t newPos
= NSToIntRound((aNewThumbPos
- offset
) / mRatio
);
800 mContent
->AsElement()->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::snap
,
801 nsGkAtoms::_true
, eCaseMatters
)) {
802 // If snap="true", then the slider may only be set to min + (increment * x).
803 // Otherwise, the slider may be set to any positive integer.
804 int32_t increment
= GetIncrement(aScrollbar
);
805 newPos
= NSToIntRound(newPos
/ float(increment
)) * increment
;
808 SetCurrentPosition(aScrollbar
, newPos
, aIsSmooth
);
811 // Use this function when you know the target scroll position of the scrolled
812 // content. aNewPos should be passed to this function as a position as if the
813 // minpos is 0. That is, the minpos will be added to the position by this
814 // function. In a reverse direction slider, the newpos should be the distance
816 void nsSliderFrame::SetCurrentPosition(nsIContent
* aScrollbar
, int32_t aNewPos
,
818 // get min and max position from our content node
819 int32_t minpos
= GetMinPosition(aScrollbar
);
820 int32_t maxpos
= GetMaxPosition(aScrollbar
);
822 // in reverse direction sliders, flip the value so that it goes from
823 // right to left, or bottom to top.
824 if (mContent
->AsElement()->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::dir
,
825 nsGkAtoms::reverse
, eCaseMatters
))
826 aNewPos
= maxpos
- aNewPos
;
830 // get the new position and make sure it is in bounds
831 if (aNewPos
< minpos
|| maxpos
< minpos
)
833 else if (aNewPos
> maxpos
)
836 SetCurrentPositionInternal(aScrollbar
, aNewPos
, aIsSmooth
);
839 void nsSliderFrame::SetCurrentPositionInternal(nsIContent
* aScrollbar
,
842 nsCOMPtr
<nsIContent
> scrollbar
= aScrollbar
;
843 nsIFrame
* scrollbarBox
= GetScrollbar();
844 AutoWeakFrame
weakFrame(this);
848 nsScrollbarFrame
* scrollbarFrame
= do_QueryFrame(scrollbarBox
);
849 if (scrollbarFrame
) {
850 // See if we have a mediator.
851 nsIScrollbarMediator
* mediator
= scrollbarFrame
->GetScrollbarMediator();
854 nsPresContext::CSSPixelsToAppUnits(GetCurrentPosition(scrollbar
));
855 nscoord newPos
= nsPresContext::CSSPixelsToAppUnits(aNewPos
);
856 mediator
->ThumbMoved(scrollbarFrame
, oldPos
, newPos
);
857 if (!weakFrame
.IsAlive()) {
860 UpdateAttribute(scrollbar
->AsElement(), aNewPos
, /* aNotify */ false,
862 CurrentPositionChanged();
863 mUserChanged
= false;
868 UpdateAttribute(scrollbar
->AsElement(), aNewPos
, true, aIsSmooth
);
869 if (!weakFrame
.IsAlive()) {
872 mUserChanged
= false;
875 printf("Current Pos=%d\n", aNewPos
);
879 void nsSliderFrame::SetInitialChildList(ChildListID aListID
,
880 nsFrameList
& aChildList
) {
881 nsBoxFrame::SetInitialChildList(aListID
, aChildList
);
882 if (aListID
== kPrincipalList
) {
887 nsresult
nsSliderMediator::HandleEvent(dom::Event
* aEvent
) {
888 // Only process the event if the thumb is not being dragged.
889 if (mSlider
&& !mSlider
->isDraggingThumb()) return mSlider
->StartDrag(aEvent
);
894 class AsyncScrollbarDragStarter final
: public nsAPostRefreshObserver
{
896 AsyncScrollbarDragStarter(mozilla::PresShell
* aPresShell
, nsIWidget
* aWidget
,
897 const AsyncDragMetrics
& aDragMetrics
)
898 : mPresShell(aPresShell
), mWidget(aWidget
), mDragMetrics(aDragMetrics
) {}
899 virtual ~AsyncScrollbarDragStarter() {}
901 void DidRefresh() override
{
903 MOZ_ASSERT_UNREACHABLE(
904 "Post-refresh observer fired again after failed attempt at "
909 mWidget
->StartAsyncScrollbarDrag(mDragMetrics
);
911 if (!mPresShell
->RemovePostRefreshObserver(this)) {
912 MOZ_ASSERT_UNREACHABLE(
913 "Unable to unregister post-refresh observer! Leaking it instead of "
914 "leaving garbage registered");
915 // Graceful handling, just in case...
916 mPresShell
= nullptr;
925 RefPtr
<mozilla::PresShell
> mPresShell
;
926 RefPtr
<nsIWidget
> mWidget
;
927 AsyncDragMetrics mDragMetrics
;
930 static bool ScrollFrameWillBuildScrollInfoLayer(nsIFrame
* aScrollFrame
) {
932 * Note: if changing the conditions in this function, make a corresponding
933 * change to nsDisplayListBuilder::ShouldBuildScrollInfoItemsForHoisting()
934 * in nsDisplayList.cpp.
936 nsIFrame
* current
= aScrollFrame
;
938 if (nsSVGIntegrationUtils::UsesSVGEffectsNotSupportedInCompositor(
942 current
= nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(current
);
947 nsIScrollableFrame
* nsSliderFrame::GetScrollFrame() {
948 nsIFrame
* scrollbarBox
= GetScrollbar();
953 nsContainerFrame
* scrollFrame
= scrollbarBox
->GetParent();
958 nsIScrollableFrame
* scrollFrameAsScrollable
= do_QueryFrame(scrollFrame
);
959 return scrollFrameAsScrollable
;
962 void nsSliderFrame::StartAPZDrag(WidgetGUIEvent
* aEvent
) {
963 if (!aEvent
->mFlags
.mHandledByAPZ
) {
967 if (!gfxPlatform::GetPlatform()->SupportsApzDragInput()) {
971 nsIFrame
* scrollbarBox
= GetScrollbar();
972 nsContainerFrame
* scrollFrame
= scrollbarBox
->GetParent();
977 nsIContent
* scrollableContent
= scrollFrame
->GetContent();
978 if (!scrollableContent
) {
982 // APZ dragging requires the scrollbar to be layerized, which doesn't
983 // happen for scroll info layers.
984 if (ScrollFrameWillBuildScrollInfoLayer(scrollFrame
)) {
988 // Custom scrollbar mediators are not supported in the APZ codepath.
989 if (UsesCustomScrollbarMediator(scrollbarBox
)) {
993 bool isHorizontal
= IsXULHorizontal();
995 mozilla::layers::ScrollableLayerGuid::ViewID scrollTargetId
;
996 bool hasID
= nsLayoutUtils::FindIDFor(scrollableContent
, &scrollTargetId
);
998 hasID
&& (scrollTargetId
!= layers::ScrollableLayerGuid::NULL_SCROLL_ID
);
1004 if (!nsLayoutUtils::HasDisplayPort(scrollableContent
)) {
1008 mozilla::PresShell
* presShell
= PresShell();
1009 uint64_t inputblockId
= InputAPZContext::GetInputBlockId();
1010 uint32_t presShellId
= presShell
->GetPresShellId();
1011 AsyncDragMetrics
dragMetrics(
1012 scrollTargetId
, presShellId
, inputblockId
,
1013 NSAppUnitsToFloatPixels(mDragStart
, float(AppUnitsPerCSSPixel())),
1014 isHorizontal
? ScrollDirection::eHorizontal
: ScrollDirection::eVertical
);
1016 // It's important to set this before calling
1017 // nsIWidget::StartAsyncScrollbarDrag(), because in some configurations, that
1018 // can call AsyncScrollbarDragRejected() synchronously, which clears the flag
1019 // (and we want it to stay cleared in that case).
1020 mScrollingWithAPZ
= true;
1022 // When we start an APZ drag, we wont get mouse events for the drag.
1023 // APZ will consume them all and only notify us of the new scroll position.
1024 bool waitForRefresh
= InputAPZContext::HavePendingLayerization();
1025 nsIWidget
* widget
= this->GetNearestWidget();
1026 if (waitForRefresh
) {
1027 waitForRefresh
= presShell
->AddPostRefreshObserver(
1028 new AsyncScrollbarDragStarter(presShell
, widget
, dragMetrics
));
1030 if (!waitForRefresh
) {
1031 widget
->StartAsyncScrollbarDrag(dragMetrics
);
1035 nsresult
nsSliderFrame::StartDrag(Event
* aEvent
) {
1037 printf("Begin dragging\n");
1039 if (mContent
->AsElement()->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::disabled
,
1040 nsGkAtoms::_true
, eCaseMatters
))
1043 WidgetGUIEvent
* event
= aEvent
->WidgetEventPtr()->AsGUIEvent();
1045 if (!ShouldScrollForEvent(event
)) {
1050 if (!GetEventPoint(event
, pt
)) {
1053 bool isHorizontal
= IsXULHorizontal();
1054 nscoord pos
= isHorizontal
? pt
.x
: pt
.y
;
1056 // If we should scroll-to-click, first place the middle of the slider thumb
1058 nsCOMPtr
<nsIContent
> scrollbar
;
1059 nscoord newpos
= pos
;
1060 bool scrollToClick
= ShouldScrollToClickForEvent(event
);
1061 if (scrollToClick
) {
1062 // adjust so that the middle of the thumb is placed under the click
1063 nsIFrame
* thumbFrame
= mFrames
.FirstChild();
1067 nsSize thumbSize
= thumbFrame
->GetSize();
1068 nscoord thumbLength
= isHorizontal
? thumbSize
.width
: thumbSize
.height
;
1070 newpos
-= (thumbLength
/ 2);
1072 nsIFrame
* scrollbarBox
= GetScrollbar();
1073 scrollbar
= GetContentOfBox(scrollbarBox
);
1078 if (scrollToClick
) {
1079 // should aMaySnap be true here?
1080 SetCurrentThumbPosition(scrollbar
, newpos
, false, false);
1083 nsIFrame
* thumbFrame
= mFrames
.FirstChild();
1088 #ifdef MOZ_WIDGET_GTK
1089 RefPtr
<Element
> thumb
= thumbFrame
->GetContent()->AsElement();
1090 thumb
->SetAttr(kNameSpaceID_None
, nsGkAtoms::active
,
1091 NS_LITERAL_STRING("true"), true);
1095 mThumbStart
= thumbFrame
->GetPosition().x
;
1097 mThumbStart
= thumbFrame
->GetPosition().y
;
1099 mDragStart
= pos
- mThumbStart
;
1101 mScrollingWithAPZ
= false;
1102 StartAPZDrag(event
); // sets mScrollingWithAPZ=true if appropriate
1105 printf("Pressed mDragStart=%d\n", mDragStart
);
1108 if (!mScrollingWithAPZ
) {
1109 SuppressDisplayport();
1115 nsresult
nsSliderFrame::StopDrag() {
1119 mScrollingWithAPZ
= false;
1121 UnsuppressDisplayport();
1123 #ifdef MOZ_WIDGET_GTK
1124 nsIFrame
* thumbFrame
= mFrames
.FirstChild();
1126 RefPtr
<Element
> thumb
= thumbFrame
->GetContent()->AsElement();
1127 thumb
->UnsetAttr(kNameSpaceID_None
, nsGkAtoms::active
, true);
1138 void nsSliderFrame::DragThumb(bool aGrabMouseEvents
) {
1139 mDragFinished
= !aGrabMouseEvents
;
1141 if (aGrabMouseEvents
) {
1142 PresShell::SetCapturingContent(GetContent(),
1143 CaptureFlags::IgnoreAllowedState
);
1145 PresShell::ReleaseCapturingContent();
1149 bool nsSliderFrame::isDraggingThumb() const {
1150 return PresShell::GetCapturingContent() == GetContent();
1153 void nsSliderFrame::AddListener() {
1155 mMediator
= new nsSliderMediator(this);
1158 nsIFrame
* thumbFrame
= mFrames
.FirstChild();
1162 thumbFrame
->GetContent()->AddSystemEventListener(
1163 NS_LITERAL_STRING("mousedown"), mMediator
, false, false);
1164 thumbFrame
->GetContent()->AddSystemEventListener(
1165 NS_LITERAL_STRING("touchstart"), mMediator
, false, false);
1168 void nsSliderFrame::RemoveListener() {
1169 NS_ASSERTION(mMediator
, "No listener was ever added!!");
1171 nsIFrame
* thumbFrame
= mFrames
.FirstChild();
1172 if (!thumbFrame
) return;
1174 thumbFrame
->GetContent()->RemoveSystemEventListener(
1175 NS_LITERAL_STRING("mousedown"), mMediator
, false);
1178 bool nsSliderFrame::ShouldScrollForEvent(WidgetGUIEvent
* aEvent
) {
1179 switch (aEvent
->mMessage
) {
1185 uint16_t button
= aEvent
->AsMouseEvent()->mButton
;
1186 #ifdef MOZ_WIDGET_GTK
1187 return (button
== MouseButton::eLeft
) ||
1188 (button
== MouseButton::eRight
&& GetScrollToClick()) ||
1189 (button
== MouseButton::eMiddle
&& gMiddlePref
&&
1190 !GetScrollToClick());
1192 return (button
== MouseButton::eLeft
) ||
1193 (button
== MouseButton::eMiddle
&& gMiddlePref
);
1201 bool nsSliderFrame::ShouldScrollToClickForEvent(WidgetGUIEvent
* aEvent
) {
1202 if (!ShouldScrollForEvent(aEvent
)) {
1206 if (aEvent
->mMessage
!= eMouseDown
&& aEvent
->mMessage
!= eTouchStart
) {
1210 #if defined(XP_MACOSX) || defined(MOZ_WIDGET_GTK)
1211 // On Mac and Linux, clicking the scrollbar thumb should never scroll to
1213 if (IsEventOverThumb(aEvent
)) {
1218 if (aEvent
->mMessage
== eTouchStart
) {
1219 return GetScrollToClick();
1222 WidgetMouseEvent
* mouseEvent
= aEvent
->AsMouseEvent();
1223 if (mouseEvent
->mButton
== MouseButton::eLeft
) {
1225 bool invertPref
= mouseEvent
->IsAlt();
1227 bool invertPref
= mouseEvent
->IsShift();
1229 return GetScrollToClick() != invertPref
;
1232 #ifdef MOZ_WIDGET_GTK
1233 if (mouseEvent
->mButton
== MouseButton::eRight
) {
1234 return !GetScrollToClick();
1241 bool nsSliderFrame::IsEventOverThumb(WidgetGUIEvent
* aEvent
) {
1242 nsIFrame
* thumbFrame
= mFrames
.FirstChild();
1248 if (!GetEventPoint(aEvent
, eventPoint
)) {
1252 nsRect thumbRect
= thumbFrame
->GetRect();
1253 #if defined(MOZ_WIDGET_GTK)
1254 /* Scrollbar track can have padding, so it's better to check that eventPoint
1255 * is inside of actual thumb, not just its one axis. The part of the scrollbar
1256 * track adjacent to thumb can actually receive events in GTK3 */
1257 return eventPoint
.x
>= thumbRect
.x
&& eventPoint
.x
< thumbRect
.XMost() &&
1258 eventPoint
.y
>= thumbRect
.y
&& eventPoint
.y
< thumbRect
.YMost();
1260 bool isHorizontal
= IsXULHorizontal();
1261 nscoord eventPos
= isHorizontal
? eventPoint
.x
: eventPoint
.y
;
1262 nscoord thumbStart
= isHorizontal
? thumbRect
.x
: thumbRect
.y
;
1263 nscoord thumbEnd
= isHorizontal
? thumbRect
.XMost() : thumbRect
.YMost();
1265 return eventPos
>= thumbStart
&& eventPos
< thumbEnd
;
1270 nsSliderFrame::HandlePress(nsPresContext
* aPresContext
, WidgetGUIEvent
* aEvent
,
1271 nsEventStatus
* aEventStatus
) {
1272 if (!ShouldScrollForEvent(aEvent
) || ShouldScrollToClickForEvent(aEvent
)) {
1276 if (IsEventOverThumb(aEvent
)) {
1280 nsIFrame
* thumbFrame
= mFrames
.FirstChild();
1281 if (!thumbFrame
) // display:none?
1284 if (mContent
->AsElement()->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::disabled
,
1285 nsGkAtoms::_true
, eCaseMatters
))
1288 nsRect thumbRect
= thumbFrame
->GetRect();
1292 if (!GetEventPoint(aEvent
, eventPoint
)) {
1296 if (IsXULHorizontal() ? eventPoint
.x
< thumbRect
.x
1297 : eventPoint
.y
< thumbRect
.y
)
1302 // On Linux we want to keep scrolling in the direction indicated by |change|
1303 // until the mouse is released. On the other platforms we want to stop
1304 // scrolling as soon as the scrollbar thumb has reached the current mouse
1306 #ifdef MOZ_WIDGET_GTK
1308 GetXULClientRect(clientRect
);
1310 // Set the destination point to the very end of the scrollbar so that
1311 // scrolling doesn't stop halfway through.
1313 mDestinationPoint
= nsPoint(clientRect
.width
, clientRect
.height
);
1315 mDestinationPoint
= nsPoint(0, 0);
1318 mDestinationPoint
= eventPoint
;
1327 nsSliderFrame::HandleRelease(nsPresContext
* aPresContext
,
1328 WidgetGUIEvent
* aEvent
,
1329 nsEventStatus
* aEventStatus
) {
1332 nsIFrame
* scrollbar
= GetScrollbar();
1333 nsScrollbarFrame
* sb
= do_QueryFrame(scrollbar
);
1335 nsIScrollbarMediator
* m
= sb
->GetScrollbarMediator();
1337 m
->ScrollbarReleased(sb
);
1343 void nsSliderFrame::DestroyFrom(nsIFrame
* aDestructRoot
,
1344 PostDestroyData
& aPostDestroyData
) {
1345 // tell our mediator if we have one we are gone.
1347 mMediator
->SetSlider(nullptr);
1348 mMediator
= nullptr;
1352 // call base class Destroy()
1353 nsBoxFrame::DestroyFrom(aDestructRoot
, aPostDestroyData
);
1356 nsSize
nsSliderFrame::GetXULPrefSize(nsBoxLayoutState
& aState
) {
1358 return nsBoxFrame::GetXULPrefSize(aState
);
1361 nsSize
nsSliderFrame::GetXULMinSize(nsBoxLayoutState
& aState
) {
1364 // our min size is just our borders and padding
1365 return nsBox::GetXULMinSize(aState
);
1368 nsSize
nsSliderFrame::GetXULMaxSize(nsBoxLayoutState
& aState
) {
1370 return nsBoxFrame::GetXULMaxSize(aState
);
1373 void nsSliderFrame::EnsureOrient() {
1374 nsIFrame
* scrollbarBox
= GetScrollbar();
1377 (scrollbarBox
->GetStateBits() & NS_STATE_IS_HORIZONTAL
) != 0;
1379 AddStateBits(NS_STATE_IS_HORIZONTAL
);
1381 RemoveStateBits(NS_STATE_IS_HORIZONTAL
);
1384 void nsSliderFrame::Notify(void) {
1387 nsIFrame
* thumbFrame
= mFrames
.FirstChild();
1392 nsRect thumbRect
= thumbFrame
->GetRect();
1394 bool isHorizontal
= IsXULHorizontal();
1396 // See if the thumb has moved past our destination point.
1397 // if it has we want to stop.
1400 if (thumbRect
.x
< mDestinationPoint
.x
) stop
= true;
1402 if (thumbRect
.x
+ thumbRect
.width
> mDestinationPoint
.x
) stop
= true;
1406 if (thumbRect
.y
< mDestinationPoint
.y
) stop
= true;
1408 if (thumbRect
.y
+ thumbRect
.height
> mDestinationPoint
.y
) stop
= true;
1415 PageScroll(mChange
);
1419 void nsSliderFrame::PageScroll(nscoord aChange
) {
1420 if (mContent
->AsElement()->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::dir
,
1421 nsGkAtoms::reverse
, eCaseMatters
)) {
1424 nsIFrame
* scrollbar
= GetScrollbar();
1425 nsScrollbarFrame
* sb
= do_QueryFrame(scrollbar
);
1427 nsIScrollbarMediator
* m
= sb
->GetScrollbarMediator();
1428 sb
->SetIncrementToPage(aChange
);
1430 m
->ScrollByPage(sb
, aChange
, nsIScrollbarMediator::ENABLE_SNAP
);
1434 PageUpDown(aChange
);
1437 float nsSliderFrame::GetThumbRatio() const {
1438 // mRatio is in thumb app units per scrolled css pixels. Convert it to a
1439 // ratio of the thumb's CSS pixels per scrolled CSS pixels. (Note the thumb
1440 // is in the scrollframe's parent's space whereas the scrolled CSS pixels
1441 // are in the scrollframe's space).
1442 return mRatio
/ mozilla::AppUnitsPerCSSPixel();
1445 void nsSliderFrame::AsyncScrollbarDragInitiated(uint64_t aDragBlockId
) {
1446 mAPZDragInitiated
= Some(aDragBlockId
);
1449 void nsSliderFrame::AsyncScrollbarDragRejected() {
1450 mScrollingWithAPZ
= false;
1451 // Only suppress the displayport if we're still dragging the thumb.
1452 // Otherwise, no one will unsuppress it.
1453 if (isDraggingThumb()) {
1454 SuppressDisplayport();
1458 void nsSliderFrame::SuppressDisplayport() {
1459 if (!mSuppressionActive
) {
1460 PresShell()->SuppressDisplayport(true);
1461 mSuppressionActive
= true;
1465 void nsSliderFrame::UnsuppressDisplayport() {
1466 if (mSuppressionActive
) {
1467 PresShell()->SuppressDisplayport(false);
1468 mSuppressionActive
= false;
1472 bool nsSliderFrame::OnlySystemGroupDispatch(EventMessage aMessage
) const {
1473 // If we are in a native anonymous subtree, do not dispatch mouse-move events
1474 // targeted at this slider frame to web content. This matches the behaviour
1475 // of other browsers.
1476 return aMessage
== eMouseMove
&& isDraggingThumb() &&
1477 GetContent()->IsInNativeAnonymousSubtree();
1480 NS_IMPL_ISUPPORTS(nsSliderMediator
, nsIDOMEventListener
)