CTTE: Add more node conversion helpers
[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 Computer, 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 "CaptionUserPreferences.h"
36 #include "DOMTokenList.h"
37 #include "EventHandler.h"
38 #include "EventNames.h"
39 #include "ExceptionCodePlaceholder.h"
40 #include "Frame.h"
41 #include "GraphicsContext.h"
42 #include "HTMLVideoElement.h"
43 #include "ImageBuffer.h"
44 #include "Language.h"
45 #include "LocalizedStrings.h"
46 #include "MediaControls.h"
47 #include "MouseEvent.h"
48 #include "Page.h"
49 #include "PageGroup.h"
50 #include "RenderLayer.h"
51 #include "RenderMediaControlElements.h"
52 #include "RenderSlider.h"
53 #include "RenderTheme.h"
54 #include "RenderVideo.h"
55 #include "RenderView.h"
56 #include "Settings.h"
57 #include "ShadowRoot.h"
58 #if ENABLE(VIDEO_TRACK)
59 #include "TextTrack.h"
60 #include "TextTrackList.h"
61 #endif
62
63 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
64 #include "RenderWidget.h"
65 #endif
66
67 namespace WebCore {
68
69 using namespace HTMLNames;
70
71 static const AtomicString& getMediaControlCurrentTimeDisplayElementShadowPseudoId();
72 static const AtomicString& getMediaControlTimeRemainingDisplayElementShadowPseudoId();
73
74 MediaControlPanelElement::MediaControlPanelElement(Document& document)
75     : MediaControlDivElement(document, MediaControlsPanel)
76     , m_canBeDragged(false)
77     , m_isBeingDragged(false)
78     , m_isDisplayed(false)
79     , m_opaque(true)
80     , m_transitionTimer(this, &MediaControlPanelElement::transitionTimerFired)
81 {
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     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 = static_cast<MouseEvent*>(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 }
270
271 PassRefPtr<MediaControlPanelEnclosureElement> MediaControlPanelEnclosureElement::create(Document& document)
272 {
273     return adoptRef(new MediaControlPanelEnclosureElement(document));
274 }
275
276 const AtomicString& MediaControlPanelEnclosureElement::shadowPseudoId() const
277 {
278     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-enclosure", AtomicString::ConstructFromLiteral));
279     return id;
280 }
281
282 // ----------------------------
283
284 MediaControlOverlayEnclosureElement::MediaControlOverlayEnclosureElement(Document& document)
285     // Mapping onto same MediaControlElementType as panel element, since it has similar properties.
286     : MediaControlDivElement(document, MediaControlsPanel)
287 {
288 }
289
290 PassRefPtr<MediaControlOverlayEnclosureElement> MediaControlOverlayEnclosureElement::create(Document& document)
291 {
292     return adoptRef(new MediaControlOverlayEnclosureElement(document));
293 }
294
295 const AtomicString& MediaControlOverlayEnclosureElement::shadowPseudoId() const
296 {
297     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-overlay-enclosure", AtomicString::ConstructFromLiteral));
298     return id;
299 }
300
301 // ----------------------------
302
303 MediaControlTimelineContainerElement::MediaControlTimelineContainerElement(Document& document)
304     : MediaControlDivElement(document, MediaTimelineContainer)
305 {
306 }
307
308 PassRefPtr<MediaControlTimelineContainerElement> MediaControlTimelineContainerElement::create(Document& document)
309 {
310     RefPtr<MediaControlTimelineContainerElement> element = adoptRef(new MediaControlTimelineContainerElement(document));
311     element->hide();
312     return element.release();
313 }
314
315 const AtomicString& MediaControlTimelineContainerElement::shadowPseudoId() const
316 {
317     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-timeline-container", AtomicString::ConstructFromLiteral));
318     return id;
319 }
320
321 void MediaControlTimelineContainerElement::setTimeDisplaysHidden(bool hidden)
322 {
323     for (unsigned i = 0; i < childNodeCount(); ++i) {
324         Node* child = childNode(i);
325         if (!child || !child->isElementNode())
326             continue;
327         Element* element = toElement(child);
328         if (element->shadowPseudoId() != getMediaControlTimeRemainingDisplayElementShadowPseudoId()
329             && element->shadowPseudoId() != getMediaControlCurrentTimeDisplayElementShadowPseudoId())
330             continue;
331
332         MediaControlTimeDisplayElement* timeDisplay = static_cast<MediaControlTimeDisplayElement*>(element);
333         if (hidden)
334             timeDisplay->hide();
335         else
336             timeDisplay->show();
337     }
338 }
339
340 RenderElement* MediaControlTimelineContainerElement::createRenderer(RenderArena& arena, RenderStyle&)
341 {
342     return new (arena) RenderMediaControlTimelineContainer(this);
343 }
344
345 // ----------------------------
346
347 MediaControlVolumeSliderContainerElement::MediaControlVolumeSliderContainerElement(Document& document)
348     : MediaControlDivElement(document, MediaVolumeSliderContainer)
349 {
350 }
351
352 PassRefPtr<MediaControlVolumeSliderContainerElement> MediaControlVolumeSliderContainerElement::create(Document& document)
353 {
354     RefPtr<MediaControlVolumeSliderContainerElement> element = adoptRef(new MediaControlVolumeSliderContainerElement(document));
355     element->hide();
356     return element.release();
357 }
358
359 RenderElement* MediaControlVolumeSliderContainerElement::createRenderer(RenderArena& arena, RenderStyle&)
360 {
361     return new (arena) RenderMediaVolumeSliderContainer(this);
362 }
363
364 void MediaControlVolumeSliderContainerElement::defaultEventHandler(Event* event)
365 {
366     if (!event->isMouseEvent() || event->type() != eventNames().mouseoutEvent)
367         return;
368
369     // Poor man's mouseleave event detection.
370     MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
371     EventTarget* relatedTarget = mouseEvent->relatedTarget();
372     if (!relatedTarget || !relatedTarget->toNode())
373         return;
374
375     if (this->containsIncludingShadowDOM(relatedTarget->toNode()))
376         return;
377
378     hide();
379 }
380
381 const AtomicString& MediaControlVolumeSliderContainerElement::shadowPseudoId() const
382 {
383     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-volume-slider-container", AtomicString::ConstructFromLiteral));
384     return id;
385 }
386
387 // ----------------------------
388
389 MediaControlStatusDisplayElement::MediaControlStatusDisplayElement(Document& document)
390     : MediaControlDivElement(document, MediaStatusDisplay)
391     , m_stateBeingDisplayed(Nothing)
392 {
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     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 }
449
450 PassRefPtr<MediaControlPanelMuteButtonElement> MediaControlPanelMuteButtonElement::create(Document& document, MediaControls* controls)
451 {
452     ASSERT(controls);
453
454     RefPtr<MediaControlPanelMuteButtonElement> button = adoptRef(new MediaControlPanelMuteButtonElement(document, controls));
455     button->ensureUserAgentShadowRoot();
456     button->setType("button");
457     return button.release();
458 }
459
460 void MediaControlPanelMuteButtonElement::defaultEventHandler(Event* event)
461 {
462     if (event->type() == eventNames().mouseoverEvent)
463         m_controls->showVolumeSlider();
464
465     MediaControlMuteButtonElement::defaultEventHandler(event);
466 }
467
468 const AtomicString& MediaControlPanelMuteButtonElement::shadowPseudoId() const
469 {
470     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-mute-button", AtomicString::ConstructFromLiteral));
471     return id;
472 }
473
474 // ----------------------------
475
476 MediaControlVolumeSliderMuteButtonElement::MediaControlVolumeSliderMuteButtonElement(Document& document)
477     : MediaControlMuteButtonElement(document, MediaMuteButton)
478 {
479 }
480
481 PassRefPtr<MediaControlVolumeSliderMuteButtonElement> MediaControlVolumeSliderMuteButtonElement::create(Document& document)
482 {
483     RefPtr<MediaControlVolumeSliderMuteButtonElement> button = adoptRef(new MediaControlVolumeSliderMuteButtonElement(document));
484     button->ensureUserAgentShadowRoot();
485     button->setType("button");
486     return button.release();
487 }
488
489 const AtomicString& MediaControlVolumeSliderMuteButtonElement::shadowPseudoId() const
490 {
491     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-volume-slider-mute-button", AtomicString::ConstructFromLiteral));
492     return id;
493 }
494
495 // ----------------------------
496
497 MediaControlPlayButtonElement::MediaControlPlayButtonElement(Document& document)
498     : MediaControlInputElement(document, MediaPlayButton)
499 {
500 }
501
502 PassRefPtr<MediaControlPlayButtonElement> MediaControlPlayButtonElement::create(Document& document)
503 {
504     RefPtr<MediaControlPlayButtonElement> button = adoptRef(new MediaControlPlayButtonElement(document));
505     button->ensureUserAgentShadowRoot();
506     button->setType("button");
507     return button.release();
508 }
509
510 void MediaControlPlayButtonElement::defaultEventHandler(Event* event)
511 {
512     if (event->type() == eventNames().clickEvent) {
513         if (mediaController()->canPlay())
514             mediaController()->play();
515         else
516             mediaController()->pause();
517         updateDisplayType();
518         event->setDefaultHandled();
519     }
520     HTMLInputElement::defaultEventHandler(event);
521 }
522
523 void MediaControlPlayButtonElement::updateDisplayType()
524 {
525     setDisplayType(mediaController()->canPlay() ? MediaPlayButton : MediaPauseButton);
526 }
527
528 const AtomicString& MediaControlPlayButtonElement::shadowPseudoId() const
529 {
530     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-play-button", AtomicString::ConstructFromLiteral));
531     return id;
532 }
533
534 // ----------------------------
535
536 MediaControlOverlayPlayButtonElement::MediaControlOverlayPlayButtonElement(Document& document)
537     : MediaControlInputElement(document, MediaOverlayPlayButton)
538 {
539 }
540
541 PassRefPtr<MediaControlOverlayPlayButtonElement> MediaControlOverlayPlayButtonElement::create(Document& document)
542 {
543     RefPtr<MediaControlOverlayPlayButtonElement> button = adoptRef(new MediaControlOverlayPlayButtonElement(document));
544     button->ensureUserAgentShadowRoot();
545     button->setType("button");
546     return button.release();
547 }
548
549 void MediaControlOverlayPlayButtonElement::defaultEventHandler(Event* event)
550 {
551     if (event->type() == eventNames().clickEvent && mediaController()->canPlay()) {
552         mediaController()->play();
553         updateDisplayType();
554         event->setDefaultHandled();
555     }
556     HTMLInputElement::defaultEventHandler(event);
557 }
558
559 void MediaControlOverlayPlayButtonElement::updateDisplayType()
560 {
561     if (mediaController()->canPlay()) {
562         show();
563     } else
564         hide();
565 }
566
567 const AtomicString& MediaControlOverlayPlayButtonElement::shadowPseudoId() const
568 {
569     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-overlay-play-button", AtomicString::ConstructFromLiteral));
570     return id;
571 }
572
573
574 // ----------------------------
575
576 MediaControlSeekForwardButtonElement::MediaControlSeekForwardButtonElement(Document& document)
577     : MediaControlSeekButtonElement(document, MediaSeekForwardButton)
578 {
579 }
580
581 PassRefPtr<MediaControlSeekForwardButtonElement> MediaControlSeekForwardButtonElement::create(Document& document)
582 {
583     RefPtr<MediaControlSeekForwardButtonElement> button = adoptRef(new MediaControlSeekForwardButtonElement(document));
584     button->ensureUserAgentShadowRoot();
585     button->setType("button");
586     return button.release();
587 }
588
589 const AtomicString& MediaControlSeekForwardButtonElement::shadowPseudoId() const
590 {
591     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-seek-forward-button", AtomicString::ConstructFromLiteral));
592     return id;
593 }
594
595 // ----------------------------
596
597 MediaControlSeekBackButtonElement::MediaControlSeekBackButtonElement(Document& document)
598     : MediaControlSeekButtonElement(document, MediaSeekBackButton)
599 {
600 }
601
602 PassRefPtr<MediaControlSeekBackButtonElement> MediaControlSeekBackButtonElement::create(Document& document)
603 {
604     RefPtr<MediaControlSeekBackButtonElement> button = adoptRef(new MediaControlSeekBackButtonElement(document));
605     button->ensureUserAgentShadowRoot();
606     button->setType("button");
607     return button.release();
608 }
609
610 const AtomicString& MediaControlSeekBackButtonElement::shadowPseudoId() const
611 {
612     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-seek-back-button", AtomicString::ConstructFromLiteral));
613     return id;
614 }
615
616 // ----------------------------
617
618 MediaControlRewindButtonElement::MediaControlRewindButtonElement(Document& document)
619     : MediaControlInputElement(document, MediaRewindButton)
620 {
621 }
622
623 PassRefPtr<MediaControlRewindButtonElement> MediaControlRewindButtonElement::create(Document& document)
624 {
625     RefPtr<MediaControlRewindButtonElement> button = adoptRef(new MediaControlRewindButtonElement(document));
626     button->ensureUserAgentShadowRoot();
627     button->setType("button");
628     return button.release();
629 }
630
631 void MediaControlRewindButtonElement::defaultEventHandler(Event* event)
632 {
633     if (event->type() == eventNames().clickEvent) {
634         mediaController()->setCurrentTime(max(0.0, mediaController()->currentTime() - 30), IGNORE_EXCEPTION);
635         event->setDefaultHandled();
636     }
637     HTMLInputElement::defaultEventHandler(event);
638 }
639
640 const AtomicString& MediaControlRewindButtonElement::shadowPseudoId() const
641 {
642     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-rewind-button", AtomicString::ConstructFromLiteral));
643     return id;
644 }
645
646 // ----------------------------
647
648 MediaControlReturnToRealtimeButtonElement::MediaControlReturnToRealtimeButtonElement(Document& document)
649     : MediaControlInputElement(document, MediaReturnToRealtimeButton)
650 {
651 }
652
653 PassRefPtr<MediaControlReturnToRealtimeButtonElement> MediaControlReturnToRealtimeButtonElement::create(Document& document)
654 {
655     RefPtr<MediaControlReturnToRealtimeButtonElement> button = adoptRef(new MediaControlReturnToRealtimeButtonElement(document));
656     button->ensureUserAgentShadowRoot();
657     button->setType("button");
658     button->hide();
659     return button.release();
660 }
661
662 void MediaControlReturnToRealtimeButtonElement::defaultEventHandler(Event* event)
663 {
664     if (event->type() == eventNames().clickEvent) {
665         mediaController()->returnToRealtime();
666         event->setDefaultHandled();
667     }
668     HTMLInputElement::defaultEventHandler(event);
669 }
670
671 const AtomicString& MediaControlReturnToRealtimeButtonElement::shadowPseudoId() const
672 {
673     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-return-to-realtime-button", AtomicString::ConstructFromLiteral));
674     return id;
675 }
676
677 // ----------------------------
678
679 MediaControlToggleClosedCaptionsButtonElement::MediaControlToggleClosedCaptionsButtonElement(Document& document, MediaControls* controls)
680     : MediaControlInputElement(document, MediaShowClosedCaptionsButton)
681 #if PLATFORM(MAC) || PLATFORM(WIN) || PLATFORM(GTK)
682     , m_controls(controls)
683 #endif
684 {
685 #if !PLATFORM(MAC) && !PLATFORM(WIN) || !PLATFORM(GTK)
686     UNUSED_PARAM(controls);
687 #endif
688 }
689
690 PassRefPtr<MediaControlToggleClosedCaptionsButtonElement> MediaControlToggleClosedCaptionsButtonElement::create(Document& document, MediaControls* controls)
691 {
692     ASSERT(controls);
693
694     RefPtr<MediaControlToggleClosedCaptionsButtonElement> button = adoptRef(new MediaControlToggleClosedCaptionsButtonElement(document, controls));
695     button->ensureUserAgentShadowRoot();
696     button->setType("button");
697     button->hide();
698     return button.release();
699 }
700
701 void MediaControlToggleClosedCaptionsButtonElement::updateDisplayType()
702 {
703     bool captionsVisible = mediaController()->closedCaptionsVisible();
704     setDisplayType(captionsVisible ? MediaHideClosedCaptionsButton : MediaShowClosedCaptionsButton);
705     setChecked(captionsVisible);
706 }
707
708 void MediaControlToggleClosedCaptionsButtonElement::defaultEventHandler(Event* event)
709 {
710     if (event->type() == eventNames().clickEvent) {
711         // FIXME: It's not great that the shared code is dictating behavior of platform-specific
712         // UI. Not all ports may want the closed captions button to toggle a list of tracks, so
713         // we have to use #if.
714         // https://bugs.webkit.org/show_bug.cgi?id=101877
715 #if !PLATFORM(MAC) && !PLATFORM(WIN) && !PLATFORM(GTK)
716         mediaController()->setClosedCaptionsVisible(!mediaController()->closedCaptionsVisible());
717         setChecked(mediaController()->closedCaptionsVisible());
718         updateDisplayType();
719 #else
720         m_controls->toggleClosedCaptionTrackList();
721 #endif
722         event->setDefaultHandled();
723     }
724
725     HTMLInputElement::defaultEventHandler(event);
726 }
727
728 const AtomicString& MediaControlToggleClosedCaptionsButtonElement::shadowPseudoId() const
729 {
730     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-toggle-closed-captions-button", AtomicString::ConstructFromLiteral));
731     return id;
732 }
733
734 // ----------------------------
735
736 MediaControlClosedCaptionsContainerElement::MediaControlClosedCaptionsContainerElement(Document& document)
737     : MediaControlDivElement(document, MediaClosedCaptionsContainer)
738 {
739 }
740
741 PassRefPtr<MediaControlClosedCaptionsContainerElement> MediaControlClosedCaptionsContainerElement::create(Document& document)
742 {
743     RefPtr<MediaControlClosedCaptionsContainerElement> element = adoptRef(new MediaControlClosedCaptionsContainerElement(document));
744     element->setAttribute(dirAttr, "auto");
745     element->hide();
746     return element.release();
747 }
748
749 const AtomicString& MediaControlClosedCaptionsContainerElement::shadowPseudoId() const
750 {
751     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-closed-captions-container", AtomicString::ConstructFromLiteral));
752     return id;
753 }
754
755 // ----------------------------
756
757 MediaControlClosedCaptionsTrackListElement::MediaControlClosedCaptionsTrackListElement(Document& document, MediaControls* controls)
758     : MediaControlDivElement(document, MediaClosedCaptionsTrackList)
759     , m_controls(controls)
760 {
761 }
762
763 PassRefPtr<MediaControlClosedCaptionsTrackListElement> MediaControlClosedCaptionsTrackListElement::create(Document& document, MediaControls* controls)
764 {
765     ASSERT(controls);
766     RefPtr<MediaControlClosedCaptionsTrackListElement> element = adoptRef(new MediaControlClosedCaptionsTrackListElement(document, controls));
767     return element.release();
768 }
769
770 void MediaControlClosedCaptionsTrackListElement::defaultEventHandler(Event* event)
771 {
772 #if ENABLE(VIDEO_TRACK)
773     if (event->type() == eventNames().clickEvent) {
774         Node* target = event->target()->toNode();
775         if (!target || !target->isElementNode())
776             return;
777
778         // When we created the elements in the track list, we gave them a custom
779         // attribute representing the index in the HTMLMediaElement's list of tracks.
780         // Check if the event target has such a custom element and, if so,
781         // tell the HTMLMediaElement to enable that track.
782
783         RefPtr<TextTrack> textTrack;
784         MenuItemToTrackMap::iterator iter = m_menuToTrackMap.find(toElement(target));
785         if (iter != m_menuToTrackMap.end())
786             textTrack = iter->value;
787         m_menuToTrackMap.clear();
788         m_controls->toggleClosedCaptionTrackList();
789         if (!textTrack)
790             return;
791
792         HTMLMediaElement* mediaElement = parentMediaElement(this);
793         if (!mediaElement)
794             return;
795
796         mediaElement->setSelectedTextTrack(textTrack.get());
797
798         updateDisplay();
799     }
800
801     MediaControlDivElement::defaultEventHandler(event);
802 #endif
803 }
804
805 const AtomicString& MediaControlClosedCaptionsTrackListElement::shadowPseudoId() const
806 {
807     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-closed-captions-track-list", AtomicString::ConstructFromLiteral));
808     return id;
809 }
810
811 void MediaControlClosedCaptionsTrackListElement::updateDisplay()
812 {
813 #if ENABLE(VIDEO_TRACK)
814     DEFINE_STATIC_LOCAL(AtomicString, selectedClassValue, ("selected", AtomicString::ConstructFromLiteral));
815
816     if (!mediaController()->hasClosedCaptions())
817         return;
818
819     if (!document().page())
820         return;
821     CaptionUserPreferences::CaptionDisplayMode displayMode = document().page()->group().captionPreferences()->captionDisplayMode();
822
823     HTMLMediaElement* mediaElement = parentMediaElement(this);
824     if (!mediaElement)
825         return;
826
827     TextTrackList* trackList = mediaElement->textTracks();
828     if (!trackList || !trackList->length())
829         return;
830
831     rebuildTrackListMenu();
832
833     RefPtr<Element> offMenuItem;
834     bool trackMenuItemSelected = false;
835
836     for (unsigned i = 0, length = m_menuItems.size(); i < length; ++i) {
837         RefPtr<Element> trackItem = m_menuItems[i];
838
839         RefPtr<TextTrack> textTrack;
840         MenuItemToTrackMap::iterator iter = m_menuToTrackMap.find(trackItem.get());
841         if (iter == m_menuToTrackMap.end())
842             continue;
843         textTrack = iter->value;
844         if (!textTrack)
845             continue;
846
847         if (textTrack == TextTrack::captionMenuOffItem()) {
848             offMenuItem = trackItem;
849             continue;
850         }
851
852         if (textTrack == TextTrack::captionMenuAutomaticItem()) {
853             if (displayMode == CaptionUserPreferences::Automatic)
854                 trackItem->classList()->add(selectedClassValue, ASSERT_NO_EXCEPTION);
855             else
856                 trackItem->classList()->remove(selectedClassValue, ASSERT_NO_EXCEPTION);
857             continue;
858         }
859
860         if (displayMode != CaptionUserPreferences::Automatic && textTrack->mode() == TextTrack::showingKeyword()) {
861             trackMenuItemSelected = true;
862             trackItem->classList()->add(selectedClassValue, ASSERT_NO_EXCEPTION);
863         } else
864             trackItem->classList()->remove(selectedClassValue, ASSERT_NO_EXCEPTION);
865     }
866
867     if (offMenuItem) {
868         if (displayMode == CaptionUserPreferences::ForcedOnly && !trackMenuItemSelected)
869             offMenuItem->classList()->add(selectedClassValue, ASSERT_NO_EXCEPTION);
870         else
871             offMenuItem->classList()->remove(selectedClassValue, ASSERT_NO_EXCEPTION);
872     }
873 #endif
874 }
875
876 void MediaControlClosedCaptionsTrackListElement::rebuildTrackListMenu()
877 {
878 #if ENABLE(VIDEO_TRACK)
879     // Remove any existing content.
880     removeChildren();
881     m_menuItems.clear();
882     m_menuToTrackMap.clear();
883
884     if (!mediaController()->hasClosedCaptions())
885         return;
886
887     HTMLMediaElement* mediaElement = parentMediaElement(this);
888     if (!mediaElement)
889         return;
890
891     TextTrackList* trackList = mediaElement->textTracks();
892     if (!trackList || !trackList->length())
893         return;
894
895     if (!document().page())
896         return;
897     CaptionUserPreferences* captionPreferences = document().page()->group().captionPreferences();
898     Vector<RefPtr<TextTrack> > tracksForMenu = captionPreferences->sortedTrackListForMenu(trackList);
899
900     RefPtr<Element> captionsHeader = document().createElement(h3Tag, ASSERT_NO_EXCEPTION);
901     captionsHeader->appendChild(document().createTextNode(textTrackSubtitlesText()));
902     appendChild(captionsHeader);
903     RefPtr<Element> captionsMenuList = document().createElement(ulTag, ASSERT_NO_EXCEPTION);
904
905     for (unsigned i = 0, length = tracksForMenu.size(); i < length; ++i) {
906         RefPtr<TextTrack> textTrack = tracksForMenu[i];
907         RefPtr<Element> menuItem = document().createElement(liTag, ASSERT_NO_EXCEPTION);
908         menuItem->appendChild(document().createTextNode(captionPreferences->displayNameForTrack(textTrack.get())));
909         captionsMenuList->appendChild(menuItem);
910         m_menuItems.append(menuItem);
911         m_menuToTrackMap.add(menuItem, textTrack);
912     }
913
914     appendChild(captionsMenuList);
915 #endif
916 }
917
918 // ----------------------------
919
920 MediaControlTimelineElement::MediaControlTimelineElement(Document& document, MediaControls* controls)
921     : MediaControlInputElement(document, MediaSlider)
922     , m_controls(controls)
923 {
924 }
925
926 PassRefPtr<MediaControlTimelineElement> MediaControlTimelineElement::create(Document& document, MediaControls* controls)
927 {
928     ASSERT(controls);
929
930     RefPtr<MediaControlTimelineElement> timeline = adoptRef(new MediaControlTimelineElement(document, controls));
931     timeline->ensureUserAgentShadowRoot();
932     timeline->setType("range");
933     timeline->setAttribute(precisionAttr, "float");
934     return timeline.release();
935 }
936
937 void MediaControlTimelineElement::defaultEventHandler(Event* event)
938 {
939     // Left button is 0. Rejects mouse events not from left button.
940     if (event->isMouseEvent() && static_cast<MouseEvent*>(event)->button())
941         return;
942
943     if (!attached())
944         return;
945
946     if (event->type() == eventNames().mousedownEvent)
947         mediaController()->beginScrubbing();
948
949     if (event->type() == eventNames().mouseupEvent)
950         mediaController()->endScrubbing();
951
952     MediaControlInputElement::defaultEventHandler(event);
953
954     if (event->type() == eventNames().mouseoverEvent || event->type() == eventNames().mouseoutEvent || event->type() == eventNames().mousemoveEvent)
955         return;
956
957     double time = value().toDouble();
958     if (event->type() == eventNames().inputEvent && time != mediaController()->currentTime())
959         mediaController()->setCurrentTime(time, IGNORE_EXCEPTION);
960
961     RenderSlider* slider = toRenderSlider(renderer());
962     if (slider && slider->inDragMode())
963         m_controls->updateCurrentTimeDisplay();
964 }
965
966 bool MediaControlTimelineElement::willRespondToMouseClickEvents()
967 {
968     if (!attached())
969         return false;
970
971     return true;
972 }
973
974 void MediaControlTimelineElement::setPosition(double currentTime)
975 {
976     setValue(String::number(currentTime));
977 }
978
979 void MediaControlTimelineElement::setDuration(double duration)
980 {
981     setAttribute(maxAttr, AtomicString::number(std::isfinite(duration) ? duration : 0));
982 }
983
984
985 const AtomicString& MediaControlTimelineElement::shadowPseudoId() const
986 {
987     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-timeline", AtomicString::ConstructFromLiteral));
988     return id;
989 }
990
991 // ----------------------------
992
993 MediaControlPanelVolumeSliderElement::MediaControlPanelVolumeSliderElement(Document& document)
994     : MediaControlVolumeSliderElement(document)
995 {
996 }
997
998 PassRefPtr<MediaControlPanelVolumeSliderElement> MediaControlPanelVolumeSliderElement::create(Document& document)
999 {
1000     RefPtr<MediaControlPanelVolumeSliderElement> slider = adoptRef(new MediaControlPanelVolumeSliderElement(document));
1001     slider->ensureUserAgentShadowRoot();
1002     slider->setType("range");
1003     slider->setAttribute(precisionAttr, "float");
1004     slider->setAttribute(maxAttr, "1");
1005     return slider.release();
1006 }
1007
1008 const AtomicString& MediaControlPanelVolumeSliderElement::shadowPseudoId() const
1009 {
1010     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-volume-slider", AtomicString::ConstructFromLiteral));
1011     return id;
1012 }
1013
1014 // ----------------------------
1015
1016 MediaControlFullscreenVolumeSliderElement::MediaControlFullscreenVolumeSliderElement(Document& document)
1017     : MediaControlVolumeSliderElement(document)
1018 {
1019 }
1020
1021 PassRefPtr<MediaControlFullscreenVolumeSliderElement> MediaControlFullscreenVolumeSliderElement::create(Document& document)
1022 {
1023     RefPtr<MediaControlFullscreenVolumeSliderElement> slider = adoptRef(new MediaControlFullscreenVolumeSliderElement(document));
1024     slider->ensureUserAgentShadowRoot();
1025     slider->setType("range");
1026     slider->setAttribute(precisionAttr, "float");
1027     slider->setAttribute(maxAttr, "1");
1028     return slider.release();
1029 }
1030
1031 const AtomicString& MediaControlFullscreenVolumeSliderElement::shadowPseudoId() const
1032 {
1033     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-volume-slider", AtomicString::ConstructFromLiteral));
1034     return id;
1035 }
1036
1037 // ----------------------------
1038
1039 MediaControlFullscreenButtonElement::MediaControlFullscreenButtonElement(Document& document)
1040     : MediaControlInputElement(document, MediaEnterFullscreenButton)
1041 {
1042 }
1043
1044 PassRefPtr<MediaControlFullscreenButtonElement> MediaControlFullscreenButtonElement::create(Document& document)
1045 {
1046     RefPtr<MediaControlFullscreenButtonElement> button = adoptRef(new MediaControlFullscreenButtonElement(document));
1047     button->ensureUserAgentShadowRoot();
1048     button->setType("button");
1049     button->hide();
1050     return button.release();
1051 }
1052
1053 void MediaControlFullscreenButtonElement::defaultEventHandler(Event* event)
1054 {
1055     if (event->type() == eventNames().clickEvent) {
1056 #if ENABLE(FULLSCREEN_API)
1057         // Only use the new full screen API if the fullScreenEnabled setting has
1058         // been explicitly enabled. Otherwise, use the old fullscreen API. This
1059         // allows apps which embed a WebView to retain the existing full screen
1060         // video implementation without requiring them to implement their own full
1061         // screen behavior.
1062         if (document().settings() && document().settings()->fullScreenEnabled()) {
1063             if (document().webkitIsFullScreen() && document().webkitCurrentFullScreenElement() == parentMediaElement(this))
1064                 document().webkitCancelFullScreen();
1065             else
1066                 document().requestFullScreenForElement(parentMediaElement(this), 0, Document::ExemptIFrameAllowFullScreenRequirement);
1067         } else
1068 #endif
1069             mediaController()->enterFullscreen();
1070         event->setDefaultHandled();
1071     }
1072     HTMLInputElement::defaultEventHandler(event);
1073 }
1074
1075 const AtomicString& MediaControlFullscreenButtonElement::shadowPseudoId() const
1076 {
1077     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-button", AtomicString::ConstructFromLiteral));
1078     return id;
1079 }
1080
1081 void MediaControlFullscreenButtonElement::setIsFullscreen(bool isFullscreen)
1082 {
1083     setDisplayType(isFullscreen ? MediaExitFullscreenButton : MediaEnterFullscreenButton);
1084 }
1085
1086 // ----------------------------
1087
1088 MediaControlFullscreenVolumeMinButtonElement::MediaControlFullscreenVolumeMinButtonElement(Document& document)
1089     : MediaControlInputElement(document, MediaUnMuteButton)
1090 {
1091 }
1092
1093 PassRefPtr<MediaControlFullscreenVolumeMinButtonElement> MediaControlFullscreenVolumeMinButtonElement::create(Document& document)
1094 {
1095     RefPtr<MediaControlFullscreenVolumeMinButtonElement> button = adoptRef(new MediaControlFullscreenVolumeMinButtonElement(document));
1096     button->ensureUserAgentShadowRoot();
1097     button->setType("button");
1098     return button.release();
1099 }
1100
1101 void MediaControlFullscreenVolumeMinButtonElement::defaultEventHandler(Event* event)
1102 {
1103     if (event->type() == eventNames().clickEvent) {
1104         ExceptionCode code = 0;
1105         mediaController()->setVolume(0, code);
1106         event->setDefaultHandled();
1107     }
1108     HTMLInputElement::defaultEventHandler(event);
1109 }
1110
1111 const AtomicString& MediaControlFullscreenVolumeMinButtonElement::shadowPseudoId() const
1112 {
1113     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-volume-min-button", AtomicString::ConstructFromLiteral));
1114     return id;
1115 }
1116
1117 // ----------------------------
1118
1119 MediaControlFullscreenVolumeMaxButtonElement::MediaControlFullscreenVolumeMaxButtonElement(Document& document)
1120 : MediaControlInputElement(document, MediaMuteButton)
1121 {
1122 }
1123
1124 PassRefPtr<MediaControlFullscreenVolumeMaxButtonElement> MediaControlFullscreenVolumeMaxButtonElement::create(Document& document)
1125 {
1126     RefPtr<MediaControlFullscreenVolumeMaxButtonElement> button = adoptRef(new MediaControlFullscreenVolumeMaxButtonElement(document));
1127     button->ensureUserAgentShadowRoot();
1128     button->setType("button");
1129     return button.release();
1130 }
1131
1132 void MediaControlFullscreenVolumeMaxButtonElement::defaultEventHandler(Event* event)
1133 {
1134     if (event->type() == eventNames().clickEvent) {
1135         ExceptionCode code = 0;
1136         mediaController()->setVolume(1, code);
1137         event->setDefaultHandled();
1138     }
1139     HTMLInputElement::defaultEventHandler(event);
1140 }
1141
1142 const AtomicString& MediaControlFullscreenVolumeMaxButtonElement::shadowPseudoId() const
1143 {
1144     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-volume-max-button", AtomicString::ConstructFromLiteral));
1145     return id;
1146 }
1147
1148 // ----------------------------
1149
1150 MediaControlTimeRemainingDisplayElement::MediaControlTimeRemainingDisplayElement(Document& document)
1151     : MediaControlTimeDisplayElement(document, MediaTimeRemainingDisplay)
1152 {
1153 }
1154
1155 PassRefPtr<MediaControlTimeRemainingDisplayElement> MediaControlTimeRemainingDisplayElement::create(Document& document)
1156 {
1157     return adoptRef(new MediaControlTimeRemainingDisplayElement(document));
1158 }
1159
1160 static const AtomicString& getMediaControlTimeRemainingDisplayElementShadowPseudoId()
1161 {
1162     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-time-remaining-display", AtomicString::ConstructFromLiteral));
1163     return id;
1164 }
1165
1166 const AtomicString& MediaControlTimeRemainingDisplayElement::shadowPseudoId() const
1167 {
1168     return getMediaControlTimeRemainingDisplayElementShadowPseudoId();
1169 }
1170
1171 // ----------------------------
1172
1173 MediaControlCurrentTimeDisplayElement::MediaControlCurrentTimeDisplayElement(Document& document)
1174     : MediaControlTimeDisplayElement(document, MediaCurrentTimeDisplay)
1175 {
1176 }
1177
1178 PassRefPtr<MediaControlCurrentTimeDisplayElement> MediaControlCurrentTimeDisplayElement::create(Document& document)
1179 {
1180     return adoptRef(new MediaControlCurrentTimeDisplayElement(document));
1181 }
1182
1183 static const AtomicString& getMediaControlCurrentTimeDisplayElementShadowPseudoId()
1184 {
1185     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-current-time-display", AtomicString::ConstructFromLiteral));
1186     return id;
1187 }
1188
1189 const AtomicString& MediaControlCurrentTimeDisplayElement::shadowPseudoId() const
1190 {
1191     return getMediaControlCurrentTimeDisplayElementShadowPseudoId();
1192 }
1193
1194 // ----------------------------
1195
1196 #if ENABLE(VIDEO_TRACK)
1197
1198 MediaControlTextTrackContainerElement::MediaControlTextTrackContainerElement(Document& document)
1199     : MediaControlDivElement(document, MediaTextTrackDisplayContainer)
1200     , m_updateTimer(this, &MediaControlTextTrackContainerElement::updateTimerFired)
1201     , m_fontSize(0)
1202     , m_fontSizeIsImportant(false)
1203 {
1204 }
1205
1206 PassRefPtr<MediaControlTextTrackContainerElement> MediaControlTextTrackContainerElement::create(Document& document)
1207 {
1208     RefPtr<MediaControlTextTrackContainerElement> element = adoptRef(new MediaControlTextTrackContainerElement(document));
1209     element->hide();
1210     return element.release();
1211 }
1212
1213 RenderElement* MediaControlTextTrackContainerElement::createRenderer(RenderArena& arena, RenderStyle&)
1214 {
1215     return new (arena) RenderTextTrackContainerElement(this);
1216 }
1217
1218 const AtomicString& MediaControlTextTrackContainerElement::textTrackContainerElementShadowPseudoId()
1219 {
1220     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-text-track-container", AtomicString::ConstructFromLiteral));
1221     return id;
1222 }
1223     
1224 const AtomicString& MediaControlTextTrackContainerElement::shadowPseudoId() const
1225 {
1226     return textTrackContainerElementShadowPseudoId();
1227 }
1228
1229 void MediaControlTextTrackContainerElement::updateDisplay()
1230 {
1231     if (!mediaController()->closedCaptionsVisible())
1232         removeChildren();
1233
1234     HTMLMediaElement* mediaElement = parentMediaElement(this);
1235     // 1. If the media element is an audio element, or is another playback
1236     // mechanism with no rendering area, abort these steps. There is nothing to
1237     // render.
1238     if (!mediaElement || !mediaElement->isVideo())
1239         return;
1240
1241     // 2. Let video be the media element or other playback mechanism.
1242     HTMLVideoElement* video = toHTMLVideoElement(mediaElement);
1243
1244     // 3. Let output be an empty list of absolutely positioned CSS block boxes.
1245     Vector<RefPtr<HTMLDivElement> > output;
1246
1247     // 4. If the user agent is exposing a user interface for video, add to
1248     // output one or more completely transparent positioned CSS block boxes that
1249     // cover the same region as the user interface.
1250
1251     // 5. If the last time these rules were run, the user agent was not exposing
1252     // a user interface for video, but now it is, let reset be true. Otherwise,
1253     // let reset be false.
1254
1255     // There is nothing to be done explicitly for 4th and 5th steps, as
1256     // everything is handled through CSS. The caption box is on top of the
1257     // controls box, in a container set with the -webkit-box display property.
1258
1259     // 6. Let tracks be the subset of video's list of text tracks that have as
1260     // their rules for updating the text track rendering these rules for
1261     // updating the display of WebVTT text tracks, and whose text track mode is
1262     // showing or showing by default.
1263     // 7. Let cues be an empty list of text track cues.
1264     // 8. For each track track in tracks, append to cues all the cues from
1265     // track's list of cues that have their text track cue active flag set.
1266     CueList activeCues = video->currentlyActiveCues();
1267
1268     // 9. If reset is false, then, for each text track cue cue in cues: if cue's
1269     // text track cue display state has a set of CSS boxes, then add those boxes
1270     // to output, and remove cue from cues.
1271
1272     // There is nothing explicitly to be done here, as all the caching occurs
1273     // within the TextTrackCue instance itself. If parameters of the cue change,
1274     // the display tree is cleared.
1275
1276     // 10. For each text track cue cue in cues that has not yet had
1277     // corresponding CSS boxes added to output, in text track cue order, run the
1278     // following substeps:
1279     for (size_t i = 0; i < activeCues.size(); ++i) {
1280         TextTrackCue* cue = activeCues[i].data();
1281
1282         ASSERT(cue->isActive());
1283         if (!cue->track() || !cue->track()->isRendered() || !cue->isActive() || cue->text().isEmpty())
1284             continue;
1285
1286         RefPtr<TextTrackCueBox> displayBox = cue->getDisplayTree(m_videoDisplaySize.size());
1287         if (displayBox->hasChildNodes() && !contains(displayBox.get())) {
1288             // Note: the display tree of a cue is removed when the active flag of the cue is unset.
1289             appendChild(displayBox, ASSERT_NO_EXCEPTION, AttachNow);
1290             cue->setFontSize(m_fontSize, m_videoDisplaySize.size(), m_fontSizeIsImportant);
1291         }
1292     }
1293
1294     // 11. Return output.
1295     if (hasChildNodes()) {
1296         show();
1297         if (mediaElement->requiresTextTrackRepresentation()) {
1298             if (!m_textTrackRepresentation)
1299                 m_textTrackRepresentation = TextTrackRepresentation::create(this);
1300             mediaElement->setTextTrackRepresentation(m_textTrackRepresentation.get());
1301
1302             if (Page* page = document().page())
1303                 m_textTrackRepresentation->setContentScale(page->deviceScaleFactor());
1304
1305             m_textTrackRepresentation->update();
1306             setInlineStyleProperty(CSSPropertyWidth, m_videoDisplaySize.size().width(), CSSPrimitiveValue::CSS_PX);
1307             setInlineStyleProperty(CSSPropertyHeight, m_videoDisplaySize.size().height(), CSSPrimitiveValue::CSS_PX);
1308         }
1309     } else {
1310         hide();
1311         clearTextTrackRepresentation();
1312     }
1313 }
1314
1315 void MediaControlTextTrackContainerElement::updateTimerFired(Timer<MediaControlTextTrackContainerElement>*)
1316 {
1317     if (!document().page())
1318         return;
1319
1320     if (m_textTrackRepresentation) {
1321         setInlineStyleProperty(CSSPropertyWidth, m_videoDisplaySize.size().width(), CSSPrimitiveValue::CSS_PX);
1322         setInlineStyleProperty(CSSPropertyHeight, m_videoDisplaySize.size().height(), CSSPrimitiveValue::CSS_PX);
1323     }
1324     
1325     HTMLMediaElement* mediaElement = parentMediaElement(this);
1326     if (!mediaElement)
1327         return;
1328
1329     float smallestDimension = std::min(m_videoDisplaySize.size().height(), m_videoDisplaySize.size().width());
1330     float fontScale = document().page()->group().captionPreferences()->captionFontSizeScaleAndImportance(m_fontSizeIsImportant);
1331     m_fontSize = lroundf(smallestDimension * fontScale);
1332     
1333     CueList activeCues = mediaElement->currentlyActiveCues();
1334     for (size_t i = 0; i < activeCues.size(); ++i) {
1335         TextTrackCue* cue = activeCues[i].data();
1336         cue->setFontSize(m_fontSize, m_videoDisplaySize.size(), m_fontSizeIsImportant);
1337     }
1338     updateDisplay();
1339 }
1340
1341 void MediaControlTextTrackContainerElement::clearTextTrackRepresentation()
1342 {
1343     if (HTMLMediaElement* mediaElement = parentMediaElement(this))
1344         mediaElement->setTextTrackRepresentation(nullptr);
1345     m_textTrackRepresentation = nullptr;
1346     removeInlineStyleProperty(CSSPropertyPosition);
1347     removeInlineStyleProperty(CSSPropertyWidth);
1348     removeInlineStyleProperty(CSSPropertyHeight);
1349     removeInlineStyleProperty(CSSPropertyLeft);
1350     removeInlineStyleProperty(CSSPropertyTop);
1351 }
1352
1353 void MediaControlTextTrackContainerElement::enteredFullscreen()
1354 {
1355     updateSizes(true);
1356 }
1357
1358 void MediaControlTextTrackContainerElement::exitedFullscreen()
1359 {
1360     clearTextTrackRepresentation();
1361     updateSizes(true);
1362 }
1363
1364 void MediaControlTextTrackContainerElement::updateSizes(bool forceUpdate)
1365 {
1366     HTMLMediaElement* mediaElement = parentMediaElement(this);
1367     if (!mediaElement)
1368         return;
1369
1370     if (!document().page())
1371         return;
1372
1373     IntRect videoBox;
1374
1375     if (m_textTrackRepresentation)
1376         videoBox = m_textTrackRepresentation->bounds();
1377     else {
1378 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
1379         if (!mediaElement->renderer() || !mediaElement->renderer()->isWidget())
1380             return;
1381         videoBox = pixelSnappedIntRect(toRenderWidget(mediaElement->renderer())->contentBoxRect());
1382 #else
1383         if (!mediaElement->renderer() || !mediaElement->renderer()->isVideo())
1384             return;
1385         videoBox = toRenderVideo(*mediaElement->renderer()).videoBox();
1386 #endif
1387     }
1388
1389     if (!forceUpdate && m_videoDisplaySize == videoBox)
1390         return;
1391     m_videoDisplaySize = videoBox;
1392
1393     // FIXME (121170): This function is called during layout, and should lay out the text tracks immediately.
1394     m_updateTimer.startOneShot(0);
1395 }
1396
1397 PassRefPtr<Image> MediaControlTextTrackContainerElement::createTextTrackRepresentationImage()
1398 {
1399     if (!hasChildNodes())
1400         return nullptr;
1401
1402     Frame* frame = document().frame();
1403     if (!frame)
1404         return nullptr;
1405
1406     document().updateLayout();
1407
1408     auto renderer = this->renderer();
1409     if (!renderer)
1410         return nullptr;
1411
1412     if (!renderer->hasLayer())
1413         return nullptr;
1414
1415     RenderLayer* layer = toRenderLayerModelObject(renderer)->layer();
1416
1417     float deviceScaleFactor = 1;
1418     if (Page* page = document().page())
1419         deviceScaleFactor = page->deviceScaleFactor();
1420
1421     IntRect paintingRect = IntRect(IntPoint(), layer->size());
1422
1423     OwnPtr<ImageBuffer> buffer(ImageBuffer::create(paintingRect.size(), deviceScaleFactor, ColorSpaceDeviceRGB));
1424     if (!buffer)
1425         return nullptr;
1426
1427     layer->paint(buffer->context(), paintingRect, PaintBehaviorFlattenCompositingLayers, nullptr, nullptr, RenderLayer::PaintLayerPaintingCompositingAllPhases);
1428
1429     return buffer->copyImage();
1430 }
1431
1432 void MediaControlTextTrackContainerElement::textTrackRepresentationBoundsChanged(const IntRect&)
1433 {
1434     updateSizes();
1435 }
1436
1437 #endif // ENABLE(VIDEO_TRACK)
1438
1439 // ----------------------------
1440
1441 } // namespace WebCore
1442
1443 #endif // ENABLE(VIDEO)