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