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
57namespace WebCore {
58
59using namespace HTMLNames;
60
61WTF_MAKE_ISO_ALLOCATED_IMPL(SliderThumbElement);
62WTF_MAKE_ISO_ALLOCATED_IMPL(SliderContainerElement);
63WTF_MAKE_ISO_ALLOCATED_IMPL(RenderSliderThumb);
64
65inline 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
72inline 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
87RenderSliderThumb::RenderSliderThumb(SliderThumbElement& element, RenderStyle&& style)
88 : RenderBlockFlow(element, WTFMove(style))
89{
90}
91
92void 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
110bool 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
119class RenderSliderContainer final : public RenderFlexibleBox {
120 WTF_MAKE_ISO_ALLOCATED_INLINE(RenderSliderContainer);
121public:
122 RenderSliderContainer(SliderContainerElement& element, RenderStyle&& style)
123 : RenderFlexibleBox(element, WTFMove(style))
124 {
125 }
126
127public:
128 RenderBox::LogicalExtentComputedValues computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop) const override;
129
130private:
131 void layout() override;
132 bool isFlexibleBoxImpl() const override { return true; }
133};
134
135RenderBox::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
163void 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
208SliderThumbElement::SliderThumbElement(Document& document)
209 : HTMLDivElement(HTMLNames::divTag, document)
210{
211 setHasCustomStyleResolveCallbacks();
212}
213
214void 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
223RenderPtr<RenderElement> SliderThumbElement::createElementRenderer(RenderStyle&& style, const RenderTreePosition&)
224{
225 return createRenderer<RenderSliderThumb>(*this, WTFMove(style));
226}
227
228bool SliderThumbElement::isDisabledFormControl() const
229{
230 auto input = hostInput();
231 return !input || input->isDisabledFormControl();
232}
233
234bool SliderThumbElement::matchesReadWritePseudoClass() const
235{
236 auto input = hostInput();
237 return input && input->matchesReadWritePseudoClass();
238}
239
240RefPtr<Element> SliderThumbElement::focusDelegate()
241{
242 return hostInput();
243}
244
245void 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
254void 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
321void SliderThumbElement::startDragging()
322{
323 if (RefPtr<Frame> frame = document().frame()) {
324 frame->eventHandler().setCapturingMouseEventsElement(this);
325 m_inDragMode = true;
326 }
327}
328
329void 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)
342void 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 AtomicString& 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
383bool 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
392bool 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
403void 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
416unsigned SliderThumbElement::exclusiveTouchIdentifier() const
417{
418 return m_exclusiveTouchIdentifier;
419}
420
421void SliderThumbElement::setExclusiveTouchIdentifier(unsigned identifier)
422{
423 ASSERT(m_exclusiveTouchIdentifier == NoIdentifier);
424 m_exclusiveTouchIdentifier = identifier;
425}
426
427void SliderThumbElement::clearExclusiveTouchIdentifier()
428{
429 m_exclusiveTouchIdentifier = NoIdentifier;
430}
431
432static 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
443void 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
466void 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
485void 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
508void SliderThumbElement::didAttachRenderers()
509{
510 if (shouldAcceptTouchEvents())
511 registerForTouchEvents();
512}
513
514void 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 AtomicString& 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
543bool SliderThumbElement::shouldAcceptTouchEvents()
544{
545 return renderer() && !isDisabledFormControl();
546}
547
548void SliderThumbElement::registerForTouchEvents()
549{
550 if (m_isRegisteredAsTouchEventListener)
551 return;
552
553 ASSERT(shouldAcceptTouchEvents());
554
555 document().addTouchEventHandler(*this);
556 m_isRegisteredAsTouchEventListener = true;
557}
558
559void 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
573void 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
586RefPtr<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
593Optional<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 AtomicString> sliderThumbShadowPseudoId("-webkit-slider-thumb", AtomicString::ConstructFromLiteral);
598 static NeverDestroyed<const AtomicString> mediaSliderThumbShadowPseudoId("-webkit-media-slider-thumb", AtomicString::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
619const AtomicString& SliderThumbElement::shadowPseudoId() const
620{
621 return m_shadowPseudoId;
622}
623
624Ref<Element> SliderThumbElement::cloneElementWithoutAttributesAndChildren(Document& targetDocument)
625{
626 return create(targetDocument);
627}
628
629// --------------------------------
630
631inline SliderContainerElement::SliderContainerElement(Document& document)
632 : HTMLDivElement(HTMLNames::divTag, document)
633{
634 setHasCustomStyleResolveCallbacks();
635}
636
637Ref<SliderContainerElement> SliderContainerElement::create(Document& document)
638{
639 return adoptRef(*new SliderContainerElement(document));
640}
641
642RenderPtr<RenderElement> SliderContainerElement::createElementRenderer(RenderStyle&& style, const RenderTreePosition&)
643{
644 return createRenderer<RenderSliderContainer>(*this, WTFMove(style));
645}
646
647Optional<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 AtomicString> mediaSliderContainer("-webkit-media-slider-container", AtomicString::ConstructFromLiteral);
652 static NeverDestroyed<const AtomicString> sliderContainer("-webkit-slider-container", AtomicString::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
673const AtomicString& SliderContainerElement::shadowPseudoId() const
674{
675 return m_shadowPseudoId;
676}
677
678}
679