c3545178e6b71041ba24cebaa7ad5ee67da54e69
[framework/web/webkit-efl.git] / Source / WebCore / html / shadow / MediaControlElements.cpp
1 /*
2  * Copyright (C) 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer.
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include "config.h"
30
31 #if ENABLE(VIDEO)
32
33 #include "MediaControlElements.h"
34
35 #include "CSSStyleDeclaration.h"
36 #include "CSSValueKeywords.h"
37 #include "DOMTokenList.h"
38 #include "EventNames.h"
39 #include "FloatConversion.h"
40 #include "FloatPoint.h"
41 #include "Frame.h"
42 #include "HTMLDivElement.h"
43 #include "HTMLMediaElement.h"
44 #include "HTMLNames.h"
45 #include "HTMLVideoElement.h"
46 #include "LayoutRepainter.h"
47 #include "LocalizedStrings.h"
48 #include "MediaControls.h"
49 #include "MouseEvent.h"
50 #include "Page.h"
51 #include "RenderDeprecatedFlexibleBox.h"
52 #include "RenderInline.h"
53 #include "RenderMedia.h"
54 #include "RenderSlider.h"
55 #include "RenderText.h"
56 #include "RenderTheme.h"
57 #include "RenderVideo.h"
58 #include "RenderView.h"
59 #include "ScriptController.h"
60 #include "Settings.h"
61 #include "StyleResolver.h"
62 #include "Text.h"
63
64 namespace WebCore {
65
66 using namespace HTMLNames;
67 using namespace std;
68
69 // FIXME: These constants may need to be tweaked to better match the seeking in the QuickTime plug-in.
70
71 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
72 static const float cSeekForwardTime = 5.0f;
73 static const float cSeekBackTime = (-5.0f);
74 #endif
75 static const float cSkipRepeatDelay = 0.1f;
76 static const float cSkipTime = 0.2f;
77
78 static const float cScanRepeatDelay = 1.5f;
79 static const float cScanMaximumRate = 8;
80
81 HTMLMediaElement* toParentMediaElement(Node* node)
82 {
83     if (!node)
84         return 0;
85     Node* mediaNode = node->shadowHost();
86     if (!mediaNode)
87         mediaNode = node;
88     if (!mediaNode || !mediaNode->isElementNode() || !static_cast<Element*>(mediaNode)->isMediaElement())
89         return 0;
90
91     return static_cast<HTMLMediaElement*>(mediaNode);
92 }
93
94 MediaControlElementType mediaControlElementType(Node* node)
95 {
96     ASSERT(node->isMediaControlElement());
97     HTMLElement* element = toHTMLElement(node);
98     if (element->hasTagName(inputTag))
99         return static_cast<MediaControlInputElement*>(element)->displayType();
100     return static_cast<MediaControlElement*>(element)->displayType();
101 }
102
103 // ----------------------------
104
105 MediaControlElement::MediaControlElement(Document* document)
106     : HTMLDivElement(divTag, document)
107     , m_mediaController(0)
108 {
109 }
110
111 void MediaControlElement::show()
112 {
113     document()->didAddTouchEventHandler();
114     document()->addListenerType(Document::TOUCH_LISTENER);
115     removeInlineStyleProperty(CSSPropertyDisplay);
116 }
117
118 void MediaControlElement::hide()
119 {
120     document()->didRemoveTouchEventHandler();
121     setInlineStyleProperty(CSSPropertyDisplay, CSSValueNone);
122 }
123
124 // ----------------------------
125
126 inline MediaControlPanelElement::MediaControlPanelElement(Document* document)
127     : MediaControlElement(document)
128     , m_canBeDragged(false)
129     , m_isBeingDragged(false)
130     , m_isDisplayed(false)
131     , m_opaque(true)
132     , m_transitionTimer(this, &MediaControlPanelElement::transitionTimerFired)
133 {
134 }
135
136 PassRefPtr<MediaControlPanelElement> MediaControlPanelElement::create(Document* document)
137 {
138     return adoptRef(new MediaControlPanelElement(document));
139 }
140
141 MediaControlElementType MediaControlPanelElement::displayType() const
142 {
143     return MediaControlsPanel;
144 }
145
146 const AtomicString& MediaControlPanelElement::shadowPseudoId() const
147 {
148     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-panel"));
149     return id;
150 }
151
152 void MediaControlPanelElement::startDrag(const LayoutPoint& eventLocation)
153 {
154     if (!m_canBeDragged)
155         return;
156
157     if (m_isBeingDragged)
158         return;
159
160     RenderObject* renderer = this->renderer();
161     if (!renderer || !renderer->isBox())
162         return;
163
164     Frame* frame = document()->frame();
165     if (!frame)
166         return;
167
168     m_dragStartEventLocation = eventLocation;
169
170     frame->eventHandler()->setCapturingMouseEventsNode(this);
171
172     m_isBeingDragged = true;
173 }
174
175 void MediaControlPanelElement::continueDrag(const LayoutPoint& eventLocation)
176 {
177     if (!m_isBeingDragged)
178         return;
179
180     LayoutSize distanceDragged = eventLocation - m_dragStartEventLocation;
181     setPosition(LayoutPoint(distanceDragged.width(), distanceDragged.height()));
182 }
183
184 void MediaControlPanelElement::endDrag()
185 {
186     if (!m_isBeingDragged)
187         return;
188
189     m_isBeingDragged = false;
190
191     Frame* frame = document()->frame();
192     if (!frame)
193         return;
194
195     frame->eventHandler()->setCapturingMouseEventsNode(0);
196 }
197
198 void MediaControlPanelElement::startTimer()
199 {
200     stopTimer();
201
202     // The timer is required to set the property display:'none' on the panel,
203     // such that captions are correctly displayed at the bottom of the video
204     // at the end of the fadeout transition.
205     double duration = document()->page() ? document()->page()->theme()->mediaControlsFadeOutDuration() : 0;
206     m_transitionTimer.startOneShot(duration);
207 }
208
209 void MediaControlPanelElement::stopTimer()
210 {
211     if (m_transitionTimer.isActive())
212         m_transitionTimer.stop();
213 }
214
215
216 void MediaControlPanelElement::transitionTimerFired(Timer<MediaControlPanelElement>*)
217 {
218     if (!m_opaque)
219         hide();
220
221     stopTimer();
222 }
223
224 void MediaControlPanelElement::setPosition(const LayoutPoint& position)
225 {
226     double left = position.x();
227     double top = position.y();
228
229     // Set the left and top to control the panel's position; this depends on it being absolute positioned.
230     // Set the margin to zero since the position passed in will already include the effect of the margin.
231     setInlineStyleProperty(CSSPropertyLeft, left, CSSPrimitiveValue::CSS_PX);
232     setInlineStyleProperty(CSSPropertyTop, top, CSSPrimitiveValue::CSS_PX);
233     setInlineStyleProperty(CSSPropertyMarginLeft, 0.0, CSSPrimitiveValue::CSS_PX);
234     setInlineStyleProperty(CSSPropertyMarginTop, 0.0, CSSPrimitiveValue::CSS_PX);
235
236     ExceptionCode ignored;
237     classList()->add("dragged", ignored);
238 }
239
240 void MediaControlPanelElement::resetPosition()
241 {
242     removeInlineStyleProperty(CSSPropertyLeft);
243     removeInlineStyleProperty(CSSPropertyTop);
244     removeInlineStyleProperty(CSSPropertyMarginLeft);
245     removeInlineStyleProperty(CSSPropertyMarginTop);
246
247     ExceptionCode ignored;
248     classList()->remove("dragged", ignored);
249 }
250
251 void MediaControlPanelElement::makeOpaque()
252 {
253     if (m_opaque)
254         return;
255
256     double duration = document()->page() ? document()->page()->theme()->mediaControlsFadeInDuration() : 0;
257
258     setInlineStyleProperty(CSSPropertyWebkitTransitionProperty, CSSPropertyOpacity);
259     setInlineStyleProperty(CSSPropertyWebkitTransitionDuration, duration, CSSPrimitiveValue::CSS_S);
260     setInlineStyleProperty(CSSPropertyOpacity, 1.0, CSSPrimitiveValue::CSS_NUMBER);
261
262     m_opaque = true;
263
264     if (m_isDisplayed)
265         show();
266 }
267
268 void MediaControlPanelElement::makeTransparent()
269 {
270     if (!m_opaque)
271         return;
272
273     setInlineStyleProperty(CSSPropertyWebkitTransitionProperty, CSSPropertyOpacity);
274     setInlineStyleProperty(CSSPropertyWebkitTransitionDuration, document()->page()->theme()->mediaControlsFadeOutDuration(), CSSPrimitiveValue::CSS_S);
275     setInlineStyleProperty(CSSPropertyOpacity, 0.0, CSSPrimitiveValue::CSS_NUMBER);
276
277     m_opaque = false;
278     startTimer();
279 }
280
281 void MediaControlPanelElement::defaultEventHandler(Event* event)
282 {
283     MediaControlElement::defaultEventHandler(event);
284
285     if (event->isMouseEvent()) {
286         LayoutPoint location = static_cast<MouseEvent*>(event)->absoluteLocation();
287         if (event->type() == eventNames().mousedownEvent && event->target() == this) {
288             startDrag(location);
289             event->setDefaultHandled();
290         } else if (event->type() == eventNames().mousemoveEvent && m_isBeingDragged)
291             continueDrag(location);
292         else if (event->type() == eventNames().mouseupEvent && m_isBeingDragged) {
293             continueDrag(location);
294             endDrag();
295             event->setDefaultHandled();
296         }
297     }
298 }
299
300 void MediaControlPanelElement::setCanBeDragged(bool canBeDragged)
301 {
302     if (m_canBeDragged == canBeDragged)
303         return;
304
305     m_canBeDragged = canBeDragged;
306
307     if (!canBeDragged)
308         endDrag();
309 }
310
311 void MediaControlPanelElement::setIsDisplayed(bool isDisplayed)
312 {
313     m_isDisplayed = isDisplayed;
314 }
315
316 // ----------------------------
317
318 inline MediaControlTimelineContainerElement::MediaControlTimelineContainerElement(Document* document)
319     : MediaControlElement(document)
320 {
321 }
322
323 PassRefPtr<MediaControlTimelineContainerElement> MediaControlTimelineContainerElement::create(Document* document)
324 {
325     RefPtr<MediaControlTimelineContainerElement> element = adoptRef(new MediaControlTimelineContainerElement(document));
326     element->hide();
327     return element.release();
328 }
329
330 MediaControlElementType MediaControlTimelineContainerElement::displayType() const
331 {
332     return MediaTimelineContainer;
333 }
334
335 const AtomicString& MediaControlTimelineContainerElement::shadowPseudoId() const
336 {
337     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-timeline-container"));
338     return id;
339 }
340
341 // ----------------------------
342
343 class RenderMediaVolumeSliderContainer : public RenderBlock {
344 public:
345     RenderMediaVolumeSliderContainer(Node*);
346
347 private:
348     virtual void layout();
349 };
350
351 RenderMediaVolumeSliderContainer::RenderMediaVolumeSliderContainer(Node* node)
352     : RenderBlock(node)
353 {
354 }
355
356 void RenderMediaVolumeSliderContainer::layout()
357 {
358     RenderBlock::layout();
359
360     if (style()->display() == NONE || !nextSibling() || !nextSibling()->isBox())
361         return;
362
363     RenderBox* buttonBox = toRenderBox(nextSibling());
364     int absoluteOffsetTop = buttonBox->localToAbsolute(FloatPoint(0, -size().height())).y();
365
366     LayoutStateDisabler layoutStateDisabler(view());
367
368     // If the slider would be rendered outside the page, it should be moved below the controls.
369     if (UNLIKELY(absoluteOffsetTop < 0))
370         setY(buttonBox->offsetTop() + theme()->volumeSliderOffsetFromMuteButton(buttonBox, pixelSnappedSize()).y());
371 }
372
373 inline MediaControlVolumeSliderContainerElement::MediaControlVolumeSliderContainerElement(Document* document)
374     : MediaControlElement(document)
375 {
376 }
377
378 PassRefPtr<MediaControlVolumeSliderContainerElement> MediaControlVolumeSliderContainerElement::create(Document* document)
379 {
380     RefPtr<MediaControlVolumeSliderContainerElement> element = adoptRef(new MediaControlVolumeSliderContainerElement(document));
381     element->hide();
382     return element.release();
383 }
384
385 RenderObject* MediaControlVolumeSliderContainerElement::createRenderer(RenderArena* arena, RenderStyle*)
386 {
387     return new (arena) RenderMediaVolumeSliderContainer(this);
388 }
389
390 void MediaControlVolumeSliderContainerElement::defaultEventHandler(Event* event)
391 {
392     if (!event->isMouseEvent() || event->type() != eventNames().mouseoutEvent)
393         return;
394
395     // Poor man's mouseleave event detection.
396     MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
397     if (!mouseEvent->relatedTarget() || !mouseEvent->relatedTarget()->toNode())
398         return;
399
400     if (this->containsIncludingShadowDOM(mouseEvent->relatedTarget()->toNode()))
401         return;
402
403     hide();
404 }
405
406 MediaControlElementType MediaControlVolumeSliderContainerElement::displayType() const
407 {
408     return MediaVolumeSliderContainer;
409 }
410
411 const AtomicString& MediaControlVolumeSliderContainerElement::shadowPseudoId() const
412 {
413     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-volume-slider-container"));
414     return id;
415 }
416
417 // ----------------------------
418
419 inline MediaControlStatusDisplayElement::MediaControlStatusDisplayElement(Document* document)
420     : MediaControlElement(document)
421     , m_stateBeingDisplayed(Nothing)
422 {
423 }
424
425 PassRefPtr<MediaControlStatusDisplayElement> MediaControlStatusDisplayElement::create(Document* document)
426 {
427     RefPtr<MediaControlStatusDisplayElement> element = adoptRef(new MediaControlStatusDisplayElement(document));
428     element->hide();
429     return element.release();
430 }
431
432 void MediaControlStatusDisplayElement::update()
433 {
434     // Get the new state that we'll have to display.
435     StateBeingDisplayed newStateToDisplay = Nothing;
436
437     if (mediaController()->readyState() <= MediaControllerInterface::HAVE_METADATA && mediaController()->hasCurrentSrc())
438         newStateToDisplay = Loading;
439     else if (mediaController()->isLiveStream())
440         newStateToDisplay = LiveBroadcast;
441
442     if (newStateToDisplay == m_stateBeingDisplayed)
443         return;
444
445     ExceptionCode e;
446
447     if (m_stateBeingDisplayed == Nothing)
448         show();
449     else if (newStateToDisplay == Nothing)
450         hide();
451
452     m_stateBeingDisplayed = newStateToDisplay;
453
454     switch (m_stateBeingDisplayed) {
455     case Nothing:
456         setInnerText("", e);
457         break;
458     case Loading:
459         setInnerText(mediaElementLoadingStateText(), e);
460         break;
461     case LiveBroadcast:
462         setInnerText(mediaElementLiveBroadcastStateText(), e);
463         break;
464     }
465 }
466
467 MediaControlElementType MediaControlStatusDisplayElement::displayType() const
468 {
469     return MediaStatusDisplay;
470 }
471
472 const AtomicString& MediaControlStatusDisplayElement::shadowPseudoId() const
473 {
474     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-status-display"));
475     return id;
476 }
477
478 // ----------------------------
479
480 MediaControlInputElement::MediaControlInputElement(Document* document, MediaControlElementType displayType)
481     : HTMLInputElement(inputTag, document, 0, false)
482     , m_mediaController(0)
483     , m_displayType(displayType)
484 {
485 }
486
487 void MediaControlInputElement::show()
488 {
489     removeInlineStyleProperty(CSSPropertyDisplay);
490 }
491
492 void MediaControlInputElement::hide()
493 {
494     setInlineStyleProperty(CSSPropertyDisplay, CSSValueNone);
495 }
496
497
498 void MediaControlInputElement::setDisplayType(MediaControlElementType displayType)
499 {
500     if (displayType == m_displayType)
501         return;
502
503     m_displayType = displayType;
504     if (RenderObject* object = renderer())
505         object->repaint();
506 }
507
508 // ----------------------------
509
510 inline MediaControlMuteButtonElement::MediaControlMuteButtonElement(Document* document, MediaControlElementType displayType)
511     : MediaControlInputElement(document, displayType)
512 {
513 }
514
515 void MediaControlMuteButtonElement::defaultEventHandler(Event* event)
516 {
517     if (event->type() == eventNames().clickEvent) {
518         mediaController()->setMuted(!mediaController()->muted());
519         event->setDefaultHandled();
520     }
521
522     HTMLInputElement::defaultEventHandler(event);
523 }
524
525 void MediaControlMuteButtonElement::changedMute()
526 {
527     updateDisplayType();
528 }
529
530 void MediaControlMuteButtonElement::updateDisplayType()
531 {
532     setDisplayType(mediaController()->muted() ? MediaUnMuteButton : MediaMuteButton);
533 }
534
535 // ----------------------------
536
537 inline MediaControlPanelMuteButtonElement::MediaControlPanelMuteButtonElement(Document* document, MediaControls* controls)
538     : MediaControlMuteButtonElement(document, MediaMuteButton)
539     , m_controls(controls)
540 {
541 }
542
543 PassRefPtr<MediaControlPanelMuteButtonElement> MediaControlPanelMuteButtonElement::create(Document* document, MediaControls* controls)
544 {
545     ASSERT(controls);
546
547     RefPtr<MediaControlPanelMuteButtonElement> button = adoptRef(new MediaControlPanelMuteButtonElement(document, controls));
548     button->createShadowSubtree();
549     button->setType("button");
550     return button.release();
551 }
552
553 void MediaControlPanelMuteButtonElement::defaultEventHandler(Event* event)
554 {
555     if (event->type() == eventNames().mouseoverEvent)
556         m_controls->showVolumeSlider();
557
558     MediaControlMuteButtonElement::defaultEventHandler(event);
559 }
560
561 const AtomicString& MediaControlPanelMuteButtonElement::shadowPseudoId() const
562 {
563     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-mute-button"));
564     return id;
565 }
566
567 // ----------------------------
568
569 inline MediaControlVolumeSliderMuteButtonElement::MediaControlVolumeSliderMuteButtonElement(Document* document)
570     : MediaControlMuteButtonElement(document, MediaMuteButton)
571 {
572 }
573
574 PassRefPtr<MediaControlVolumeSliderMuteButtonElement> MediaControlVolumeSliderMuteButtonElement::create(Document* document)
575 {
576     RefPtr<MediaControlVolumeSliderMuteButtonElement> button = adoptRef(new MediaControlVolumeSliderMuteButtonElement(document));
577     button->createShadowSubtree();
578     button->setType("button");
579     return button.release();
580 }
581
582 const AtomicString& MediaControlVolumeSliderMuteButtonElement::shadowPseudoId() const
583 {
584     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-volume-slider-mute-button"));
585     return id;
586 }
587
588 // ----------------------------
589
590 inline MediaControlPlayButtonElement::MediaControlPlayButtonElement(Document* document)
591     : MediaControlInputElement(document, MediaPlayButton)
592 {
593 }
594
595 PassRefPtr<MediaControlPlayButtonElement> MediaControlPlayButtonElement::create(Document* document)
596 {
597     RefPtr<MediaControlPlayButtonElement> button = adoptRef(new MediaControlPlayButtonElement(document));
598     button->createShadowSubtree();
599     button->setType("button");
600     return button.release();
601 }
602
603 void MediaControlPlayButtonElement::defaultEventHandler(Event* event)
604 {
605     if (event->type() == eventNames().clickEvent) {
606         if (mediaController()->canPlay())
607             mediaController()->play();
608         else
609             mediaController()->pause();
610         updateDisplayType();
611         event->setDefaultHandled();
612     }
613     HTMLInputElement::defaultEventHandler(event);
614 }
615
616 void MediaControlPlayButtonElement::updateDisplayType()
617 {
618     setDisplayType(mediaController()->canPlay() ? MediaPlayButton : MediaPauseButton);
619 }
620
621 const AtomicString& MediaControlPlayButtonElement::shadowPseudoId() const
622 {
623     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-play-button"));
624     return id;
625 }
626
627 // ----------------------------
628
629 inline MediaControlOverlayPlayButtonElement::MediaControlOverlayPlayButtonElement(Document* document)
630     : MediaControlInputElement(document, MediaOverlayPlayButton)
631 {
632 }
633
634 PassRefPtr<MediaControlOverlayPlayButtonElement> MediaControlOverlayPlayButtonElement::create(Document* document)
635 {
636     RefPtr<MediaControlOverlayPlayButtonElement> button = adoptRef(new MediaControlOverlayPlayButtonElement(document));
637     button->createShadowSubtree();
638     button->setType("button");
639     return button.release();
640 }
641
642 void MediaControlOverlayPlayButtonElement::defaultEventHandler(Event* event)
643 {
644     if (event->type() == eventNames().clickEvent && mediaController()->canPlay()) {
645         mediaController()->play();
646         updateDisplayType();
647         event->setDefaultHandled();
648     }
649     HTMLInputElement::defaultEventHandler(event);
650 }
651
652 void MediaControlOverlayPlayButtonElement::updateDisplayType()
653 {
654     if (mediaController()->canPlay()) {
655         show();
656         setDisplayType(MediaOverlayPlayButton);
657     } else
658         hide();
659 }
660
661 const AtomicString& MediaControlOverlayPlayButtonElement::shadowPseudoId() const
662 {
663     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-overlay-play-button"));
664     return id;
665 }
666
667 // ----------------------------
668
669 inline MediaControlSeekButtonElement::MediaControlSeekButtonElement(Document* document, MediaControlElementType displayType)
670     : MediaControlInputElement(document, displayType)
671     , m_actionOnStop(Nothing)
672     , m_seekType(Skip)
673     , m_seekTimer(this, &MediaControlSeekButtonElement::seekTimerFired)
674 {
675 }
676
677 void MediaControlSeekButtonElement::defaultEventHandler(Event* event)
678 {
679 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
680     if (event->type() == eventNames().clickEvent) {
681         ExceptionCode ignoredCode;
682         float seekTime = isForwardButton() ? cSeekForwardTime : cSeekBackTime;
683         mediaController()->setCurrentTime(mediaController()->currentTime() + seekTime, ignoredCode);
684         event->setDefaultHandled();
685     }
686 #else
687     // Set the mousedown and mouseup events as defaultHandled so they
688     // do not trigger drag start or end actions in MediaControlPanelElement.
689     if (event->type() == eventNames().mousedownEvent || event->type() == eventNames().mouseupEvent)
690         event->setDefaultHandled();
691 #endif
692 }
693
694 void MediaControlSeekButtonElement::setActive(bool flag, bool pause)
695 {
696     if (flag == active())
697         return;
698
699     if (flag)
700         startTimer();
701     else
702         stopTimer();
703
704     MediaControlInputElement::setActive(flag, pause);
705 }
706
707 void MediaControlSeekButtonElement::startTimer()
708 {
709     m_seekType = mediaController()->supportsScanning() ? Scan : Skip;
710
711     if (m_seekType == Skip) {
712         // Seeking by skipping requires the video to be paused during seeking.
713         m_actionOnStop = mediaController()->paused() ? Nothing : Play;
714         mediaController()->pause();
715     } else {
716         // Seeking by scanning requires the video to be playing during seeking.
717         m_actionOnStop = mediaController()->paused() ? Pause : Nothing;
718         mediaController()->play();
719         mediaController()->setPlaybackRate(nextRate());
720     }
721
722     m_seekTimer.start(0, m_seekType == Skip ? cSkipRepeatDelay : cScanRepeatDelay);
723 }
724
725 void MediaControlSeekButtonElement::stopTimer()
726 {
727     if (m_seekType == Scan)
728         mediaController()->setPlaybackRate(mediaController()->defaultPlaybackRate());
729
730     if (m_actionOnStop == Play)
731         mediaController()->play();
732     else if (m_actionOnStop == Pause)
733         mediaController()->pause();
734
735     if (m_seekTimer.isActive())
736         m_seekTimer.stop();
737 }
738
739 float MediaControlSeekButtonElement::nextRate() const
740 {
741     float rate = std::min(cScanMaximumRate, fabsf(mediaController()->playbackRate() * 2));
742     if (!isForwardButton())
743         rate *= -1;
744     return rate;
745 }
746
747 void MediaControlSeekButtonElement::seekTimerFired(Timer<MediaControlSeekButtonElement>*)
748 {
749     if (m_seekType == Skip) {
750         ExceptionCode ec;
751         float skipTime = isForwardButton() ? cSkipTime : -cSkipTime;
752         mediaController()->setCurrentTime(mediaController()->currentTime() + skipTime, ec);
753     } else
754         mediaController()->setPlaybackRate(nextRate());
755 }
756
757 // ----------------------------
758
759 inline MediaControlSeekForwardButtonElement::MediaControlSeekForwardButtonElement(Document* document)
760     : MediaControlSeekButtonElement(document, MediaSeekForwardButton)
761 {
762 }
763
764 PassRefPtr<MediaControlSeekForwardButtonElement> MediaControlSeekForwardButtonElement::create(Document* document)
765 {
766     RefPtr<MediaControlSeekForwardButtonElement> button = adoptRef(new MediaControlSeekForwardButtonElement(document));
767     button->createShadowSubtree();
768     button->setType("button");
769     return button.release();
770 }
771
772 const AtomicString& MediaControlSeekForwardButtonElement::shadowPseudoId() const
773 {
774     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-seek-forward-button"));
775     return id;
776 }
777
778 // ----------------------------
779
780 inline MediaControlSeekBackButtonElement::MediaControlSeekBackButtonElement(Document* document)
781     : MediaControlSeekButtonElement(document, MediaSeekBackButton)
782 {
783 }
784
785 PassRefPtr<MediaControlSeekBackButtonElement> MediaControlSeekBackButtonElement::create(Document* document)
786 {
787     RefPtr<MediaControlSeekBackButtonElement> button = adoptRef(new MediaControlSeekBackButtonElement(document));
788     button->createShadowSubtree();
789     button->setType("button");
790     return button.release();
791 }
792
793 const AtomicString& MediaControlSeekBackButtonElement::shadowPseudoId() const
794 {
795     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-seek-back-button"));
796     return id;
797 }
798
799 // ----------------------------
800
801 inline MediaControlRewindButtonElement::MediaControlRewindButtonElement(Document* document)
802     : MediaControlInputElement(document, MediaRewindButton)
803 {
804 }
805
806 PassRefPtr<MediaControlRewindButtonElement> MediaControlRewindButtonElement::create(Document* document)
807 {
808     RefPtr<MediaControlRewindButtonElement> button = adoptRef(new MediaControlRewindButtonElement(document));
809     button->createShadowSubtree();
810     button->setType("button");
811     return button.release();
812 }
813
814 void MediaControlRewindButtonElement::defaultEventHandler(Event* event)
815 {
816     if (event->type() == eventNames().clickEvent) {
817         ExceptionCode ignoredCode;
818         mediaController()->setCurrentTime(max(0.0f, mediaController()->currentTime() - 30), ignoredCode);
819         event->setDefaultHandled();
820     }
821     HTMLInputElement::defaultEventHandler(event);
822 }
823
824 const AtomicString& MediaControlRewindButtonElement::shadowPseudoId() const
825 {
826     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-rewind-button"));
827     return id;
828 }
829
830 // ----------------------------
831
832 inline MediaControlReturnToRealtimeButtonElement::MediaControlReturnToRealtimeButtonElement(Document* document)
833     : MediaControlInputElement(document, MediaReturnToRealtimeButton)
834 {
835 }
836
837 PassRefPtr<MediaControlReturnToRealtimeButtonElement> MediaControlReturnToRealtimeButtonElement::create(Document* document)
838 {
839     RefPtr<MediaControlReturnToRealtimeButtonElement> button = adoptRef(new MediaControlReturnToRealtimeButtonElement(document));
840     button->createShadowSubtree();
841     button->setType("button");
842     button->hide();
843     return button.release();
844 }
845
846 void MediaControlReturnToRealtimeButtonElement::defaultEventHandler(Event* event)
847 {
848     if (event->type() == eventNames().clickEvent) {
849         mediaController()->returnToRealtime();
850         event->setDefaultHandled();
851     }
852     HTMLInputElement::defaultEventHandler(event);
853 }
854
855 const AtomicString& MediaControlReturnToRealtimeButtonElement::shadowPseudoId() const
856 {
857     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-return-to-realtime-button"));
858     return id;
859 }
860
861 // ----------------------------
862
863 inline MediaControlToggleClosedCaptionsButtonElement::MediaControlToggleClosedCaptionsButtonElement(Document* document)
864     : MediaControlInputElement(document, MediaShowClosedCaptionsButton)
865 {
866 }
867
868 PassRefPtr<MediaControlToggleClosedCaptionsButtonElement> MediaControlToggleClosedCaptionsButtonElement::create(Document* document)
869 {
870     RefPtr<MediaControlToggleClosedCaptionsButtonElement> button = adoptRef(new MediaControlToggleClosedCaptionsButtonElement(document));
871     button->createShadowSubtree();
872     button->setType("button");
873     button->hide();
874     return button.release();
875 }
876
877 void MediaControlToggleClosedCaptionsButtonElement::defaultEventHandler(Event* event)
878 {
879     if (event->type() == eventNames().clickEvent) {
880         mediaController()->setClosedCaptionsVisible(!mediaController()->closedCaptionsVisible());
881         setChecked(mediaController()->closedCaptionsVisible());
882         updateDisplayType();
883         event->setDefaultHandled();
884     }
885
886     HTMLInputElement::defaultEventHandler(event);
887 }
888
889 void MediaControlToggleClosedCaptionsButtonElement::updateDisplayType()
890 {
891     setDisplayType(mediaController()->closedCaptionsVisible() ? MediaHideClosedCaptionsButton : MediaShowClosedCaptionsButton);
892 }
893
894 const AtomicString& MediaControlToggleClosedCaptionsButtonElement::shadowPseudoId() const
895 {
896     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-toggle-closed-captions-button"));
897     return id;
898 }
899
900 // ----------------------------
901
902 MediaControlTimelineElement::MediaControlTimelineElement(Document* document, MediaControls* controls)
903     : MediaControlInputElement(document, MediaSlider)
904     , m_controls(controls)
905 {
906 }
907
908 PassRefPtr<MediaControlTimelineElement> MediaControlTimelineElement::create(Document* document, MediaControls* controls)
909 {
910     ASSERT(controls);
911
912     RefPtr<MediaControlTimelineElement> timeline = adoptRef(new MediaControlTimelineElement(document, controls));
913     timeline->createShadowSubtree();
914     timeline->setType("range");
915     timeline->setAttribute(precisionAttr, "float");
916     return timeline.release();
917 }
918
919 void MediaControlTimelineElement::defaultEventHandler(Event* event)
920 {
921     // Left button is 0. Rejects mouse events not from left button.
922     if (event->isMouseEvent() && static_cast<MouseEvent*>(event)->button())
923         return;
924
925     if (!attached())
926         return;
927
928     if (event->type() == eventNames().mousedownEvent)
929         mediaController()->beginScrubbing();
930
931     if (event->type() == eventNames().mouseupEvent)
932         mediaController()->endScrubbing();
933
934     MediaControlInputElement::defaultEventHandler(event);
935
936     if (event->type() == eventNames().mouseoverEvent || event->type() == eventNames().mouseoutEvent || event->type() == eventNames().mousemoveEvent)
937         return;
938
939     float time = narrowPrecisionToFloat(value().toDouble());
940     if (event->type() == eventNames().inputEvent && time != mediaController()->currentTime()) {
941         ExceptionCode ec;
942         mediaController()->setCurrentTime(time, ec);
943     }
944
945     RenderSlider* slider = toRenderSlider(renderer());
946     if (slider && slider->inDragMode())
947         m_controls->updateTimeDisplay();
948 }
949
950 bool MediaControlTimelineElement::willRespondToMouseClickEvents()
951 {
952     if (!attached())
953         return false;
954
955     return true;
956 }
957
958 void MediaControlTimelineElement::setPosition(float currentTime)
959 {
960     setValue(String::number(currentTime));
961 }
962
963 void MediaControlTimelineElement::setDuration(float duration)
964 {
965     setAttribute(maxAttr, String::number(isfinite(duration) ? duration : 0));
966 }
967
968
969 const AtomicString& MediaControlTimelineElement::shadowPseudoId() const
970 {
971     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-timeline"));
972     return id;
973 }
974
975 // ----------------------------
976
977 inline MediaControlVolumeSliderElement::MediaControlVolumeSliderElement(Document* document)
978     : MediaControlInputElement(document, MediaVolumeSlider)
979     , m_clearMutedOnUserInteraction(false)
980 {
981 }
982
983 PassRefPtr<MediaControlVolumeSliderElement> MediaControlVolumeSliderElement::create(Document* document)
984 {
985     RefPtr<MediaControlVolumeSliderElement> slider = adoptRef(new MediaControlVolumeSliderElement(document));
986     slider->createShadowSubtree();
987     slider->setType("range");
988     slider->setAttribute(precisionAttr, "float");
989     slider->setAttribute(maxAttr, "1");
990     return slider.release();
991 }
992
993 void MediaControlVolumeSliderElement::defaultEventHandler(Event* event)
994 {
995     // Left button is 0. Rejects mouse events not from left button.
996     if (event->isMouseEvent() && static_cast<MouseEvent*>(event)->button())
997         return;
998
999     if (!attached())
1000         return;
1001
1002     MediaControlInputElement::defaultEventHandler(event);
1003
1004     if (event->type() == eventNames().mouseoverEvent || event->type() == eventNames().mouseoutEvent || event->type() == eventNames().mousemoveEvent)
1005         return;
1006
1007     float volume = narrowPrecisionToFloat(value().toDouble());
1008     if (volume != mediaController()->volume()) {
1009         ExceptionCode ec = 0;
1010         mediaController()->setVolume(volume, ec);
1011         ASSERT(!ec);
1012     }
1013     if (m_clearMutedOnUserInteraction)
1014         mediaController()->setMuted(false);
1015 }
1016
1017 bool MediaControlVolumeSliderElement::willRespondToMouseMoveEvents()
1018 {
1019     if (!attached())
1020         return false;
1021
1022     return MediaControlInputElement::willRespondToMouseMoveEvents();
1023 }
1024
1025 bool MediaControlVolumeSliderElement::willRespondToMouseClickEvents()
1026 {
1027     if (!attached())
1028         return false;
1029
1030     return MediaControlInputElement::willRespondToMouseClickEvents();
1031 }
1032
1033 void MediaControlVolumeSliderElement::setVolume(float volume)
1034 {
1035     if (value().toFloat() != volume)
1036         setValue(String::number(volume));
1037 }
1038
1039 void MediaControlVolumeSliderElement::setClearMutedOnUserInteraction(bool clearMute)
1040 {
1041     m_clearMutedOnUserInteraction = clearMute;
1042 }
1043
1044 const AtomicString& MediaControlVolumeSliderElement::shadowPseudoId() const
1045 {
1046     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-volume-slider"));
1047     return id;
1048 }
1049
1050 // ----------------------------
1051
1052 inline MediaControlFullscreenVolumeSliderElement::MediaControlFullscreenVolumeSliderElement(Document* document)
1053     : MediaControlVolumeSliderElement(document)
1054 {
1055 }
1056
1057 PassRefPtr<MediaControlFullscreenVolumeSliderElement> MediaControlFullscreenVolumeSliderElement::create(Document* document)
1058 {
1059     RefPtr<MediaControlFullscreenVolumeSliderElement> slider = adoptRef(new MediaControlFullscreenVolumeSliderElement(document));
1060     slider->createShadowSubtree();
1061     slider->setType("range");
1062     slider->setAttribute(precisionAttr, "float");
1063     slider->setAttribute(maxAttr, "1");
1064     return slider.release();
1065 }
1066
1067 const AtomicString& MediaControlFullscreenVolumeSliderElement::shadowPseudoId() const
1068 {
1069     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-volume-slider"));
1070     return id;
1071 }
1072
1073 // ----------------------------
1074
1075 inline MediaControlFullscreenButtonElement::MediaControlFullscreenButtonElement(Document* document, MediaControls*)
1076     : MediaControlInputElement(document, MediaEnterFullscreenButton)
1077 {
1078 }
1079
1080 PassRefPtr<MediaControlFullscreenButtonElement> MediaControlFullscreenButtonElement::create(Document* document, MediaControls* controls)
1081 {
1082     ASSERT(controls);
1083
1084     RefPtr<MediaControlFullscreenButtonElement> button = adoptRef(new MediaControlFullscreenButtonElement(document, controls));
1085     button->createShadowSubtree();
1086     button->setType("button");
1087     button->hide();
1088     return button.release();
1089 }
1090
1091 void MediaControlFullscreenButtonElement::defaultEventHandler(Event* event)
1092 {
1093     if (event->type() == eventNames().clickEvent) {
1094 #if ENABLE(FULLSCREEN_API)
1095         // Only use the new full screen API if the fullScreenEnabled setting has
1096         // been explicitly enabled. Otherwise, use the old fullscreen API. This
1097         // allows apps which embed a WebView to retain the existing full screen
1098         // video implementation without requiring them to implement their own full
1099         // screen behavior.
1100         if (document()->settings() && document()->settings()->fullScreenEnabled()) {
1101             if (document()->webkitIsFullScreen() && document()->webkitCurrentFullScreenElement() == toParentMediaElement(this))
1102                 document()->webkitCancelFullScreen();
1103             else {
1104 #if ENABLE(TIZEN_FULLSCREEN_API)
1105                 document()->requestFullScreenForElement(toParentMediaElement(this), Element::HARDWARE_BACKKEY_EXIT, Document::ExemptIFrameAllowFullScreenRequirement);
1106 #else
1107                 document()->requestFullScreenForElement(toParentMediaElement(this), 0, Document::ExemptIFrameAllowFullScreenRequirement);
1108 #endif
1109             }
1110         } else
1111 #endif
1112             mediaController()->enterFullscreen();
1113         event->setDefaultHandled();
1114     }
1115     HTMLInputElement::defaultEventHandler(event);
1116 }
1117
1118 const AtomicString& MediaControlFullscreenButtonElement::shadowPseudoId() const
1119 {
1120     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-button"));
1121     return id;
1122 }
1123
1124 void MediaControlFullscreenButtonElement::setIsFullscreen(bool isFullscreen)
1125 {
1126     setDisplayType(isFullscreen ? MediaExitFullscreenButton : MediaEnterFullscreenButton);
1127 }
1128
1129 // ----------------------------
1130
1131 inline MediaControlFullscreenVolumeMinButtonElement::MediaControlFullscreenVolumeMinButtonElement(Document* document)
1132     : MediaControlInputElement(document, MediaUnMuteButton)
1133 {
1134 }
1135
1136 PassRefPtr<MediaControlFullscreenVolumeMinButtonElement> MediaControlFullscreenVolumeMinButtonElement::create(Document* document)
1137 {
1138     RefPtr<MediaControlFullscreenVolumeMinButtonElement> button = adoptRef(new MediaControlFullscreenVolumeMinButtonElement(document));
1139     button->createShadowSubtree();
1140     button->setType("button");
1141     return button.release();
1142 }
1143
1144 void MediaControlFullscreenVolumeMinButtonElement::defaultEventHandler(Event* event)
1145 {
1146     if (event->type() == eventNames().clickEvent) {
1147         ExceptionCode code = 0;
1148         mediaController()->setVolume(0, code);
1149         event->setDefaultHandled();
1150     }
1151     HTMLInputElement::defaultEventHandler(event);
1152 }
1153
1154 const AtomicString& MediaControlFullscreenVolumeMinButtonElement::shadowPseudoId() const
1155 {
1156     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-volume-min-button"));
1157     return id;
1158 }
1159
1160 // ----------------------------
1161
1162 inline MediaControlFullscreenVolumeMaxButtonElement::MediaControlFullscreenVolumeMaxButtonElement(Document* document)
1163 : MediaControlInputElement(document, MediaMuteButton)
1164 {
1165 }
1166
1167 PassRefPtr<MediaControlFullscreenVolumeMaxButtonElement> MediaControlFullscreenVolumeMaxButtonElement::create(Document* document)
1168 {
1169     RefPtr<MediaControlFullscreenVolumeMaxButtonElement> button = adoptRef(new MediaControlFullscreenVolumeMaxButtonElement(document));
1170     button->createShadowSubtree();
1171     button->setType("button");
1172     return button.release();
1173 }
1174
1175 void MediaControlFullscreenVolumeMaxButtonElement::defaultEventHandler(Event* event)
1176 {
1177     if (event->type() == eventNames().clickEvent) {
1178         ExceptionCode code = 0;
1179         mediaController()->setVolume(1, code);
1180         event->setDefaultHandled();
1181     }
1182     HTMLInputElement::defaultEventHandler(event);
1183 }
1184
1185 const AtomicString& MediaControlFullscreenVolumeMaxButtonElement::shadowPseudoId() const
1186 {
1187     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-volume-max-button"));
1188     return id;
1189 }
1190
1191 // ----------------------------
1192
1193 class RenderMediaControlTimeDisplay : public RenderDeprecatedFlexibleBox {
1194 public:
1195     RenderMediaControlTimeDisplay(Node*);
1196
1197 private:
1198     virtual void layout();
1199 };
1200
1201 RenderMediaControlTimeDisplay::RenderMediaControlTimeDisplay(Node* node)
1202     : RenderDeprecatedFlexibleBox(node)
1203 {
1204 }
1205
1206 // We want the timeline slider to be at least 100 pixels wide.
1207 // FIXME: Eliminate hard-coded widths altogether.
1208 static const int minWidthToDisplayTimeDisplays = 45 + 100 + 45;
1209
1210 void RenderMediaControlTimeDisplay::layout()
1211 {
1212     RenderDeprecatedFlexibleBox::layout();
1213     RenderBox* timelineContainerBox = parentBox();
1214     while (timelineContainerBox && timelineContainerBox->isAnonymous())
1215         timelineContainerBox = timelineContainerBox->parentBox();
1216
1217     if (timelineContainerBox && timelineContainerBox->width() < minWidthToDisplayTimeDisplays)
1218         setWidth(0);
1219 }
1220
1221 inline MediaControlTimeDisplayElement::MediaControlTimeDisplayElement(Document* document)
1222     : MediaControlElement(document)
1223     , m_currentValue(0)
1224 {
1225 }
1226
1227 void MediaControlTimeDisplayElement::setCurrentValue(float time)
1228 {
1229     m_currentValue = time;
1230 }
1231
1232 RenderObject* MediaControlTimeDisplayElement::createRenderer(RenderArena* arena, RenderStyle*)
1233 {
1234     return new (arena) RenderMediaControlTimeDisplay(this);
1235 }
1236
1237 // ----------------------------
1238
1239 PassRefPtr<MediaControlTimeRemainingDisplayElement> MediaControlTimeRemainingDisplayElement::create(Document* document)
1240 {
1241     return adoptRef(new MediaControlTimeRemainingDisplayElement(document));
1242 }
1243
1244 MediaControlTimeRemainingDisplayElement::MediaControlTimeRemainingDisplayElement(Document* document)
1245     : MediaControlTimeDisplayElement(document)
1246 {
1247 }
1248
1249 MediaControlElementType MediaControlTimeRemainingDisplayElement::displayType() const
1250 {
1251     return MediaTimeRemainingDisplay;
1252 }
1253
1254 const AtomicString& MediaControlTimeRemainingDisplayElement::shadowPseudoId() const
1255 {
1256     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-time-remaining-display"));
1257     return id;
1258 }
1259
1260 // ----------------------------
1261
1262 PassRefPtr<MediaControlCurrentTimeDisplayElement> MediaControlCurrentTimeDisplayElement::create(Document* document)
1263 {
1264     return adoptRef(new MediaControlCurrentTimeDisplayElement(document));
1265 }
1266
1267 MediaControlCurrentTimeDisplayElement::MediaControlCurrentTimeDisplayElement(Document* document)
1268     : MediaControlTimeDisplayElement(document)
1269 {
1270 }
1271
1272 MediaControlElementType MediaControlCurrentTimeDisplayElement::displayType() const
1273 {
1274     return MediaCurrentTimeDisplay;
1275 }
1276
1277 const AtomicString& MediaControlCurrentTimeDisplayElement::shadowPseudoId() const
1278 {
1279     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-current-time-display"));
1280     return id;
1281 }
1282
1283 // ----------------------------
1284
1285 #if ENABLE(VIDEO_TRACK)
1286
1287 class RenderTextTrackContainerElement : public RenderBlock {
1288 public:
1289     RenderTextTrackContainerElement(Node*);
1290
1291 private:
1292     virtual void layout();
1293 };
1294
1295 RenderTextTrackContainerElement::RenderTextTrackContainerElement(Node* node)
1296     : RenderBlock(node)
1297 {
1298 }
1299
1300 void RenderTextTrackContainerElement::layout()
1301 {
1302     RenderBlock::layout();
1303     if (style()->display() == NONE)
1304         return;
1305
1306     ASSERT(mediaControlElementType(node()) == MediaTextTrackDisplayContainer);
1307
1308     LayoutStateDisabler layoutStateDisabler(view());
1309     static_cast<MediaControlTextTrackContainerElement*>(node())->updateSizes();
1310 }
1311
1312 inline MediaControlTextTrackContainerElement::MediaControlTextTrackContainerElement(Document* document)
1313     : MediaControlElement(document)
1314     , m_fontSize(0)
1315 {
1316 }
1317
1318 PassRefPtr<MediaControlTextTrackContainerElement> MediaControlTextTrackContainerElement::create(Document* document)
1319 {
1320     RefPtr<MediaControlTextTrackContainerElement> element = adoptRef(new MediaControlTextTrackContainerElement(document));
1321     element->hide();
1322     return element.release();
1323 }
1324
1325 RenderObject* MediaControlTextTrackContainerElement::createRenderer(RenderArena* arena, RenderStyle*)
1326 {
1327     return new (arena) RenderTextTrackContainerElement(this);
1328 }
1329
1330 const AtomicString& MediaControlTextTrackContainerElement::shadowPseudoId() const
1331 {
1332     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-text-track-container"));
1333     return id;
1334 }
1335
1336 void MediaControlTextTrackContainerElement::updateDisplay()
1337 {
1338     HTMLMediaElement* mediaElement = toParentMediaElement(this);
1339
1340     // 1. If the media element is an audio element, or is another playback
1341     // mechanism with no rendering area, abort these steps. There is nothing to
1342     // render.
1343     if (!mediaElement->isVideo())
1344         return;
1345
1346     // 2. Let video be the media element or other playback mechanism.
1347     HTMLVideoElement* video = static_cast<HTMLVideoElement*>(mediaElement);
1348
1349     // 3. Let output be an empty list of absolutely positioned CSS block boxes.
1350     Vector<RefPtr<HTMLDivElement> > output;
1351
1352     // 4. If the user agent is exposing a user interface for video, add to
1353     // output one or more completely transparent positioned CSS block boxes that
1354     // cover the same region as the user interface.
1355
1356     // 5. If the last time these rules were run, the user agent was not exposing
1357     // a user interface for video, but now it is, let reset be true. Otherwise,
1358     // let reset be false.
1359
1360     // There is nothing to be done explicitly for 4th and 5th steps, as
1361     // everything is handled through CSS. The caption box is on top of the
1362     // controls box, in a container set with the -webkit-box display property.
1363
1364     // 6. Let tracks be the subset of video's list of text tracks that have as
1365     // their rules for updating the text track rendering these rules for
1366     // updating the display of WebVTT text tracks, and whose text track mode is
1367     // showing or showing by default.
1368     // 7. Let cues be an empty list of text track cues.
1369     // 8. For each track track in tracks, append to cues all the cues from
1370     // track's list of cues that have their text track cue active flag set.
1371     CueList activeCues = video->currentlyActiveCues();
1372
1373     // 9. If reset is false, then, for each text track cue cue in cues: if cue's
1374     // text track cue display state has a set of CSS boxes, then add those boxes
1375     // to output, and remove cue from cues.
1376
1377     // There is nothing explicitly to be done here, as all the caching occurs
1378     // within the TextTrackCue instance itself. If parameters of the cue change,
1379     // the display tree is cleared.
1380
1381     // 10. For each text track cue cue in cues that has not yet had
1382     // corresponding CSS boxes added to output, in text track cue order, run the
1383     // following substeps:
1384     for (size_t i = 0; i < activeCues.size(); ++i) {
1385         TextTrackCue* cue = activeCues[i].data();
1386
1387         ASSERT(cue->isActive());
1388         if (!cue->track() || !cue->track()->isRendered())
1389             continue;
1390
1391         RefPtr<TextTrackCueBox> displayBox = cue->getDisplayTree();
1392
1393         if (displayBox->hasChildNodes() && !contains(static_cast<Node*>(displayBox.get())))
1394             // Note: the display tree of a cue is removed when the active flag of the cue is unset.
1395             appendChild(displayBox, ASSERT_NO_EXCEPTION, false);
1396     }
1397
1398     // 11. Return output.
1399     hasChildNodes() ? show() : hide();
1400 }
1401
1402 static const float mimimumFontSize = 16;
1403 static const float videoHeightFontSizePercentage = .05;
1404 static const float trackBottomMultiplier = .9;
1405
1406 void MediaControlTextTrackContainerElement::updateSizes()
1407 {
1408     HTMLMediaElement* mediaElement = toParentMediaElement(this);
1409     if (!mediaElement || !mediaElement->renderer() || !mediaElement->renderer()->isVideo())
1410         return;
1411
1412     IntRect videoBox = toRenderVideo(mediaElement->renderer())->videoBox();
1413     if (m_videoDisplaySize == videoBox)
1414         return;
1415     m_videoDisplaySize = videoBox;
1416
1417     float fontSize = m_videoDisplaySize.size().height() * videoHeightFontSizePercentage;
1418     if (fontSize != m_fontSize) {
1419         m_fontSize = fontSize;
1420         setInlineStyleProperty(CSSPropertyFontSize, String::number(fontSize) + "px");
1421     }
1422 }
1423
1424 #endif // ENABLE(VIDEO_TRACK)
1425
1426 // ----------------------------
1427
1428 } // namespace WebCore
1429
1430 #endif // ENABLE(VIDEO)