Bug 1793629 - Implement attention indicator for the unified extensions button, r...
[gecko.git] / layout / xul / nsScrollbarButtonFrame.cpp
blob176c0ec09d35550140c91b7b082c3901070e9058
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/. */
7 //
8 // Eric Vaughan
9 // Netscape Communications
11 // See documentation in associated header file
14 #include "nsScrollbarButtonFrame.h"
15 #include "nsPresContext.h"
16 #include "nsIContent.h"
17 #include "nsCOMPtr.h"
18 #include "nsNameSpaceManager.h"
19 #include "nsGkAtoms.h"
20 #include "nsLayoutUtils.h"
21 #include "nsSliderFrame.h"
22 #include "nsScrollbarFrame.h"
23 #include "nsIScrollbarMediator.h"
24 #include "nsRepeatService.h"
25 #include "mozilla/LookAndFeel.h"
26 #include "mozilla/MouseEvents.h"
27 #include "mozilla/PresShell.h"
28 #include "mozilla/Telemetry.h"
30 using namespace mozilla;
33 // NS_NewToolbarFrame
35 // Creates a new Toolbar frame and returns it
37 nsIFrame* NS_NewScrollbarButtonFrame(PresShell* aPresShell,
38 ComputedStyle* aStyle) {
39 return new (aPresShell)
40 nsScrollbarButtonFrame(aStyle, aPresShell->GetPresContext());
43 NS_IMPL_FRAMEARENA_HELPERS(nsScrollbarButtonFrame)
45 nsresult nsScrollbarButtonFrame::HandleEvent(nsPresContext* aPresContext,
46 WidgetGUIEvent* aEvent,
47 nsEventStatus* aEventStatus) {
48 NS_ENSURE_ARG_POINTER(aEventStatus);
50 // If a web page calls event.preventDefault() we still want to
51 // scroll when scroll arrow is clicked. See bug 511075.
52 if (!mContent->IsInNativeAnonymousSubtree() &&
53 nsEventStatus_eConsumeNoDefault == *aEventStatus) {
54 return NS_OK;
57 switch (aEvent->mMessage) {
58 case eMouseDown:
59 mCursorOnThis = true;
60 // if we didn't handle the press ourselves, pass it on to the superclass
61 if (HandleButtonPress(aPresContext, aEvent, aEventStatus)) {
62 return NS_OK;
64 break;
65 case eMouseUp:
66 HandleRelease(aPresContext, aEvent, aEventStatus);
67 break;
68 case eMouseOut:
69 mCursorOnThis = false;
70 break;
71 case eMouseMove: {
72 nsPoint cursor = nsLayoutUtils::GetEventCoordinatesRelativeTo(
73 aEvent, RelativeTo{this});
74 nsRect frameRect(nsPoint(0, 0), GetSize());
75 mCursorOnThis = frameRect.Contains(cursor);
76 break;
78 default:
79 break;
82 return nsBoxFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
85 bool nsScrollbarButtonFrame::HandleButtonPress(nsPresContext* aPresContext,
86 WidgetGUIEvent* aEvent,
87 nsEventStatus* aEventStatus) {
88 // Get the desired action for the scrollbar button.
89 LookAndFeel::IntID tmpAction;
90 uint16_t button = aEvent->AsMouseEvent()->mButton;
91 if (button == MouseButton::ePrimary) {
92 tmpAction = LookAndFeel::IntID::ScrollButtonLeftMouseButtonAction;
93 } else if (button == MouseButton::eMiddle) {
94 tmpAction = LookAndFeel::IntID::ScrollButtonMiddleMouseButtonAction;
95 } else if (button == MouseButton::eSecondary) {
96 tmpAction = LookAndFeel::IntID::ScrollButtonRightMouseButtonAction;
97 } else {
98 return false;
101 // Get the button action metric from the pres. shell.
102 int32_t pressedButtonAction;
103 if (NS_FAILED(LookAndFeel::GetInt(tmpAction, &pressedButtonAction))) {
104 return false;
107 // get the scrollbar control
108 nsIFrame* scrollbar;
109 GetParentWithTag(nsGkAtoms::scrollbar, this, scrollbar);
111 if (scrollbar == nullptr) return false;
113 static dom::Element::AttrValuesArray strings[] = {
114 nsGkAtoms::increment, nsGkAtoms::decrement, nullptr};
115 int32_t index = mContent->AsElement()->FindAttrValueIn(
116 kNameSpaceID_None, nsGkAtoms::type, strings, eCaseMatters);
117 int32_t direction;
118 if (index == 0)
119 direction = 1;
120 else if (index == 1)
121 direction = -1;
122 else
123 return false;
125 bool repeat = pressedButtonAction != 2;
126 // set this attribute so we can style it later
127 AutoWeakFrame weakFrame(this);
128 mContent->AsElement()->SetAttr(kNameSpaceID_None, nsGkAtoms::active,
129 u"true"_ns, true);
131 PresShell::SetCapturingContent(mContent, CaptureFlags::IgnoreAllowedState);
133 if (!weakFrame.IsAlive()) {
134 return false;
137 if (nsScrollbarFrame* sb = do_QueryFrame(scrollbar)) {
138 nsIScrollbarMediator* m = sb->GetScrollbarMediator();
139 switch (pressedButtonAction) {
140 case 0:
141 sb->SetIncrementToLine(direction);
142 if (m) {
143 m->ScrollByLine(sb, direction, ScrollSnapFlags::IntendedDirection);
145 break;
146 case 1:
147 sb->SetIncrementToPage(direction);
148 if (m) {
149 m->ScrollByPage(sb, direction,
150 ScrollSnapFlags::IntendedDirection |
151 ScrollSnapFlags::IntendedEndPosition);
153 break;
154 case 2:
155 sb->SetIncrementToWhole(direction);
156 if (m) {
157 m->ScrollByWhole(sb, direction, ScrollSnapFlags::IntendedEndPosition);
159 break;
160 case 3:
161 default:
162 // We were told to ignore this click, or someone assigned a non-standard
163 // value to the button's action.
164 return false;
166 if (!weakFrame.IsAlive()) {
167 return false;
170 if (!m) {
171 sb->MoveToNewPosition(nsScrollbarFrame::ImplementsScrollByUnit::No);
172 if (!weakFrame.IsAlive()) {
173 return false;
177 if (repeat) {
178 StartRepeat();
180 return true;
183 NS_IMETHODIMP
184 nsScrollbarButtonFrame::HandleRelease(nsPresContext* aPresContext,
185 WidgetGUIEvent* aEvent,
186 nsEventStatus* aEventStatus) {
187 PresShell::ReleaseCapturingContent();
188 // we're not active anymore
189 mContent->AsElement()->UnsetAttr(kNameSpaceID_None, nsGkAtoms::active, true);
190 StopRepeat();
191 nsIFrame* scrollbar;
192 GetParentWithTag(nsGkAtoms::scrollbar, this, scrollbar);
193 nsScrollbarFrame* sb = do_QueryFrame(scrollbar);
194 if (sb) {
195 nsIScrollbarMediator* m = sb->GetScrollbarMediator();
196 if (m) {
197 m->ScrollbarReleased(sb);
200 return NS_OK;
203 void nsScrollbarButtonFrame::Notify() {
204 if (mCursorOnThis ||
205 LookAndFeel::GetInt(LookAndFeel::IntID::ScrollbarButtonAutoRepeatBehavior,
206 0)) {
207 // get the scrollbar control
208 nsIFrame* scrollbar;
209 GetParentWithTag(nsGkAtoms::scrollbar, this, scrollbar);
210 nsScrollbarFrame* sb = do_QueryFrame(scrollbar);
211 if (sb) {
212 nsIScrollbarMediator* m = sb->GetScrollbarMediator();
213 if (m) {
214 m->RepeatButtonScroll(sb);
215 } else {
216 sb->MoveToNewPosition(nsScrollbarFrame::ImplementsScrollByUnit::No);
222 nsresult nsScrollbarButtonFrame::GetChildWithTag(nsAtom* atom, nsIFrame* start,
223 nsIFrame*& result) {
224 // recursively search our children
225 for (nsIFrame* childFrame : start->PrincipalChildList()) {
226 // get the content node
227 nsIContent* child = childFrame->GetContent();
229 if (child) {
230 // see if it is the child
231 if (child->IsXULElement(atom)) {
232 result = childFrame;
234 return NS_OK;
238 // recursive search the child
239 GetChildWithTag(atom, childFrame, result);
240 if (result != nullptr) return NS_OK;
243 result = nullptr;
244 return NS_OK;
247 nsresult nsScrollbarButtonFrame::GetParentWithTag(nsAtom* toFind,
248 nsIFrame* start,
249 nsIFrame*& result) {
250 while (start) {
251 start = start->GetParent();
253 if (start) {
254 // get the content node
255 nsIContent* child = start->GetContent();
257 if (child && child->IsXULElement(toFind)) {
258 result = start;
259 return NS_OK;
264 result = nullptr;
265 return NS_OK;
268 void nsScrollbarButtonFrame::DestroyFrom(nsIFrame* aDestructRoot,
269 PostDestroyData& aPostDestroyData) {
270 // Ensure our repeat service isn't going... it's possible that a scrollbar can
271 // disappear out from under you while you're in the process of scrolling.
272 StopRepeat();
273 nsBoxFrame::DestroyFrom(aDestructRoot, aPostDestroyData);