summaryrefslogtreecommitdiffstats
path: root/layout/xul/nsScrollbarButtonFrame.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'layout/xul/nsScrollbarButtonFrame.cpp')
-rw-r--r--layout/xul/nsScrollbarButtonFrame.cpp272
1 files changed, 272 insertions, 0 deletions
diff --git a/layout/xul/nsScrollbarButtonFrame.cpp b/layout/xul/nsScrollbarButtonFrame.cpp
new file mode 100644
index 0000000000..e9bb69016a
--- /dev/null
+++ b/layout/xul/nsScrollbarButtonFrame.cpp
@@ -0,0 +1,272 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//
+// Eric Vaughan
+// Netscape Communications
+//
+// See documentation in associated header file
+//
+
+#include "nsScrollbarButtonFrame.h"
+#include "nsPresContext.h"
+#include "nsIContent.h"
+#include "nsCOMPtr.h"
+#include "nsNameSpaceManager.h"
+#include "nsGkAtoms.h"
+#include "nsLayoutUtils.h"
+#include "nsSliderFrame.h"
+#include "nsScrollbarFrame.h"
+#include "nsIScrollbarMediator.h"
+#include "nsRepeatService.h"
+#include "mozilla/LookAndFeel.h"
+#include "mozilla/MouseEvents.h"
+#include "mozilla/PresShell.h"
+#include "mozilla/Telemetry.h"
+
+using namespace mozilla;
+
+//
+// NS_NewToolbarFrame
+//
+// Creates a new Toolbar frame and returns it
+//
+nsIFrame* NS_NewScrollbarButtonFrame(PresShell* aPresShell,
+ ComputedStyle* aStyle) {
+ return new (aPresShell)
+ nsScrollbarButtonFrame(aStyle, aPresShell->GetPresContext());
+}
+NS_IMPL_FRAMEARENA_HELPERS(nsScrollbarButtonFrame)
+
+nsresult nsScrollbarButtonFrame::HandleEvent(nsPresContext* aPresContext,
+ WidgetGUIEvent* aEvent,
+ nsEventStatus* aEventStatus) {
+ NS_ENSURE_ARG_POINTER(aEventStatus);
+
+ // If a web page calls event.preventDefault() we still want to
+ // scroll when scroll arrow is clicked. See bug 511075.
+ if (!mContent->IsInNativeAnonymousSubtree() &&
+ nsEventStatus_eConsumeNoDefault == *aEventStatus) {
+ return NS_OK;
+ }
+
+ switch (aEvent->mMessage) {
+ case eMouseDown:
+ mCursorOnThis = true;
+ // if we didn't handle the press ourselves, pass it on to the superclass
+ if (HandleButtonPress(aPresContext, aEvent, aEventStatus)) {
+ return NS_OK;
+ }
+ break;
+ case eMouseUp:
+ HandleRelease(aPresContext, aEvent, aEventStatus);
+ break;
+ case eMouseOut:
+ mCursorOnThis = false;
+ break;
+ case eMouseMove: {
+ nsPoint cursor = nsLayoutUtils::GetEventCoordinatesRelativeTo(
+ aEvent, RelativeTo{this});
+ nsRect frameRect(nsPoint(0, 0), GetSize());
+ mCursorOnThis = frameRect.Contains(cursor);
+ break;
+ }
+ default:
+ break;
+ }
+
+ return SimpleXULLeafFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
+}
+
+bool nsScrollbarButtonFrame::HandleButtonPress(nsPresContext* aPresContext,
+ WidgetGUIEvent* aEvent,
+ nsEventStatus* aEventStatus) {
+ // Get the desired action for the scrollbar button.
+ LookAndFeel::IntID tmpAction;
+ uint16_t button = aEvent->AsMouseEvent()->mButton;
+ if (button == MouseButton::ePrimary) {
+ tmpAction = LookAndFeel::IntID::ScrollButtonLeftMouseButtonAction;
+ } else if (button == MouseButton::eMiddle) {
+ tmpAction = LookAndFeel::IntID::ScrollButtonMiddleMouseButtonAction;
+ } else if (button == MouseButton::eSecondary) {
+ tmpAction = LookAndFeel::IntID::ScrollButtonRightMouseButtonAction;
+ } else {
+ return false;
+ }
+
+ // Get the button action metric from the pres. shell.
+ int32_t pressedButtonAction;
+ if (NS_FAILED(LookAndFeel::GetInt(tmpAction, &pressedButtonAction))) {
+ return false;
+ }
+
+ // get the scrollbar control
+ nsIFrame* scrollbar;
+ GetParentWithTag(nsGkAtoms::scrollbar, this, scrollbar);
+
+ if (scrollbar == nullptr) return false;
+
+ static dom::Element::AttrValuesArray strings[] = {
+ nsGkAtoms::increment, nsGkAtoms::decrement, nullptr};
+ int32_t index = mContent->AsElement()->FindAttrValueIn(
+ kNameSpaceID_None, nsGkAtoms::type, strings, eCaseMatters);
+ int32_t direction;
+ if (index == 0)
+ direction = 1;
+ else if (index == 1)
+ direction = -1;
+ else
+ return false;
+
+ bool repeat = pressedButtonAction != 2;
+ // set this attribute so we can style it later
+ AutoWeakFrame weakFrame(this);
+ mContent->AsElement()->SetAttr(kNameSpaceID_None, nsGkAtoms::active,
+ u"true"_ns, true);
+
+ PresShell::SetCapturingContent(mContent, CaptureFlags::IgnoreAllowedState);
+
+ if (!weakFrame.IsAlive()) {
+ return false;
+ }
+
+ if (nsScrollbarFrame* sb = do_QueryFrame(scrollbar)) {
+ nsIScrollbarMediator* m = sb->GetScrollbarMediator();
+ switch (pressedButtonAction) {
+ case 0:
+ sb->SetIncrementToLine(direction);
+ if (m) {
+ m->ScrollByLine(sb, direction, ScrollSnapFlags::IntendedDirection);
+ }
+ break;
+ case 1:
+ sb->SetIncrementToPage(direction);
+ if (m) {
+ m->ScrollByPage(sb, direction,
+ ScrollSnapFlags::IntendedDirection |
+ ScrollSnapFlags::IntendedEndPosition);
+ }
+ break;
+ case 2:
+ sb->SetIncrementToWhole(direction);
+ if (m) {
+ m->ScrollByWhole(sb, direction, ScrollSnapFlags::IntendedEndPosition);
+ }
+ break;
+ case 3:
+ default:
+ // We were told to ignore this click, or someone assigned a non-standard
+ // value to the button's action.
+ return false;
+ }
+ if (!weakFrame.IsAlive()) {
+ return false;
+ }
+
+ if (!m) {
+ sb->MoveToNewPosition(nsScrollbarFrame::ImplementsScrollByUnit::No);
+ if (!weakFrame.IsAlive()) {
+ return false;
+ }
+ }
+ }
+ if (repeat) {
+ StartRepeat();
+ }
+ return true;
+}
+
+NS_IMETHODIMP
+nsScrollbarButtonFrame::HandleRelease(nsPresContext* aPresContext,
+ WidgetGUIEvent* aEvent,
+ nsEventStatus* aEventStatus) {
+ PresShell::ReleaseCapturingContent();
+ // we're not active anymore
+ mContent->AsElement()->UnsetAttr(kNameSpaceID_None, nsGkAtoms::active, true);
+ StopRepeat();
+ nsIFrame* scrollbar;
+ GetParentWithTag(nsGkAtoms::scrollbar, this, scrollbar);
+ nsScrollbarFrame* sb = do_QueryFrame(scrollbar);
+ if (sb) {
+ nsIScrollbarMediator* m = sb->GetScrollbarMediator();
+ if (m) {
+ m->ScrollbarReleased(sb);
+ }
+ }
+ return NS_OK;
+}
+
+void nsScrollbarButtonFrame::Notify() {
+ if (mCursorOnThis ||
+ LookAndFeel::GetInt(LookAndFeel::IntID::ScrollbarButtonAutoRepeatBehavior,
+ 0)) {
+ // get the scrollbar control
+ nsIFrame* scrollbar;
+ GetParentWithTag(nsGkAtoms::scrollbar, this, scrollbar);
+ nsScrollbarFrame* sb = do_QueryFrame(scrollbar);
+ if (sb) {
+ nsIScrollbarMediator* m = sb->GetScrollbarMediator();
+ if (m) {
+ m->RepeatButtonScroll(sb);
+ } else {
+ sb->MoveToNewPosition(nsScrollbarFrame::ImplementsScrollByUnit::No);
+ }
+ }
+ }
+}
+
+nsresult nsScrollbarButtonFrame::GetChildWithTag(nsAtom* atom, nsIFrame* start,
+ nsIFrame*& result) {
+ // recursively search our children
+ for (nsIFrame* childFrame : start->PrincipalChildList()) {
+ // get the content node
+ nsIContent* child = childFrame->GetContent();
+
+ if (child) {
+ // see if it is the child
+ if (child->IsXULElement(atom)) {
+ result = childFrame;
+
+ return NS_OK;
+ }
+ }
+
+ // recursive search the child
+ GetChildWithTag(atom, childFrame, result);
+ if (result != nullptr) return NS_OK;
+ }
+
+ result = nullptr;
+ return NS_OK;
+}
+
+nsresult nsScrollbarButtonFrame::GetParentWithTag(nsAtom* toFind,
+ nsIFrame* start,
+ nsIFrame*& result) {
+ while (start) {
+ start = start->GetParent();
+
+ if (start) {
+ // get the content node
+ nsIContent* child = start->GetContent();
+
+ if (child && child->IsXULElement(toFind)) {
+ result = start;
+ return NS_OK;
+ }
+ }
+ }
+
+ result = nullptr;
+ return NS_OK;
+}
+
+void nsScrollbarButtonFrame::Destroy(DestroyContext& aContext) {
+ // Ensure our repeat service isn't going... it's possible that a scrollbar can
+ // disappear out from under you while you're in the process of scrolling.
+ StopRepeat();
+ SimpleXULLeafFrame::Destroy(aContext);
+}