Bug 1675375 Part 7: Update expectations in helper_hittest_clippath.html. r=botond
[gecko.git] / layout / xul / nsScrollbarFrame.cpp
blobb332f6224f3d6344d58fcfffcae775e9b8e83ee6
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 "nsScrollbarFrame.h"
15 #include "nsSliderFrame.h"
16 #include "nsScrollbarButtonFrame.h"
17 #include "nsContentCreatorFunctions.h"
18 #include "nsGkAtoms.h"
19 #include "nsIScrollableFrame.h"
20 #include "nsIScrollbarMediator.h"
21 #include "nsStyleConsts.h"
22 #include "nsIContent.h"
23 #include "mozilla/LookAndFeel.h"
24 #include "mozilla/PresShell.h"
25 #include "mozilla/dom/Element.h"
26 #include "mozilla/dom/MutationEventBinding.h"
27 #include "mozilla/StaticPrefs_apz.h"
29 using namespace mozilla;
30 using mozilla::dom::Element;
33 // NS_NewScrollbarFrame
35 // Creates a new scrollbar frame and returns it
37 nsIFrame* NS_NewScrollbarFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
38 return new (aPresShell)
39 nsScrollbarFrame(aStyle, aPresShell->GetPresContext());
42 NS_IMPL_FRAMEARENA_HELPERS(nsScrollbarFrame)
44 NS_QUERYFRAME_HEAD(nsScrollbarFrame)
45 NS_QUERYFRAME_ENTRY(nsScrollbarFrame)
46 NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
47 NS_QUERYFRAME_TAIL_INHERITING(nsBoxFrame)
49 void nsScrollbarFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
50 nsIFrame* aPrevInFlow) {
51 nsBoxFrame::Init(aContent, aParent, aPrevInFlow);
53 // We want to be a reflow root since we use reflows to move the
54 // slider. Any reflow inside the scrollbar frame will be a reflow to
55 // move the slider and will thus not change anything outside of the
56 // scrollbar or change the size of the scrollbar frame.
57 AddStateBits(NS_FRAME_REFLOW_ROOT);
60 void nsScrollbarFrame::DestroyFrom(nsIFrame* aDestructRoot,
61 PostDestroyData& aPostDestroyData) {
62 aPostDestroyData.AddAnonymousContent(mUpTopButton.forget());
63 aPostDestroyData.AddAnonymousContent(mDownTopButton.forget());
64 aPostDestroyData.AddAnonymousContent(mSlider.forget());
65 aPostDestroyData.AddAnonymousContent(mUpBottomButton.forget());
66 aPostDestroyData.AddAnonymousContent(mDownBottomButton.forget());
67 nsBoxFrame::DestroyFrom(aDestructRoot, aPostDestroyData);
70 void nsScrollbarFrame::Reflow(nsPresContext* aPresContext,
71 ReflowOutput& aDesiredSize,
72 const ReflowInput& aReflowInput,
73 nsReflowStatus& aStatus) {
74 MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
76 nsBoxFrame::Reflow(aPresContext, aDesiredSize, aReflowInput, aStatus);
78 // nsGfxScrollFrame may have told us to shrink to nothing. If so, make sure
79 // our desired size agrees.
80 if (aReflowInput.AvailableWidth() == 0) {
81 aDesiredSize.Width() = 0;
83 if (aReflowInput.AvailableHeight() == 0) {
84 aDesiredSize.Height() = 0;
88 nsresult nsScrollbarFrame::AttributeChanged(int32_t aNameSpaceID,
89 nsAtom* aAttribute,
90 int32_t aModType) {
91 nsresult rv =
92 nsBoxFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
94 // Update value in our children
95 UpdateChildrenAttributeValue(aAttribute, true);
97 // if the current position changes, notify any nsGfxScrollFrame
98 // parent we may have
99 if (aAttribute != nsGkAtoms::curpos) return rv;
101 nsIScrollableFrame* scrollable = do_QueryFrame(GetParent());
102 if (!scrollable) return rv;
104 nsCOMPtr<nsIContent> content(mContent);
105 scrollable->CurPosAttributeChanged(content);
106 return rv;
109 NS_IMETHODIMP
110 nsScrollbarFrame::HandlePress(nsPresContext* aPresContext,
111 WidgetGUIEvent* aEvent,
112 nsEventStatus* aEventStatus) {
113 return NS_OK;
116 NS_IMETHODIMP
117 nsScrollbarFrame::HandleMultiplePress(nsPresContext* aPresContext,
118 WidgetGUIEvent* aEvent,
119 nsEventStatus* aEventStatus,
120 bool aControlHeld) {
121 return NS_OK;
124 NS_IMETHODIMP
125 nsScrollbarFrame::HandleDrag(nsPresContext* aPresContext,
126 WidgetGUIEvent* aEvent,
127 nsEventStatus* aEventStatus) {
128 return NS_OK;
131 NS_IMETHODIMP
132 nsScrollbarFrame::HandleRelease(nsPresContext* aPresContext,
133 WidgetGUIEvent* aEvent,
134 nsEventStatus* aEventStatus) {
135 return NS_OK;
138 void nsScrollbarFrame::SetScrollbarMediatorContent(nsIContent* aMediator) {
139 mScrollbarMediator = aMediator;
142 nsIScrollbarMediator* nsScrollbarFrame::GetScrollbarMediator() {
143 if (!mScrollbarMediator) {
144 return nullptr;
146 nsIFrame* f = mScrollbarMediator->GetPrimaryFrame();
147 nsIScrollableFrame* scrollFrame = do_QueryFrame(f);
148 nsIScrollbarMediator* sbm;
150 if (scrollFrame) {
151 nsIFrame* scrolledFrame = scrollFrame->GetScrolledFrame();
152 sbm = do_QueryFrame(scrolledFrame);
153 if (sbm) {
154 return sbm;
157 sbm = do_QueryFrame(f);
158 if (f && !sbm) {
159 f = f->PresShell()->GetRootScrollFrame();
160 if (f && f->GetContent() == mScrollbarMediator) {
161 return do_QueryFrame(f);
164 return sbm;
167 nsresult nsScrollbarFrame::GetXULMargin(nsMargin& aMargin) {
168 aMargin.SizeTo(0, 0, 0, 0);
170 const bool overlayScrollbars =
171 !!LookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars);
173 const bool horizontal = IsXULHorizontal();
174 bool didSetMargin = false;
176 if (overlayScrollbars) {
177 nsSize minSize;
178 bool widthSet = false;
179 bool heightSet = false;
180 AddXULMinSize(this, minSize, widthSet, heightSet);
181 if (horizontal) {
182 if (heightSet) {
183 aMargin.top = -minSize.height;
184 didSetMargin = true;
186 } else {
187 if (widthSet) {
188 aMargin.left = -minSize.width;
189 didSetMargin = true;
194 if (!didSetMargin) {
195 DebugOnly<nsresult> rv = nsIFrame::GetXULMargin(aMargin);
196 // TODO(emilio): Should probably not be fallible, it's not like anybody
197 // cares about the return value anyway.
198 MOZ_ASSERT(NS_SUCCEEDED(rv), "nsIFrame::GetXULMargin can't really fail");
201 if (!horizontal) {
202 nsIScrollbarMediator* scrollFrame = GetScrollbarMediator();
203 if (scrollFrame && !scrollFrame->IsScrollbarOnRight()) {
204 std::swap(aMargin.left, aMargin.right);
208 return NS_OK;
211 void nsScrollbarFrame::SetIncrementToLine(int32_t aDirection) {
212 mSmoothScroll = true;
213 mDirection = aDirection;
214 mScrollUnit = ScrollUnit::LINES;
216 // get the scrollbar's content node
217 nsIContent* content = GetContent();
218 mIncrement = aDirection * nsSliderFrame::GetIncrement(content);
221 void nsScrollbarFrame::SetIncrementToPage(int32_t aDirection) {
222 mSmoothScroll = true;
223 mDirection = aDirection;
224 mScrollUnit = ScrollUnit::PAGES;
226 // get the scrollbar's content node
227 nsIContent* content = GetContent();
228 mIncrement = aDirection * nsSliderFrame::GetPageIncrement(content);
231 void nsScrollbarFrame::SetIncrementToWhole(int32_t aDirection) {
232 // Don't repeat or use smooth scrolling if scrolling to beginning or end
233 // of a page.
234 mSmoothScroll = false;
235 mDirection = aDirection;
236 mScrollUnit = ScrollUnit::WHOLE;
238 // get the scrollbar's content node
239 nsIContent* content = GetContent();
240 if (aDirection == -1)
241 mIncrement = -nsSliderFrame::GetCurrentPosition(content);
242 else
243 mIncrement = nsSliderFrame::GetMaxPosition(content) -
244 nsSliderFrame::GetCurrentPosition(content);
247 int32_t nsScrollbarFrame::MoveToNewPosition(
248 ImplementsScrollByUnit aImplementsScrollByUnit) {
249 if (aImplementsScrollByUnit == ImplementsScrollByUnit::Yes &&
250 StaticPrefs::apz_scrollbarbuttonrepeat_enabled()) {
251 nsIScrollbarMediator* m = GetScrollbarMediator();
252 MOZ_ASSERT(m);
253 // aImplementsScrollByUnit being Yes indicates the caller doesn't care
254 // about the return value.
255 m->ScrollByUnit(this,
256 mSmoothScroll ? ScrollMode::Smooth : ScrollMode::Instant,
257 mDirection, mScrollUnit, nsIScrollbarMediator::ENABLE_SNAP);
258 return 0;
261 // get the scrollbar's content node
262 RefPtr<Element> content = GetContent()->AsElement();
264 // get the current pos
265 int32_t curpos = nsSliderFrame::GetCurrentPosition(content);
267 // get the max pos
268 int32_t maxpos = nsSliderFrame::GetMaxPosition(content);
270 // increment the given amount
271 if (mIncrement) {
272 curpos += mIncrement;
275 // make sure the current position is between the current and max positions
276 if (curpos < 0) {
277 curpos = 0;
278 } else if (curpos > maxpos) {
279 curpos = maxpos;
282 // set the current position of the slider.
283 nsAutoString curposStr;
284 curposStr.AppendInt(curpos);
286 AutoWeakFrame weakFrame(this);
287 if (mSmoothScroll) {
288 content->SetAttr(kNameSpaceID_None, nsGkAtoms::smooth, u"true"_ns, false);
290 content->SetAttr(kNameSpaceID_None, nsGkAtoms::curpos, curposStr, false);
291 // notify the nsScrollbarFrame of the change
292 AttributeChanged(kNameSpaceID_None, nsGkAtoms::curpos,
293 dom::MutationEvent_Binding::MODIFICATION);
294 if (!weakFrame.IsAlive()) {
295 return curpos;
297 // notify all nsSliderFrames of the change
298 for (const auto& childList : ChildLists()) {
299 for (nsIFrame* f : childList.mList) {
300 nsSliderFrame* sliderFrame = do_QueryFrame(f);
301 if (sliderFrame) {
302 sliderFrame->AttributeChanged(kNameSpaceID_None, nsGkAtoms::curpos,
303 dom::MutationEvent_Binding::MODIFICATION);
304 if (!weakFrame.IsAlive()) {
305 return curpos;
310 content->UnsetAttr(kNameSpaceID_None, nsGkAtoms::smooth, false);
311 return curpos;
314 static already_AddRefed<Element> MakeScrollbarButton(
315 dom::NodeInfo* aNodeInfo, bool aVertical, bool aBottom, bool aDown,
316 AnonymousContentKey& aKey) {
317 MOZ_ASSERT(aNodeInfo);
318 MOZ_ASSERT(
319 aNodeInfo->Equals(nsGkAtoms::scrollbarbutton, nullptr, kNameSpaceID_XUL));
321 static constexpr nsLiteralString kSbattrValues[2][2] = {
323 u"scrollbar-up-top"_ns,
324 u"scrollbar-up-bottom"_ns,
327 u"scrollbar-down-top"_ns,
328 u"scrollbar-down-bottom"_ns,
332 static constexpr nsLiteralString kTypeValues[2] = {
333 u"decrement"_ns,
334 u"increment"_ns,
337 aKey = AnonymousContentKey::Type_ScrollbarButton;
338 if (aVertical) {
339 aKey |= AnonymousContentKey::Flag_Vertical;
341 if (aBottom) {
342 aKey |= AnonymousContentKey::Flag_ScrollbarButton_Bottom;
344 if (aDown) {
345 aKey |= AnonymousContentKey::Flag_ScrollbarButton_Down;
348 RefPtr<Element> e;
349 NS_TrustedNewXULElement(getter_AddRefs(e), do_AddRef(aNodeInfo));
350 e->SetAttr(kNameSpaceID_None, nsGkAtoms::sbattr,
351 kSbattrValues[aDown][aBottom], false);
352 e->SetAttr(kNameSpaceID_None, nsGkAtoms::type, kTypeValues[aDown], false);
353 return e.forget();
356 nsresult nsScrollbarFrame::CreateAnonymousContent(
357 nsTArray<ContentInfo>& aElements) {
358 nsNodeInfoManager* nodeInfoManager = mContent->NodeInfo()->NodeInfoManager();
360 Element* el(GetContent()->AsElement());
362 // If there are children already in the node, don't create any anonymous
363 // content (this only apply to crashtests/369038-1.xhtml)
364 if (el->HasChildren()) {
365 return NS_OK;
368 nsAutoString orient;
369 el->GetAttr(kNameSpaceID_None, nsGkAtoms::orient, orient);
370 bool vertical = orient.EqualsLiteral("vertical");
372 RefPtr<dom::NodeInfo> sbbNodeInfo =
373 nodeInfoManager->GetNodeInfo(nsGkAtoms::scrollbarbutton, nullptr,
374 kNameSpaceID_XUL, nsINode::ELEMENT_NODE);
376 bool createButtons = PresContext()->Theme()->ThemeSupportsScrollbarButtons();
378 if (createButtons) {
379 AnonymousContentKey key;
380 mUpTopButton =
381 MakeScrollbarButton(sbbNodeInfo, vertical, /* aBottom */ false,
382 /* aDown */ false, key);
383 aElements.AppendElement(ContentInfo(mUpTopButton, key));
386 if (createButtons) {
387 AnonymousContentKey key;
388 mDownTopButton =
389 MakeScrollbarButton(sbbNodeInfo, vertical, /* aBottom */ false,
390 /* aDown */ true, key);
391 aElements.AppendElement(ContentInfo(mDownTopButton, key));
395 AnonymousContentKey key = AnonymousContentKey::Type_Slider;
396 if (vertical) {
397 key |= AnonymousContentKey::Flag_Vertical;
400 NS_TrustedNewXULElement(
401 getter_AddRefs(mSlider),
402 nodeInfoManager->GetNodeInfo(nsGkAtoms::slider, nullptr,
403 kNameSpaceID_XUL, nsINode::ELEMENT_NODE));
404 mSlider->SetAttr(kNameSpaceID_None, nsGkAtoms::orient, orient, false);
405 mSlider->SetAttr(kNameSpaceID_None, nsGkAtoms::flex, u"1"_ns, false);
407 aElements.AppendElement(ContentInfo(mSlider, key));
409 NS_TrustedNewXULElement(
410 getter_AddRefs(mThumb),
411 nodeInfoManager->GetNodeInfo(nsGkAtoms::thumb, nullptr,
412 kNameSpaceID_XUL, nsINode::ELEMENT_NODE));
413 mThumb->SetAttr(kNameSpaceID_None, nsGkAtoms::orient, orient, false);
414 mSlider->AppendChildTo(mThumb, false);
417 if (createButtons) {
418 AnonymousContentKey key;
419 mUpBottomButton =
420 MakeScrollbarButton(sbbNodeInfo, vertical, /* aBottom */ true,
421 /* aDown */ false, key);
422 aElements.AppendElement(ContentInfo(mUpBottomButton, key));
425 if (createButtons) {
426 AnonymousContentKey key;
427 mDownBottomButton =
428 MakeScrollbarButton(sbbNodeInfo, vertical, /* aBottom */ true,
429 /* aDown */ true, key);
430 aElements.AppendElement(ContentInfo(mDownBottomButton, key));
433 // Don't cache styles if we are inside a <select> element, since we have
434 // some UA style sheet rules that depend on the <select>'s attributes.
435 if (GetContent()->GetParent() &&
436 GetContent()->GetParent()->IsHTMLElement(nsGkAtoms::select)) {
437 for (auto& info : aElements) {
438 info.mKey = AnonymousContentKey::None;
442 UpdateChildrenAttributeValue(nsGkAtoms::curpos, false);
443 UpdateChildrenAttributeValue(nsGkAtoms::maxpos, false);
444 UpdateChildrenAttributeValue(nsGkAtoms::disabled, false);
445 UpdateChildrenAttributeValue(nsGkAtoms::pageincrement, false);
446 UpdateChildrenAttributeValue(nsGkAtoms::increment, false);
448 return NS_OK;
451 void nsScrollbarFrame::UpdateChildrenAttributeValue(nsAtom* aAttribute,
452 bool aNotify) {
453 Element* el(GetContent()->AsElement());
455 nsAutoString value;
456 el->GetAttr(kNameSpaceID_None, aAttribute, value);
458 if (!el->HasAttr(kNameSpaceID_None, aAttribute)) {
459 if (mUpTopButton) {
460 mUpTopButton->UnsetAttr(kNameSpaceID_None, aAttribute, aNotify);
462 if (mDownTopButton) {
463 mDownTopButton->UnsetAttr(kNameSpaceID_None, aAttribute, aNotify);
465 if (mSlider) {
466 mSlider->UnsetAttr(kNameSpaceID_None, aAttribute, aNotify);
468 if (mThumb && aAttribute == nsGkAtoms::disabled) {
469 mThumb->UnsetAttr(kNameSpaceID_None, nsGkAtoms::collapsed, aNotify);
471 if (mUpBottomButton) {
472 mUpBottomButton->UnsetAttr(kNameSpaceID_None, aAttribute, aNotify);
474 if (mDownBottomButton) {
475 mDownBottomButton->UnsetAttr(kNameSpaceID_None, aAttribute, aNotify);
477 return;
480 if (aAttribute == nsGkAtoms::curpos || aAttribute == nsGkAtoms::maxpos) {
481 if (mUpTopButton) {
482 mUpTopButton->SetAttr(kNameSpaceID_None, aAttribute, value, aNotify);
484 if (mDownTopButton) {
485 mDownTopButton->SetAttr(kNameSpaceID_None, aAttribute, value, aNotify);
487 if (mSlider) {
488 mSlider->SetAttr(kNameSpaceID_None, aAttribute, value, aNotify);
490 if (mUpBottomButton) {
491 mUpBottomButton->SetAttr(kNameSpaceID_None, aAttribute, value, aNotify);
493 if (mDownBottomButton) {
494 mDownBottomButton->SetAttr(kNameSpaceID_None, aAttribute, value, aNotify);
496 } else if (aAttribute == nsGkAtoms::disabled) {
497 if (mUpTopButton) {
498 mUpTopButton->SetAttr(kNameSpaceID_None, aAttribute, value, aNotify);
500 if (mDownTopButton) {
501 mDownTopButton->SetAttr(kNameSpaceID_None, aAttribute, value, aNotify);
503 if (mSlider) {
504 mSlider->SetAttr(kNameSpaceID_None, aAttribute, value, aNotify);
506 // Set the value on "collapsed" attribute.
507 if (mThumb) {
508 mThumb->SetAttr(kNameSpaceID_None, nsGkAtoms::collapsed, value, aNotify);
510 if (mUpBottomButton) {
511 mUpBottomButton->SetAttr(kNameSpaceID_None, aAttribute, value, aNotify);
513 if (mDownBottomButton) {
514 mDownBottomButton->SetAttr(kNameSpaceID_None, aAttribute, value, aNotify);
516 } else if (aAttribute == nsGkAtoms::pageincrement ||
517 aAttribute == nsGkAtoms::increment) {
518 if (mSlider) {
519 mSlider->SetAttr(kNameSpaceID_None, aAttribute, value, aNotify);
524 void nsScrollbarFrame::AppendAnonymousContentTo(
525 nsTArray<nsIContent*>& aElements, uint32_t aFilter) {
526 if (mUpTopButton) {
527 aElements.AppendElement(mUpTopButton);
530 if (mDownTopButton) {
531 aElements.AppendElement(mDownTopButton);
534 if (mSlider) {
535 aElements.AppendElement(mSlider);
538 if (mUpBottomButton) {
539 aElements.AppendElement(mUpBottomButton);
542 if (mDownBottomButton) {
543 aElements.AppendElement(mDownBottomButton);