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