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 "CSSStyleSelector.h"
36#include "EventNames.h"
37#include "FloatConversion.h"
38#include "Frame.h"
39#include "HTMLNames.h"
40#include "LocalizedStrings.h"
41#include "MediaControls.h"
42#include "MouseEvent.h"
43#include "Page.h"
44#include "RenderFlexibleBox.h"
45#include "RenderMedia.h"
46#include "RenderSlider.h"
47#include "RenderTheme.h"
48#include "RenderView.h"
49#include "Settings.h"
50
51namespace WebCore {
52
53using namespace HTMLNames;
54
55HTMLMediaElement* toParentMediaElement(RenderObject* o)
56{
57    Node* node = o->node();
58    Node* mediaNode = node ? node->shadowAncestorNode() : 0;
59    if (!mediaNode || (!mediaNode->hasTagName(HTMLNames::videoTag) && !mediaNode->hasTagName(HTMLNames::audioTag)))
60        return 0;
61
62    return static_cast<HTMLMediaElement*>(mediaNode);
63}
64
65// FIXME: These constants may need to be tweaked to better match the seeking in the QuickTime plug-in.
66static const float cSeekRepeatDelay = 0.1f;
67static const float cStepTime = 0.07f;
68static const float cSeekTime = 0.2f;
69
70// ----------------------------
71
72MediaControlElement::MediaControlElement(HTMLMediaElement* mediaElement)
73    : HTMLDivElement(divTag, mediaElement->document())
74    , m_mediaElement(mediaElement)
75{
76}
77
78static const String& displayString()
79{
80    DEFINE_STATIC_LOCAL(String, s, ("display"));
81    return s;
82}
83
84void MediaControlElement::show()
85{
86    ExceptionCode ec;
87    // FIXME: Make more efficient <http://webkit.org/b/58157>
88    style()->removeProperty(displayString(), ec);
89}
90
91void MediaControlElement::hide()
92{
93    ExceptionCode ec;
94    // FIXME: Make more efficient <http://webkit.org/b/58157>
95    DEFINE_STATIC_LOCAL(String, none, ("none"));
96    style()->setProperty(displayString(), none, ec);
97}
98
99// ----------------------------
100
101inline MediaControlPanelElement::MediaControlPanelElement(HTMLMediaElement* mediaElement)
102    : MediaControlElement(mediaElement)
103{
104}
105
106PassRefPtr<MediaControlPanelElement> MediaControlPanelElement::create(HTMLMediaElement* mediaElement)
107{
108    return adoptRef(new MediaControlPanelElement(mediaElement));
109}
110
111MediaControlElementType MediaControlPanelElement::displayType() const
112{
113    return MediaControlsPanel;
114}
115
116const AtomicString& MediaControlPanelElement::shadowPseudoId() const
117{
118    DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-panel"));
119    return id;
120}
121
122// ----------------------------
123
124inline MediaControlTimelineContainerElement::MediaControlTimelineContainerElement(HTMLMediaElement* mediaElement)
125    : MediaControlElement(mediaElement)
126{
127}
128
129PassRefPtr<MediaControlTimelineContainerElement> MediaControlTimelineContainerElement::create(HTMLMediaElement* mediaElement)
130{
131    RefPtr<MediaControlTimelineContainerElement> element = adoptRef(new MediaControlTimelineContainerElement(mediaElement));
132    element->hide();
133    return element.release();
134}
135
136MediaControlElementType MediaControlTimelineContainerElement::displayType() const
137{
138    return MediaTimelineContainer;
139}
140
141const AtomicString& MediaControlTimelineContainerElement::shadowPseudoId() const
142{
143    DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-timeline-container"));
144    return id;
145}
146
147// ----------------------------
148
149class RenderMediaVolumeSliderContainer : public RenderBlock {
150public:
151    RenderMediaVolumeSliderContainer(Node*);
152
153private:
154    virtual void layout();
155};
156
157RenderMediaVolumeSliderContainer::RenderMediaVolumeSliderContainer(Node* node)
158    : RenderBlock(node)
159{
160}
161
162void RenderMediaVolumeSliderContainer::layout()
163{
164    RenderBlock::layout();
165    if (style()->display() == NONE || !previousSibling() || !previousSibling()->isBox())
166        return;
167
168    RenderBox* buttonBox = toRenderBox(previousSibling());
169
170    if (view())
171        view()->disableLayoutState();
172
173    IntPoint offset = theme()->volumeSliderOffsetFromMuteButton(buttonBox, IntSize(width(), height()));
174    setX(offset.x() + buttonBox->offsetLeft());
175    setY(offset.y() + buttonBox->offsetTop());
176
177    if (view())
178        view()->enableLayoutState();
179}
180
181inline MediaControlVolumeSliderContainerElement::MediaControlVolumeSliderContainerElement(HTMLMediaElement* mediaElement)
182    : MediaControlElement(mediaElement)
183{
184}
185
186PassRefPtr<MediaControlVolumeSliderContainerElement> MediaControlVolumeSliderContainerElement::create(HTMLMediaElement* mediaElement)
187{
188    RefPtr<MediaControlVolumeSliderContainerElement> element = adoptRef(new MediaControlVolumeSliderContainerElement(mediaElement));
189    element->hide();
190    return element.release();
191}
192
193RenderObject* MediaControlVolumeSliderContainerElement::createRenderer(RenderArena* arena, RenderStyle*)
194{
195    return new (arena) RenderMediaVolumeSliderContainer(this);
196}
197
198void MediaControlVolumeSliderContainerElement::defaultEventHandler(Event* event)
199{
200    if (!event->isMouseEvent() || event->type() != eventNames().mouseoutEvent)
201        return;
202
203    // Poor man's mouseleave event detection.
204    MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
205    if (!mouseEvent->relatedTarget() || !mouseEvent->relatedTarget()->toNode())
206        return;
207
208    if (this->containsIncludingShadowDOM(mouseEvent->relatedTarget()->toNode()))
209        return;
210
211    hide();
212}
213
214
215MediaControlElementType MediaControlVolumeSliderContainerElement::displayType() const
216{
217    return MediaVolumeSliderContainer;
218}
219
220const AtomicString& MediaControlVolumeSliderContainerElement::shadowPseudoId() const
221{
222    DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-volume-slider-container"));
223    return id;
224}
225
226// ----------------------------
227
228inline MediaControlStatusDisplayElement::MediaControlStatusDisplayElement(HTMLMediaElement* mediaElement)
229    : MediaControlElement(mediaElement)
230    , m_stateBeingDisplayed(Nothing)
231{
232}
233
234PassRefPtr<MediaControlStatusDisplayElement> MediaControlStatusDisplayElement::create(HTMLMediaElement* mediaElement)
235{
236    RefPtr<MediaControlStatusDisplayElement> element = adoptRef(new MediaControlStatusDisplayElement(mediaElement));
237    element->hide();
238    return element.release();
239}
240
241void MediaControlStatusDisplayElement::update()
242{
243    // Get the new state that we'll have to display.
244    StateBeingDisplayed newStateToDisplay = Nothing;
245
246    if (mediaElement()->readyState() != HTMLMediaElement::HAVE_ENOUGH_DATA && !mediaElement()->currentSrc().isEmpty())
247        newStateToDisplay = Loading;
248    else if (mediaElement()->movieLoadType() == MediaPlayer::LiveStream)
249        newStateToDisplay = LiveBroadcast;
250
251    if (newStateToDisplay == m_stateBeingDisplayed)
252        return;
253
254    ExceptionCode e;
255
256    if (m_stateBeingDisplayed == Nothing)
257        show();
258    else if (newStateToDisplay == Nothing)
259        hide();
260
261    m_stateBeingDisplayed = newStateToDisplay;
262
263    switch (m_stateBeingDisplayed) {
264    case Nothing:
265        setInnerText("", e);
266        break;
267    case Loading:
268        setInnerText(mediaElementLoadingStateText(), e);
269        break;
270    case LiveBroadcast:
271        setInnerText(mediaElementLiveBroadcastStateText(), e);
272        break;
273    }
274}
275
276MediaControlElementType MediaControlStatusDisplayElement::displayType() const
277{
278    return MediaStatusDisplay;
279}
280
281const AtomicString& MediaControlStatusDisplayElement::shadowPseudoId() const
282{
283    DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-status-display"));
284    return id;
285}
286
287// ----------------------------
288
289MediaControlInputElement::MediaControlInputElement(HTMLMediaElement* mediaElement, MediaControlElementType displayType)
290    : HTMLInputElement(inputTag, mediaElement->document(), 0, false)
291    , m_mediaElement(mediaElement)
292    , m_displayType(displayType)
293{
294}
295
296void MediaControlInputElement::show()
297{
298    ExceptionCode ec;
299    style()->removeProperty(displayString(), ec);
300}
301
302void MediaControlInputElement::hide()
303{
304    ExceptionCode ec;
305    DEFINE_STATIC_LOCAL(String, none, ("none"));
306    style()->setProperty(displayString(), none, ec);
307}
308
309
310void MediaControlInputElement::setDisplayType(MediaControlElementType displayType)
311{
312    if (displayType == m_displayType)
313        return;
314
315    m_displayType = displayType;
316    if (RenderObject* object = renderer())
317        object->repaint();
318}
319
320// ----------------------------
321
322inline MediaControlMuteButtonElement::MediaControlMuteButtonElement(HTMLMediaElement* mediaElement, MediaControlElementType displayType)
323    : MediaControlInputElement(mediaElement, displayType)
324{
325}
326
327void MediaControlMuteButtonElement::defaultEventHandler(Event* event)
328{
329    if (event->type() == eventNames().clickEvent) {
330        mediaElement()->setMuted(!mediaElement()->muted());
331        event->setDefaultHandled();
332    }
333
334    HTMLInputElement::defaultEventHandler(event);
335}
336
337void MediaControlMuteButtonElement::changedMute()
338{
339    updateDisplayType();
340}
341
342void MediaControlMuteButtonElement::updateDisplayType()
343{
344    setDisplayType(mediaElement()->muted() ? MediaUnMuteButton : MediaMuteButton);
345}
346
347// ----------------------------
348
349inline MediaControlPanelMuteButtonElement::MediaControlPanelMuteButtonElement(HTMLMediaElement* mediaElement, MediaControls* controls)
350    : MediaControlMuteButtonElement(mediaElement, MediaMuteButton)
351    , m_controls(controls)
352{
353}
354
355PassRefPtr<MediaControlPanelMuteButtonElement> MediaControlPanelMuteButtonElement::create(HTMLMediaElement* mediaElement, MediaControls* controls)
356{
357    ASSERT(controls);
358
359    RefPtr<MediaControlPanelMuteButtonElement> button = adoptRef(new MediaControlPanelMuteButtonElement(mediaElement, controls));
360    button->setType("button");
361    return button.release();
362}
363
364void MediaControlPanelMuteButtonElement::defaultEventHandler(Event* event)
365{
366    if (event->type() == eventNames().mouseoverEvent)
367        m_controls->showVolumeSlider();
368
369    MediaControlMuteButtonElement::defaultEventHandler(event);
370}
371
372const AtomicString& MediaControlPanelMuteButtonElement::shadowPseudoId() const
373{
374    DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-mute-button"));
375    return id;
376}
377
378// ----------------------------
379
380inline MediaControlVolumeSliderMuteButtonElement::MediaControlVolumeSliderMuteButtonElement(HTMLMediaElement* mediaElement)
381    : MediaControlMuteButtonElement(mediaElement, MediaMuteButton)
382{
383}
384
385PassRefPtr<MediaControlVolumeSliderMuteButtonElement> MediaControlVolumeSliderMuteButtonElement::create(HTMLMediaElement* mediaElement)
386{
387    RefPtr<MediaControlVolumeSliderMuteButtonElement> button = adoptRef(new MediaControlVolumeSliderMuteButtonElement(mediaElement));
388    button->setType("button");
389    return button.release();
390}
391
392const AtomicString& MediaControlVolumeSliderMuteButtonElement::shadowPseudoId() const
393{
394    DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-volume-slider-mute-button"));
395    return id;
396}
397
398// ----------------------------
399
400inline MediaControlPlayButtonElement::MediaControlPlayButtonElement(HTMLMediaElement* mediaElement)
401    : MediaControlInputElement(mediaElement, MediaPlayButton)
402{
403}
404
405PassRefPtr<MediaControlPlayButtonElement> MediaControlPlayButtonElement::create(HTMLMediaElement* mediaElement)
406{
407    RefPtr<MediaControlPlayButtonElement> button = adoptRef(new MediaControlPlayButtonElement(mediaElement));
408    button->setType("button");
409    return button.release();
410}
411
412void MediaControlPlayButtonElement::defaultEventHandler(Event* event)
413{
414    if (event->type() == eventNames().clickEvent) {
415        mediaElement()->togglePlayState();
416        updateDisplayType();
417        event->setDefaultHandled();
418    }
419    HTMLInputElement::defaultEventHandler(event);
420}
421
422void MediaControlPlayButtonElement::updateDisplayType()
423{
424    setDisplayType(mediaElement()->canPlay() ? MediaPlayButton : MediaPauseButton);
425}
426
427const AtomicString& MediaControlPlayButtonElement::shadowPseudoId() const
428{
429    DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-play-button"));
430    return id;
431}
432
433// ----------------------------
434
435inline MediaControlSeekButtonElement::MediaControlSeekButtonElement(HTMLMediaElement* mediaElement, MediaControlElementType displayType)
436    : MediaControlInputElement(mediaElement, displayType)
437    , m_seeking(false)
438    , m_capturing(false)
439    , m_seekTimer(this, &MediaControlSeekButtonElement::seekTimerFired)
440{
441}
442
443void MediaControlSeekButtonElement::defaultEventHandler(Event* event)
444{
445    if (event->type() == eventNames().mousedownEvent) {
446        if (Frame* frame = document()->frame()) {
447            m_capturing = true;
448            frame->eventHandler()->setCapturingMouseEventsNode(this);
449        }
450        mediaElement()->pause(event->fromUserGesture());
451        m_seekTimer.startRepeating(cSeekRepeatDelay);
452        event->setDefaultHandled();
453    } else if (event->type() == eventNames().mouseupEvent) {
454        if (m_capturing)
455            if (Frame* frame = document()->frame()) {
456                m_capturing = false;
457                frame->eventHandler()->setCapturingMouseEventsNode(0);
458            }
459        ExceptionCode ec;
460        if (m_seeking || m_seekTimer.isActive()) {
461            if (!m_seeking) {
462                float stepTime = isForwardButton() ? cStepTime : -cStepTime;
463                mediaElement()->setCurrentTime(mediaElement()->currentTime() + stepTime, ec);
464            }
465            m_seekTimer.stop();
466            m_seeking = false;
467            event->setDefaultHandled();
468        }
469    }
470    HTMLInputElement::defaultEventHandler(event);
471}
472
473void MediaControlSeekButtonElement::seekTimerFired(Timer<MediaControlSeekButtonElement>*)
474{
475    ExceptionCode ec;
476    m_seeking = true;
477    float seekTime = isForwardButton() ? cSeekTime : -cSeekTime;
478    mediaElement()->setCurrentTime(mediaElement()->currentTime() + seekTime, ec);
479}
480
481void MediaControlSeekButtonElement::detach()
482{
483    if (m_capturing) {
484        if (Frame* frame = document()->frame())
485            frame->eventHandler()->setCapturingMouseEventsNode(0);
486    }
487    MediaControlInputElement::detach();
488}
489
490// ----------------------------
491
492inline MediaControlSeekForwardButtonElement::MediaControlSeekForwardButtonElement(HTMLMediaElement* mediaElement)
493    : MediaControlSeekButtonElement(mediaElement, MediaSeekForwardButton)
494{
495}
496
497PassRefPtr<MediaControlSeekForwardButtonElement> MediaControlSeekForwardButtonElement::create(HTMLMediaElement* mediaElement)
498{
499    RefPtr<MediaControlSeekForwardButtonElement> button = adoptRef(new MediaControlSeekForwardButtonElement(mediaElement));
500    button->setType("button");
501    return button.release();
502}
503
504const AtomicString& MediaControlSeekForwardButtonElement::shadowPseudoId() const
505{
506    DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-seek-forward-button"));
507    return id;
508}
509
510// ----------------------------
511
512inline MediaControlSeekBackButtonElement::MediaControlSeekBackButtonElement(HTMLMediaElement* mediaElement)
513    : MediaControlSeekButtonElement(mediaElement, MediaSeekBackButton)
514{
515}
516
517PassRefPtr<MediaControlSeekBackButtonElement> MediaControlSeekBackButtonElement::create(HTMLMediaElement* mediaElement)
518{
519    RefPtr<MediaControlSeekBackButtonElement> button = adoptRef(new MediaControlSeekBackButtonElement(mediaElement));
520    button->setType("button");
521    return button.release();
522}
523
524const AtomicString& MediaControlSeekBackButtonElement::shadowPseudoId() const
525{
526    DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-seek-back-button"));
527    return id;
528}
529
530// ----------------------------
531
532inline MediaControlRewindButtonElement::MediaControlRewindButtonElement(HTMLMediaElement* element)
533    : MediaControlInputElement(element, MediaRewindButton)
534{
535}
536
537PassRefPtr<MediaControlRewindButtonElement> MediaControlRewindButtonElement::create(HTMLMediaElement* mediaElement)
538{
539    RefPtr<MediaControlRewindButtonElement> button = adoptRef(new MediaControlRewindButtonElement(mediaElement));
540    button->setType("button");
541    return button.release();
542}
543
544void MediaControlRewindButtonElement::defaultEventHandler(Event* event)
545{
546    if (event->type() == eventNames().clickEvent) {
547        mediaElement()->rewind(30);
548        event->setDefaultHandled();
549    }
550    HTMLInputElement::defaultEventHandler(event);
551}
552
553const AtomicString& MediaControlRewindButtonElement::shadowPseudoId() const
554{
555    DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-rewind-button"));
556    return id;
557}
558
559// ----------------------------
560
561inline MediaControlReturnToRealtimeButtonElement::MediaControlReturnToRealtimeButtonElement(HTMLMediaElement* mediaElement)
562    : MediaControlInputElement(mediaElement, MediaReturnToRealtimeButton)
563{
564}
565
566PassRefPtr<MediaControlReturnToRealtimeButtonElement> MediaControlReturnToRealtimeButtonElement::create(HTMLMediaElement* mediaElement)
567{
568    RefPtr<MediaControlReturnToRealtimeButtonElement> button = adoptRef(new MediaControlReturnToRealtimeButtonElement(mediaElement));
569    button->setType("button");
570    button->hide();
571    return button.release();
572}
573
574void MediaControlReturnToRealtimeButtonElement::defaultEventHandler(Event* event)
575{
576    if (event->type() == eventNames().clickEvent) {
577        mediaElement()->returnToRealtime();
578        event->setDefaultHandled();
579    }
580    HTMLInputElement::defaultEventHandler(event);
581}
582
583const AtomicString& MediaControlReturnToRealtimeButtonElement::shadowPseudoId() const
584{
585    DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-return-to-realtime-button"));
586    return id;
587}
588
589// ----------------------------
590
591inline MediaControlToggleClosedCaptionsButtonElement::MediaControlToggleClosedCaptionsButtonElement(HTMLMediaElement* mediaElement)
592    : MediaControlInputElement(mediaElement, MediaShowClosedCaptionsButton)
593{
594}
595
596PassRefPtr<MediaControlToggleClosedCaptionsButtonElement> MediaControlToggleClosedCaptionsButtonElement::create(HTMLMediaElement* mediaElement)
597{
598    RefPtr<MediaControlToggleClosedCaptionsButtonElement> button = adoptRef(new MediaControlToggleClosedCaptionsButtonElement(mediaElement));
599    button->setType("button");
600    button->hide();
601    return button.release();
602}
603
604void MediaControlToggleClosedCaptionsButtonElement::defaultEventHandler(Event* event)
605{
606    if (event->type() == eventNames().clickEvent) {
607        mediaElement()->setClosedCaptionsVisible(!mediaElement()->closedCaptionsVisible());
608        setChecked(mediaElement()->closedCaptionsVisible());
609        updateDisplayType();
610        event->setDefaultHandled();
611    }
612
613    HTMLInputElement::defaultEventHandler(event);
614}
615
616void MediaControlToggleClosedCaptionsButtonElement::updateDisplayType()
617{
618    setDisplayType(mediaElement()->closedCaptionsVisible() ? MediaHideClosedCaptionsButton : MediaShowClosedCaptionsButton);
619}
620
621const AtomicString& MediaControlToggleClosedCaptionsButtonElement::shadowPseudoId() const
622{
623    DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-toggle-closed-captions-button"));
624    return id;
625}
626
627// ----------------------------
628
629MediaControlTimelineElement::MediaControlTimelineElement(HTMLMediaElement* mediaElement, MediaControls* controls)
630    : MediaControlInputElement(mediaElement, MediaSlider)
631    , m_controls(controls)
632{
633}
634
635PassRefPtr<MediaControlTimelineElement> MediaControlTimelineElement::create(HTMLMediaElement* mediaElement, MediaControls* controls)
636{
637    ASSERT(controls);
638
639    RefPtr<MediaControlTimelineElement> timeline = adoptRef(new MediaControlTimelineElement(mediaElement, controls));
640    timeline->setType("range");
641    timeline->setAttribute(precisionAttr, "float");
642    return timeline.release();
643}
644
645void MediaControlTimelineElement::defaultEventHandler(Event* event)
646{
647    // Left button is 0. Rejects mouse events not from left button.
648    if (event->isMouseEvent() && static_cast<MouseEvent*>(event)->button())
649        return;
650
651    if (!attached())
652        return;
653
654    if (event->type() == eventNames().mousedownEvent)
655        mediaElement()->beginScrubbing();
656
657#if PLATFORM(ANDROID) && ENABLE(TOUCH_EVENTS)
658    if (event->type() == eventNames().touchstartEvent)
659        mediaElement()->beginScrubbing();
660#endif
661
662    MediaControlInputElement::defaultEventHandler(event);
663
664    if (event->type() == eventNames().mouseoverEvent || event->type() == eventNames().mouseoutEvent || event->type() == eventNames().mousemoveEvent)
665        return;
666
667#if PLATFORM(ANDROID) && ENABLE(TOUCH_EVENTS)
668    if (event->type() == eventNames().touchmoveEvent || event->type() == eventNames().touchcancelEvent)
669        return;
670#endif
671
672    float time = narrowPrecisionToFloat(value().toDouble());
673    if (time != mediaElement()->currentTime()) {
674        // FIXME: This is fired 3 times on every click. We should not be doing that <http:/webkit.org/b/58160>.
675        ExceptionCode ec;
676        mediaElement()->setCurrentTime(time, ec);
677    }
678
679    RenderSlider* slider = toRenderSlider(renderer());
680    if (slider && slider->inDragMode())
681        m_controls->updateTimeDisplay();
682
683    if (event->type() == eventNames().mouseupEvent)
684        mediaElement()->endScrubbing();
685
686#if PLATFORM(ANDROID) && ENABLE(TOUCH_EVENTS)
687    if (event->type() == eventNames().touchendEvent)
688        mediaElement()->endScrubbing();
689#endif
690}
691
692void MediaControlTimelineElement::setPosition(float currentTime)
693{
694    setValue(String::number(currentTime));
695}
696
697void MediaControlTimelineElement::setDuration(float duration)
698{
699    setAttribute(maxAttr, String::number(isfinite(duration) ? duration : 0));
700}
701
702
703const AtomicString& MediaControlTimelineElement::shadowPseudoId() const
704{
705    DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-timeline"));
706    return id;
707}
708
709// ----------------------------
710
711inline MediaControlVolumeSliderElement::MediaControlVolumeSliderElement(HTMLMediaElement* mediaElement)
712    : MediaControlInputElement(mediaElement, MediaVolumeSlider)
713{
714}
715
716PassRefPtr<MediaControlVolumeSliderElement> MediaControlVolumeSliderElement::create(HTMLMediaElement* mediaElement)
717{
718    RefPtr<MediaControlVolumeSliderElement> slider = adoptRef(new MediaControlVolumeSliderElement(mediaElement));
719    slider->setType("range");
720    slider->setAttribute(precisionAttr, "float");
721    slider->setAttribute(maxAttr, "1");
722    slider->setAttribute(valueAttr, String::number(mediaElement->volume()));
723    return slider.release();
724}
725
726void MediaControlVolumeSliderElement::defaultEventHandler(Event* event)
727{
728    // Left button is 0. Rejects mouse events not from left button.
729    if (event->isMouseEvent() && static_cast<MouseEvent*>(event)->button())
730        return;
731
732    if (!attached())
733        return;
734
735    MediaControlInputElement::defaultEventHandler(event);
736
737    if (event->type() == eventNames().mouseoverEvent || event->type() == eventNames().mouseoutEvent || event->type() == eventNames().mousemoveEvent)
738        return;
739
740    float volume = narrowPrecisionToFloat(value().toDouble());
741    if (volume != mediaElement()->volume()) {
742        ExceptionCode ec = 0;
743        mediaElement()->setVolume(volume, ec);
744        ASSERT(!ec);
745    }
746}
747
748void MediaControlVolumeSliderElement::setVolume(float volume)
749{
750    if (value().toFloat() != volume)
751        setValue(String::number(volume));
752}
753
754const AtomicString& MediaControlVolumeSliderElement::shadowPseudoId() const
755{
756    DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-volume-slider"));
757    return id;
758}
759
760// ----------------------------
761
762inline MediaControlFullscreenVolumeSliderElement::MediaControlFullscreenVolumeSliderElement(HTMLMediaElement* mediaElement)
763    : MediaControlVolumeSliderElement(mediaElement)
764{
765}
766
767PassRefPtr<MediaControlFullscreenVolumeSliderElement> MediaControlFullscreenVolumeSliderElement::create(HTMLMediaElement* mediaElement)
768{
769    RefPtr<MediaControlFullscreenVolumeSliderElement> slider = adoptRef(new MediaControlFullscreenVolumeSliderElement(mediaElement));
770    slider->setType("range");
771    slider->setAttribute(precisionAttr, "float");
772    slider->setAttribute(maxAttr, "1");
773    slider->setAttribute(valueAttr, String::number(mediaElement->volume()));
774    return slider.release();
775}
776
777const AtomicString& MediaControlFullscreenVolumeSliderElement::shadowPseudoId() const
778{
779    DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-volume-slider"));
780    return id;
781}
782
783// ----------------------------
784
785inline MediaControlFullscreenButtonElement::MediaControlFullscreenButtonElement(HTMLMediaElement* mediaElement, MediaControls* controls)
786    : MediaControlInputElement(mediaElement, MediaFullscreenButton)
787    , m_controls(controls)
788{
789}
790
791PassRefPtr<MediaControlFullscreenButtonElement> MediaControlFullscreenButtonElement::create(HTMLMediaElement* mediaElement, MediaControls* controls)
792{
793    ASSERT(controls);
794
795    RefPtr<MediaControlFullscreenButtonElement> button = adoptRef(new MediaControlFullscreenButtonElement(mediaElement, controls));
796    button->setType("button");
797    button->hide();
798    return button.release();
799}
800
801void MediaControlFullscreenButtonElement::defaultEventHandler(Event* event)
802{
803    if (event->type() == eventNames().clickEvent) {
804#if ENABLE(FULLSCREEN_API)
805        // Only use the new full screen API if the fullScreenEnabled setting has
806        // been explicitly enabled. Otherwise, use the old fullscreen API. This
807        // allows apps which embed a WebView to retain the existing full screen
808        // video implementation without requiring them to implement their own full
809        // screen behavior.
810        if (document()->settings() && document()->settings()->fullScreenEnabled()) {
811            if (document()->webkitIsFullScreen() && document()->webkitCurrentFullScreenElement() == mediaElement()) {
812                document()->webkitCancelFullScreen();
813                m_controls->exitedFullscreen();
814            } else {
815                mediaElement()->webkitRequestFullScreen(0);
816                m_controls->enteredFullscreen();
817            }
818        } else
819#endif
820            mediaElement()->enterFullscreen();
821        event->setDefaultHandled();
822    }
823    HTMLInputElement::defaultEventHandler(event);
824}
825
826const AtomicString& MediaControlFullscreenButtonElement::shadowPseudoId() const
827{
828    DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-button"));
829    return id;
830}
831
832// ----------------------------
833
834inline MediaControlFullscreenVolumeMinButtonElement::MediaControlFullscreenVolumeMinButtonElement(HTMLMediaElement* mediaElement)
835    : MediaControlInputElement(mediaElement, MediaUnMuteButton)
836{
837}
838
839PassRefPtr<MediaControlFullscreenVolumeMinButtonElement> MediaControlFullscreenVolumeMinButtonElement::create(HTMLMediaElement* mediaElement)
840{
841    RefPtr<MediaControlFullscreenVolumeMinButtonElement> button = adoptRef(new MediaControlFullscreenVolumeMinButtonElement(mediaElement));
842    button->setType("button");
843    return button.release();
844}
845
846void MediaControlFullscreenVolumeMinButtonElement::defaultEventHandler(Event* event)
847{
848    if (event->type() == eventNames().clickEvent) {
849        ExceptionCode code = 0;
850        mediaElement()->setVolume(0, code);
851        event->setDefaultHandled();
852    }
853    HTMLInputElement::defaultEventHandler(event);
854}
855
856const AtomicString& MediaControlFullscreenVolumeMinButtonElement::shadowPseudoId() const
857{
858    DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-volume-min-button"));
859    return id;
860}
861
862// ----------------------------
863
864inline MediaControlFullscreenVolumeMaxButtonElement::MediaControlFullscreenVolumeMaxButtonElement(HTMLMediaElement* mediaElement)
865: MediaControlInputElement(mediaElement, MediaMuteButton)
866{
867}
868
869PassRefPtr<MediaControlFullscreenVolumeMaxButtonElement> MediaControlFullscreenVolumeMaxButtonElement::create(HTMLMediaElement* mediaElement)
870{
871    RefPtr<MediaControlFullscreenVolumeMaxButtonElement> button = adoptRef(new MediaControlFullscreenVolumeMaxButtonElement(mediaElement));
872    button->setType("button");
873    return button.release();
874}
875
876void MediaControlFullscreenVolumeMaxButtonElement::defaultEventHandler(Event* event)
877{
878    if (event->type() == eventNames().clickEvent) {
879        ExceptionCode code = 0;
880        mediaElement()->setVolume(1, code);
881        event->setDefaultHandled();
882    }
883    HTMLInputElement::defaultEventHandler(event);
884}
885
886const AtomicString& MediaControlFullscreenVolumeMaxButtonElement::shadowPseudoId() const
887{
888    DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-volume-max-button"));
889    return id;
890}
891
892// ----------------------------
893
894class RenderMediaControlTimeDisplay : public RenderFlexibleBox {
895public:
896    RenderMediaControlTimeDisplay(Node*);
897
898private:
899    virtual void layout();
900};
901
902RenderMediaControlTimeDisplay::RenderMediaControlTimeDisplay(Node* node)
903    : RenderFlexibleBox(node)
904{
905}
906
907// We want the timeline slider to be at least 100 pixels wide.
908// FIXME: Eliminate hard-coded widths altogether.
909static const int minWidthToDisplayTimeDisplays = 45 + 100 + 45;
910
911void RenderMediaControlTimeDisplay::layout()
912{
913    RenderFlexibleBox::layout();
914    RenderBox* timelineContainerBox = parentBox();
915    while (timelineContainerBox && timelineContainerBox->isAnonymous())
916        timelineContainerBox = timelineContainerBox->parentBox();
917
918    if (timelineContainerBox && timelineContainerBox->width() < minWidthToDisplayTimeDisplays)
919        setWidth(0);
920}
921
922inline MediaControlTimeDisplayElement::MediaControlTimeDisplayElement(HTMLMediaElement* mediaElement)
923    : MediaControlElement(mediaElement)
924    , m_currentValue(0)
925{
926}
927
928void MediaControlTimeDisplayElement::setCurrentValue(float time)
929{
930    m_currentValue = time;
931}
932
933RenderObject* MediaControlTimeDisplayElement::createRenderer(RenderArena* arena, RenderStyle*)
934{
935    return new (arena) RenderMediaControlTimeDisplay(this);
936}
937
938// ----------------------------
939
940PassRefPtr<MediaControlTimeRemainingDisplayElement> MediaControlTimeRemainingDisplayElement::create(HTMLMediaElement* mediaElement)
941{
942    return adoptRef(new MediaControlTimeRemainingDisplayElement(mediaElement));
943}
944
945MediaControlTimeRemainingDisplayElement::MediaControlTimeRemainingDisplayElement(HTMLMediaElement* mediaElement)
946    : MediaControlTimeDisplayElement(mediaElement)
947{
948}
949
950MediaControlElementType MediaControlTimeRemainingDisplayElement::displayType() const
951{
952    return MediaTimeRemainingDisplay;
953}
954
955const AtomicString& MediaControlTimeRemainingDisplayElement::shadowPseudoId() const
956{
957    DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-time-remaining-display"));
958    return id;
959}
960
961// ----------------------------
962
963PassRefPtr<MediaControlCurrentTimeDisplayElement> MediaControlCurrentTimeDisplayElement::create(HTMLMediaElement* mediaElement)
964{
965    return adoptRef(new MediaControlCurrentTimeDisplayElement(mediaElement));
966}
967
968MediaControlCurrentTimeDisplayElement::MediaControlCurrentTimeDisplayElement(HTMLMediaElement* mediaElement)
969    : MediaControlTimeDisplayElement(mediaElement)
970{
971}
972
973MediaControlElementType MediaControlCurrentTimeDisplayElement::displayType() const
974{
975    return MediaCurrentTimeDisplay;
976}
977
978const AtomicString& MediaControlCurrentTimeDisplayElement::shadowPseudoId() const
979{
980    DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-current-time-display"));
981    return id;
982}
983
984} // namespace WebCore
985
986#endif // ENABLE(VIDEO)
987