diff -r 000000000000 -r 6474c204b198 layout/xul/nsScrollbarButtonFrame.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/layout/xul/nsScrollbarButtonFrame.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,316 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "nsSliderFrame.h" +#include "nsScrollbarFrame.h" +#include "nsIScrollbarMediator.h" +#include "nsRepeatService.h" +#include "mozilla/LookAndFeel.h" +#include "mozilla/MouseEvents.h" + +using namespace mozilla; + +// +// NS_NewToolbarFrame +// +// Creates a new Toolbar frame and returns it +// +nsIFrame* +NS_NewScrollbarButtonFrame (nsIPresShell* aPresShell, nsStyleContext* aContext) +{ + return new (aPresShell) nsScrollbarButtonFrame(aPresShell, aContext); +} + +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->message) { + case NS_MOUSE_BUTTON_DOWN: + 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 NS_MOUSE_BUTTON_UP: + HandleRelease(aPresContext, aEvent, aEventStatus); + break; + case NS_MOUSE_EXIT_SYNTH: + mCursorOnThis = false; + break; + case NS_MOUSE_MOVE: { + nsPoint cursor = + nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this); + nsRect frameRect(nsPoint(0, 0), GetSize()); + mCursorOnThis = frameRect.Contains(cursor); + break; + } + } + + return nsButtonBoxFrame::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()->button; + if (button == WidgetMouseEvent::eLeftButton) { + tmpAction = LookAndFeel::eIntID_ScrollButtonLeftMouseButtonAction; + } else if (button == WidgetMouseEvent::eMiddleButton) { + tmpAction = LookAndFeel::eIntID_ScrollButtonMiddleMouseButtonAction; + } else if (button == WidgetMouseEvent::eRightButton) { + tmpAction = LookAndFeel::eIntID_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; + + // get the scrollbars content node + nsIContent* content = scrollbar->GetContent(); + + static nsIContent::AttrValuesArray strings[] = { &nsGkAtoms::increment, + &nsGkAtoms::decrement, + nullptr }; + int32_t index = mContent->FindAttrValueIn(kNameSpaceID_None, + nsGkAtoms::type, + strings, eCaseMatters); + int32_t direction; + if (index == 0) + direction = 1; + else if (index == 1) + direction = -1; + else + return false; + + // Whether or not to repeat the click action. + bool repeat = true; + // Use smooth scrolling by default. + bool smoothScroll = true; + switch (pressedButtonAction) { + case 0: + mIncrement = direction * nsSliderFrame::GetIncrement(content); + break; + case 1: + mIncrement = direction * nsSliderFrame::GetPageIncrement(content); + break; + case 2: + if (direction == -1) + mIncrement = -nsSliderFrame::GetCurrentPosition(content); + else + mIncrement = nsSliderFrame::GetMaxPosition(content) - + nsSliderFrame::GetCurrentPosition(content); + // Don't repeat or use smooth scrolling if scrolling to beginning or end + // of a page. + repeat = smoothScroll = false; + 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; + } + // set this attribute so we can style it later + nsWeakFrame weakFrame(this); + mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::active, NS_LITERAL_STRING("true"), true); + + nsIPresShell::SetCapturingContent(mContent, CAPTURE_IGNOREALLOWED); + + if (weakFrame.IsAlive()) { + DoButtonAction(smoothScroll); + } + if (repeat) + StartRepeat(); + return true; +} + +NS_IMETHODIMP +nsScrollbarButtonFrame::HandleRelease(nsPresContext* aPresContext, + WidgetGUIEvent* aEvent, + nsEventStatus* aEventStatus) +{ + nsIPresShell::SetCapturingContent(nullptr, 0); + // we're not active anymore + mContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::active, true); + StopRepeat(); + return NS_OK; +} + +void nsScrollbarButtonFrame::Notify() +{ + // Since this is only going to get called if we're scrolling a page length + // or a line increment, we will always use smooth scrolling. + if (mCursorOnThis || + LookAndFeel::GetInt( + LookAndFeel::eIntID_ScrollbarButtonAutoRepeatBehavior, 0)) { + DoButtonAction(true); + } +} + +void +nsScrollbarButtonFrame::MouseClicked(nsPresContext* aPresContext, + WidgetGUIEvent* aEvent) +{ + nsButtonBoxFrame::MouseClicked(aPresContext, aEvent); + //MouseClicked(); +} + +void +nsScrollbarButtonFrame::DoButtonAction(bool aSmoothScroll) +{ + // get the scrollbar control + nsIFrame* scrollbar; + GetParentWithTag(nsGkAtoms::scrollbar, this, scrollbar); + + if (scrollbar == nullptr) + return; + + // get the scrollbars content node + nsCOMPtr content = scrollbar->GetContent(); + + // get the current pos + int32_t curpos = nsSliderFrame::GetCurrentPosition(content); + int32_t oldpos = curpos; + + // get the max pos + int32_t maxpos = nsSliderFrame::GetMaxPosition(content); + + // increment the given amount + if (mIncrement) + curpos += mIncrement; + + // make sure the current position is between the current and max positions + if (curpos < 0) + curpos = 0; + else if (curpos > maxpos) + curpos = maxpos; + + nsScrollbarFrame* sb = do_QueryFrame(scrollbar); + if (sb) { + nsIScrollbarMediator* m = sb->GetScrollbarMediator(); + if (m) { + m->ScrollbarButtonPressed(sb, oldpos, curpos); + return; + } + } + + // set the current position of the slider. + nsAutoString curposStr; + curposStr.AppendInt(curpos); + + if (aSmoothScroll) + content->SetAttr(kNameSpaceID_None, nsGkAtoms::smooth, NS_LITERAL_STRING("true"), false); + content->SetAttr(kNameSpaceID_None, nsGkAtoms::curpos, curposStr, true); + if (aSmoothScroll) + content->UnsetAttr(kNameSpaceID_None, nsGkAtoms::smooth, false); +} + +nsresult +nsScrollbarButtonFrame::GetChildWithTag(nsPresContext* aPresContext, + nsIAtom* atom, nsIFrame* start, + nsIFrame*& result) +{ + // recursively search our children + nsIFrame* childFrame = start->GetFirstPrincipalChild(); + while (nullptr != childFrame) + { + // get the content node + nsIContent* child = childFrame->GetContent(); + + if (child) { + // see if it is the child + if (child->Tag() == atom) + { + result = childFrame; + + return NS_OK; + } + } + + // recursive search the child + GetChildWithTag(aPresContext, atom, childFrame, result); + if (result != nullptr) + return NS_OK; + + childFrame = childFrame->GetNextSibling(); + } + + result = nullptr; + return NS_OK; +} + +nsresult +nsScrollbarButtonFrame::GetParentWithTag(nsIAtom* toFind, nsIFrame* start, + nsIFrame*& result) +{ + while (start) + { + start = start->GetParent(); + + if (start) { + // get the content node + nsIContent* child = start->GetContent(); + + if (child && child->Tag() == toFind) { + result = start; + return NS_OK; + } + } + } + + result = nullptr; + return NS_OK; +} + +void +nsScrollbarButtonFrame::DestroyFrom(nsIFrame* aDestructRoot) +{ + // 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(); + nsButtonBoxFrame::DestroyFrom(aDestructRoot); +}