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