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