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