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