Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(148)

Side by Side Diff: third_party/WebKit/Source/core/html/shadow/SliderThumbElement.cpp

Issue 2815263002: Move form-related files in core/html/shadow to core/html/forms. (Closed)
Patch Set: Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 /*
2 * Copyright (C) 2006, 2007, 2008, 2009 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 are
7 * met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include "core/html/shadow/SliderThumbElement.h"
33
34 #include "core/dom/shadow/ShadowRoot.h"
35 #include "core/events/Event.h"
36 #include "core/events/MouseEvent.h"
37 #include "core/events/TouchEvent.h"
38 #include "core/frame/EventHandlerRegistry.h"
39 #include "core/frame/LocalFrame.h"
40 #include "core/html/HTMLInputElement.h"
41 #include "core/html/forms/StepRange.h"
42 #include "core/html/parser/HTMLParserIdioms.h"
43 #include "core/html/shadow/ShadowElementNames.h"
44 #include "core/input/EventHandler.h"
45 #include "core/layout/LayoutSliderContainer.h"
46 #include "core/layout/LayoutSliderThumb.h"
47 #include "core/layout/LayoutTheme.h"
48
49 namespace blink {
50
51 using namespace HTMLNames;
52
53 inline static bool HasVerticalAppearance(HTMLInputElement* input) {
54 DCHECK(input->GetLayoutObject());
55 const ComputedStyle& slider_style = input->GetLayoutObject()->StyleRef();
56
57 return slider_style.Appearance() == kSliderVerticalPart;
58 }
59
60 inline SliderThumbElement::SliderThumbElement(Document& document)
61 : HTMLDivElement(document), in_drag_mode_(false) {}
62
63 SliderThumbElement* SliderThumbElement::Create(Document& document) {
64 SliderThumbElement* element = new SliderThumbElement(document);
65 element->setAttribute(idAttr, ShadowElementNames::SliderThumb());
66 return element;
67 }
68
69 void SliderThumbElement::SetPositionFromValue() {
70 // Since the code to calculate position is in the LayoutSliderThumb layout
71 // path, we don't actually update the value here. Instead, we poke at the
72 // layoutObject directly to trigger layout.
73 if (GetLayoutObject())
74 GetLayoutObject()->SetNeedsLayoutAndFullPaintInvalidation(
75 LayoutInvalidationReason::kSliderValueChanged);
76 }
77
78 LayoutObject* SliderThumbElement::CreateLayoutObject(const ComputedStyle&) {
79 return new LayoutSliderThumb(this);
80 }
81
82 bool SliderThumbElement::IsDisabledFormControl() const {
83 return HostInput() && HostInput()->IsDisabledFormControl();
84 }
85
86 bool SliderThumbElement::MatchesReadOnlyPseudoClass() const {
87 return HostInput() && HostInput()->MatchesReadOnlyPseudoClass();
88 }
89
90 bool SliderThumbElement::MatchesReadWritePseudoClass() const {
91 return HostInput() && HostInput()->MatchesReadWritePseudoClass();
92 }
93
94 Node* SliderThumbElement::FocusDelegate() {
95 return HostInput();
96 }
97
98 void SliderThumbElement::DragFrom(const LayoutPoint& point) {
99 StartDragging();
100 SetPositionFromPoint(point);
101 }
102
103 void SliderThumbElement::SetPositionFromPoint(const LayoutPoint& point) {
104 HTMLInputElement* input(HostInput());
105 Element* track_element = input->UserAgentShadowRoot()->GetElementById(
106 ShadowElementNames::SliderTrack());
107
108 if (!input->GetLayoutObject() || !GetLayoutBox() ||
109 !track_element->GetLayoutBox())
110 return;
111
112 LayoutPoint offset = LayoutPoint(input->GetLayoutObject()->AbsoluteToLocal(
113 FloatPoint(point), kUseTransforms));
114 bool is_vertical = HasVerticalAppearance(input);
115 bool is_left_to_right_direction =
116 GetLayoutBox()->Style()->IsLeftToRightDirection();
117 LayoutUnit track_size;
118 LayoutUnit position;
119 LayoutUnit current_position;
120 // We need to calculate currentPosition from absolute points becaue the
121 // layoutObject for this node is usually on a layer and layoutBox()->x() and
122 // y() are unusable.
123 // FIXME: This should probably respect transforms.
124 LayoutPoint absolute_thumb_origin =
125 GetLayoutBox()->AbsoluteBoundingBoxRectIgnoringTransforms().Location();
126 LayoutPoint absolute_slider_content_origin =
127 LayoutPoint(input->GetLayoutObject()->LocalToAbsolute());
128 IntRect track_bounding_box =
129 track_element->GetLayoutObject()
130 ->AbsoluteBoundingBoxRectIgnoringTransforms();
131 IntRect input_bounding_box =
132 input->GetLayoutObject()->AbsoluteBoundingBoxRectIgnoringTransforms();
133 if (is_vertical) {
134 track_size = track_element->GetLayoutBox()->ContentHeight() -
135 GetLayoutBox()->Size().Height();
136 position = offset.Y() - GetLayoutBox()->Size().Height() / 2 -
137 track_bounding_box.Y() + input_bounding_box.Y() -
138 GetLayoutBox()->MarginBottom();
139 current_position =
140 absolute_thumb_origin.Y() - absolute_slider_content_origin.Y();
141 } else {
142 track_size = track_element->GetLayoutBox()->ContentWidth() -
143 GetLayoutBox()->Size().Width();
144 position = offset.X() - GetLayoutBox()->Size().Width() / 2 -
145 track_bounding_box.X() + input_bounding_box.X();
146 position -= is_left_to_right_direction ? GetLayoutBox()->MarginLeft()
147 : GetLayoutBox()->MarginRight();
148 current_position =
149 absolute_thumb_origin.X() - absolute_slider_content_origin.X();
150 }
151 position = std::min(position, track_size).ClampNegativeToZero();
152 const Decimal ratio =
153 Decimal::FromDouble(static_cast<double>(position) / track_size);
154 const Decimal fraction =
155 is_vertical || !is_left_to_right_direction ? Decimal(1) - ratio : ratio;
156 StepRange step_range(input->CreateStepRange(kRejectAny));
157 Decimal value =
158 step_range.ClampValue(step_range.ValueFromProportion(fraction));
159
160 Decimal closest = input->FindClosestTickMarkValue(value);
161 if (closest.IsFinite()) {
162 double closest_fraction =
163 step_range.ProportionFromValue(closest).ToDouble();
164 double closest_ratio = is_vertical || !is_left_to_right_direction
165 ? 1.0 - closest_fraction
166 : closest_fraction;
167 LayoutUnit closest_position(track_size * closest_ratio);
168 const LayoutUnit snapping_threshold(5);
169 if ((closest_position - position).Abs() <= snapping_threshold)
170 value = closest;
171 }
172
173 String value_string = SerializeForNumberType(value);
174 if (value_string == input->value())
175 return;
176
177 // FIXME: This is no longer being set from renderer. Consider updating the
178 // method name.
179 input->SetValueFromRenderer(value_string);
180 if (GetLayoutObject())
181 GetLayoutObject()->SetNeedsLayoutAndFullPaintInvalidation(
182 LayoutInvalidationReason::kSliderValueChanged);
183 }
184
185 void SliderThumbElement::StartDragging() {
186 if (LocalFrame* frame = GetDocument().GetFrame()) {
187 // Note that we get to here only we through mouse event path. The touch
188 // events are implicitly captured to the starting element and will be
189 // handled in handleTouchEvent function.
190 frame->GetEventHandler().SetPointerCapture(PointerEventFactory::kMouseId,
191 this);
192 in_drag_mode_ = true;
193 }
194 }
195
196 void SliderThumbElement::StopDragging() {
197 if (!in_drag_mode_)
198 return;
199
200 if (LocalFrame* frame = GetDocument().GetFrame()) {
201 frame->GetEventHandler().ReleasePointerCapture(
202 PointerEventFactory::kMouseId, this);
203 }
204 in_drag_mode_ = false;
205 if (GetLayoutObject())
206 GetLayoutObject()->SetNeedsLayoutAndFullPaintInvalidation(
207 LayoutInvalidationReason::kSliderValueChanged);
208 if (HostInput())
209 HostInput()->DispatchFormControlChangeEvent();
210 }
211
212 void SliderThumbElement::DefaultEventHandler(Event* event) {
213 if (event->IsPointerEvent() &&
214 event->type() == EventTypeNames::lostpointercapture) {
215 StopDragging();
216 return;
217 }
218
219 if (!event->IsMouseEvent()) {
220 HTMLDivElement::DefaultEventHandler(event);
221 return;
222 }
223
224 // FIXME: Should handle this readonly/disabled check in more general way.
225 // Missing this kind of check is likely to occur elsewhere if adding it in
226 // each shadow element.
227 HTMLInputElement* input = HostInput();
228 if (!input || input->IsDisabledFormControl()) {
229 StopDragging();
230 HTMLDivElement::DefaultEventHandler(event);
231 return;
232 }
233
234 MouseEvent* mouse_event = ToMouseEvent(event);
235 bool is_left_button = mouse_event->button() ==
236 static_cast<short>(WebPointerProperties::Button::kLeft);
237 const AtomicString& event_type = event->type();
238
239 // We intentionally do not call event->setDefaultHandled() here because
240 // MediaControlTimelineElement::defaultEventHandler() wants to handle these
241 // mouse events.
242 if (event_type == EventTypeNames::mousedown && is_left_button) {
243 StartDragging();
244 return;
245 }
246 if (event_type == EventTypeNames::mouseup && is_left_button) {
247 StopDragging();
248 return;
249 }
250 if (event_type == EventTypeNames::mousemove) {
251 if (in_drag_mode_)
252 SetPositionFromPoint(LayoutPoint(mouse_event->AbsoluteLocation()));
253 return;
254 }
255
256 HTMLDivElement::DefaultEventHandler(event);
257 }
258
259 bool SliderThumbElement::WillRespondToMouseMoveEvents() {
260 const HTMLInputElement* input = HostInput();
261 if (input && !input->IsDisabledFormControl() && in_drag_mode_)
262 return true;
263
264 return HTMLDivElement::WillRespondToMouseMoveEvents();
265 }
266
267 bool SliderThumbElement::WillRespondToMouseClickEvents() {
268 const HTMLInputElement* input = HostInput();
269 if (input && !input->IsDisabledFormControl())
270 return true;
271
272 return HTMLDivElement::WillRespondToMouseClickEvents();
273 }
274
275 void SliderThumbElement::DetachLayoutTree(const AttachContext& context) {
276 if (in_drag_mode_) {
277 if (LocalFrame* frame = GetDocument().GetFrame())
278 frame->GetEventHandler().SetCapturingMouseEventsNode(nullptr);
279 }
280 HTMLDivElement::DetachLayoutTree(context);
281 }
282
283 HTMLInputElement* SliderThumbElement::HostInput() const {
284 // Only HTMLInputElement creates SliderThumbElement instances as its shadow
285 // nodes. So, ownerShadowHost() must be an HTMLInputElement.
286 return toHTMLInputElement(OwnerShadowHost());
287 }
288
289 static const AtomicString& SliderThumbShadowPartId() {
290 DEFINE_STATIC_LOCAL(const AtomicString, slider_thumb,
291 ("-webkit-slider-thumb"));
292 return slider_thumb;
293 }
294
295 static const AtomicString& MediaSliderThumbShadowPartId() {
296 DEFINE_STATIC_LOCAL(const AtomicString, media_slider_thumb,
297 ("-webkit-media-slider-thumb"));
298 return media_slider_thumb;
299 }
300
301 const AtomicString& SliderThumbElement::ShadowPseudoId() const {
302 HTMLInputElement* input = HostInput();
303 if (!input || !input->GetLayoutObject())
304 return SliderThumbShadowPartId();
305
306 const ComputedStyle& slider_style = input->GetLayoutObject()->StyleRef();
307 switch (slider_style.Appearance()) {
308 case kMediaSliderPart:
309 case kMediaSliderThumbPart:
310 case kMediaVolumeSliderPart:
311 case kMediaVolumeSliderThumbPart:
312 return MediaSliderThumbShadowPartId();
313 default:
314 return SliderThumbShadowPartId();
315 }
316 }
317
318 // --------------------------------
319
320 inline SliderContainerElement::SliderContainerElement(Document& document)
321 : HTMLDivElement(document),
322 has_touch_event_handler_(false),
323 touch_started_(false),
324 sliding_direction_(kNoMove) {
325 UpdateTouchEventHandlerRegistry();
326 }
327
328 DEFINE_NODE_FACTORY(SliderContainerElement)
329
330 HTMLInputElement* SliderContainerElement::HostInput() const {
331 return toHTMLInputElement(OwnerShadowHost());
332 }
333
334 LayoutObject* SliderContainerElement::CreateLayoutObject(const ComputedStyle&) {
335 return new LayoutSliderContainer(this);
336 }
337
338 void SliderContainerElement::DefaultEventHandler(Event* event) {
339 if (event->IsTouchEvent()) {
340 HandleTouchEvent(ToTouchEvent(event));
341 return;
342 }
343 }
344
345 void SliderContainerElement::HandleTouchEvent(TouchEvent* event) {
346 HTMLInputElement* input = HostInput();
347 if (input->IsDisabledFormControl())
348 return;
349
350 if (event->type() == EventTypeNames::touchend) {
351 // TODO: Also do this for touchcancel?
352 input->DispatchFormControlChangeEvent();
353 event->SetDefaultHandled();
354 sliding_direction_ = kNoMove;
355 touch_started_ = false;
356 return;
357 }
358
359 // The direction of this series of touch actions has been determined, which is
360 // perpendicular to the slider, so no need to adjust the value.
361 if (!CanSlide()) {
362 return;
363 }
364
365 TouchList* touches = event->targetTouches();
366 SliderThumbElement* thumb = ToSliderThumbElement(
367 GetTreeScope().GetElementById(ShadowElementNames::SliderThumb()));
368 if (touches->length() == 1) {
369 if (event->type() == EventTypeNames::touchstart) {
370 start_point_ = touches->item(0)->AbsoluteLocation();
371 sliding_direction_ = kNoMove;
372 touch_started_ = true;
373 thumb->SetPositionFromPoint(touches->item(0)->AbsoluteLocation());
374 } else if (touch_started_) {
375 LayoutPoint current_point = touches->item(0)->AbsoluteLocation();
376 if (sliding_direction_ ==
377 kNoMove) { // Still needs to update the direction.
378 sliding_direction_ = GetDirection(current_point, start_point_);
379 }
380
381 // m_slidingDirection has been updated, so check whether it's okay to
382 // slide again.
383 if (CanSlide()) {
384 thumb->SetPositionFromPoint(touches->item(0)->AbsoluteLocation());
385 event->SetDefaultHandled();
386 }
387 }
388 }
389 }
390
391 SliderContainerElement::Direction SliderContainerElement::GetDirection(
392 LayoutPoint& point1,
393 LayoutPoint& point2) {
394 if (point1 == point2) {
395 return kNoMove;
396 }
397 if ((point1.X() - point2.X()).Abs() >= (point1.Y() - point2.Y()).Abs()) {
398 return kHorizontal;
399 }
400 return kVertical;
401 }
402
403 bool SliderContainerElement::CanSlide() {
404 if (!HostInput() || !HostInput()->GetLayoutObject() ||
405 !HostInput()->GetLayoutObject()->Style()) {
406 return false;
407 }
408 const ComputedStyle* slider_style = HostInput()->GetLayoutObject()->Style();
409 const TransformOperations& transforms = slider_style->Transform();
410 int transform_size = transforms.size();
411 if (transform_size > 0) {
412 for (int i = 0; i < transform_size; ++i) {
413 if (transforms.at(i)->GetType() == TransformOperation::kRotate) {
414 return true;
415 }
416 }
417 }
418 if ((sliding_direction_ == kVertical &&
419 slider_style->Appearance() == kSliderHorizontalPart) ||
420 (sliding_direction_ == kHorizontal &&
421 slider_style->Appearance() == kSliderVerticalPart)) {
422 return false;
423 }
424 return true;
425 }
426
427 const AtomicString& SliderContainerElement::ShadowPseudoId() const {
428 DEFINE_STATIC_LOCAL(const AtomicString, media_slider_container,
429 ("-webkit-media-slider-container"));
430 DEFINE_STATIC_LOCAL(const AtomicString, slider_container,
431 ("-webkit-slider-container"));
432
433 if (!OwnerShadowHost() || !OwnerShadowHost()->GetLayoutObject())
434 return slider_container;
435
436 const ComputedStyle& slider_style =
437 OwnerShadowHost()->GetLayoutObject()->StyleRef();
438 switch (slider_style.Appearance()) {
439 case kMediaSliderPart:
440 case kMediaSliderThumbPart:
441 case kMediaVolumeSliderPart:
442 case kMediaVolumeSliderThumbPart:
443 return media_slider_container;
444 default:
445 return slider_container;
446 }
447 }
448
449 void SliderContainerElement::UpdateTouchEventHandlerRegistry() {
450 if (has_touch_event_handler_) {
451 return;
452 }
453 if (GetDocument().GetPage() &&
454 GetDocument().Lifecycle().GetState() < DocumentLifecycle::kStopping) {
455 EventHandlerRegistry& registry =
456 GetDocument().GetPage()->GetEventHandlerRegistry();
457 registry.DidAddEventHandler(
458 *this, EventHandlerRegistry::kTouchStartOrMoveEventPassive);
459 has_touch_event_handler_ = true;
460 }
461 }
462
463 void SliderContainerElement::DidMoveToNewDocument(Document& old_document) {
464 UpdateTouchEventHandlerRegistry();
465 HTMLElement::DidMoveToNewDocument(old_document);
466 }
467
468 void SliderContainerElement::RemoveAllEventListeners() {
469 Node::RemoveAllEventListeners();
470 has_touch_event_handler_ = false;
471 }
472
473 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698