OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (C) 2006, 2008, 2010 Apple Inc. All rights reserved. | |
3 * Copyright (C) 2010 Google Inc. All rights reserved. | |
4 * | |
5 * Redistribution and use in source and binary forms, with or without | |
6 * modification, are permitted provided that the following conditions | |
7 * are met: | |
8 * 1. Redistributions of source code must retain the above copyright | |
9 * notice, this list of conditions and the following disclaimer. | |
10 * 2. Redistributions in binary form must reproduce the above copyright | |
11 * notice, this list of conditions and the following disclaimer in the | |
12 * documentation and/or other materials provided with the distribution. | |
13 * | |
14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY | |
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR | |
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
25 */ | |
26 | |
27 #include "core/html/shadow/SpinButtonElement.h" | |
28 | |
29 #include "core/HTMLNames.h" | |
30 #include "core/dom/TaskRunnerHelper.h" | |
31 #include "core/events/MouseEvent.h" | |
32 #include "core/events/WheelEvent.h" | |
33 #include "core/frame/LocalFrame.h" | |
34 #include "core/html/shadow/ShadowElementNames.h" | |
35 #include "core/input/EventHandler.h" | |
36 #include "core/layout/LayoutBox.h" | |
37 #include "core/page/ChromeClient.h" | |
38 #include "core/page/Page.h" | |
39 #include "platform/scroll/ScrollbarTheme.h" | |
40 | |
41 namespace blink { | |
42 | |
43 using namespace HTMLNames; | |
44 | |
45 inline SpinButtonElement::SpinButtonElement(Document& document, | |
46 SpinButtonOwner& spin_button_owner) | |
47 : HTMLDivElement(document), | |
48 spin_button_owner_(&spin_button_owner), | |
49 capturing_(false), | |
50 up_down_state_(kIndeterminate), | |
51 press_starting_state_(kIndeterminate), | |
52 repeating_timer_( | |
53 TaskRunnerHelper::Get(TaskType::kUnspecedTimer, &document), | |
54 this, | |
55 &SpinButtonElement::RepeatingTimerFired) {} | |
56 | |
57 SpinButtonElement* SpinButtonElement::Create( | |
58 Document& document, | |
59 SpinButtonOwner& spin_button_owner) { | |
60 SpinButtonElement* element = | |
61 new SpinButtonElement(document, spin_button_owner); | |
62 element->SetShadowPseudoId(AtomicString("-webkit-inner-spin-button")); | |
63 element->setAttribute(idAttr, ShadowElementNames::SpinButton()); | |
64 return element; | |
65 } | |
66 | |
67 void SpinButtonElement::DetachLayoutTree(const AttachContext& context) { | |
68 ReleaseCapture(kEventDispatchDisallowed); | |
69 HTMLDivElement::DetachLayoutTree(context); | |
70 } | |
71 | |
72 void SpinButtonElement::DefaultEventHandler(Event* event) { | |
73 if (!event->IsMouseEvent()) { | |
74 if (!event->DefaultHandled()) | |
75 HTMLDivElement::DefaultEventHandler(event); | |
76 return; | |
77 } | |
78 | |
79 LayoutBox* box = GetLayoutBox(); | |
80 if (!box) { | |
81 if (!event->DefaultHandled()) | |
82 HTMLDivElement::DefaultEventHandler(event); | |
83 return; | |
84 } | |
85 | |
86 if (!ShouldRespondToMouseEvents()) { | |
87 if (!event->DefaultHandled()) | |
88 HTMLDivElement::DefaultEventHandler(event); | |
89 return; | |
90 } | |
91 | |
92 MouseEvent* mouse_event = ToMouseEvent(event); | |
93 IntPoint local = RoundedIntPoint(box->AbsoluteToLocal( | |
94 FloatPoint(mouse_event->AbsoluteLocation()), kUseTransforms)); | |
95 if (mouse_event->type() == EventTypeNames::mousedown && | |
96 mouse_event->button() == | |
97 static_cast<short>(WebPointerProperties::Button::kLeft)) { | |
98 if (box->PixelSnappedBorderBoxRect().Contains(local)) { | |
99 if (spin_button_owner_) | |
100 spin_button_owner_->FocusAndSelectSpinButtonOwner(); | |
101 if (GetLayoutObject()) { | |
102 if (up_down_state_ != kIndeterminate) { | |
103 // A JavaScript event handler called in doStepAction() below | |
104 // might change the element state and we might need to | |
105 // cancel the repeating timer by the state change. If we | |
106 // started the timer after doStepAction(), we would have no | |
107 // chance to cancel the timer. | |
108 StartRepeatingTimer(); | |
109 DoStepAction(up_down_state_ == kUp ? 1 : -1); | |
110 } | |
111 } | |
112 event->SetDefaultHandled(); | |
113 } | |
114 } else if (mouse_event->type() == EventTypeNames::mouseup && | |
115 mouse_event->button() == | |
116 static_cast<short>(WebPointerProperties::Button::kLeft)) { | |
117 ReleaseCapture(); | |
118 } else if (event->type() == EventTypeNames::mousemove) { | |
119 if (box->PixelSnappedBorderBoxRect().Contains(local)) { | |
120 if (!capturing_) { | |
121 if (LocalFrame* frame = GetDocument().GetFrame()) { | |
122 frame->GetEventHandler().SetCapturingMouseEventsNode(this); | |
123 capturing_ = true; | |
124 if (Page* page = GetDocument().GetPage()) | |
125 page->GetChromeClient().RegisterPopupOpeningObserver(this); | |
126 } | |
127 } | |
128 UpDownState old_up_down_state = up_down_state_; | |
129 up_down_state_ = (local.Y() < box->Size().Height() / 2) ? kUp : kDown; | |
130 if (up_down_state_ != old_up_down_state) | |
131 GetLayoutObject()->SetShouldDoFullPaintInvalidation(); | |
132 } else { | |
133 ReleaseCapture(); | |
134 up_down_state_ = kIndeterminate; | |
135 } | |
136 } | |
137 | |
138 if (!event->DefaultHandled()) | |
139 HTMLDivElement::DefaultEventHandler(event); | |
140 } | |
141 | |
142 void SpinButtonElement::WillOpenPopup() { | |
143 ReleaseCapture(); | |
144 up_down_state_ = kIndeterminate; | |
145 } | |
146 | |
147 void SpinButtonElement::ForwardEvent(Event* event) { | |
148 if (!GetLayoutBox()) | |
149 return; | |
150 | |
151 if (!event->HasInterface(EventNames::WheelEvent)) | |
152 return; | |
153 | |
154 if (!spin_button_owner_) | |
155 return; | |
156 | |
157 if (!spin_button_owner_->ShouldSpinButtonRespondToWheelEvents()) | |
158 return; | |
159 | |
160 DoStepAction(ToWheelEvent(event)->wheelDeltaY()); | |
161 event->SetDefaultHandled(); | |
162 } | |
163 | |
164 bool SpinButtonElement::WillRespondToMouseMoveEvents() { | |
165 if (GetLayoutBox() && ShouldRespondToMouseEvents()) | |
166 return true; | |
167 | |
168 return HTMLDivElement::WillRespondToMouseMoveEvents(); | |
169 } | |
170 | |
171 bool SpinButtonElement::WillRespondToMouseClickEvents() { | |
172 if (GetLayoutBox() && ShouldRespondToMouseEvents()) | |
173 return true; | |
174 | |
175 return HTMLDivElement::WillRespondToMouseClickEvents(); | |
176 } | |
177 | |
178 void SpinButtonElement::DoStepAction(int amount) { | |
179 if (!spin_button_owner_) | |
180 return; | |
181 | |
182 if (amount > 0) | |
183 spin_button_owner_->SpinButtonStepUp(); | |
184 else if (amount < 0) | |
185 spin_button_owner_->SpinButtonStepDown(); | |
186 } | |
187 | |
188 void SpinButtonElement::ReleaseCapture(EventDispatch event_dispatch) { | |
189 StopRepeatingTimer(); | |
190 if (!capturing_) | |
191 return; | |
192 if (LocalFrame* frame = GetDocument().GetFrame()) { | |
193 frame->GetEventHandler().SetCapturingMouseEventsNode(nullptr); | |
194 capturing_ = false; | |
195 if (Page* page = GetDocument().GetPage()) | |
196 page->GetChromeClient().UnregisterPopupOpeningObserver(this); | |
197 } | |
198 if (spin_button_owner_) | |
199 spin_button_owner_->SpinButtonDidReleaseMouseCapture(event_dispatch); | |
200 } | |
201 | |
202 bool SpinButtonElement::MatchesReadOnlyPseudoClass() const { | |
203 return OwnerShadowHost()->MatchesReadOnlyPseudoClass(); | |
204 } | |
205 | |
206 bool SpinButtonElement::MatchesReadWritePseudoClass() const { | |
207 return OwnerShadowHost()->MatchesReadWritePseudoClass(); | |
208 } | |
209 | |
210 void SpinButtonElement::StartRepeatingTimer() { | |
211 press_starting_state_ = up_down_state_; | |
212 ScrollbarTheme& theme = ScrollbarTheme::GetTheme(); | |
213 repeating_timer_.Start(theme.InitialAutoscrollTimerDelay(), | |
214 theme.AutoscrollTimerDelay(), BLINK_FROM_HERE); | |
215 } | |
216 | |
217 void SpinButtonElement::StopRepeatingTimer() { | |
218 repeating_timer_.Stop(); | |
219 } | |
220 | |
221 void SpinButtonElement::Step(int amount) { | |
222 if (!ShouldRespondToMouseEvents()) | |
223 return; | |
224 // On Mac OS, NSStepper updates the value for the button under the mouse | |
225 // cursor regardless of the button pressed at the beginning. So the | |
226 // following check is not needed for Mac OS. | |
227 #if !OS(MACOSX) | |
228 if (up_down_state_ != press_starting_state_) | |
229 return; | |
230 #endif | |
231 DoStepAction(amount); | |
232 } | |
233 | |
234 void SpinButtonElement::RepeatingTimerFired(TimerBase*) { | |
235 if (up_down_state_ != kIndeterminate) | |
236 Step(up_down_state_ == kUp ? 1 : -1); | |
237 } | |
238 | |
239 void SpinButtonElement::SetHovered(bool flag) { | |
240 if (!flag) | |
241 up_down_state_ = kIndeterminate; | |
242 HTMLDivElement::SetHovered(flag); | |
243 } | |
244 | |
245 bool SpinButtonElement::ShouldRespondToMouseEvents() { | |
246 return !spin_button_owner_ || | |
247 spin_button_owner_->ShouldSpinButtonRespondToMouseEvents(); | |
248 } | |
249 | |
250 DEFINE_TRACE(SpinButtonElement) { | |
251 visitor->Trace(spin_button_owner_); | |
252 HTMLDivElement::Trace(visitor); | |
253 } | |
254 | |
255 } // namespace blink | |
OLD | NEW |