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 #include "nsCOMPtr.h"
7 #include "nsButtonBoxFrame.h"
8 #include "nsIContent.h"
9 #include "nsIDOMXULButtonElement.h"
10 #include "nsGkAtoms.h"
11 #include "nsNameSpaceManager.h"
12 #include "nsPresContext.h"
13 #include "nsDisplayList.h"
14 #include "nsContentUtils.h"
15 #include "mozilla/dom/Element.h"
16 #include "mozilla/dom/Event.h"
17 #include "mozilla/dom/MouseEventBinding.h"
18 #include "mozilla/EventStateManager.h"
19 #include "mozilla/EventStates.h"
20 #include "mozilla/MouseEvents.h"
21 #include "mozilla/PresShell.h"
22 #include "mozilla/TextEvents.h"
23
24 using namespace mozilla;
25 using namespace mozilla::dom;
26
NS_IMPL_ISUPPORTS(nsButtonBoxFrame::nsButtonBoxListener,nsIDOMEventListener)27 NS_IMPL_ISUPPORTS(nsButtonBoxFrame::nsButtonBoxListener, nsIDOMEventListener)
28
29 nsresult nsButtonBoxFrame::nsButtonBoxListener::HandleEvent(
30 dom::Event* aEvent) {
31 if (!mButtonBoxFrame) {
32 return NS_OK;
33 }
34
35 nsAutoString eventType;
36 aEvent->GetType(eventType);
37
38 if (eventType.EqualsLiteral("blur")) {
39 mButtonBoxFrame->Blurred();
40 return NS_OK;
41 }
42
43 MOZ_ASSERT_UNREACHABLE("Unexpected eventType");
44 return NS_OK;
45 }
46
47 //
48 // NS_NewXULButtonFrame
49 //
50 // Creates a new Button frame and returns it
51 //
NS_NewButtonBoxFrame(PresShell * aPresShell,ComputedStyle * aStyle)52 nsIFrame* NS_NewButtonBoxFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
53 return new (aPresShell)
54 nsButtonBoxFrame(aStyle, aPresShell->GetPresContext());
55 }
56
NS_IMPL_FRAMEARENA_HELPERS(nsButtonBoxFrame)57 NS_IMPL_FRAMEARENA_HELPERS(nsButtonBoxFrame)
58
59 nsButtonBoxFrame::nsButtonBoxFrame(ComputedStyle* aStyle,
60 nsPresContext* aPresContext, ClassID aID)
61 : nsBoxFrame(aStyle, aPresContext, aID, false),
62 mButtonBoxListener(nullptr),
63 mIsHandlingKeyEvent(false) {}
64
Init(nsIContent * aContent,nsContainerFrame * aParent,nsIFrame * aPrevInFlow)65 void nsButtonBoxFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
66 nsIFrame* aPrevInFlow) {
67 nsBoxFrame::Init(aContent, aParent, aPrevInFlow);
68
69 mButtonBoxListener = new nsButtonBoxListener(this);
70
71 mContent->AddSystemEventListener(u"blur"_ns, mButtonBoxListener, false);
72 }
73
DestroyFrom(nsIFrame * aDestructRoot,PostDestroyData & aPostDestroyData)74 void nsButtonBoxFrame::DestroyFrom(nsIFrame* aDestructRoot,
75 PostDestroyData& aPostDestroyData) {
76 mContent->RemoveSystemEventListener(u"blur"_ns, mButtonBoxListener, false);
77
78 mButtonBoxListener->mButtonBoxFrame = nullptr;
79 mButtonBoxListener = nullptr;
80
81 nsBoxFrame::DestroyFrom(aDestructRoot, aPostDestroyData);
82 }
83
BuildDisplayListForChildren(nsDisplayListBuilder * aBuilder,const nsDisplayListSet & aLists)84 void nsButtonBoxFrame::BuildDisplayListForChildren(
85 nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) {
86 // override, since we don't want children to get events
87 if (aBuilder->IsForEventDelivery()) return;
88 nsBoxFrame::BuildDisplayListForChildren(aBuilder, aLists);
89 }
90
HandleEvent(nsPresContext * aPresContext,WidgetGUIEvent * aEvent,nsEventStatus * aEventStatus)91 nsresult nsButtonBoxFrame::HandleEvent(nsPresContext* aPresContext,
92 WidgetGUIEvent* aEvent,
93 nsEventStatus* aEventStatus) {
94 NS_ENSURE_ARG_POINTER(aEventStatus);
95 if (nsEventStatus_eConsumeNoDefault == *aEventStatus) {
96 return NS_OK;
97 }
98
99 switch (aEvent->mMessage) {
100 case eKeyDown: {
101 WidgetKeyboardEvent* keyEvent = aEvent->AsKeyboardEvent();
102 if (!keyEvent) {
103 break;
104 }
105 if (NS_VK_SPACE == keyEvent->mKeyCode) {
106 EventStateManager* esm = aPresContext->EventStateManager();
107 // :hover:active state
108 esm->SetContentState(mContent, NS_EVENT_STATE_HOVER);
109 esm->SetContentState(mContent, NS_EVENT_STATE_ACTIVE);
110 mIsHandlingKeyEvent = true;
111 }
112 break;
113 }
114
115 // On mac, Return fires the default button, not the focused one.
116 #ifndef XP_MACOSX
117 case eKeyPress: {
118 WidgetKeyboardEvent* keyEvent = aEvent->AsKeyboardEvent();
119 if (!keyEvent) {
120 break;
121 }
122 if (NS_VK_RETURN == keyEvent->mKeyCode) {
123 RefPtr<nsIDOMXULButtonElement> button =
124 mContent->AsElement()->AsXULButton();
125 if (button) {
126 MouseClicked(aEvent);
127 *aEventStatus = nsEventStatus_eConsumeNoDefault;
128 }
129 }
130 break;
131 }
132 #endif
133
134 case eKeyUp: {
135 WidgetKeyboardEvent* keyEvent = aEvent->AsKeyboardEvent();
136 if (!keyEvent) {
137 break;
138 }
139 if (NS_VK_SPACE == keyEvent->mKeyCode) {
140 mIsHandlingKeyEvent = false;
141 // only activate on keyup if we're already in the :hover:active state
142 NS_ASSERTION(mContent->IsElement(), "How do we have a non-element?");
143 EventStates buttonState = mContent->AsElement()->State();
144 if (buttonState.HasAllStates(NS_EVENT_STATE_ACTIVE |
145 NS_EVENT_STATE_HOVER)) {
146 // return to normal state
147 EventStateManager* esm = aPresContext->EventStateManager();
148 esm->SetContentState(nullptr, NS_EVENT_STATE_ACTIVE);
149 esm->SetContentState(nullptr, NS_EVENT_STATE_HOVER);
150 MouseClicked(aEvent);
151 }
152 }
153 break;
154 }
155
156 case eMouseClick: {
157 WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
158 if (mouseEvent->IsLeftClickEvent()) {
159 MouseClicked(mouseEvent);
160 }
161 break;
162 }
163
164 default:
165 break;
166 }
167
168 return nsBoxFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
169 }
170
Blurred()171 void nsButtonBoxFrame::Blurred() {
172 NS_ASSERTION(mContent->IsElement(), "How do we have a non-element?");
173 EventStates buttonState = mContent->AsElement()->State();
174 if (mIsHandlingKeyEvent &&
175 buttonState.HasAllStates(NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_HOVER)) {
176 // return to normal state
177 EventStateManager* esm = PresContext()->EventStateManager();
178 esm->SetContentState(nullptr, NS_EVENT_STATE_ACTIVE);
179 esm->SetContentState(nullptr, NS_EVENT_STATE_HOVER);
180 }
181 mIsHandlingKeyEvent = false;
182 }
183
MouseClicked(WidgetGUIEvent * aEvent)184 void nsButtonBoxFrame::MouseClicked(WidgetGUIEvent* aEvent) {
185 // Don't execute if we're disabled.
186 if (mContent->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled,
187 nsGkAtoms::_true, eCaseMatters))
188 return;
189
190 // Have the content handle the event, propagating it according to normal DOM
191 // rules.
192 RefPtr<mozilla::PresShell> presShell = PresContext()->GetPresShell();
193 if (!presShell) {
194 return;
195 }
196
197 // Execute the oncommand event handler.
198 nsCOMPtr<nsIContent> content = mContent;
199 WidgetInputEvent* inputEvent = aEvent->AsInputEvent();
200 WidgetMouseEventBase* mouseEvent = aEvent->AsMouseEventBase();
201 nsContentUtils::DispatchXULCommand(
202 content, aEvent->IsTrusted(), nullptr, presShell, inputEvent->IsControl(),
203 inputEvent->IsAlt(), inputEvent->IsShift(), inputEvent->IsMeta(),
204 mouseEvent ? mouseEvent->mInputSource
205 : MouseEvent_Binding::MOZ_SOURCE_UNKNOWN,
206 mouseEvent ? mouseEvent->mButton : 0);
207 }
208