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