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