Replace WTF::move with WTFMove
[WebKit-https.git] / Source / WebCore / html / shadow / SliderThumbElement.cpp
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
33 #include "config.h"
34 #include "SliderThumbElement.h"
35
36 #include "CSSValueKeywords.h"
37 #include "Event.h"
38 #include "EventHandler.h"
39 #include "Frame.h"
40 #include "HTMLInputElement.h"
41 #include "HTMLParserIdioms.h"
42 #include "MouseEvent.h"
43 #include "RenderFlexibleBox.h"
44 #include "RenderSlider.h"
45 #include "RenderTheme.h"
46 #include "ShadowRoot.h"
47
48 #if ENABLE(IOS_TOUCH_EVENTS)
49 #include "Document.h"
50 #include "Page.h"
51 #include "TouchEvent.h"
52 #endif
53
54 namespace WebCore {
55
56 using namespace HTMLNames;
57
58 inline static Decimal sliderPosition(HTMLInputElement& element)
59 {
60     const StepRange stepRange(element.createStepRange(RejectAny));
61     const Decimal oldValue = parseToDecimalForNumberType(element.value(), stepRange.defaultValue());
62     return stepRange.proportionFromValue(stepRange.clampValue(oldValue));
63 }
64
65 inline static bool hasVerticalAppearance(HTMLInputElement& input)
66 {
67     ASSERT(input.renderer());
68     const RenderStyle& sliderStyle = input.renderer()->style();
69
70 #if ENABLE(VIDEO)
71     if (sliderStyle.appearance() == MediaVolumeSliderPart && input.renderer()->theme().usesVerticalVolumeSlider())
72         return true;
73 #endif
74
75     return sliderStyle.appearance() == SliderVerticalPart;
76 }
77
78 // --------------------------------
79
80 RenderSliderThumb::RenderSliderThumb(SliderThumbElement& element, Ref<RenderStyle>&& style)
81     : RenderBlockFlow(element, WTFMove(style))
82 {
83 }
84
85 void RenderSliderThumb::updateAppearance(RenderStyle* parentStyle)
86 {
87     if (parentStyle->appearance() == SliderVerticalPart)
88         style().setAppearance(SliderThumbVerticalPart);
89     else if (parentStyle->appearance() == SliderHorizontalPart)
90         style().setAppearance(SliderThumbHorizontalPart);
91     else if (parentStyle->appearance() == MediaSliderPart)
92         style().setAppearance(MediaSliderThumbPart);
93     else if (parentStyle->appearance() == MediaVolumeSliderPart)
94         style().setAppearance(MediaVolumeSliderThumbPart);
95     else if (parentStyle->appearance() == MediaFullScreenVolumeSliderPart)
96         style().setAppearance(MediaFullScreenVolumeSliderThumbPart);
97     if (style().hasAppearance()) {
98         ASSERT(element());
99         theme().adjustSliderThumbSize(style(), element());
100     }
101 }
102
103 bool RenderSliderThumb::isSliderThumb() const
104 {
105     return true;
106 }
107
108 // --------------------------------
109
110 // FIXME: Find a way to cascade appearance and adjust heights, and get rid of this class.
111 // http://webkit.org/b/62535
112 class RenderSliderContainer final : public RenderFlexibleBox {
113 public:
114     RenderSliderContainer(SliderContainerElement& element, Ref<RenderStyle>&& style)
115         : RenderFlexibleBox(element, WTFMove(style))
116     {
117     }
118
119 public:
120     virtual void computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues&) const override;
121
122 private:
123     virtual void layout() override;
124     bool isFlexibleBoxImpl() const override { return true; }
125 };
126
127 void RenderSliderContainer::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const
128 {
129     ASSERT(element()->shadowHost());
130     auto& input = downcast<HTMLInputElement>(*element()->shadowHost());
131     bool isVertical = hasVerticalAppearance(input);
132
133 #if ENABLE(DATALIST_ELEMENT)
134     if (input.renderer()->isSlider() && !isVertical && input.list()) {
135         int offsetFromCenter = theme().sliderTickOffsetFromTrackCenter();
136         LayoutUnit trackHeight = 0;
137         if (offsetFromCenter < 0)
138             trackHeight = -2 * offsetFromCenter;
139         else {
140             int tickLength = theme().sliderTickSize().height();
141             trackHeight = 2 * (offsetFromCenter + tickLength);
142         }
143         float zoomFactor = style().effectiveZoom();
144         if (zoomFactor != 1.0)
145             trackHeight *= zoomFactor;
146
147         RenderBox::computeLogicalHeight(trackHeight, logicalTop, computedValues);
148         return;
149     }
150 #endif
151     if (isVertical)
152         logicalHeight = RenderSlider::defaultTrackLength;
153     RenderBox::computeLogicalHeight(logicalHeight, logicalTop, computedValues);
154 }
155
156 void RenderSliderContainer::layout()
157 {
158     ASSERT(element()->shadowHost());
159     auto& input = downcast<HTMLInputElement>(*element()->shadowHost());
160     bool isVertical = hasVerticalAppearance(input);
161     style().setFlexDirection(isVertical ? FlowColumn : FlowRow);
162     TextDirection oldTextDirection = style().direction();
163     if (isVertical) {
164         // FIXME: Work around rounding issues in RTL vertical sliders. We want them to
165         // render identically to LTR vertical sliders. We can remove this work around when
166         // subpixel rendering is enabled on all ports.
167         style().setDirection(LTR);
168     }
169
170     RenderBox* thumb = input.sliderThumbElement() ? input.sliderThumbElement()->renderBox() : nullptr;
171     RenderBox* track = input.sliderTrackElement() ? input.sliderTrackElement()->renderBox() : nullptr;
172     // Force a layout to reset the position of the thumb so the code below doesn't move the thumb to the wrong place.
173     // FIXME: Make a custom Render class for the track and move the thumb positioning code there.
174     if (track)
175         track->setChildNeedsLayout(MarkOnlyThis);
176
177     RenderFlexibleBox::layout();
178
179     style().setDirection(oldTextDirection);
180     // These should always exist, unless someone mutates the shadow DOM (e.g., in the inspector).
181     if (!thumb || !track)
182         return;
183
184     double percentageOffset = sliderPosition(input).toDouble();
185     LayoutUnit availableExtent = isVertical ? track->contentHeight() : track->contentWidth();
186     availableExtent -= isVertical ? thumb->height() : thumb->width();
187     LayoutUnit offset = percentageOffset * availableExtent;
188     LayoutPoint thumbLocation = thumb->location();
189     if (isVertical)
190         thumbLocation.setY(thumbLocation.y() + track->contentHeight() - thumb->height() - offset);
191     else if (style().isLeftToRightDirection())
192         thumbLocation.setX(thumbLocation.x() + offset);
193     else
194         thumbLocation.setX(thumbLocation.x() - offset);
195     thumb->setLocation(thumbLocation);
196     thumb->repaint();
197 }
198
199 // --------------------------------
200
201 SliderThumbElement::SliderThumbElement(Document& document)
202     : HTMLDivElement(HTMLNames::divTag, document)
203     , m_inDragMode(false)
204 #if ENABLE(IOS_TOUCH_EVENTS)
205     , m_exclusiveTouchIdentifier(NoIdentifier)
206     , m_isRegisteredAsTouchEventListener(false)
207 #endif
208 {
209     setHasCustomStyleResolveCallbacks();
210 }
211
212 void SliderThumbElement::setPositionFromValue()
213 {
214     // Since the code to calculate position is in the RenderSliderThumb layout
215     // path, we don't actually update the value here. Instead, we poke at the
216     // renderer directly to trigger layout.
217     if (renderer())
218         renderer()->setNeedsLayout();
219 }
220
221 RenderPtr<RenderElement> SliderThumbElement::createElementRenderer(Ref<RenderStyle>&& style, const RenderTreePosition&)
222 {
223     return createRenderer<RenderSliderThumb>(*this, WTFMove(style));
224 }
225
226 bool SliderThumbElement::isDisabledFormControl() const
227 {
228     HTMLInputElement* input = hostInput();
229     return !input || input->isDisabledFormControl();
230 }
231
232 bool SliderThumbElement::matchesReadWritePseudoClass() const
233 {
234     HTMLInputElement* input = hostInput();
235     return input && input->matchesReadWritePseudoClass();
236 }
237
238 Element* SliderThumbElement::focusDelegate()
239 {
240     return hostInput();
241 }
242
243 void SliderThumbElement::dragFrom(const LayoutPoint& point)
244 {
245     Ref<SliderThumbElement> protect(*this);
246     setPositionFromPoint(point);
247 #if !PLATFORM(IOS)
248     startDragging();
249 #endif
250 }
251
252 void SliderThumbElement::setPositionFromPoint(const LayoutPoint& absolutePoint)
253 {
254     RefPtr<HTMLInputElement> input = hostInput();
255     if (!input || !input->renderer() || !renderBox())
256         return;
257
258     HTMLElement* trackElement = input->sliderTrackElement();
259     if (!trackElement->renderBox())
260         return;
261
262     // Do all the tracking math relative to the input's renderer's box.
263     RenderBox& inputRenderer = downcast<RenderBox>(*input->renderer());
264     RenderBox& trackRenderer = *trackElement->renderBox();
265
266     bool isVertical = hasVerticalAppearance(*input);
267     bool isLeftToRightDirection = renderBox()->style().isLeftToRightDirection();
268     
269     LayoutPoint offset(inputRenderer.absoluteToLocal(absolutePoint, UseTransforms));
270     FloatRect trackBoundingBox = trackRenderer.localToContainerQuad(FloatRect(0, 0, trackRenderer.width(), trackRenderer.height()), &inputRenderer).enclosingBoundingBox();
271
272     LayoutUnit trackLength;
273     LayoutUnit position;
274     if (isVertical) {
275         trackLength = trackRenderer.contentHeight() - renderBox()->height();
276         position = offset.y() - renderBox()->height() / 2 - trackBoundingBox.y() - renderBox()->marginBottom();
277     } else {
278         trackLength = trackRenderer.contentWidth() - renderBox()->width();
279         position = offset.x() - renderBox()->width() / 2 - trackBoundingBox.x();
280         position -= isLeftToRightDirection ? renderBox()->marginLeft() : renderBox()->marginRight();
281     }
282
283     position = std::max<LayoutUnit>(0, std::min(position, trackLength));
284     const Decimal ratio = Decimal::fromDouble(static_cast<double>(position) / trackLength);
285     const Decimal fraction = isVertical || !isLeftToRightDirection ? Decimal(1) - ratio : ratio;
286     StepRange stepRange(input->createStepRange(RejectAny));
287     Decimal value = stepRange.clampValue(stepRange.valueFromProportion(fraction));
288
289 #if ENABLE(DATALIST_ELEMENT)
290     const LayoutUnit snappingThreshold = renderer()->theme().sliderTickSnappingThreshold();
291     if (snappingThreshold > 0) {
292         Decimal closest = input->findClosestTickMarkValue(value);
293         if (closest.isFinite()) {
294             double closestFraction = stepRange.proportionFromValue(closest).toDouble();
295             double closestRatio = isVertical || !isLeftToRightDirection ? 1.0 - closestFraction : closestFraction;
296             LayoutUnit closestPosition = trackLength * closestRatio;
297             if ((closestPosition - position).abs() <= snappingThreshold)
298                 value = closest;
299         }
300     }
301 #endif
302
303     String valueString = serializeForNumberType(value);
304     if (valueString == input->value())
305         return;
306
307     // FIXME: This is no longer being set from renderer. Consider updating the method name.
308     input->setValueFromRenderer(valueString);
309     if (renderer())
310         renderer()->setNeedsLayout();
311 }
312
313 void SliderThumbElement::startDragging()
314 {
315     if (Frame* frame = document().frame()) {
316         frame->eventHandler().setCapturingMouseEventsElement(this);
317         m_inDragMode = true;
318     }
319 }
320
321 void SliderThumbElement::stopDragging()
322 {
323     if (!m_inDragMode)
324         return;
325
326     if (Frame* frame = document().frame())
327         frame->eventHandler().setCapturingMouseEventsElement(nullptr);
328     m_inDragMode = false;
329     if (renderer())
330         renderer()->setNeedsLayout();
331
332     RefPtr<HTMLInputElement> input = hostInput();
333     if (input)
334         input->dispatchFormControlChangeEvent();
335 }
336
337 #if !PLATFORM(IOS)
338 void SliderThumbElement::defaultEventHandler(Event* event)
339 {
340     if (!is<MouseEvent>(*event)) {
341         HTMLDivElement::defaultEventHandler(event);
342         return;
343     }
344
345     // FIXME: Should handle this readonly/disabled check in more general way.
346     // Missing this kind of check is likely to occur elsewhere if adding it in each shadow element.
347     HTMLInputElement* input = hostInput();
348     if (!input || input->isDisabledOrReadOnly()) {
349         stopDragging();
350         HTMLDivElement::defaultEventHandler(event);
351         return;
352     }
353
354     MouseEvent& mouseEvent = downcast<MouseEvent>(*event);
355     bool isLeftButton = mouseEvent.button() == LeftButton;
356     const AtomicString& eventType = mouseEvent.type();
357
358     // We intentionally do not call event->setDefaultHandled() here because
359     // MediaControlTimelineElement::defaultEventHandler() wants to handle these
360     // mouse events.
361     if (eventType == eventNames().mousedownEvent && isLeftButton) {
362         startDragging();
363         return;
364     } else if (eventType == eventNames().mouseupEvent && isLeftButton) {
365         stopDragging();
366         return;
367     } else if (eventType == eventNames().mousemoveEvent) {
368         if (m_inDragMode)
369             setPositionFromPoint(mouseEvent.absoluteLocation());
370         return;
371     }
372
373     HTMLDivElement::defaultEventHandler(&mouseEvent);
374 }
375 #endif
376
377 #if !PLATFORM(IOS)
378 bool SliderThumbElement::willRespondToMouseMoveEvents()
379 {
380     const HTMLInputElement* input = hostInput();
381     if (input && !input->isDisabledOrReadOnly() && m_inDragMode)
382         return true;
383
384     return HTMLDivElement::willRespondToMouseMoveEvents();
385 }
386
387 bool SliderThumbElement::willRespondToMouseClickEvents()
388 {
389     const HTMLInputElement* input = hostInput();
390     if (input && !input->isDisabledOrReadOnly())
391         return true;
392
393     return HTMLDivElement::willRespondToMouseClickEvents();
394 }
395 #endif // !PLATFORM(IOS)
396
397 void SliderThumbElement::willDetachRenderers()
398 {
399     if (m_inDragMode) {
400         if (Frame* frame = document().frame())
401             frame->eventHandler().setCapturingMouseEventsElement(nullptr);
402     }
403 #if ENABLE(IOS_TOUCH_EVENTS)
404     unregisterForTouchEvents();
405 #endif
406 }
407
408 #if ENABLE(IOS_TOUCH_EVENTS)
409 unsigned SliderThumbElement::exclusiveTouchIdentifier() const
410 {
411     return m_exclusiveTouchIdentifier;
412 }
413
414 void SliderThumbElement::setExclusiveTouchIdentifier(unsigned identifier)
415 {
416     ASSERT(m_exclusiveTouchIdentifier == NoIdentifier);
417     m_exclusiveTouchIdentifier = identifier;
418 }
419
420 void SliderThumbElement::clearExclusiveTouchIdentifier()
421 {
422     m_exclusiveTouchIdentifier = NoIdentifier;
423 }
424
425 static Touch* findTouchWithIdentifier(TouchList& list, unsigned identifier)
426 {
427     unsigned length = list.length();
428     for (unsigned i = 0; i < length; ++i) {
429         Touch* touch = list.item(i);
430         if (touch->identifier() == identifier)
431             return touch;
432     }
433     return nullptr;
434 }
435
436 void SliderThumbElement::handleTouchStart(TouchEvent* touchEvent)
437 {
438     TouchList* targetTouches = touchEvent->targetTouches();
439     if (!targetTouches)
440         return;
441
442     if (targetTouches->length() != 1)
443         return;
444
445     Touch* touch = targetTouches->item(0);
446     if (!renderer())
447         return;
448     IntRect boundingBox = renderer()->absoluteBoundingBoxRect();
449     // Ignore the touch if it is not really inside the thumb.
450     if (!boundingBox.contains(touch->pageX(), touch->pageY()))
451         return;
452
453     setExclusiveTouchIdentifier(touch->identifier());
454
455     startDragging();
456     touchEvent->setDefaultHandled();
457 }
458
459 void SliderThumbElement::handleTouchMove(TouchEvent* touchEvent)
460 {
461     unsigned identifier = exclusiveTouchIdentifier();
462     if (identifier == NoIdentifier)
463         return;
464
465     TouchList* targetTouches = touchEvent->targetTouches();
466     if (!targetTouches)
467         return;
468
469     Touch* touch = findTouchWithIdentifier(*targetTouches, identifier);
470     if (!touch)
471         return;
472
473     if (m_inDragMode)
474         setPositionFromPoint(IntPoint(touch->pageX(), touch->pageY()));
475     touchEvent->setDefaultHandled();
476 }
477
478 void SliderThumbElement::handleTouchEndAndCancel(TouchEvent* touchEvent)
479 {
480     unsigned identifier = exclusiveTouchIdentifier();
481     if (identifier == NoIdentifier)
482         return;
483
484     TouchList* targetTouches = touchEvent->targetTouches();
485     if (!targetTouches)
486         return;
487     // If our exclusive touch still exists, it was not the touch
488     // that ended, so we should not stop dragging.
489     Touch* exclusiveTouch = findTouchWithIdentifier(*targetTouches, identifier);
490     if (exclusiveTouch)
491         return;
492
493     clearExclusiveTouchIdentifier();
494
495     stopDragging();
496 }
497
498 void SliderThumbElement::didAttachRenderers()
499 {
500     if (shouldAcceptTouchEvents())
501         registerForTouchEvents();
502 }
503
504 void SliderThumbElement::handleTouchEvent(TouchEvent* touchEvent)
505 {
506     HTMLInputElement* input = hostInput();
507     ASSERT(input);
508     if (input->isReadOnly() || input->isDisabledFormControl()) {
509         clearExclusiveTouchIdentifier();
510         stopDragging();
511         touchEvent->setDefaultHandled();
512         HTMLDivElement::defaultEventHandler(touchEvent);
513         return;
514     }
515
516     const AtomicString& eventType = touchEvent->type();
517     if (eventType == eventNames().touchstartEvent) {
518         handleTouchStart(touchEvent);
519         return;
520     }
521     if (eventType == eventNames().touchendEvent || eventType == eventNames().touchcancelEvent) {
522         handleTouchEndAndCancel(touchEvent);
523         return;
524     }
525     if (eventType == eventNames().touchmoveEvent) {
526         handleTouchMove(touchEvent);
527         return;
528     }
529
530     HTMLDivElement::defaultEventHandler(touchEvent);
531 }
532
533 bool SliderThumbElement::shouldAcceptTouchEvents()
534 {
535     return renderer() && !isDisabledFormControl();
536 }
537
538 void SliderThumbElement::registerForTouchEvents()
539 {
540     if (m_isRegisteredAsTouchEventListener)
541         return;
542
543     ASSERT(shouldAcceptTouchEvents());
544
545     document().addTouchEventListener(this);
546     m_isRegisteredAsTouchEventListener = true;
547 }
548
549 void SliderThumbElement::unregisterForTouchEvents()
550 {
551     if (!m_isRegisteredAsTouchEventListener)
552         return;
553
554     clearExclusiveTouchIdentifier();
555     stopDragging();
556
557     document().removeTouchEventListener(this);
558     m_isRegisteredAsTouchEventListener = false;
559 }
560
561 void SliderThumbElement::disabledAttributeChanged()
562 {
563     if (shouldAcceptTouchEvents())
564         registerForTouchEvents();
565     else
566         unregisterForTouchEvents();
567 }
568 #endif // ENABLE(IOS_TOUCH_EVENTS)
569
570 HTMLInputElement* SliderThumbElement::hostInput() const
571 {
572     // Only HTMLInputElement creates SliderThumbElement instances as its shadow nodes.
573     // So, shadowHost() must be an HTMLInputElement.
574     return downcast<HTMLInputElement>(shadowHost());
575 }
576
577 static const AtomicString& sliderThumbShadowPseudoId()
578 {
579     DEPRECATED_DEFINE_STATIC_LOCAL(const AtomicString, sliderThumb, ("-webkit-slider-thumb", AtomicString::ConstructFromLiteral));
580     return sliderThumb;
581 }
582
583 static const AtomicString& mediaSliderThumbShadowPseudoId()
584 {
585     DEPRECATED_DEFINE_STATIC_LOCAL(const AtomicString, mediaSliderThumb, ("-webkit-media-slider-thumb", AtomicString::ConstructFromLiteral));
586     return mediaSliderThumb;
587 }
588
589 const AtomicString& SliderThumbElement::shadowPseudoId() const
590 {
591     // FIXME: this code needs to go away, it is very very wrong.
592     // The value of shadowPseudoId() is needed to resolve the style of the shadow tree. In this case,
593     // that value depends on the style, which means the style needs to be computed twice to get
594     // a correct value: once to get the Input's appearance, then a second time to style the shadow tree correctly.
595
596     HTMLInputElement* input = hostInput();
597     if (!input)
598         return sliderThumbShadowPseudoId();
599     if (!input->renderer())
600         return emptyAtom;
601
602     const RenderStyle& sliderStyle = input->renderer()->style();
603     switch (sliderStyle.appearance()) {
604     case MediaSliderPart:
605     case MediaSliderThumbPart:
606     case MediaVolumeSliderPart:
607     case MediaVolumeSliderThumbPart:
608     case MediaFullScreenVolumeSliderPart:
609     case MediaFullScreenVolumeSliderThumbPart:
610         return mediaSliderThumbShadowPseudoId();
611     default:
612         return sliderThumbShadowPseudoId();
613     }
614 }
615
616 Ref<Element> SliderThumbElement::cloneElementWithoutAttributesAndChildren(Document& targetDocument)
617 {
618     return create(targetDocument);
619 }
620
621 // --------------------------------
622
623 inline SliderContainerElement::SliderContainerElement(Document& document)
624     : HTMLDivElement(HTMLNames::divTag, document)
625 {
626 }
627
628 Ref<SliderContainerElement> SliderContainerElement::create(Document& document)
629 {
630     return adoptRef(*new SliderContainerElement(document));
631 }
632
633 RenderPtr<RenderElement> SliderContainerElement::createElementRenderer(Ref<RenderStyle>&& style, const RenderTreePosition&)
634 {
635     return createRenderer<RenderSliderContainer>(*this, WTFMove(style));
636 }
637
638 const AtomicString& SliderContainerElement::shadowPseudoId() const
639 {
640     // FIXME: this code needs to go away, it is very very wrong.
641     // The value of shadowPseudoId() is needed to resolve the style of the shadow tree. In this case,
642     // that value depends on the style, which means the style needs to be computed twice to get
643     // a correct value: once to get the Input's appearance, then a second time to style the shadow tree correctly.
644
645     DEPRECATED_DEFINE_STATIC_LOCAL(const AtomicString, mediaSliderContainer, ("-webkit-media-slider-container", AtomicString::ConstructFromLiteral));
646     DEPRECATED_DEFINE_STATIC_LOCAL(const AtomicString, sliderContainer, ("-webkit-slider-container", AtomicString::ConstructFromLiteral));
647
648     if (!is<HTMLInputElement>(*shadowHost()))
649         return sliderContainer;
650
651     auto& input = downcast<HTMLInputElement>(*shadowHost());
652     if (!input.renderer())
653         return emptyAtom;
654
655     const RenderStyle& sliderStyle = input.renderer()->style();
656     switch (sliderStyle.appearance()) {
657     case MediaSliderPart:
658     case MediaSliderThumbPart:
659     case MediaVolumeSliderPart:
660     case MediaVolumeSliderThumbPart:
661     case MediaFullScreenVolumeSliderPart:
662     case MediaFullScreenVolumeSliderThumbPart:
663         return mediaSliderContainer;
664     default:
665         return sliderContainer;
666     }
667 }
668
669 }