2011-01-12 Dimitri Glazkov <dglazkov@chromium.org>
[WebKit.git] / Source / WebCore / rendering / MediaControlElements.cpp
1 /*
2  * Copyright (C) 2008, 2009, 2010 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 "EventNames.h"
36 #include "FloatConversion.h"
37 #include "Frame.h"
38 #include "HTMLNames.h"
39 #include "LocalizedStrings.h"
40 #include "MouseEvent.h"
41 #include "Page.h"
42 #include "RenderMedia.h"
43 #include "RenderSlider.h"
44 #include "RenderTheme.h"
45 #include "Settings.h"
46
47 namespace WebCore {
48
49 using namespace HTMLNames;
50
51 HTMLMediaElement* toParentMediaElement(RenderObject* o)
52 {
53     Node* node = o->node();
54     Node* mediaNode = node ? node->shadowAncestorNode() : 0;
55     if (!mediaNode || (!mediaNode->hasTagName(HTMLNames::videoTag) && !mediaNode->hasTagName(HTMLNames::audioTag)))
56         return 0;
57
58     return static_cast<HTMLMediaElement*>(mediaNode);
59 }
60
61 // FIXME: These constants may need to be tweaked to better match the seeking in the QuickTime plug-in.
62 static const float cSeekRepeatDelay = 0.1f;
63 static const float cStepTime = 0.07f;
64 static const float cSeekTime = 0.2f;
65
66 inline MediaControlShadowRootElement::MediaControlShadowRootElement(HTMLMediaElement* mediaElement)
67     : HTMLDivElement(divTag, mediaElement->document())
68 {
69     setShadowHost(mediaElement);
70 }
71
72 PassRefPtr<MediaControlShadowRootElement> MediaControlShadowRootElement::create(HTMLMediaElement* mediaElement)
73 {
74     RefPtr<MediaControlShadowRootElement> element = adoptRef(new MediaControlShadowRootElement(mediaElement));
75
76     RefPtr<RenderStyle> rootStyle = RenderStyle::create();
77     rootStyle->inheritFrom(mediaElement->renderer()->style());
78     rootStyle->setDisplay(BLOCK);
79     rootStyle->setPosition(RelativePosition);
80
81     RenderMediaControlShadowRoot* renderer = new (mediaElement->renderer()->renderArena()) RenderMediaControlShadowRoot(element.get());
82     renderer->setStyle(rootStyle.release());
83
84     element->setRenderer(renderer);
85     element->setAttached();
86     element->setInDocument();
87
88     return element.release();
89 }
90
91 void MediaControlShadowRootElement::updateStyle()
92 {
93     if (renderer()) {
94         RenderStyle* timelineContainerStyle = shadowHost()->renderer()->getCachedPseudoStyle(MEDIA_CONTROLS_TIMELINE_CONTAINER);
95         renderer()->setStyle(timelineContainerStyle);
96     }
97 }
98
99 void MediaControlShadowRootElement::detach()
100 {
101     HTMLDivElement::detach();
102     // FIXME: Remove once shadow DOM uses Element::setShadowRoot().
103     setShadowHost(0);
104 }
105
106 // ----------------------------
107
108 MediaControlElement::MediaControlElement(HTMLMediaElement* mediaElement, PseudoId pseudo)
109     : HTMLDivElement(divTag, mediaElement->document())
110     , m_mediaElement(mediaElement)
111     , m_pseudoStyleId(pseudo)
112 {
113     setInDocument();
114     switch (pseudo) {
115     case MEDIA_CONTROLS_CURRENT_TIME_DISPLAY:
116         m_displayType = MediaCurrentTimeDisplay;
117         break;
118     case MEDIA_CONTROLS_TIME_REMAINING_DISPLAY:
119         m_displayType = MediaTimeRemainingDisplay;
120         break;
121     case MEDIA_CONTROLS_TIMELINE_CONTAINER:
122         m_displayType = MediaTimelineContainer;
123         break;
124     case MEDIA_CONTROLS_STATUS_DISPLAY:
125         m_displayType = MediaStatusDisplay;
126         break;
127     case MEDIA_CONTROLS_PANEL:
128         m_displayType = MediaControlsPanel;
129         break;
130     case MEDIA_CONTROLS_VOLUME_SLIDER_CONTAINER:
131         m_displayType = MediaVolumeSliderContainer;
132         break;
133     default:
134         ASSERT_NOT_REACHED();
135         break;
136     }
137 }
138
139 PassRefPtr<MediaControlElement> MediaControlElement::create(HTMLMediaElement* mediaElement, PseudoId pseudoStyleId)
140 {
141     return adoptRef(new MediaControlElement(mediaElement, pseudoStyleId));
142 }
143
144 void MediaControlElement::attachToParent(Element* parent)
145 {
146     // FIXME: This code seems very wrong.  Why are we magically adding |this| to the DOM here?
147     //        We shouldn't be calling parser API methods outside of the parser!
148     parent->parserAddChild(this);
149 }
150
151 void MediaControlElement::update()
152 {
153     if (renderer())
154         renderer()->updateFromElement();
155     updateStyle();
156 }
157
158 PassRefPtr<RenderStyle> MediaControlElement::styleForElement()
159 {
160     RenderStyle* style = m_mediaElement->renderer()->getCachedPseudoStyle(m_pseudoStyleId);
161     if (!style)
162         return 0;
163     
164     // text-decoration can't be overrided from CSS. So we do it here.
165     // See https://bugs.webkit.org/show_bug.cgi?id=27015
166     style->setTextDecoration(TDNONE);
167     style->setTextDecorationsInEffect(TDNONE);
168
169     return style;
170 }
171
172 bool MediaControlElement::rendererIsNeeded(RenderStyle* style)
173 {
174     ASSERT(document()->page());
175
176     return HTMLDivElement::rendererIsNeeded(style) && parentNode() && parentNode()->renderer()
177         && (!style->hasAppearance() || document()->page()->theme()->shouldRenderMediaControlPart(style->appearance(), m_mediaElement));
178 }
179     
180 void MediaControlElement::attach()
181 {
182     RefPtr<RenderStyle> style = styleForElement();
183     if (!style)
184         return;
185     bool needsRenderer = rendererIsNeeded(style.get());
186     if (!needsRenderer)
187         return;
188     RenderObject* renderer = createRenderer(m_mediaElement->renderer()->renderArena(), style.get());
189     if (!renderer)
190         return;
191     renderer->setStyle(style.get());
192     setRenderer(renderer);
193     if (parentNode() && parentNode()->renderer()) {
194         // Find next sibling with a renderer to determine where to insert.
195         Node* sibling = nextSibling();
196         while (sibling && !sibling->renderer())
197             sibling = sibling->nextSibling();
198         parentNode()->renderer()->addChild(renderer, sibling ? sibling->renderer() : 0);
199     }
200     ContainerNode::attach();
201 }
202
203 void MediaControlElement::updateStyle()
204 {
205     if (!m_mediaElement || !m_mediaElement->renderer())
206         return;
207
208     RefPtr<RenderStyle> style = styleForElement();
209     if (!style)
210         return;
211
212     bool needsRenderer = rendererIsNeeded(style.get()) && parentNode() && parentNode()->renderer();
213     if (renderer() && !needsRenderer)
214         detach();
215     else if (!renderer() && needsRenderer)
216         attach();
217     else if (renderer()) {
218         renderer()->setStyle(style.get());
219
220         // Make sure that if there is any innerText renderer, it is updated as well.
221         if (firstChild() && firstChild()->renderer())
222             firstChild()->renderer()->setStyle(style.get());
223     }
224 }
225
226 // ----------------------------
227
228 inline MediaControlTimelineContainerElement::MediaControlTimelineContainerElement(HTMLMediaElement* mediaElement)
229     : MediaControlElement(mediaElement, MEDIA_CONTROLS_TIMELINE_CONTAINER)
230 {
231 }
232
233 PassRefPtr<MediaControlTimelineContainerElement> MediaControlTimelineContainerElement::create(HTMLMediaElement* mediaElement)
234 {
235     return adoptRef(new MediaControlTimelineContainerElement(mediaElement));
236 }
237
238 bool MediaControlTimelineContainerElement::rendererIsNeeded(RenderStyle* style)
239 {
240     if (!MediaControlElement::rendererIsNeeded(style))
241         return false;
242
243     // This is for MediaControllerThemeClassic:
244     // If there is no style for MediaControlStatusDisplayElement style, don't hide
245     // the timeline.
246     if (!mediaElement()->renderer()->getCachedPseudoStyle(MEDIA_CONTROLS_STATUS_DISPLAY))
247         return true;
248
249     float duration = mediaElement()->duration();
250     return !isnan(duration) && !isinf(duration);
251 }
252
253 // ----------------------------
254
255 inline MediaControlVolumeSliderContainerElement::MediaControlVolumeSliderContainerElement(HTMLMediaElement* mediaElement)
256     : MediaControlElement(mediaElement, MEDIA_CONTROLS_VOLUME_SLIDER_CONTAINER)
257     , m_isVisible(false)
258     , m_x(0)
259     , m_y(0)
260 {
261 }
262
263 PassRefPtr<MediaControlVolumeSliderContainerElement> MediaControlVolumeSliderContainerElement::create(HTMLMediaElement* mediaElement)
264 {
265     return adoptRef(new MediaControlVolumeSliderContainerElement(mediaElement));
266 }
267
268 PassRefPtr<RenderStyle> MediaControlVolumeSliderContainerElement::styleForElement()
269 {
270     RefPtr<RenderStyle> style = MediaControlElement::styleForElement();
271     style->setPosition(AbsolutePosition);
272     style->setLeft(Length(m_x, Fixed));
273     style->setTop(Length(m_y, Fixed));
274     style->setDisplay(m_isVisible ? BLOCK : NONE);
275     return style;
276 }
277
278 void MediaControlVolumeSliderContainerElement::setVisible(bool visible)
279 {
280     if (visible == m_isVisible)
281         return;
282     m_isVisible = visible;
283 }
284
285 void MediaControlVolumeSliderContainerElement::setPosition(int x, int y)
286 {
287     if (x == m_x && y == m_y)
288         return;
289     m_x = x;
290     m_y = y;
291 }
292
293 bool MediaControlVolumeSliderContainerElement::hitTest(const IntPoint& absPoint)
294 {
295     if (renderer() && renderer()->style()->hasAppearance())
296         return renderer()->theme()->hitTestMediaControlPart(renderer(), absPoint);
297
298     return false;
299 }
300
301 // ----------------------------
302
303 inline MediaControlStatusDisplayElement::MediaControlStatusDisplayElement(HTMLMediaElement* mediaElement)
304     : MediaControlElement(mediaElement, MEDIA_CONTROLS_STATUS_DISPLAY)
305     , m_stateBeingDisplayed(Nothing)
306 {
307 }
308
309 PassRefPtr<MediaControlStatusDisplayElement> MediaControlStatusDisplayElement::create(HTMLMediaElement* mediaElement)
310 {
311     return adoptRef(new MediaControlStatusDisplayElement(mediaElement));
312 }
313
314 void MediaControlStatusDisplayElement::update()
315 {
316     MediaControlElement::update();
317
318     // Get the new state that we'll have to display.
319     StateBeingDisplayed newStateToDisplay = Nothing;
320
321     if (mediaElement()->readyState() != HTMLMediaElement::HAVE_ENOUGH_DATA && !mediaElement()->currentSrc().isEmpty())
322         newStateToDisplay = Loading;
323     else if (mediaElement()->movieLoadType() == MediaPlayer::LiveStream)
324         newStateToDisplay = LiveBroadcast;
325
326     // Propagate only if needed.
327     if (newStateToDisplay == m_stateBeingDisplayed)
328         return;
329     m_stateBeingDisplayed = newStateToDisplay;
330
331     ExceptionCode e;
332     switch (m_stateBeingDisplayed) {
333     case Nothing:
334         setInnerText("", e);
335         break;
336     case Loading:
337         setInnerText(mediaElementLoadingStateText(), e);
338         break;
339     case LiveBroadcast:
340         setInnerText(mediaElementLiveBroadcastStateText(), e);
341         break;
342     }
343 }
344
345 bool MediaControlStatusDisplayElement::rendererIsNeeded(RenderStyle* style)
346 {
347     if (!MediaControlElement::rendererIsNeeded(style))
348         return false;
349     float duration = mediaElement()->duration();
350     return (isnan(duration) || isinf(duration));
351 }
352
353 // ----------------------------
354     
355 MediaControlInputElement::MediaControlInputElement(HTMLMediaElement* mediaElement, PseudoId pseudo)
356     : HTMLInputElement(inputTag, mediaElement->document())
357     , m_mediaElement(mediaElement)
358     , m_pseudoStyleId(pseudo)
359 {
360     setInDocument();
361
362     switch (pseudo) {
363     case MEDIA_CONTROLS_MUTE_BUTTON:
364         m_displayType = MediaMuteButton;
365         break;
366     case MEDIA_CONTROLS_PLAY_BUTTON:
367         m_displayType = MediaPlayButton;
368         break;
369     case MEDIA_CONTROLS_SEEK_FORWARD_BUTTON:
370         m_displayType = MediaSeekForwardButton;
371         break;
372     case MEDIA_CONTROLS_SEEK_BACK_BUTTON:
373         m_displayType = MediaSeekBackButton;
374         break;
375     case MEDIA_CONTROLS_FULLSCREEN_BUTTON:
376         m_displayType = MediaFullscreenButton;
377         break;
378     case MEDIA_CONTROLS_TIMELINE:
379         m_displayType = MediaSlider;
380         break;
381     case MEDIA_CONTROLS_RETURN_TO_REALTIME_BUTTON:
382         m_displayType = MediaReturnToRealtimeButton;
383         break;
384     case MEDIA_CONTROLS_REWIND_BUTTON:
385         m_displayType = MediaRewindButton;
386         break;
387     case MEDIA_CONTROLS_VOLUME_SLIDER:
388         m_displayType = MediaVolumeSlider;
389         break;
390     case MEDIA_CONTROLS_VOLUME_SLIDER_MUTE_BUTTON:
391         m_displayType = MediaVolumeSliderMuteButton;
392         break;
393     case MEDIA_CONTROLS_TOGGLE_CLOSED_CAPTIONS_BUTTON:
394         m_displayType = MediaShowClosedCaptionsButton;
395         break;
396     default:
397         ASSERT_NOT_REACHED();
398         break;
399     }
400 }
401
402 void MediaControlInputElement::attachToParent(Element* parent)
403 {
404     // FIXME: This code seems very wrong.  Why are we magically adding |this| to the DOM here?
405     //        We shouldn't be calling parser API methods outside of the parser!
406     parent->parserAddChild(this);
407 }
408
409 void MediaControlInputElement::update()
410 {
411     updateDisplayType();
412     if (renderer())
413         renderer()->updateFromElement();
414     updateStyle();
415 }
416
417 PassRefPtr<RenderStyle> MediaControlInputElement::styleForElement()
418 {
419     return mediaElement()->renderer()->getCachedPseudoStyle(m_pseudoStyleId);
420 }
421
422 bool MediaControlInputElement::rendererIsNeeded(RenderStyle* style)
423 {
424     ASSERT(document()->page());
425
426     return HTMLInputElement::rendererIsNeeded(style) && parentNode() && parentNode()->renderer()
427         && (!style->hasAppearance() || document()->page()->theme()->shouldRenderMediaControlPart(style->appearance(), mediaElement()));
428 }
429
430 void MediaControlInputElement::attach()
431 {
432     RefPtr<RenderStyle> style = styleForElement();
433     if (!style)
434         return;
435     
436     bool needsRenderer = rendererIsNeeded(style.get());
437     if (!needsRenderer)
438         return;
439     RenderObject* renderer = createRenderer(mediaElement()->renderer()->renderArena(), style.get());
440     if (!renderer)
441         return;
442     renderer->setStyle(style.get());
443     setRenderer(renderer);
444     if (parentNode() && parentNode()->renderer()) {
445         // Find next sibling with a renderer to determine where to insert.
446         Node* sibling = nextSibling();
447         while (sibling && !sibling->renderer())
448             sibling = sibling->nextSibling();
449         parentNode()->renderer()->addChild(renderer, sibling ? sibling->renderer() : 0);
450     }  
451     ContainerNode::attach();
452     // FIXME: Currently, MeidaControlInput circumvents the normal attachment
453     // and style recalc cycle and thus we need to add extra logic to be aware of
454     // the shadow DOM. Remove this once all media controls are transitioned to use the regular
455     // style calculation.
456     if (Node* shadowNode = shadowRoot())
457         shadowNode->attach();
458 }
459
460 void MediaControlInputElement::updateStyle()
461 {
462     if (!mediaElement() || !mediaElement()->renderer())
463         return;
464     
465     RefPtr<RenderStyle> style = styleForElement();
466     if (!style)
467         return;
468     
469     bool needsRenderer = rendererIsNeeded(style.get()) && parentNode() && parentNode()->renderer();
470     if (renderer() && !needsRenderer)
471         detach();
472     else if (!renderer() && needsRenderer)
473         attach();
474     else if (renderer())
475         renderer()->setStyle(style.get());
476
477     // FIXME: Currently, MeidaControlInput circumvents the normal attachment
478     // and style recalc cycle and thus we need to add extra logic to be aware of
479     // the shadow DOM. Remove this once all media controls are transitioned to use
480     // the new shadow DOM.
481     if (Node* shadowNode = shadowRoot())
482         shadowNode->recalcStyle(Node::Force);
483 }
484     
485 bool MediaControlInputElement::hitTest(const IntPoint& absPoint)
486 {
487     if (renderer() && renderer()->style()->hasAppearance())
488         return renderer()->theme()->hitTestMediaControlPart(renderer(), absPoint);
489
490     return false;
491 }
492
493 void MediaControlInputElement::setDisplayType(MediaControlElementType displayType)
494 {
495     if (displayType == m_displayType)
496         return;
497
498     m_displayType = displayType;
499     if (RenderObject* object = renderer())
500         object->repaint();
501 }
502
503 // ----------------------------
504
505 inline MediaControlMuteButtonElement::MediaControlMuteButtonElement(HTMLMediaElement* mediaElement, ButtonLocation location)
506     : MediaControlInputElement(mediaElement, location == Controller ? MEDIA_CONTROLS_MUTE_BUTTON : MEDIA_CONTROLS_VOLUME_SLIDER_MUTE_BUTTON)
507 {
508 }
509
510 PassRefPtr<MediaControlMuteButtonElement> MediaControlMuteButtonElement::create(HTMLMediaElement* mediaElement, ButtonLocation location)
511 {
512     RefPtr<MediaControlMuteButtonElement> button = adoptRef(new MediaControlMuteButtonElement(mediaElement, location));
513     button->setType("button");
514     return button.release();
515 }
516
517 void MediaControlMuteButtonElement::defaultEventHandler(Event* event)
518 {
519     if (event->type() == eventNames().clickEvent) {
520         mediaElement()->setMuted(!mediaElement()->muted());
521         event->setDefaultHandled();
522     }
523     HTMLInputElement::defaultEventHandler(event);
524 }
525
526 void MediaControlMuteButtonElement::updateDisplayType()
527 {
528     setDisplayType(mediaElement()->muted() ? MediaUnMuteButton : MediaMuteButton);
529 }
530
531 // ----------------------------
532
533 inline MediaControlPlayButtonElement::MediaControlPlayButtonElement(HTMLMediaElement* mediaElement)
534     : MediaControlInputElement(mediaElement, MEDIA_CONTROLS_PLAY_BUTTON)
535 {
536 }
537
538 PassRefPtr<MediaControlPlayButtonElement> MediaControlPlayButtonElement::create(HTMLMediaElement* mediaElement)
539 {
540     RefPtr<MediaControlPlayButtonElement> button = adoptRef(new MediaControlPlayButtonElement(mediaElement));
541     button->setType("button");
542     return button.release();
543 }
544
545 void MediaControlPlayButtonElement::defaultEventHandler(Event* event)
546 {
547     if (event->type() == eventNames().clickEvent) {
548         mediaElement()->togglePlayState();
549         event->setDefaultHandled();
550     }
551     HTMLInputElement::defaultEventHandler(event);
552 }
553
554 void MediaControlPlayButtonElement::updateDisplayType()
555 {
556     setDisplayType(mediaElement()->canPlay() ? MediaPlayButton : MediaPauseButton);
557 }
558
559 // ----------------------------
560
561 inline MediaControlSeekButtonElement::MediaControlSeekButtonElement(HTMLMediaElement* mediaElement, PseudoId pseudoId)
562     : MediaControlInputElement(mediaElement, pseudoId)
563     , m_seeking(false)
564     , m_capturing(false)
565     , m_seekTimer(this, &MediaControlSeekButtonElement::seekTimerFired)
566 {
567 }
568
569 PassRefPtr<MediaControlSeekButtonElement> MediaControlSeekButtonElement::create(HTMLMediaElement* mediaElement, PseudoId pseudoStyleId)
570 {
571     RefPtr<MediaControlSeekButtonElement> button = adoptRef(new MediaControlSeekButtonElement(mediaElement, pseudoStyleId));
572     button->setType("button");
573     return button.release();
574 }
575
576 inline bool MediaControlSeekButtonElement::isForwardButton() const
577 {
578     return pseudoStyleId() == MEDIA_CONTROLS_SEEK_FORWARD_BUTTON;
579 }
580
581 void MediaControlSeekButtonElement::defaultEventHandler(Event* event)
582 {
583     if (event->type() == eventNames().mousedownEvent) {
584         if (Frame* frame = document()->frame()) {
585             m_capturing = true;
586             frame->eventHandler()->setCapturingMouseEventsNode(this);
587         }
588         mediaElement()->pause(event->fromUserGesture());
589         m_seekTimer.startRepeating(cSeekRepeatDelay);
590         event->setDefaultHandled();
591     } else if (event->type() == eventNames().mouseupEvent) {
592         if (m_capturing)
593             if (Frame* frame = document()->frame()) {
594                 m_capturing = false;
595                 frame->eventHandler()->setCapturingMouseEventsNode(0);
596             }
597         ExceptionCode ec;
598         if (m_seeking || m_seekTimer.isActive()) {
599             if (!m_seeking) {
600                 float stepTime = isForwardButton() ? cStepTime : -cStepTime;
601                 mediaElement()->setCurrentTime(mediaElement()->currentTime() + stepTime, ec);
602             }
603             m_seekTimer.stop();
604             m_seeking = false;
605             event->setDefaultHandled();
606         }
607     }
608     HTMLInputElement::defaultEventHandler(event);
609 }
610
611 void MediaControlSeekButtonElement::seekTimerFired(Timer<MediaControlSeekButtonElement>*)
612 {
613     ExceptionCode ec;
614     m_seeking = true;
615     float seekTime = isForwardButton() ? cSeekTime : -cSeekTime;
616     mediaElement()->setCurrentTime(mediaElement()->currentTime() + seekTime, ec);
617 }
618
619 void MediaControlSeekButtonElement::detach()
620 {
621     if (m_capturing) {
622         if (Frame* frame = document()->frame())
623             frame->eventHandler()->setCapturingMouseEventsNode(0);      
624     }
625     MediaControlInputElement::detach();
626 }
627
628 // ----------------------------
629
630 inline MediaControlRewindButtonElement::MediaControlRewindButtonElement(HTMLMediaElement* element)
631     : MediaControlInputElement(element, MEDIA_CONTROLS_REWIND_BUTTON)
632 {
633 }
634
635 PassRefPtr<MediaControlRewindButtonElement> MediaControlRewindButtonElement::create(HTMLMediaElement* mediaElement)
636 {
637     RefPtr<MediaControlRewindButtonElement> button = adoptRef(new MediaControlRewindButtonElement(mediaElement));
638     button->setType("button");
639     return button.release();
640 }
641
642 void MediaControlRewindButtonElement::defaultEventHandler(Event* event)
643 {
644     if (event->type() == eventNames().clickEvent) {
645         mediaElement()->rewind(30);
646         event->setDefaultHandled();
647     }    
648     HTMLInputElement::defaultEventHandler(event);
649 }
650
651 // ----------------------------
652
653 inline MediaControlReturnToRealtimeButtonElement::MediaControlReturnToRealtimeButtonElement(HTMLMediaElement* mediaElement)
654     : MediaControlInputElement(mediaElement, MEDIA_CONTROLS_RETURN_TO_REALTIME_BUTTON)
655 {
656 }
657
658 PassRefPtr<MediaControlReturnToRealtimeButtonElement> MediaControlReturnToRealtimeButtonElement::create(HTMLMediaElement* mediaElement)
659 {
660     RefPtr<MediaControlReturnToRealtimeButtonElement> button = adoptRef(new MediaControlReturnToRealtimeButtonElement(mediaElement));
661     button->setType("button");
662     return button.release();
663 }
664
665 void MediaControlReturnToRealtimeButtonElement::defaultEventHandler(Event* event)
666 {
667     if (event->type() == eventNames().clickEvent) {
668         mediaElement()->returnToRealtime();
669         event->setDefaultHandled();
670     }
671     HTMLInputElement::defaultEventHandler(event);
672 }
673
674
675 // ----------------------------
676
677 inline MediaControlToggleClosedCaptionsButtonElement::MediaControlToggleClosedCaptionsButtonElement(HTMLMediaElement* mediaElement)
678     : MediaControlInputElement(mediaElement, MEDIA_CONTROLS_TOGGLE_CLOSED_CAPTIONS_BUTTON)
679 {
680 }
681
682 PassRefPtr<MediaControlToggleClosedCaptionsButtonElement> MediaControlToggleClosedCaptionsButtonElement::create(HTMLMediaElement* mediaElement)
683 {
684     RefPtr<MediaControlToggleClosedCaptionsButtonElement> button = adoptRef(new MediaControlToggleClosedCaptionsButtonElement(mediaElement));
685     button->setType("button");
686     return button.release();
687 }
688
689 void MediaControlToggleClosedCaptionsButtonElement::defaultEventHandler(Event* event)
690 {
691     if (event->type() == eventNames().clickEvent) {
692         mediaElement()->setClosedCaptionsVisible(!mediaElement()->closedCaptionsVisible());
693         setChecked(mediaElement()->closedCaptionsVisible());
694         event->setDefaultHandled();
695     }
696     HTMLInputElement::defaultEventHandler(event);
697 }
698
699 void MediaControlToggleClosedCaptionsButtonElement::updateDisplayType()
700 {
701     setDisplayType(mediaElement()->closedCaptionsVisible() ? MediaHideClosedCaptionsButton : MediaShowClosedCaptionsButton);
702 }
703
704 // ----------------------------
705
706 MediaControlTimelineElement::MediaControlTimelineElement(HTMLMediaElement* mediaElement)
707     : MediaControlInputElement(mediaElement, MEDIA_CONTROLS_TIMELINE)
708 {
709 }
710
711 PassRefPtr<MediaControlTimelineElement> MediaControlTimelineElement::create(HTMLMediaElement* mediaElement)
712 {
713     RefPtr<MediaControlTimelineElement> timeline = adoptRef(new MediaControlTimelineElement(mediaElement));
714     timeline->setType("range");
715     return timeline.release();
716 }
717
718 void MediaControlTimelineElement::defaultEventHandler(Event* event)
719 {
720     // Left button is 0. Rejects mouse events not from left button.
721     if (event->isMouseEvent() && static_cast<MouseEvent*>(event)->button())
722         return;
723
724     if (!attached())
725         return;
726
727     if (event->type() == eventNames().mousedownEvent)
728         mediaElement()->beginScrubbing();
729
730     MediaControlInputElement::defaultEventHandler(event);
731
732     if (event->type() == eventNames().mouseoverEvent || event->type() == eventNames().mouseoutEvent || event->type() == eventNames().mousemoveEvent)
733         return;
734
735     float time = narrowPrecisionToFloat(value().toDouble());
736     if (time != mediaElement()->currentTime()) {
737         ExceptionCode ec;
738         mediaElement()->setCurrentTime(time, ec);
739     }
740
741     RenderSlider* slider = toRenderSlider(renderer());
742     if (slider && slider->inDragMode())
743         toRenderMedia(mediaElement()->renderer())->updateTimeDisplay();
744
745     if (event->type() == eventNames().mouseupEvent)
746         mediaElement()->endScrubbing();
747 }
748
749 void MediaControlTimelineElement::update(bool updateDuration) 
750 {
751     if (updateDuration) {
752         float duration = mediaElement()->duration();
753         setAttribute(maxAttr, String::number(isfinite(duration) ? duration : 0));
754     }
755     setValue(String::number(mediaElement()->currentTime()));
756     MediaControlInputElement::update();
757 }
758
759 // ----------------------------
760
761 inline MediaControlVolumeSliderElement::MediaControlVolumeSliderElement(HTMLMediaElement* mediaElement)
762     : MediaControlInputElement(mediaElement, MEDIA_CONTROLS_VOLUME_SLIDER)
763 {
764 }
765
766 PassRefPtr<MediaControlVolumeSliderElement> MediaControlVolumeSliderElement::create(HTMLMediaElement* mediaElement)
767 {
768     RefPtr<MediaControlVolumeSliderElement> slider = adoptRef(new MediaControlVolumeSliderElement(mediaElement));
769     slider->setType("range");
770     return slider.release();
771 }
772
773 void MediaControlVolumeSliderElement::defaultEventHandler(Event* event)
774 {
775     // Left button is 0. Rejects mouse events not from left button.
776     if (event->isMouseEvent() && static_cast<MouseEvent*>(event)->button())
777         return;
778
779     if (!attached())
780         return;
781
782     MediaControlInputElement::defaultEventHandler(event);
783
784     if (event->type() == eventNames().mouseoverEvent || event->type() == eventNames().mouseoutEvent || event->type() == eventNames().mousemoveEvent)
785         return;
786
787     float volume = narrowPrecisionToFloat(value().toDouble());
788     if (volume != mediaElement()->volume()) {
789         ExceptionCode ec = 0;
790         mediaElement()->setVolume(volume, ec);
791         ASSERT(!ec);
792     }
793 }
794
795 void MediaControlVolumeSliderElement::update()
796 {
797     float volume = mediaElement()->volume();
798     if (value().toFloat() != volume)
799         setValue(String::number(volume));
800     MediaControlInputElement::update();
801 }
802
803 // ----------------------------
804
805 inline MediaControlFullscreenButtonElement::MediaControlFullscreenButtonElement(HTMLMediaElement* mediaElement)
806     : MediaControlInputElement(mediaElement, MEDIA_CONTROLS_FULLSCREEN_BUTTON)
807 {
808 }
809
810 PassRefPtr<MediaControlFullscreenButtonElement> MediaControlFullscreenButtonElement::create(HTMLMediaElement* mediaElement)
811 {
812     RefPtr<MediaControlFullscreenButtonElement> button = adoptRef(new MediaControlFullscreenButtonElement(mediaElement));
813     button->setType("button");
814     return button.release();
815 }
816
817 void MediaControlFullscreenButtonElement::defaultEventHandler(Event* event)
818 {
819     if (event->type() == eventNames().clickEvent) {
820 #if ENABLE(FULLSCREEN_API)
821         // Only use the new full screen API if the fullScreenEnabled setting has 
822         // been explicitly enabled.  Otherwise, use the old fullscreen API.  This
823         // allows apps which embed a WebView to retain the existing full screen
824         // video implementation without requiring them to implement their own full 
825         // screen behavior.
826         if (document()->settings() && document()->settings()->fullScreenEnabled()) {
827             if (document()->webkitIsFullScreen() && document()->webkitCurrentFullScreenElement() == mediaElement())
828                 document()->webkitCancelFullScreen();
829             else
830                 mediaElement()->webkitRequestFullScreen(0);
831         } else
832 #endif
833             mediaElement()->enterFullscreen();
834         event->setDefaultHandled();
835     }
836     HTMLInputElement::defaultEventHandler(event);
837 }
838
839 // ----------------------------
840
841 inline MediaControlTimeDisplayElement::MediaControlTimeDisplayElement(HTMLMediaElement* mediaElement, PseudoId pseudo)
842     : MediaControlElement(mediaElement, pseudo)
843     , m_currentValue(0)
844     , m_isVisible(true)
845 {
846 }
847
848 PassRefPtr<MediaControlTimeDisplayElement> MediaControlTimeDisplayElement::create(HTMLMediaElement* mediaElement, PseudoId pseudoStyleId)
849 {
850     return adoptRef(new MediaControlTimeDisplayElement(mediaElement, pseudoStyleId));
851 }
852
853 PassRefPtr<RenderStyle> MediaControlTimeDisplayElement::styleForElement()
854 {
855     RefPtr<RenderStyle> style = MediaControlElement::styleForElement();
856     if (!m_isVisible) {
857         style = RenderStyle::clone(style.get());
858         style->setWidth(Length(0, Fixed));
859     }
860     return style;
861 }
862
863 void MediaControlTimeDisplayElement::setVisible(bool visible)
864 {
865     if (visible == m_isVisible)
866         return;
867     m_isVisible = visible;
868
869     // This function is used during the RenderMedia::layout()
870     // call, where we cannot change the renderer at this time.
871     if (!renderer() || !renderer()->style())
872         return;
873
874     RefPtr<RenderStyle> style = styleForElement();
875     renderer()->setStyle(style.get());
876 }
877
878 void MediaControlTimeDisplayElement::setCurrentValue(float time)
879 {
880     m_currentValue = time;
881 }
882
883 } // namespace WebCore
884
885 #endif // ENABLE(VIDEO)