22ce339d3482584fcf13f4caeed8a0b456ffa7d3
[WebKit-https.git] / Source / WebCore / html / shadow / MediaControlElements.cpp
1 /*
2  * Copyright (C) 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
3  * Copyright (C) 2012 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
7  * are met:
8  *
9  * 1.  Redistributions of source code must retain the above copyright
10  *     notice, this list of conditions and the following disclaimer.
11  * 2.  Redistributions in binary form must reproduce the above copyright
12  *     notice, this list of conditions and the following disclaimer in the
13  *     documentation and/or other materials provided with the distribution.
14  * 3.  Neither the name of Apple Inc. ("Apple") nor the names of
15  *     its contributors may be used to endorse or promote products derived
16  *     from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include "config.h"
31
32 #if ENABLE(VIDEO)
33 #include "MediaControlElements.h"
34
35 #include "DOMTokenList.h"
36 #include "ElementChildIterator.h"
37 #include "EventHandler.h"
38 #include "EventNames.h"
39 #include "ExceptionCodePlaceholder.h"
40 #include "Frame.h"
41 #include "GraphicsContext.h"
42 #include "HTMLVideoElement.h"
43 #include "ImageBuffer.h"
44 #include "Language.h"
45 #include "LocalizedStrings.h"
46 #include "Logging.h"
47 #include "MediaControls.h"
48 #include "PageGroup.h"
49 #include "RenderLayer.h"
50 #include "RenderMediaControlElements.h"
51 #include "RenderSlider.h"
52 #include "RenderVideo.h"
53 #include "RenderView.h"
54 #include "Settings.h"
55 #include "ShadowRoot.h"
56 #if ENABLE(VIDEO_TRACK)
57 #include "TextTrackList.h"
58 #endif
59 #include "VTTRegionList.h"
60
61 namespace WebCore {
62
63 using namespace HTMLNames;
64
65 static const AtomicString& getMediaControlCurrentTimeDisplayElementShadowPseudoId();
66 static const AtomicString& getMediaControlTimeRemainingDisplayElementShadowPseudoId();
67
68 MediaControlPanelElement::MediaControlPanelElement(Document& document)
69     : MediaControlDivElement(document, MediaControlsPanel)
70     , m_canBeDragged(false)
71     , m_isBeingDragged(false)
72     , m_isDisplayed(false)
73     , m_opaque(true)
74     , m_transitionTimer(*this, &MediaControlPanelElement::transitionTimerFired)
75 {
76     setPseudo(AtomicString("-webkit-media-controls-panel", AtomicString::ConstructFromLiteral));
77 }
78
79 Ref<MediaControlPanelElement> MediaControlPanelElement::create(Document& document)
80 {
81     return adoptRef(*new MediaControlPanelElement(document));
82 }
83
84 void MediaControlPanelElement::startDrag(const LayoutPoint& eventLocation)
85 {
86     if (!m_canBeDragged)
87         return;
88
89     if (m_isBeingDragged)
90         return;
91
92     auto renderer = this->renderer();
93     if (!renderer || !renderer->isBox())
94         return;
95
96     Frame* frame = document().frame();
97     if (!frame)
98         return;
99
100     m_lastDragEventLocation = eventLocation;
101
102     frame->eventHandler().setCapturingMouseEventsElement(this);
103
104     m_isBeingDragged = true;
105 }
106
107 void MediaControlPanelElement::continueDrag(const LayoutPoint& eventLocation)
108 {
109     if (!m_isBeingDragged)
110         return;
111
112     LayoutSize distanceDragged = eventLocation - m_lastDragEventLocation;
113     m_cumulativeDragOffset.move(distanceDragged);
114     m_lastDragEventLocation = eventLocation;
115     setPosition(m_cumulativeDragOffset);
116 }
117
118 void MediaControlPanelElement::endDrag()
119 {
120     if (!m_isBeingDragged)
121         return;
122
123     m_isBeingDragged = false;
124
125     Frame* frame = document().frame();
126     if (!frame)
127         return;
128
129     frame->eventHandler().setCapturingMouseEventsElement(nullptr);
130 }
131
132 void MediaControlPanelElement::startTimer()
133 {
134     stopTimer();
135
136     // The timer is required to set the property display:'none' on the panel,
137     // such that captions are correctly displayed at the bottom of the video
138     // at the end of the fadeout transition.
139     double duration = document().page() ? document().page()->theme().mediaControlsFadeOutDuration() : 0;
140     m_transitionTimer.startOneShot(duration);
141 }
142
143 void MediaControlPanelElement::stopTimer()
144 {
145     if (m_transitionTimer.isActive())
146         m_transitionTimer.stop();
147 }
148
149 void MediaControlPanelElement::transitionTimerFired()
150 {
151     if (!m_opaque)
152         hide();
153
154     stopTimer();
155 }
156
157 void MediaControlPanelElement::setPosition(const LayoutPoint& position)
158 {
159     double left = position.x();
160     double top = position.y();
161
162     // Set the left and top to control the panel's position; this depends on it being absolute positioned.
163     // Set the margin to zero since the position passed in will already include the effect of the margin.
164     setInlineStyleProperty(CSSPropertyLeft, left, CSSPrimitiveValue::CSS_PX);
165     setInlineStyleProperty(CSSPropertyTop, top, CSSPrimitiveValue::CSS_PX);
166     setInlineStyleProperty(CSSPropertyMarginLeft, 0.0, CSSPrimitiveValue::CSS_PX);
167     setInlineStyleProperty(CSSPropertyMarginTop, 0.0, CSSPrimitiveValue::CSS_PX);
168
169     classList().add("dragged", IGNORE_EXCEPTION);
170 }
171
172 void MediaControlPanelElement::resetPosition()
173 {
174     removeInlineStyleProperty(CSSPropertyLeft);
175     removeInlineStyleProperty(CSSPropertyTop);
176     removeInlineStyleProperty(CSSPropertyMarginLeft);
177     removeInlineStyleProperty(CSSPropertyMarginTop);
178
179     classList().remove("dragged", IGNORE_EXCEPTION);
180
181     m_cumulativeDragOffset.setX(0);
182     m_cumulativeDragOffset.setY(0);
183 }
184
185 void MediaControlPanelElement::makeOpaque()
186 {
187     if (m_opaque)
188         return;
189
190     double duration = document().page() ? document().page()->theme().mediaControlsFadeInDuration() : 0;
191
192     setInlineStyleProperty(CSSPropertyWebkitTransitionProperty, CSSPropertyOpacity);
193     setInlineStyleProperty(CSSPropertyWebkitTransitionDuration, duration, CSSPrimitiveValue::CSS_S);
194     setInlineStyleProperty(CSSPropertyOpacity, 1.0, CSSPrimitiveValue::CSS_NUMBER);
195
196     m_opaque = true;
197
198     if (m_isDisplayed)
199         show();
200 }
201
202 void MediaControlPanelElement::makeTransparent()
203 {
204     if (!m_opaque)
205         return;
206
207     double duration = document().page() ? document().page()->theme().mediaControlsFadeOutDuration() : 0;
208
209     setInlineStyleProperty(CSSPropertyWebkitTransitionProperty, CSSPropertyOpacity);
210     setInlineStyleProperty(CSSPropertyWebkitTransitionDuration, duration, CSSPrimitiveValue::CSS_S);
211     setInlineStyleProperty(CSSPropertyOpacity, 0.0, CSSPrimitiveValue::CSS_NUMBER);
212
213     m_opaque = false;
214     startTimer();
215 }
216
217 void MediaControlPanelElement::defaultEventHandler(Event* event)
218 {
219     MediaControlDivElement::defaultEventHandler(event);
220
221     if (is<MouseEvent>(*event)) {
222         LayoutPoint location = downcast<MouseEvent>(*event).absoluteLocation();
223         if (event->type() == eventNames().mousedownEvent && event->target() == this) {
224             startDrag(location);
225             event->setDefaultHandled();
226         } else if (event->type() == eventNames().mousemoveEvent && m_isBeingDragged)
227             continueDrag(location);
228         else if (event->type() == eventNames().mouseupEvent && m_isBeingDragged) {
229             continueDrag(location);
230             endDrag();
231             event->setDefaultHandled();
232         }
233     }
234 }
235
236 void MediaControlPanelElement::setCanBeDragged(bool canBeDragged)
237 {
238     if (m_canBeDragged == canBeDragged)
239         return;
240
241     m_canBeDragged = canBeDragged;
242
243     if (!canBeDragged)
244         endDrag();
245 }
246
247 void MediaControlPanelElement::setIsDisplayed(bool isDisplayed)
248 {
249     m_isDisplayed = isDisplayed;
250 }
251
252 // ----------------------------
253
254 MediaControlPanelEnclosureElement::MediaControlPanelEnclosureElement(Document& document)
255     // Mapping onto same MediaControlElementType as panel element, since it has similar properties.
256     : MediaControlDivElement(document, MediaControlsPanel)
257 {
258     setPseudo(AtomicString("-webkit-media-controls-enclosure", AtomicString::ConstructFromLiteral));
259 }
260
261 Ref<MediaControlPanelEnclosureElement> MediaControlPanelEnclosureElement::create(Document& document)
262 {
263     return adoptRef(*new MediaControlPanelEnclosureElement(document));
264 }
265
266 // ----------------------------
267
268 MediaControlOverlayEnclosureElement::MediaControlOverlayEnclosureElement(Document& document)
269     // Mapping onto same MediaControlElementType as panel element, since it has similar properties.
270     : MediaControlDivElement(document, MediaControlsPanel)
271 {
272     setPseudo(AtomicString("-webkit-media-controls-overlay-enclosure", AtomicString::ConstructFromLiteral));
273 }
274
275 Ref<MediaControlOverlayEnclosureElement> MediaControlOverlayEnclosureElement::create(Document& document)
276 {
277     return adoptRef(*new MediaControlOverlayEnclosureElement(document));
278 }
279
280 // ----------------------------
281
282 MediaControlTimelineContainerElement::MediaControlTimelineContainerElement(Document& document)
283     : MediaControlDivElement(document, MediaTimelineContainer)
284 {
285     setPseudo(AtomicString("-webkit-media-controls-timeline-container", AtomicString::ConstructFromLiteral));
286 }
287
288 Ref<MediaControlTimelineContainerElement> MediaControlTimelineContainerElement::create(Document& document)
289 {
290     Ref<MediaControlTimelineContainerElement> element = adoptRef(*new MediaControlTimelineContainerElement(document));
291     element->hide();
292     return element;
293 }
294
295 void MediaControlTimelineContainerElement::setTimeDisplaysHidden(bool hidden)
296 {
297     for (auto& element : childrenOfType<Element>(*this)) {
298         if (element.shadowPseudoId() != getMediaControlTimeRemainingDisplayElementShadowPseudoId()
299             && element.shadowPseudoId() != getMediaControlCurrentTimeDisplayElementShadowPseudoId())
300             continue;
301
302         MediaControlTimeDisplayElement& timeDisplay = static_cast<MediaControlTimeDisplayElement&>(element);
303         if (hidden)
304             timeDisplay.hide();
305         else
306             timeDisplay.show();
307     }
308 }
309
310 RenderPtr<RenderElement> MediaControlTimelineContainerElement::createElementRenderer(Ref<RenderStyle>&& style, const RenderTreePosition&)
311 {
312     return createRenderer<RenderMediaControlTimelineContainer>(*this, WTF::move(style));
313 }
314
315 // ----------------------------
316
317 MediaControlVolumeSliderContainerElement::MediaControlVolumeSliderContainerElement(Document& document)
318     : MediaControlDivElement(document, MediaVolumeSliderContainer)
319 {
320     setPseudo(AtomicString("-webkit-media-controls-volume-slider-container", AtomicString::ConstructFromLiteral));
321 }
322
323 Ref<MediaControlVolumeSliderContainerElement> MediaControlVolumeSliderContainerElement::create(Document& document)
324 {
325     Ref<MediaControlVolumeSliderContainerElement> element = adoptRef(*new MediaControlVolumeSliderContainerElement(document));
326     element->hide();
327     return element;
328 }
329
330 RenderPtr<RenderElement> MediaControlVolumeSliderContainerElement::createElementRenderer(Ref<RenderStyle>&& style, const RenderTreePosition&)
331 {
332     return createRenderer<RenderMediaVolumeSliderContainer>(*this, WTF::move(style));
333 }
334
335 void MediaControlVolumeSliderContainerElement::defaultEventHandler(Event* event)
336 {
337     if (!is<MouseEvent>(*event) || event->type() != eventNames().mouseoutEvent)
338         return;
339
340     // Poor man's mouseleave event detection.
341     MouseEvent& mouseEvent = downcast<MouseEvent>(*event);
342     EventTarget* relatedTarget = mouseEvent.relatedTarget();
343     if (!relatedTarget || !relatedTarget->toNode())
344         return;
345
346     if (this->containsIncludingShadowDOM(relatedTarget->toNode()))
347         return;
348
349     hide();
350 }
351
352 // ----------------------------
353
354 MediaControlStatusDisplayElement::MediaControlStatusDisplayElement(Document& document)
355     : MediaControlDivElement(document, MediaStatusDisplay)
356     , m_stateBeingDisplayed(Nothing)
357 {
358     setPseudo(AtomicString("-webkit-media-controls-status-display", AtomicString::ConstructFromLiteral));
359 }
360
361 Ref<MediaControlStatusDisplayElement> MediaControlStatusDisplayElement::create(Document& document)
362 {
363     Ref<MediaControlStatusDisplayElement> element = adoptRef(*new MediaControlStatusDisplayElement(document));
364     element->hide();
365     return element;
366 }
367
368 void MediaControlStatusDisplayElement::update()
369 {
370     // Get the new state that we'll have to display.
371     StateBeingDisplayed newStateToDisplay = Nothing;
372
373     if (mediaController()->readyState() <= MediaControllerInterface::HAVE_METADATA && mediaController()->hasCurrentSrc())
374         newStateToDisplay = Loading;
375     else if (mediaController()->isLiveStream())
376         newStateToDisplay = LiveBroadcast;
377
378     if (newStateToDisplay == m_stateBeingDisplayed)
379         return;
380
381     if (m_stateBeingDisplayed == Nothing)
382         show();
383     else if (newStateToDisplay == Nothing)
384         hide();
385
386     m_stateBeingDisplayed = newStateToDisplay;
387
388     switch (m_stateBeingDisplayed) {
389     case Nothing:
390         setInnerText("", IGNORE_EXCEPTION);
391         break;
392     case Loading:
393         setInnerText(mediaElementLoadingStateText(), IGNORE_EXCEPTION);
394         break;
395     case LiveBroadcast:
396         setInnerText(mediaElementLiveBroadcastStateText(), IGNORE_EXCEPTION);
397         break;
398     }
399 }
400
401 // ----------------------------
402
403 MediaControlPanelMuteButtonElement::MediaControlPanelMuteButtonElement(Document& document, MediaControls* controls)
404     : MediaControlMuteButtonElement(document, MediaMuteButton)
405     , m_controls(controls)
406 {
407     setPseudo(AtomicString("-webkit-media-controls-mute-button", AtomicString::ConstructFromLiteral));
408 }
409
410 Ref<MediaControlPanelMuteButtonElement> MediaControlPanelMuteButtonElement::create(Document& document, MediaControls* controls)
411 {
412     ASSERT(controls);
413
414     Ref<MediaControlPanelMuteButtonElement> button = adoptRef(*new MediaControlPanelMuteButtonElement(document, controls));
415     button->ensureUserAgentShadowRoot();
416     button->setType("button");
417     return button;
418 }
419
420 void MediaControlPanelMuteButtonElement::defaultEventHandler(Event* event)
421 {
422     if (event->type() == eventNames().mouseoverEvent)
423         m_controls->showVolumeSlider();
424
425     MediaControlMuteButtonElement::defaultEventHandler(event);
426 }
427
428 // ----------------------------
429
430 MediaControlVolumeSliderMuteButtonElement::MediaControlVolumeSliderMuteButtonElement(Document& document)
431     : MediaControlMuteButtonElement(document, MediaMuteButton)
432 {
433     setPseudo(AtomicString("-webkit-media-controls-volume-slider-mute-button", AtomicString::ConstructFromLiteral));
434 }
435
436 Ref<MediaControlVolumeSliderMuteButtonElement> MediaControlVolumeSliderMuteButtonElement::create(Document& document)
437 {
438     Ref<MediaControlVolumeSliderMuteButtonElement> button = adoptRef(*new MediaControlVolumeSliderMuteButtonElement(document));
439     button->ensureUserAgentShadowRoot();
440     button->setType("button");
441     return button;
442 }
443
444 // ----------------------------
445
446 MediaControlPlayButtonElement::MediaControlPlayButtonElement(Document& document)
447     : MediaControlInputElement(document, MediaPlayButton)
448 {
449     setPseudo(AtomicString("-webkit-media-controls-play-button", AtomicString::ConstructFromLiteral));
450 }
451
452 Ref<MediaControlPlayButtonElement> MediaControlPlayButtonElement::create(Document& document)
453 {
454     Ref<MediaControlPlayButtonElement> button = adoptRef(*new MediaControlPlayButtonElement(document));
455     button->ensureUserAgentShadowRoot();
456     button->setType("button");
457     return button;
458 }
459
460 void MediaControlPlayButtonElement::defaultEventHandler(Event* event)
461 {
462     if (event->type() == eventNames().clickEvent) {
463         if (mediaController()->canPlay())
464             mediaController()->play();
465         else
466             mediaController()->pause();
467         updateDisplayType();
468         event->setDefaultHandled();
469     }
470     HTMLInputElement::defaultEventHandler(event);
471 }
472
473 void MediaControlPlayButtonElement::updateDisplayType()
474 {
475     setDisplayType(mediaController()->canPlay() ? MediaPlayButton : MediaPauseButton);
476 }
477
478 // ----------------------------
479
480 MediaControlOverlayPlayButtonElement::MediaControlOverlayPlayButtonElement(Document& document)
481     : MediaControlInputElement(document, MediaOverlayPlayButton)
482 {
483     setPseudo(AtomicString("-webkit-media-controls-overlay-play-button", AtomicString::ConstructFromLiteral));
484 }
485
486 Ref<MediaControlOverlayPlayButtonElement> MediaControlOverlayPlayButtonElement::create(Document& document)
487 {
488     Ref<MediaControlOverlayPlayButtonElement> button = adoptRef(*new MediaControlOverlayPlayButtonElement(document));
489     button->ensureUserAgentShadowRoot();
490     button->setType("button");
491     return button;
492 }
493
494 void MediaControlOverlayPlayButtonElement::defaultEventHandler(Event* event)
495 {
496     if (event->type() == eventNames().clickEvent && mediaController()->canPlay()) {
497         mediaController()->play();
498         updateDisplayType();
499         event->setDefaultHandled();
500     }
501     HTMLInputElement::defaultEventHandler(event);
502 }
503
504 void MediaControlOverlayPlayButtonElement::updateDisplayType()
505 {
506     if (mediaController()->canPlay()) {
507         show();
508     } else
509         hide();
510 }
511
512 // ----------------------------
513
514 MediaControlSeekForwardButtonElement::MediaControlSeekForwardButtonElement(Document& document)
515     : MediaControlSeekButtonElement(document, MediaSeekForwardButton)
516 {
517     setPseudo(AtomicString("-webkit-media-controls-seek-forward-button", AtomicString::ConstructFromLiteral));
518 }
519
520 Ref<MediaControlSeekForwardButtonElement> MediaControlSeekForwardButtonElement::create(Document& document)
521 {
522     Ref<MediaControlSeekForwardButtonElement> button = adoptRef(*new MediaControlSeekForwardButtonElement(document));
523     button->ensureUserAgentShadowRoot();
524     button->setType("button");
525     return button;
526 }
527
528 // ----------------------------
529
530 MediaControlSeekBackButtonElement::MediaControlSeekBackButtonElement(Document& document)
531     : MediaControlSeekButtonElement(document, MediaSeekBackButton)
532 {
533     setPseudo(AtomicString("-webkit-media-controls-seek-back-button", AtomicString::ConstructFromLiteral));
534 }
535
536 Ref<MediaControlSeekBackButtonElement> MediaControlSeekBackButtonElement::create(Document& document)
537 {
538     Ref<MediaControlSeekBackButtonElement> button = adoptRef(*new MediaControlSeekBackButtonElement(document));
539     button->ensureUserAgentShadowRoot();
540     button->setType("button");
541     return button;
542 }
543
544 // ----------------------------
545
546 MediaControlRewindButtonElement::MediaControlRewindButtonElement(Document& document)
547     : MediaControlInputElement(document, MediaRewindButton)
548 {
549     setPseudo(AtomicString("-webkit-media-controls-rewind-button", AtomicString::ConstructFromLiteral));
550 }
551
552 Ref<MediaControlRewindButtonElement> MediaControlRewindButtonElement::create(Document& document)
553 {
554     Ref<MediaControlRewindButtonElement> button = adoptRef(*new MediaControlRewindButtonElement(document));
555     button->ensureUserAgentShadowRoot();
556     button->setType("button");
557     return button;
558 }
559
560 void MediaControlRewindButtonElement::defaultEventHandler(Event* event)
561 {
562     if (event->type() == eventNames().clickEvent) {
563         mediaController()->setCurrentTime(std::max<double>(0, mediaController()->currentTime() - 30));
564         event->setDefaultHandled();
565     }
566     HTMLInputElement::defaultEventHandler(event);
567 }
568
569 // ----------------------------
570
571 MediaControlReturnToRealtimeButtonElement::MediaControlReturnToRealtimeButtonElement(Document& document)
572     : MediaControlInputElement(document, MediaReturnToRealtimeButton)
573 {
574     setPseudo(AtomicString("-webkit-media-controls-return-to-realtime-button", AtomicString::ConstructFromLiteral));
575 }
576
577 Ref<MediaControlReturnToRealtimeButtonElement> MediaControlReturnToRealtimeButtonElement::create(Document& document)
578 {
579     Ref<MediaControlReturnToRealtimeButtonElement> button = adoptRef(*new MediaControlReturnToRealtimeButtonElement(document));
580     button->ensureUserAgentShadowRoot();
581     button->setType("button");
582     button->hide();
583     return button;
584 }
585
586 void MediaControlReturnToRealtimeButtonElement::defaultEventHandler(Event* event)
587 {
588     if (event->type() == eventNames().clickEvent) {
589         mediaController()->returnToRealtime();
590         event->setDefaultHandled();
591     }
592     HTMLInputElement::defaultEventHandler(event);
593 }
594
595 // ----------------------------
596
597 MediaControlToggleClosedCaptionsButtonElement::MediaControlToggleClosedCaptionsButtonElement(Document& document, MediaControls* controls)
598     : MediaControlInputElement(document, MediaShowClosedCaptionsButton)
599 #if PLATFORM(COCOA) || PLATFORM(WIN) || PLATFORM(GTK)
600     , m_controls(controls)
601 #endif
602 {
603 #if !PLATFORM(COCOA) && !PLATFORM(WIN) || !PLATFORM(GTK)
604     UNUSED_PARAM(controls);
605 #endif
606     setPseudo(AtomicString("-webkit-media-controls-toggle-closed-captions-button", AtomicString::ConstructFromLiteral));
607 }
608
609 Ref<MediaControlToggleClosedCaptionsButtonElement> MediaControlToggleClosedCaptionsButtonElement::create(Document& document, MediaControls* controls)
610 {
611     ASSERT(controls);
612
613     Ref<MediaControlToggleClosedCaptionsButtonElement> button = adoptRef(*new MediaControlToggleClosedCaptionsButtonElement(document, controls));
614     button->ensureUserAgentShadowRoot();
615     button->setType("button");
616     button->hide();
617     return button;
618 }
619
620 void MediaControlToggleClosedCaptionsButtonElement::updateDisplayType()
621 {
622     bool captionsVisible = mediaController()->closedCaptionsVisible();
623     setDisplayType(captionsVisible ? MediaHideClosedCaptionsButton : MediaShowClosedCaptionsButton);
624     setChecked(captionsVisible);
625 }
626
627 void MediaControlToggleClosedCaptionsButtonElement::defaultEventHandler(Event* event)
628 {
629     if (event->type() == eventNames().clickEvent) {
630         // FIXME: It's not great that the shared code is dictating behavior of platform-specific
631         // UI. Not all ports may want the closed captions button to toggle a list of tracks, so
632         // we have to use #if.
633         // https://bugs.webkit.org/show_bug.cgi?id=101877
634 #if !PLATFORM(COCOA) && !PLATFORM(WIN) && !PLATFORM(GTK)
635         mediaController()->setClosedCaptionsVisible(!mediaController()->closedCaptionsVisible());
636         setChecked(mediaController()->closedCaptionsVisible());
637         updateDisplayType();
638 #else
639         m_controls->toggleClosedCaptionTrackList();
640 #endif
641         event->setDefaultHandled();
642     }
643
644     HTMLInputElement::defaultEventHandler(event);
645 }
646
647 // ----------------------------
648
649 MediaControlClosedCaptionsContainerElement::MediaControlClosedCaptionsContainerElement(Document& document)
650     : MediaControlDivElement(document, MediaClosedCaptionsContainer)
651 {
652     setPseudo(AtomicString("-webkit-media-controls-closed-captions-container", AtomicString::ConstructFromLiteral));
653 }
654
655 Ref<MediaControlClosedCaptionsContainerElement> MediaControlClosedCaptionsContainerElement::create(Document& document)
656 {
657     Ref<MediaControlClosedCaptionsContainerElement> element = adoptRef(*new MediaControlClosedCaptionsContainerElement(document));
658     element->setAttribute(dirAttr, "auto");
659     element->hide();
660     return element;
661 }
662
663 // ----------------------------
664
665 MediaControlClosedCaptionsTrackListElement::MediaControlClosedCaptionsTrackListElement(Document& document, MediaControls* controls)
666     : MediaControlDivElement(document, MediaClosedCaptionsTrackList)
667 #if ENABLE(VIDEO_TRACK)
668     , m_controls(controls)
669 #endif
670 {
671 #if !ENABLE(VIDEO_TRACK)
672     UNUSED_PARAM(controls);
673 #endif
674     setPseudo(AtomicString("-webkit-media-controls-closed-captions-track-list", AtomicString::ConstructFromLiteral));
675 }
676
677 Ref<MediaControlClosedCaptionsTrackListElement> MediaControlClosedCaptionsTrackListElement::create(Document& document, MediaControls* controls)
678 {
679     ASSERT(controls);
680     Ref<MediaControlClosedCaptionsTrackListElement> element = adoptRef(*new MediaControlClosedCaptionsTrackListElement(document, controls));
681     return element;
682 }
683
684 void MediaControlClosedCaptionsTrackListElement::defaultEventHandler(Event* event)
685 {
686 #if ENABLE(VIDEO_TRACK)
687     if (event->type() == eventNames().clickEvent) {
688         Node* target = event->target()->toNode();
689         if (!is<Element>(target))
690             return;
691
692         // When we created the elements in the track list, we gave them a custom
693         // attribute representing the index in the HTMLMediaElement's list of tracks.
694         // Check if the event target has such a custom element and, if so,
695         // tell the HTMLMediaElement to enable that track.
696
697         RefPtr<TextTrack> textTrack;
698         MenuItemToTrackMap::iterator iter = m_menuToTrackMap.find(downcast<Element>(target));
699         if (iter != m_menuToTrackMap.end())
700             textTrack = iter->value;
701         m_menuToTrackMap.clear();
702         m_controls->toggleClosedCaptionTrackList();
703         if (!textTrack)
704             return;
705
706         HTMLMediaElement* mediaElement = parentMediaElement(this);
707         if (!mediaElement)
708             return;
709
710         mediaElement->setSelectedTextTrack(textTrack.get());
711
712         updateDisplay();
713     }
714
715     MediaControlDivElement::defaultEventHandler(event);
716 #else
717     UNUSED_PARAM(event);
718 #endif
719 }
720
721 void MediaControlClosedCaptionsTrackListElement::updateDisplay()
722 {
723 #if ENABLE(VIDEO_TRACK)
724     DEPRECATED_DEFINE_STATIC_LOCAL(AtomicString, selectedClassValue, ("selected", AtomicString::ConstructFromLiteral));
725
726     if (!mediaController()->hasClosedCaptions())
727         return;
728
729     if (!document().page())
730         return;
731     CaptionUserPreferences::CaptionDisplayMode displayMode = document().page()->group().captionPreferences()->captionDisplayMode();
732
733     HTMLMediaElement* mediaElement = parentMediaElement(this);
734     if (!mediaElement)
735         return;
736
737     TextTrackList* trackList = mediaElement->textTracks();
738     if (!trackList || !trackList->length())
739         return;
740
741     rebuildTrackListMenu();
742
743     RefPtr<Element> offMenuItem;
744     bool trackMenuItemSelected = false;
745
746     for (unsigned i = 0, length = m_menuItems.size(); i < length; ++i) {
747         RefPtr<Element> trackItem = m_menuItems[i];
748
749         RefPtr<TextTrack> textTrack;
750         MenuItemToTrackMap::iterator iter = m_menuToTrackMap.find(trackItem.get());
751         if (iter == m_menuToTrackMap.end())
752             continue;
753         textTrack = iter->value;
754         if (!textTrack)
755             continue;
756
757         if (textTrack == TextTrack::captionMenuOffItem()) {
758             offMenuItem = trackItem;
759             continue;
760         }
761
762         if (textTrack == TextTrack::captionMenuAutomaticItem()) {
763             if (displayMode == CaptionUserPreferences::Automatic)
764                 trackItem->classList().add(selectedClassValue, ASSERT_NO_EXCEPTION);
765             else
766                 trackItem->classList().remove(selectedClassValue, ASSERT_NO_EXCEPTION);
767             continue;
768         }
769
770         if (displayMode != CaptionUserPreferences::Automatic && textTrack->mode() == TextTrack::showingKeyword()) {
771             trackMenuItemSelected = true;
772             trackItem->classList().add(selectedClassValue, ASSERT_NO_EXCEPTION);
773         } else
774             trackItem->classList().remove(selectedClassValue, ASSERT_NO_EXCEPTION);
775     }
776
777     if (offMenuItem) {
778         if (displayMode == CaptionUserPreferences::ForcedOnly && !trackMenuItemSelected)
779             offMenuItem->classList().add(selectedClassValue, ASSERT_NO_EXCEPTION);
780         else
781             offMenuItem->classList().remove(selectedClassValue, ASSERT_NO_EXCEPTION);
782     }
783 #endif
784 }
785
786 void MediaControlClosedCaptionsTrackListElement::rebuildTrackListMenu()
787 {
788 #if ENABLE(VIDEO_TRACK)
789     // Remove any existing content.
790     removeChildren();
791     m_menuItems.clear();
792     m_menuToTrackMap.clear();
793
794     if (!mediaController()->hasClosedCaptions())
795         return;
796
797     HTMLMediaElement* mediaElement = parentMediaElement(this);
798     if (!mediaElement)
799         return;
800
801     TextTrackList* trackList = mediaElement->textTracks();
802     if (!trackList || !trackList->length())
803         return;
804
805     if (!document().page())
806         return;
807     CaptionUserPreferences* captionPreferences = document().page()->group().captionPreferences();
808     Vector<RefPtr<TextTrack>> tracksForMenu = captionPreferences->sortedTrackListForMenu(trackList);
809
810     Ref<Element> captionsHeader = document().createElement(h3Tag, ASSERT_NO_EXCEPTION);
811     captionsHeader->appendChild(document().createTextNode(textTrackSubtitlesText()));
812     appendChild(WTF::move(captionsHeader));
813     Ref<Element> captionsMenuList = document().createElement(ulTag, ASSERT_NO_EXCEPTION);
814
815     for (unsigned i = 0, length = tracksForMenu.size(); i < length; ++i) {
816         RefPtr<TextTrack> textTrack = tracksForMenu[i];
817         Ref<Element> menuItem = document().createElement(liTag, ASSERT_NO_EXCEPTION);
818         menuItem->appendChild(document().createTextNode(captionPreferences->displayNameForTrack(textTrack.get())));
819         captionsMenuList->appendChild(menuItem.copyRef());
820         m_menuItems.append(menuItem.ptr());
821         m_menuToTrackMap.add(menuItem.ptr(), textTrack);
822     }
823
824     appendChild(WTF::move(captionsMenuList));
825 #endif
826 }
827
828 // ----------------------------
829
830 MediaControlTimelineElement::MediaControlTimelineElement(Document& document, MediaControls* controls)
831     : MediaControlInputElement(document, MediaSlider)
832     , m_controls(controls)
833 {
834     setPseudo(AtomicString("-webkit-media-controls-timeline", AtomicString::ConstructFromLiteral));
835 }
836
837 Ref<MediaControlTimelineElement> MediaControlTimelineElement::create(Document& document, MediaControls* controls)
838 {
839     ASSERT(controls);
840
841     Ref<MediaControlTimelineElement> timeline = adoptRef(*new MediaControlTimelineElement(document, controls));
842     timeline->ensureUserAgentShadowRoot();
843     timeline->setType("range");
844     timeline->setAttribute(precisionAttr, "float");
845     return timeline;
846 }
847
848 void MediaControlTimelineElement::defaultEventHandler(Event* event)
849 {
850     // Left button is 0. Rejects mouse events not from left button.
851     if (is<MouseEvent>(*event) && downcast<MouseEvent>(*event).button())
852         return;
853
854     if (!renderer())
855         return;
856
857     if (event->type() == eventNames().mousedownEvent)
858         mediaController()->beginScrubbing();
859
860     if (event->type() == eventNames().mouseupEvent)
861         mediaController()->endScrubbing();
862
863     MediaControlInputElement::defaultEventHandler(event);
864
865     if (event->type() == eventNames().mouseoverEvent || event->type() == eventNames().mouseoutEvent || event->type() == eventNames().mousemoveEvent)
866         return;
867
868     double time = value().toDouble();
869     if (event->type() == eventNames().inputEvent && time != mediaController()->currentTime())
870         mediaController()->setCurrentTime(time);
871
872     RenderSlider& slider = downcast<RenderSlider>(*renderer());
873     if (slider.inDragMode())
874         m_controls->updateCurrentTimeDisplay();
875 }
876
877 #if !PLATFORM(IOS)
878 bool MediaControlTimelineElement::willRespondToMouseClickEvents()
879 {
880     if (!renderer())
881         return false;
882
883     return true;
884 }
885 #endif // !PLATFORM(IOS)
886
887 void MediaControlTimelineElement::setPosition(double currentTime)
888 {
889     setValue(String::number(currentTime));
890 }
891
892 void MediaControlTimelineElement::setDuration(double duration)
893 {
894     setAttribute(maxAttr, AtomicString::number(std::isfinite(duration) ? duration : 0));
895 }
896
897 // ----------------------------
898
899 MediaControlPanelVolumeSliderElement::MediaControlPanelVolumeSliderElement(Document& document)
900     : MediaControlVolumeSliderElement(document)
901 {
902     setPseudo(AtomicString("-webkit-media-controls-volume-slider", AtomicString::ConstructFromLiteral));
903 }
904
905 Ref<MediaControlPanelVolumeSliderElement> MediaControlPanelVolumeSliderElement::create(Document& document)
906 {
907     Ref<MediaControlPanelVolumeSliderElement> slider = adoptRef(*new MediaControlPanelVolumeSliderElement(document));
908     slider->ensureUserAgentShadowRoot();
909     slider->setType("range");
910     slider->setAttribute(precisionAttr, "float");
911     slider->setAttribute(maxAttr, "1");
912     return slider;
913 }
914
915 // ----------------------------
916
917 MediaControlFullscreenVolumeSliderElement::MediaControlFullscreenVolumeSliderElement(Document& document)
918     : MediaControlVolumeSliderElement(document)
919 {
920     setPseudo(AtomicString("-webkit-media-controls-fullscreen-volume-slider", AtomicString::ConstructFromLiteral));
921 }
922
923 Ref<MediaControlFullscreenVolumeSliderElement> MediaControlFullscreenVolumeSliderElement::create(Document& document)
924 {
925     Ref<MediaControlFullscreenVolumeSliderElement> slider = adoptRef(*new MediaControlFullscreenVolumeSliderElement(document));
926     slider->ensureUserAgentShadowRoot();
927     slider->setType("range");
928     slider->setAttribute(precisionAttr, "float");
929     slider->setAttribute(maxAttr, "1");
930     return slider;
931 }
932
933 // ----------------------------
934
935 MediaControlFullscreenButtonElement::MediaControlFullscreenButtonElement(Document& document)
936     : MediaControlInputElement(document, MediaEnterFullscreenButton)
937 {
938     setPseudo(AtomicString("-webkit-media-controls-fullscreen-button", AtomicString::ConstructFromLiteral));
939 }
940
941 Ref<MediaControlFullscreenButtonElement> MediaControlFullscreenButtonElement::create(Document& document)
942 {
943     Ref<MediaControlFullscreenButtonElement> button = adoptRef(*new MediaControlFullscreenButtonElement(document));
944     button->ensureUserAgentShadowRoot();
945     button->setType("button");
946     button->hide();
947     return button;
948 }
949
950 void MediaControlFullscreenButtonElement::defaultEventHandler(Event* event)
951 {
952     if (event->type() == eventNames().clickEvent) {
953 #if ENABLE(FULLSCREEN_API)
954         // Only use the new full screen API if the fullScreenEnabled setting has
955         // been explicitly enabled. Otherwise, use the old fullscreen API. This
956         // allows apps which embed a WebView to retain the existing full screen
957         // video implementation without requiring them to implement their own full
958         // screen behavior.
959         if (document().settings() && document().settings()->fullScreenEnabled()) {
960             if (document().webkitIsFullScreen() && document().webkitCurrentFullScreenElement() == parentMediaElement(this))
961                 document().webkitCancelFullScreen();
962             else
963                 document().requestFullScreenForElement(parentMediaElement(this), 0, Document::ExemptIFrameAllowFullScreenRequirement);
964         } else
965 #endif
966             mediaController()->enterFullscreen();
967         event->setDefaultHandled();
968     }
969     HTMLInputElement::defaultEventHandler(event);
970 }
971
972 void MediaControlFullscreenButtonElement::setIsFullscreen(bool isFullscreen)
973 {
974     setDisplayType(isFullscreen ? MediaExitFullscreenButton : MediaEnterFullscreenButton);
975 }
976
977 // ----------------------------
978
979 MediaControlFullscreenVolumeMinButtonElement::MediaControlFullscreenVolumeMinButtonElement(Document& document)
980     : MediaControlInputElement(document, MediaUnMuteButton)
981 {
982     setPseudo(AtomicString("-webkit-media-controls-fullscreen-volume-min-button", AtomicString::ConstructFromLiteral));
983 }
984
985 Ref<MediaControlFullscreenVolumeMinButtonElement> MediaControlFullscreenVolumeMinButtonElement::create(Document& document)
986 {
987     Ref<MediaControlFullscreenVolumeMinButtonElement> button = adoptRef(*new MediaControlFullscreenVolumeMinButtonElement(document));
988     button->ensureUserAgentShadowRoot();
989     button->setType("button");
990     return button;
991 }
992
993 void MediaControlFullscreenVolumeMinButtonElement::defaultEventHandler(Event* event)
994 {
995     if (event->type() == eventNames().clickEvent) {
996         ExceptionCode code = 0;
997         mediaController()->setVolume(0, code);
998         event->setDefaultHandled();
999     }
1000     HTMLInputElement::defaultEventHandler(event);
1001 }
1002
1003 // ----------------------------
1004
1005 MediaControlFullscreenVolumeMaxButtonElement::MediaControlFullscreenVolumeMaxButtonElement(Document& document)
1006 : MediaControlInputElement(document, MediaMuteButton)
1007 {
1008     setPseudo(AtomicString("-webkit-media-controls-fullscreen-volume-max-button", AtomicString::ConstructFromLiteral));
1009 }
1010
1011 Ref<MediaControlFullscreenVolumeMaxButtonElement> MediaControlFullscreenVolumeMaxButtonElement::create(Document& document)
1012 {
1013     Ref<MediaControlFullscreenVolumeMaxButtonElement> button = adoptRef(*new MediaControlFullscreenVolumeMaxButtonElement(document));
1014     button->ensureUserAgentShadowRoot();
1015     button->setType("button");
1016     return button;
1017 }
1018
1019 void MediaControlFullscreenVolumeMaxButtonElement::defaultEventHandler(Event* event)
1020 {
1021     if (event->type() == eventNames().clickEvent) {
1022         ExceptionCode code = 0;
1023         mediaController()->setVolume(1, code);
1024         event->setDefaultHandled();
1025     }
1026     HTMLInputElement::defaultEventHandler(event);
1027 }
1028
1029 // ----------------------------
1030
1031 MediaControlTimeRemainingDisplayElement::MediaControlTimeRemainingDisplayElement(Document& document)
1032     : MediaControlTimeDisplayElement(document, MediaTimeRemainingDisplay)
1033 {
1034     setPseudo(getMediaControlTimeRemainingDisplayElementShadowPseudoId());
1035 }
1036
1037 Ref<MediaControlTimeRemainingDisplayElement> MediaControlTimeRemainingDisplayElement::create(Document& document)
1038 {
1039     return adoptRef(*new MediaControlTimeRemainingDisplayElement(document));
1040 }
1041
1042 static const AtomicString& getMediaControlTimeRemainingDisplayElementShadowPseudoId()
1043 {
1044     DEPRECATED_DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-time-remaining-display", AtomicString::ConstructFromLiteral));
1045     return id;
1046 }
1047
1048 // ----------------------------
1049
1050 MediaControlCurrentTimeDisplayElement::MediaControlCurrentTimeDisplayElement(Document& document)
1051     : MediaControlTimeDisplayElement(document, MediaCurrentTimeDisplay)
1052 {
1053     setPseudo(getMediaControlCurrentTimeDisplayElementShadowPseudoId());
1054 }
1055
1056 Ref<MediaControlCurrentTimeDisplayElement> MediaControlCurrentTimeDisplayElement::create(Document& document)
1057 {
1058     return adoptRef(*new MediaControlCurrentTimeDisplayElement(document));
1059 }
1060
1061 static const AtomicString& getMediaControlCurrentTimeDisplayElementShadowPseudoId()
1062 {
1063     DEPRECATED_DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-current-time-display", AtomicString::ConstructFromLiteral));
1064     return id;
1065 }
1066
1067 // ----------------------------
1068
1069 #if ENABLE(VIDEO_TRACK)
1070
1071 MediaControlTextTrackContainerElement::MediaControlTextTrackContainerElement(Document& document)
1072     : MediaControlDivElement(document, MediaTextTrackDisplayContainer)
1073     , m_updateTimer(*this, &MediaControlTextTrackContainerElement::updateTimerFired)
1074     , m_fontSize(0)
1075     , m_fontSizeIsImportant(false)
1076     , m_updateTextTrackRepresentationStyle(false)
1077 {
1078     setPseudo(AtomicString("-webkit-media-text-track-container", AtomicString::ConstructFromLiteral));
1079 }
1080
1081 Ref<MediaControlTextTrackContainerElement> MediaControlTextTrackContainerElement::create(Document& document)
1082 {
1083     auto element = adoptRef(*new MediaControlTextTrackContainerElement(document));
1084     element->hide();
1085     return element;
1086 }
1087
1088 RenderPtr<RenderElement> MediaControlTextTrackContainerElement::createElementRenderer(Ref<RenderStyle>&& style, const RenderTreePosition&)
1089 {
1090     return createRenderer<RenderTextTrackContainerElement>(*this, WTF::move(style));
1091 }
1092
1093 static bool compareCueIntervalForDisplay(const CueInterval& one, const CueInterval& two)
1094 {
1095     return one.data()->isPositionedAbove(two.data());
1096 };
1097
1098 void MediaControlTextTrackContainerElement::updateDisplay()
1099 {
1100     if (!mediaController()->closedCaptionsVisible())
1101         removeChildren();
1102
1103     HTMLMediaElement* mediaElement = parentMediaElement(this);
1104     // 1. If the media element is an audio element, or is another playback
1105     // mechanism with no rendering area, abort these steps. There is nothing to
1106     // render.
1107     if (!mediaElement || !mediaElement->isVideo())
1108         return;
1109
1110     // 2. Let video be the media element or other playback mechanism.
1111     HTMLVideoElement& video = downcast<HTMLVideoElement>(*mediaElement);
1112
1113     // 3. Let output be an empty list of absolutely positioned CSS block boxes.
1114     Vector<RefPtr<HTMLDivElement>> output;
1115
1116     // 4. If the user agent is exposing a user interface for video, add to
1117     // output one or more completely transparent positioned CSS block boxes that
1118     // cover the same region as the user interface.
1119
1120     // 5. If the last time these rules were run, the user agent was not exposing
1121     // a user interface for video, but now it is, let reset be true. Otherwise,
1122     // let reset be false.
1123
1124     // There is nothing to be done explicitly for 4th and 5th steps, as
1125     // everything is handled through CSS. The caption box is on top of the
1126     // controls box, in a container set with the -webkit-box display property.
1127
1128     // 6. Let tracks be the subset of video's list of text tracks that have as
1129     // their rules for updating the text track rendering these rules for
1130     // updating the display of WebVTT text tracks, and whose text track mode is
1131     // showing or showing by default.
1132     // 7. Let cues be an empty list of text track cues.
1133     // 8. For each track track in tracks, append to cues all the cues from
1134     // track's list of cues that have their text track cue active flag set.
1135     CueList activeCues = video.currentlyActiveCues();
1136
1137     // 9. If reset is false, then, for each text track cue cue in cues: if cue's
1138     // text track cue display state has a set of CSS boxes, then add those boxes
1139     // to output, and remove cue from cues.
1140
1141     // There is nothing explicitly to be done here, as all the caching occurs
1142     // within the TextTrackCue instance itself. If parameters of the cue change,
1143     // the display tree is cleared.
1144
1145     // If the number of CSS boxes in the output is less than the number of cues
1146     // we wish to render (e.g., we are adding another cue in a set of roll-up
1147     // cues), remove all the existing CSS boxes representing the cues and re-add
1148     // them so that the new cue is at the bottom.
1149     // FIXME: Calling countChildNodes() here is inefficient. We don't need to
1150     // traverse all children just to check if there are less children than cues.
1151     if (countChildNodes() < activeCues.size())
1152         removeChildren();
1153
1154     // Sort the active cues for the appropriate display order. For example, for roll-up
1155     // or paint-on captions, we need to add the cues in reverse chronological order,
1156     // so that the newest captions appear at the bottom.
1157     std::sort(activeCues.begin(), activeCues.end(), &compareCueIntervalForDisplay);
1158
1159     // 10. For each text track cue cue in cues that has not yet had
1160     // corresponding CSS boxes added to output, in text track cue order, run the
1161     // following substeps:
1162     for (size_t i = 0; i < activeCues.size(); ++i) {
1163         if (!mediaController()->closedCaptionsVisible())
1164             continue;
1165
1166         TextTrackCue* textTrackCue = activeCues[i].data();
1167         if (!textTrackCue->isRenderable())
1168             continue;
1169
1170         VTTCue* cue = toVTTCue(textTrackCue);
1171
1172         ASSERT(cue->isActive());
1173         if (!cue->track() || !cue->track()->isRendered() || !cue->isActive() || cue->text().isEmpty())
1174             continue;
1175
1176         LOG(Media, "MediaControlTextTrackContainerElement::updateDisplay(%p) - adding and positioning cue #%zu: \"%s\", start=%.2f, end=%.2f, line=%.2f", this, i, cue->text().utf8().data(), cue->startTime(), cue->endTime(), cue->line());
1177
1178         RefPtr<VTTCueBox> displayBox = cue->getDisplayTree(m_videoDisplaySize.size(), m_fontSize);
1179         if (cue->track()->mode() == TextTrack::disabledKeyword())
1180             continue;
1181
1182         VTTRegion* region = cue->track()->regions()->getRegionById(cue->regionId());
1183         if (!region) {
1184             // If cue has an empty text track cue region identifier or there is no
1185             // WebVTT region whose region identifier is identical to cue's text
1186             // track cue region identifier, run the following substeps:
1187             if (displayBox->hasChildNodes() && !contains(displayBox.get())) {
1188                 // Note: the display tree of a cue is removed when the active flag of the cue is unset.
1189                 appendChild(*displayBox, ASSERT_NO_EXCEPTION);
1190                 cue->setFontSize(m_fontSize, m_videoDisplaySize.size(), m_fontSizeIsImportant);
1191             }
1192         } else {
1193             // Let region be the WebVTT region whose region identifier
1194             // matches the text track cue region identifier of cue.
1195             Ref<HTMLDivElement> regionNode = region->getDisplayTree();
1196
1197             // Append the region to the viewport, if it was not already.
1198             if (!contains(regionNode.ptr()))
1199                 appendChild(region->getDisplayTree());
1200
1201             region->appendTextTrackCueBox(displayBox);
1202         }
1203     }
1204
1205     // 11. Return output.
1206     if (hasChildNodes()) {
1207         show();
1208         updateTextTrackRepresentation();
1209     } else {
1210         hide();
1211         clearTextTrackRepresentation();
1212     }
1213 }
1214
1215 void MediaControlTextTrackContainerElement::updateActiveCuesFontSize()
1216 {
1217     if (!document().page())
1218         return;
1219
1220     HTMLMediaElement* mediaElement = parentMediaElement(this);
1221     if (!mediaElement)
1222         return;
1223
1224     float smallestDimension = std::min(m_videoDisplaySize.size().height(), m_videoDisplaySize.size().width());
1225     float fontScale = document().page()->group().captionPreferences()->captionFontSizeScaleAndImportance(m_fontSizeIsImportant);
1226     m_fontSize = lroundf(smallestDimension * fontScale);
1227     
1228     CueList activeCues = mediaElement->currentlyActiveCues();
1229     for (size_t i = 0; i < activeCues.size(); ++i) {
1230         TextTrackCue* cue = activeCues[i].data();
1231         if (!cue->isRenderable())
1232             continue;
1233
1234         toVTTCue(cue)->setFontSize(m_fontSize, m_videoDisplaySize.size(), m_fontSizeIsImportant);
1235     }
1236
1237 }
1238
1239 void MediaControlTextTrackContainerElement::updateTimerFired()
1240 {
1241     if (!document().page())
1242         return;
1243
1244     if (m_textTrackRepresentation)
1245         updateStyleForTextTrackRepresentation();
1246
1247     updateActiveCuesFontSize();
1248     updateDisplay();
1249 }
1250
1251 void MediaControlTextTrackContainerElement::updateTextTrackRepresentation()
1252 {
1253     HTMLMediaElement* mediaElement = parentMediaElement(this);
1254     if (!mediaElement)
1255         return;
1256
1257     if (!mediaElement->requiresTextTrackRepresentation())
1258         return;
1259
1260     if (!m_textTrackRepresentation) {
1261         m_textTrackRepresentation = TextTrackRepresentation::create(*this);
1262         m_updateTextTrackRepresentationStyle = true;
1263         mediaElement->setTextTrackRepresentation(m_textTrackRepresentation.get());
1264     }
1265
1266     m_textTrackRepresentation->update();
1267     updateStyleForTextTrackRepresentation();
1268 }
1269
1270 void MediaControlTextTrackContainerElement::clearTextTrackRepresentation()
1271 {
1272     if (!m_textTrackRepresentation)
1273         return;
1274
1275     m_textTrackRepresentation = nullptr;
1276     m_updateTextTrackRepresentationStyle = true;
1277     if (HTMLMediaElement* mediaElement = parentMediaElement(this))
1278         mediaElement->setTextTrackRepresentation(nullptr);
1279     updateStyleForTextTrackRepresentation();
1280     updateActiveCuesFontSize();
1281 }
1282
1283 void MediaControlTextTrackContainerElement::updateStyleForTextTrackRepresentation()
1284 {
1285     if (!m_updateTextTrackRepresentationStyle)
1286         return;
1287     m_updateTextTrackRepresentationStyle = false;
1288
1289     if (m_textTrackRepresentation) {
1290         setInlineStyleProperty(CSSPropertyWidth, m_videoDisplaySize.size().width(), CSSPrimitiveValue::CSS_PX);
1291         setInlineStyleProperty(CSSPropertyHeight, m_videoDisplaySize.size().height(), CSSPrimitiveValue::CSS_PX);
1292         setInlineStyleProperty(CSSPropertyPosition, CSSValueAbsolute);
1293         setInlineStyleProperty(CSSPropertyLeft, 0, CSSPrimitiveValue::CSS_PX);
1294         setInlineStyleProperty(CSSPropertyTop, 0, CSSPrimitiveValue::CSS_PX);
1295         return;
1296     }
1297
1298     removeInlineStyleProperty(CSSPropertyPosition);
1299     removeInlineStyleProperty(CSSPropertyWidth);
1300     removeInlineStyleProperty(CSSPropertyHeight);
1301     removeInlineStyleProperty(CSSPropertyLeft);
1302     removeInlineStyleProperty(CSSPropertyTop);
1303 }
1304
1305 void MediaControlTextTrackContainerElement::enteredFullscreen()
1306 {
1307     if (hasChildNodes())
1308         updateTextTrackRepresentation();
1309     updateSizes(true);
1310 }
1311
1312 void MediaControlTextTrackContainerElement::exitedFullscreen()
1313 {
1314     clearTextTrackRepresentation();
1315     updateSizes(true);
1316 }
1317
1318 void MediaControlTextTrackContainerElement::updateSizes(bool forceUpdate)
1319 {
1320     HTMLMediaElement* mediaElement = parentMediaElement(this);
1321     if (!mediaElement)
1322         return;
1323
1324     if (!document().page())
1325         return;
1326
1327     mediaElement->syncTextTrackBounds();
1328
1329     IntRect videoBox;
1330     if (m_textTrackRepresentation)
1331         videoBox = m_textTrackRepresentation->bounds();
1332     else {
1333         if (!is<RenderVideo>(mediaElement->renderer()))
1334             return;
1335         videoBox = downcast<RenderVideo>(*mediaElement->renderer()).videoBox();
1336     }
1337
1338     if (!forceUpdate && m_videoDisplaySize == videoBox)
1339         return;
1340
1341     m_videoDisplaySize = videoBox;
1342     m_updateTextTrackRepresentationStyle = true;
1343
1344     // FIXME (121170): This function is called during layout, and should lay out the text tracks immediately.
1345     m_updateTimer.startOneShot(0);
1346 }
1347
1348 RefPtr<Image> MediaControlTextTrackContainerElement::createTextTrackRepresentationImage()
1349 {
1350     if (!hasChildNodes())
1351         return nullptr;
1352
1353     Frame* frame = document().frame();
1354     if (!frame)
1355         return nullptr;
1356
1357     document().updateLayout();
1358
1359     auto* renderer = this->renderer();
1360     if (!renderer)
1361         return nullptr;
1362
1363     if (!renderer->hasLayer())
1364         return nullptr;
1365
1366     RenderLayer* layer = downcast<RenderLayerModelObject>(*renderer).layer();
1367
1368     float deviceScaleFactor = 1;
1369     if (Page* page = document().page())
1370         deviceScaleFactor = page->deviceScaleFactor();
1371
1372     IntRect paintingRect = IntRect(IntPoint(), layer->size());
1373
1374     std::unique_ptr<ImageBuffer> buffer(ImageBuffer::create(paintingRect.size(), deviceScaleFactor, ColorSpaceDeviceRGB));
1375     if (!buffer)
1376         return nullptr;
1377
1378     layer->paint(buffer->context(), paintingRect, LayoutSize(), PaintBehaviorFlattenCompositingLayers, nullptr, RenderLayer::PaintLayerPaintingCompositingAllPhases);
1379
1380     return buffer->copyImage();
1381 }
1382
1383 void MediaControlTextTrackContainerElement::textTrackRepresentationBoundsChanged(const IntRect&)
1384 {
1385     if (hasChildNodes())
1386         updateTextTrackRepresentation();
1387     updateSizes();
1388 }
1389
1390 #endif // ENABLE(VIDEO_TRACK)
1391
1392 // ----------------------------
1393
1394 } // namespace WebCore
1395
1396 #endif // ENABLE(VIDEO)