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/. */
6
7 #include "nsCOMPtr.h"
8 #include "nsPresContext.h"
9 #include "nsGkAtoms.h"
10 #include "nsButtonBoxFrame.h"
11 #include "nsITimer.h"
12 #include "nsRepeatService.h"
13 #include "mozilla/MouseEvents.h"
14 #include "nsIContent.h"
15
16 using namespace mozilla;
17
18 class nsAutoRepeatBoxFrame final : public nsButtonBoxFrame {
19 public:
20 NS_DECL_FRAMEARENA_HELPERS(nsAutoRepeatBoxFrame)
21
22 friend nsIFrame* NS_NewAutoRepeatBoxFrame(nsIPresShell* aPresShell,
23 nsStyleContext* aContext);
24
25 virtual void DestroyFrom(nsIFrame* aDestructRoot,
26 PostDestroyData& aPostDestroyData) override;
27
28 virtual nsresult AttributeChanged(int32_t aNameSpaceID, nsAtom* aAttribute,
29 int32_t aModType) override;
30
31 virtual nsresult HandleEvent(nsPresContext* aPresContext,
32 WidgetGUIEvent* aEvent,
33 nsEventStatus* aEventStatus) override;
34
35 NS_IMETHOD HandlePress(nsPresContext* aPresContext, WidgetGUIEvent* aEvent,
36 nsEventStatus* aEventStatus) override;
37
38 NS_IMETHOD HandleRelease(nsPresContext* aPresContext, WidgetGUIEvent* aEvent,
39 nsEventStatus* aEventStatus) override;
40
41 protected:
nsAutoRepeatBoxFrame(nsStyleContext * aContext)42 explicit nsAutoRepeatBoxFrame(nsStyleContext* aContext)
43 : nsButtonBoxFrame(aContext, kClassID) {}
44
StartRepeat()45 void StartRepeat() {
46 if (IsActivatedOnHover()) {
47 // No initial delay on hover.
48 nsRepeatService::GetInstance()->Start(Notify, this, mContent->OwnerDoc(),
49 NS_LITERAL_CSTRING("DoMouseClick"),
50 0);
51 } else {
52 nsRepeatService::GetInstance()->Start(Notify, this, mContent->OwnerDoc(),
53 NS_LITERAL_CSTRING("DoMouseClick"));
54 }
55 }
StopRepeat()56 void StopRepeat() { nsRepeatService::GetInstance()->Stop(Notify, this); }
57 void Notify();
Notify(void * aData)58 static void Notify(void* aData) {
59 static_cast<nsAutoRepeatBoxFrame*>(aData)->Notify();
60 }
61
62 bool mTrustedEvent;
63
64 bool IsActivatedOnHover();
65 };
66
NS_NewAutoRepeatBoxFrame(nsIPresShell * aPresShell,nsStyleContext * aContext)67 nsIFrame* NS_NewAutoRepeatBoxFrame(nsIPresShell* aPresShell,
68 nsStyleContext* aContext) {
69 return new (aPresShell) nsAutoRepeatBoxFrame(aContext);
70 }
71
NS_IMPL_FRAMEARENA_HELPERS(nsAutoRepeatBoxFrame)72 NS_IMPL_FRAMEARENA_HELPERS(nsAutoRepeatBoxFrame)
73
74 nsresult nsAutoRepeatBoxFrame::HandleEvent(nsPresContext* aPresContext,
75 WidgetGUIEvent* aEvent,
76 nsEventStatus* aEventStatus) {
77 NS_ENSURE_ARG_POINTER(aEventStatus);
78 if (nsEventStatus_eConsumeNoDefault == *aEventStatus) {
79 return NS_OK;
80 }
81
82 switch (aEvent->mMessage) {
83 // repeat mode may be "hover" for repeating while the mouse is hovering
84 // over the element, otherwise repetition is done while the element is
85 // active (pressed).
86 case eMouseEnterIntoWidget:
87 case eMouseOver:
88 if (IsActivatedOnHover()) {
89 StartRepeat();
90 mTrustedEvent = aEvent->IsTrusted();
91 }
92 break;
93
94 case eMouseExitFromWidget:
95 case eMouseOut:
96 // always stop on mouse exit
97 StopRepeat();
98 // Not really necessary but do this to be safe
99 mTrustedEvent = false;
100 break;
101
102 case eMouseClick: {
103 WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
104 if (mouseEvent->IsLeftClickEvent()) {
105 // skip button frame handling to prevent click handling
106 return nsBoxFrame::HandleEvent(aPresContext, mouseEvent, aEventStatus);
107 }
108 break;
109 }
110
111 default:
112 break;
113 }
114
115 return nsButtonBoxFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
116 }
117
118 NS_IMETHODIMP
HandlePress(nsPresContext * aPresContext,WidgetGUIEvent * aEvent,nsEventStatus * aEventStatus)119 nsAutoRepeatBoxFrame::HandlePress(nsPresContext* aPresContext,
120 WidgetGUIEvent* aEvent,
121 nsEventStatus* aEventStatus) {
122 if (!IsActivatedOnHover()) {
123 StartRepeat();
124 mTrustedEvent = aEvent->IsTrusted();
125 DoMouseClick(aEvent, mTrustedEvent);
126 }
127
128 return NS_OK;
129 }
130
131 NS_IMETHODIMP
HandleRelease(nsPresContext * aPresContext,WidgetGUIEvent * aEvent,nsEventStatus * aEventStatus)132 nsAutoRepeatBoxFrame::HandleRelease(nsPresContext* aPresContext,
133 WidgetGUIEvent* aEvent,
134 nsEventStatus* aEventStatus) {
135 if (!IsActivatedOnHover()) {
136 StopRepeat();
137 }
138 return NS_OK;
139 }
140
AttributeChanged(int32_t aNameSpaceID,nsAtom * aAttribute,int32_t aModType)141 nsresult nsAutoRepeatBoxFrame::AttributeChanged(int32_t aNameSpaceID,
142 nsAtom* aAttribute,
143 int32_t aModType) {
144 if (aAttribute == nsGkAtoms::type) {
145 StopRepeat();
146 }
147 return NS_OK;
148 }
149
Notify()150 void nsAutoRepeatBoxFrame::Notify() { DoMouseClick(nullptr, mTrustedEvent); }
151
DestroyFrom(nsIFrame * aDestructRoot,PostDestroyData & aPostDestroyData)152 void nsAutoRepeatBoxFrame::DestroyFrom(nsIFrame* aDestructRoot,
153 PostDestroyData& aPostDestroyData) {
154 // Ensure our repeat service isn't going... it's possible that a scrollbar can
155 // disappear out from under you while you're in the process of scrolling.
156 StopRepeat();
157 nsButtonBoxFrame::DestroyFrom(aDestructRoot, aPostDestroyData);
158 }
159
IsActivatedOnHover()160 bool nsAutoRepeatBoxFrame::IsActivatedOnHover() {
161 return mContent->AsElement()->AttrValueIs(
162 kNameSpaceID_None, nsGkAtoms::repeat, nsGkAtoms::hover, eCaseMatters);
163 }
164