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