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