WebCore/rendering/MediaControlElements.cpp
changeset 0 4f2f89ce4247
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebCore/rendering/MediaControlElements.cpp	Fri Sep 17 09:02:29 2010 +0300
@@ -0,0 +1,831 @@
+/*
+ * Copyright (C) 2008, 2009, 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if ENABLE(VIDEO)
+
+#include "MediaControlElements.h"
+
+#include "EventNames.h"
+#include "FloatConversion.h"
+#include "Frame.h"
+#include "HTMLNames.h"
+#include "LocalizedStrings.h"
+#include "MouseEvent.h"
+#include "Page.h"
+#include "RenderMedia.h"
+#include "RenderSlider.h"
+#include "RenderTheme.h"
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+HTMLMediaElement* toParentMediaElement(RenderObject* o)
+{
+    Node* node = o->node();
+    Node* mediaNode = node ? node->shadowAncestorNode() : 0;
+    if (!mediaNode || (!mediaNode->hasTagName(HTMLNames::videoTag) && !mediaNode->hasTagName(HTMLNames::audioTag)))
+        return 0;
+
+    return static_cast<HTMLMediaElement*>(mediaNode);
+}
+
+// FIXME: These constants may need to be tweaked to better match the seeking in the QuickTime plug-in.
+static const float cSeekRepeatDelay = 0.1f;
+static const float cStepTime = 0.07f;
+static const float cSeekTime = 0.2f;
+
+inline MediaControlShadowRootElement::MediaControlShadowRootElement(HTMLMediaElement* mediaElement)
+    : HTMLDivElement(divTag, mediaElement->document())
+    , m_mediaElement(mediaElement) 
+{
+}
+
+PassRefPtr<MediaControlShadowRootElement> MediaControlShadowRootElement::create(HTMLMediaElement* mediaElement)
+{
+    RefPtr<MediaControlShadowRootElement> element = adoptRef(new MediaControlShadowRootElement(mediaElement));
+
+    RefPtr<RenderStyle> rootStyle = RenderStyle::create();
+    rootStyle->inheritFrom(mediaElement->renderer()->style());
+    rootStyle->setDisplay(BLOCK);
+    rootStyle->setPosition(RelativePosition);
+
+    RenderMediaControlShadowRoot* renderer = new (mediaElement->renderer()->renderArena()) RenderMediaControlShadowRoot(element.get());
+    renderer->setStyle(rootStyle.release());
+
+    element->setRenderer(renderer);
+    element->setAttached();
+    element->setInDocument();
+
+    return element.release();
+}
+
+void MediaControlShadowRootElement::updateStyle()
+{
+    if (renderer()) {
+        RenderStyle* timelineContainerStyle = m_mediaElement->renderer()->getCachedPseudoStyle(MEDIA_CONTROLS_TIMELINE_CONTAINER);
+        renderer()->setStyle(timelineContainerStyle);
+    }
+}
+
+// ----------------------------
+
+MediaControlElement::MediaControlElement(HTMLMediaElement* mediaElement, PseudoId pseudo)
+    : HTMLDivElement(divTag, mediaElement->document())
+    , m_mediaElement(mediaElement)
+    , m_pseudoStyleId(pseudo)
+{
+    setInDocument();
+    switch (pseudo) {
+    case MEDIA_CONTROLS_CURRENT_TIME_DISPLAY:
+        m_displayType = MediaCurrentTimeDisplay;
+        break;
+    case MEDIA_CONTROLS_TIME_REMAINING_DISPLAY:
+        m_displayType = MediaTimeRemainingDisplay;
+        break;
+    case MEDIA_CONTROLS_TIMELINE_CONTAINER:
+        m_displayType = MediaTimelineContainer;
+        break;
+    case MEDIA_CONTROLS_STATUS_DISPLAY:
+        m_displayType = MediaStatusDisplay;
+        break;
+    case MEDIA_CONTROLS_PANEL:
+        m_displayType = MediaControlsPanel;
+        break;
+    case MEDIA_CONTROLS_VOLUME_SLIDER_CONTAINER:
+        m_displayType = MediaVolumeSliderContainer;
+        break;
+    default:
+        ASSERT_NOT_REACHED();
+        break;
+    }
+}
+
+PassRefPtr<MediaControlElement> MediaControlElement::create(HTMLMediaElement* mediaElement, PseudoId pseudoStyleId)
+{
+    return adoptRef(new MediaControlElement(mediaElement, pseudoStyleId));
+}
+
+void MediaControlElement::attachToParent(Element* parent)
+{
+    parent->legacyParserAddChild(this);
+}
+
+void MediaControlElement::update()
+{
+    if (renderer())
+        renderer()->updateFromElement();
+    updateStyle();
+}
+
+PassRefPtr<RenderStyle> MediaControlElement::styleForElement()
+{
+    RenderStyle* style = m_mediaElement->renderer()->getCachedPseudoStyle(m_pseudoStyleId);
+    if (!style)
+        return 0;
+    
+    // text-decoration can't be overrided from CSS. So we do it here.
+    // See https://bugs.webkit.org/show_bug.cgi?id=27015
+    style->setTextDecoration(TDNONE);
+    style->setTextDecorationsInEffect(TDNONE);
+
+    return style;
+}
+
+bool MediaControlElement::rendererIsNeeded(RenderStyle* style)
+{
+    ASSERT(document()->page());
+
+    return HTMLDivElement::rendererIsNeeded(style) && parent() && parent()->renderer()
+        && (!style->hasAppearance() || document()->page()->theme()->shouldRenderMediaControlPart(style->appearance(), m_mediaElement));
+}
+    
+void MediaControlElement::attach()
+{
+    RefPtr<RenderStyle> style = styleForElement();
+    if (!style)
+        return;
+    bool needsRenderer = rendererIsNeeded(style.get());
+    if (!needsRenderer)
+        return;
+    RenderObject* renderer = createRenderer(m_mediaElement->renderer()->renderArena(), style.get());
+    if (!renderer)
+        return;
+    renderer->setStyle(style.get());
+    setRenderer(renderer);
+    if (parent() && parent()->renderer()) {
+        // Find next sibling with a renderer to determine where to insert.
+        Node* sibling = nextSibling();
+        while (sibling && !sibling->renderer())
+            sibling = sibling->nextSibling();
+        parent()->renderer()->addChild(renderer, sibling ? sibling->renderer() : 0);
+    }
+    ContainerNode::attach();
+}
+
+void MediaControlElement::updateStyle()
+{
+    if (!m_mediaElement || !m_mediaElement->renderer())
+        return;
+
+    RefPtr<RenderStyle> style = styleForElement();
+    if (!style)
+        return;
+
+    bool needsRenderer = rendererIsNeeded(style.get()) && parent() && parent()->renderer();
+    if (renderer() && !needsRenderer)
+        detach();
+    else if (!renderer() && needsRenderer)
+        attach();
+    else if (renderer()) {
+        renderer()->setStyle(style.get());
+
+        // Make sure that if there is any innerText renderer, it is updated as well.
+        if (firstChild() && firstChild()->renderer())
+            firstChild()->renderer()->setStyle(style.get());
+    }
+}
+
+// ----------------------------
+
+inline MediaControlTimelineContainerElement::MediaControlTimelineContainerElement(HTMLMediaElement* mediaElement)
+    : MediaControlElement(mediaElement, MEDIA_CONTROLS_TIMELINE_CONTAINER)
+{
+}
+
+PassRefPtr<MediaControlTimelineContainerElement> MediaControlTimelineContainerElement::create(HTMLMediaElement* mediaElement)
+{
+    return adoptRef(new MediaControlTimelineContainerElement(mediaElement));
+}
+
+bool MediaControlTimelineContainerElement::rendererIsNeeded(RenderStyle* style)
+{
+    if (!MediaControlElement::rendererIsNeeded(style))
+        return false;
+
+    // This is for MediaControllerThemeClassic:
+    // If there is no style for MediaControlStatusDisplayElement style, don't hide
+    // the timeline.
+    if (!mediaElement()->renderer()->getCachedPseudoStyle(MEDIA_CONTROLS_STATUS_DISPLAY))
+        return true;
+
+    float duration = mediaElement()->duration();
+    return !isnan(duration) && !isinf(duration);
+}
+
+// ----------------------------
+
+inline MediaControlVolumeSliderContainerElement::MediaControlVolumeSliderContainerElement(HTMLMediaElement* mediaElement)
+    : MediaControlElement(mediaElement, MEDIA_CONTROLS_VOLUME_SLIDER_CONTAINER)
+    , m_isVisible(false)
+    , m_x(0)
+    , m_y(0)
+{
+}
+
+PassRefPtr<MediaControlVolumeSliderContainerElement> MediaControlVolumeSliderContainerElement::create(HTMLMediaElement* mediaElement)
+{
+    return adoptRef(new MediaControlVolumeSliderContainerElement(mediaElement));
+}
+
+PassRefPtr<RenderStyle> MediaControlVolumeSliderContainerElement::styleForElement()
+{
+    RefPtr<RenderStyle> style = MediaControlElement::styleForElement();
+    style->setPosition(AbsolutePosition);
+    style->setLeft(Length(m_x, Fixed));
+    style->setTop(Length(m_y, Fixed));
+    style->setDisplay(m_isVisible ? BLOCK : NONE);
+    return style;
+}
+
+void MediaControlVolumeSliderContainerElement::setVisible(bool visible)
+{
+    if (visible == m_isVisible)
+        return;
+    m_isVisible = visible;
+}
+
+void MediaControlVolumeSliderContainerElement::setPosition(int x, int y)
+{
+    if (x == m_x && y == m_y)
+        return;
+    m_x = x;
+    m_y = y;
+}
+
+bool MediaControlVolumeSliderContainerElement::hitTest(const IntPoint& absPoint)
+{
+    if (renderer() && renderer()->style()->hasAppearance())
+        return renderer()->theme()->hitTestMediaControlPart(renderer(), absPoint);
+
+    return false;
+}
+
+// ----------------------------
+
+inline MediaControlStatusDisplayElement::MediaControlStatusDisplayElement(HTMLMediaElement* mediaElement)
+    : MediaControlElement(mediaElement, MEDIA_CONTROLS_STATUS_DISPLAY)
+    , m_stateBeingDisplayed(Nothing)
+{
+}
+
+PassRefPtr<MediaControlStatusDisplayElement> MediaControlStatusDisplayElement::create(HTMLMediaElement* mediaElement)
+{
+    return adoptRef(new MediaControlStatusDisplayElement(mediaElement));
+}
+
+void MediaControlStatusDisplayElement::update()
+{
+    MediaControlElement::update();
+
+    // Get the new state that we'll have to display.
+    StateBeingDisplayed newStateToDisplay = Nothing;
+
+    if (mediaElement()->readyState() != HTMLMediaElement::HAVE_ENOUGH_DATA && !mediaElement()->currentSrc().isEmpty())
+        newStateToDisplay = Loading;
+    else if (mediaElement()->movieLoadType() == MediaPlayer::LiveStream)
+        newStateToDisplay = LiveBroadcast;
+
+    // Propagate only if needed.
+    if (newStateToDisplay == m_stateBeingDisplayed)
+        return;
+    m_stateBeingDisplayed = newStateToDisplay;
+
+    ExceptionCode e;
+    switch (m_stateBeingDisplayed) {
+    case Nothing:
+        setInnerText("", e);
+        break;
+    case Loading:
+        setInnerText(mediaElementLoadingStateText(), e);
+        break;
+    case LiveBroadcast:
+        setInnerText(mediaElementLiveBroadcastStateText(), e);
+        break;
+    }
+}
+
+bool MediaControlStatusDisplayElement::rendererIsNeeded(RenderStyle* style)
+{
+    if (!MediaControlElement::rendererIsNeeded(style))
+        return false;
+    float duration = mediaElement()->duration();
+    return (isnan(duration) || isinf(duration));
+}
+
+// ----------------------------
+    
+MediaControlInputElement::MediaControlInputElement(HTMLMediaElement* mediaElement, PseudoId pseudo, const String& type)
+    : HTMLInputElement(inputTag, mediaElement->document())
+    , m_mediaElement(mediaElement)
+    , m_pseudoStyleId(pseudo)
+{
+    setInputType(type);
+    setInDocument();
+
+    switch (pseudo) {
+    case MEDIA_CONTROLS_MUTE_BUTTON:
+        m_displayType = MediaMuteButton;
+        break;
+    case MEDIA_CONTROLS_PLAY_BUTTON:
+        m_displayType = MediaPlayButton;
+        break;
+    case MEDIA_CONTROLS_SEEK_FORWARD_BUTTON:
+        m_displayType = MediaSeekForwardButton;
+        break;
+    case MEDIA_CONTROLS_SEEK_BACK_BUTTON:
+        m_displayType = MediaSeekBackButton;
+        break;
+    case MEDIA_CONTROLS_FULLSCREEN_BUTTON:
+        m_displayType = MediaFullscreenButton;
+        break;
+    case MEDIA_CONTROLS_TIMELINE:
+        m_displayType = MediaSlider;
+        break;
+    case MEDIA_CONTROLS_RETURN_TO_REALTIME_BUTTON:
+        m_displayType = MediaReturnToRealtimeButton;
+        break;
+    case MEDIA_CONTROLS_REWIND_BUTTON:
+        m_displayType = MediaRewindButton;
+        break;
+    case MEDIA_CONTROLS_VOLUME_SLIDER:
+        m_displayType = MediaVolumeSlider;
+        break;
+    case MEDIA_CONTROLS_VOLUME_SLIDER_MUTE_BUTTON:
+        m_displayType = MediaVolumeSliderMuteButton;
+        break;
+    case MEDIA_CONTROLS_TOGGLE_CLOSED_CAPTIONS_BUTTON:
+        m_displayType = MediaShowClosedCaptionsButton;
+        break;
+    default:
+        ASSERT_NOT_REACHED();
+        break;
+    }
+}
+
+void MediaControlInputElement::attachToParent(Element* parent)
+{
+    parent->legacyParserAddChild(this);
+}
+
+void MediaControlInputElement::update()
+{
+    updateDisplayType();
+    if (renderer())
+        renderer()->updateFromElement();
+    updateStyle();
+}
+
+PassRefPtr<RenderStyle> MediaControlInputElement::styleForElement()
+{
+    return mediaElement()->renderer()->getCachedPseudoStyle(m_pseudoStyleId);
+}
+
+bool MediaControlInputElement::rendererIsNeeded(RenderStyle* style)
+{
+    ASSERT(document()->page());
+
+    return HTMLInputElement::rendererIsNeeded(style) && parent() && parent()->renderer()
+        && (!style->hasAppearance() || document()->page()->theme()->shouldRenderMediaControlPart(style->appearance(), mediaElement()));
+}
+
+void MediaControlInputElement::attach()
+{
+    RefPtr<RenderStyle> style = styleForElement();
+    if (!style)
+        return;
+    
+    bool needsRenderer = rendererIsNeeded(style.get());
+    if (!needsRenderer)
+        return;
+    RenderObject* renderer = createRenderer(mediaElement()->renderer()->renderArena(), style.get());
+    if (!renderer)
+        return;
+    renderer->setStyle(style.get());
+    setRenderer(renderer);
+    if (parent() && parent()->renderer()) {
+        // Find next sibling with a renderer to determine where to insert.
+        Node* sibling = nextSibling();
+        while (sibling && !sibling->renderer())
+            sibling = sibling->nextSibling();
+        parent()->renderer()->addChild(renderer, sibling ? sibling->renderer() : 0);
+    }  
+    ContainerNode::attach();
+}
+
+void MediaControlInputElement::updateStyle()
+{
+    if (!mediaElement() || !mediaElement()->renderer())
+        return;
+    
+    RefPtr<RenderStyle> style = styleForElement();
+    if (!style)
+        return;
+    
+    bool needsRenderer = rendererIsNeeded(style.get()) && parent() && parent()->renderer();
+    if (renderer() && !needsRenderer)
+        detach();
+    else if (!renderer() && needsRenderer)
+        attach();
+    else if (renderer())
+        renderer()->setStyle(style.get());
+}
+    
+bool MediaControlInputElement::hitTest(const IntPoint& absPoint)
+{
+    if (renderer() && renderer()->style()->hasAppearance())
+        return renderer()->theme()->hitTestMediaControlPart(renderer(), absPoint);
+
+    return false;
+}
+
+void MediaControlInputElement::setDisplayType(MediaControlElementType displayType)
+{
+    if (displayType == m_displayType)
+        return;
+
+    m_displayType = displayType;
+    if (RenderObject* object = renderer())
+        object->repaint();
+}
+
+// ----------------------------
+
+inline MediaControlMuteButtonElement::MediaControlMuteButtonElement(HTMLMediaElement* mediaElement, ButtonLocation location)
+    : MediaControlInputElement(mediaElement, location == Controller ? MEDIA_CONTROLS_MUTE_BUTTON : MEDIA_CONTROLS_VOLUME_SLIDER_MUTE_BUTTON, "button")
+{
+}
+
+PassRefPtr<MediaControlMuteButtonElement> MediaControlMuteButtonElement::create(HTMLMediaElement* mediaElement, ButtonLocation location)
+{
+    return adoptRef(new MediaControlMuteButtonElement(mediaElement, location));
+}
+
+void MediaControlMuteButtonElement::defaultEventHandler(Event* event)
+{
+    if (event->type() == eventNames().clickEvent) {
+        mediaElement()->setMuted(!mediaElement()->muted());
+        event->setDefaultHandled();
+    }
+    HTMLInputElement::defaultEventHandler(event);
+}
+
+void MediaControlMuteButtonElement::updateDisplayType()
+{
+    setDisplayType(mediaElement()->muted() ? MediaUnMuteButton : MediaMuteButton);
+}
+
+// ----------------------------
+
+inline MediaControlPlayButtonElement::MediaControlPlayButtonElement(HTMLMediaElement* mediaElement)
+    : MediaControlInputElement(mediaElement, MEDIA_CONTROLS_PLAY_BUTTON, "button")
+{
+}
+
+PassRefPtr<MediaControlPlayButtonElement> MediaControlPlayButtonElement::create(HTMLMediaElement* mediaElement)
+{
+    return adoptRef(new MediaControlPlayButtonElement(mediaElement));
+}
+
+void MediaControlPlayButtonElement::defaultEventHandler(Event* event)
+{
+    if (event->type() == eventNames().clickEvent) {
+        mediaElement()->togglePlayState();
+        event->setDefaultHandled();
+    }
+    HTMLInputElement::defaultEventHandler(event);
+}
+
+void MediaControlPlayButtonElement::updateDisplayType()
+{
+    setDisplayType(mediaElement()->canPlay() ? MediaPlayButton : MediaPauseButton);
+}
+
+// ----------------------------
+
+inline MediaControlSeekButtonElement::MediaControlSeekButtonElement(HTMLMediaElement* mediaElement, PseudoId pseudoId)
+    : MediaControlInputElement(mediaElement, pseudoId, "button")
+    , m_seeking(false)
+    , m_capturing(false)
+    , m_seekTimer(this, &MediaControlSeekButtonElement::seekTimerFired)
+{
+}
+
+PassRefPtr<MediaControlSeekButtonElement> MediaControlSeekButtonElement::create(HTMLMediaElement* mediaElement, PseudoId pseudoStyleId)
+{
+    return adoptRef(new MediaControlSeekButtonElement(mediaElement, pseudoStyleId));
+}
+
+inline bool MediaControlSeekButtonElement::isForwardButton() const
+{
+    return pseudoStyleId() == MEDIA_CONTROLS_SEEK_FORWARD_BUTTON;
+}
+
+void MediaControlSeekButtonElement::defaultEventHandler(Event* event)
+{
+    if (event->type() == eventNames().mousedownEvent) {
+        if (Frame* frame = document()->frame()) {
+            m_capturing = true;
+            frame->eventHandler()->setCapturingMouseEventsNode(this);
+        }
+        mediaElement()->pause(event->fromUserGesture());
+        m_seekTimer.startRepeating(cSeekRepeatDelay);
+        event->setDefaultHandled();
+    } else if (event->type() == eventNames().mouseupEvent) {
+        if (m_capturing)
+            if (Frame* frame = document()->frame()) {
+                m_capturing = false;
+                frame->eventHandler()->setCapturingMouseEventsNode(0);
+            }
+        ExceptionCode ec;
+        if (m_seeking || m_seekTimer.isActive()) {
+            if (!m_seeking) {
+                float stepTime = isForwardButton() ? cStepTime : -cStepTime;
+                mediaElement()->setCurrentTime(mediaElement()->currentTime() + stepTime, ec);
+            }
+            m_seekTimer.stop();
+            m_seeking = false;
+            event->setDefaultHandled();
+        }
+    }
+    HTMLInputElement::defaultEventHandler(event);
+}
+
+void MediaControlSeekButtonElement::seekTimerFired(Timer<MediaControlSeekButtonElement>*)
+{
+    ExceptionCode ec;
+    m_seeking = true;
+    float seekTime = isForwardButton() ? cSeekTime : -cSeekTime;
+    mediaElement()->setCurrentTime(mediaElement()->currentTime() + seekTime, ec);
+}
+
+void MediaControlSeekButtonElement::detach()
+{
+    if (m_capturing) {
+        if (Frame* frame = document()->frame())
+            frame->eventHandler()->setCapturingMouseEventsNode(0);      
+    }
+    MediaControlInputElement::detach();
+}
+
+// ----------------------------
+
+inline MediaControlRewindButtonElement::MediaControlRewindButtonElement(HTMLMediaElement* element)
+    : MediaControlInputElement(element, MEDIA_CONTROLS_REWIND_BUTTON, "button")
+{
+}
+
+PassRefPtr<MediaControlRewindButtonElement> MediaControlRewindButtonElement::create(HTMLMediaElement* mediaElement)
+{
+    return adoptRef(new MediaControlRewindButtonElement(mediaElement));
+}
+
+void MediaControlRewindButtonElement::defaultEventHandler(Event* event)
+{
+    if (event->type() == eventNames().clickEvent) {
+        mediaElement()->rewind(30);
+        event->setDefaultHandled();
+    }    
+    HTMLInputElement::defaultEventHandler(event);
+}
+
+
+// ----------------------------
+
+inline MediaControlReturnToRealtimeButtonElement::MediaControlReturnToRealtimeButtonElement(HTMLMediaElement* mediaElement)
+    : MediaControlInputElement(mediaElement, MEDIA_CONTROLS_RETURN_TO_REALTIME_BUTTON, "button")
+{
+}
+
+PassRefPtr<MediaControlReturnToRealtimeButtonElement> MediaControlReturnToRealtimeButtonElement::create(HTMLMediaElement* mediaElement)
+{
+    return adoptRef(new MediaControlReturnToRealtimeButtonElement(mediaElement));
+}
+
+void MediaControlReturnToRealtimeButtonElement::defaultEventHandler(Event* event)
+{
+    if (event->type() == eventNames().clickEvent) {
+        mediaElement()->returnToRealtime();
+        event->setDefaultHandled();
+    }
+    HTMLInputElement::defaultEventHandler(event);
+}
+
+
+// ----------------------------
+
+inline MediaControlToggleClosedCaptionsButtonElement::MediaControlToggleClosedCaptionsButtonElement(HTMLMediaElement* mediaElement)
+    : MediaControlInputElement(mediaElement, MEDIA_CONTROLS_TOGGLE_CLOSED_CAPTIONS_BUTTON, "button")
+{
+}
+
+PassRefPtr<MediaControlToggleClosedCaptionsButtonElement> MediaControlToggleClosedCaptionsButtonElement::create(HTMLMediaElement* mediaElement)
+{
+    return adoptRef(new MediaControlToggleClosedCaptionsButtonElement(mediaElement));
+}
+
+void MediaControlToggleClosedCaptionsButtonElement::defaultEventHandler(Event* event)
+{
+    if (event->type() == eventNames().clickEvent) {
+        mediaElement()->setClosedCaptionsVisible(!mediaElement()->closedCaptionsVisible());
+        setChecked(mediaElement()->closedCaptionsVisible());
+        event->setDefaultHandled();
+    }
+    HTMLInputElement::defaultEventHandler(event);
+}
+
+void MediaControlToggleClosedCaptionsButtonElement::updateDisplayType()
+{
+    setDisplayType(mediaElement()->closedCaptionsVisible() ? MediaHideClosedCaptionsButton : MediaShowClosedCaptionsButton);
+}
+
+// ----------------------------
+
+MediaControlTimelineElement::MediaControlTimelineElement(HTMLMediaElement* mediaElement)
+    : MediaControlInputElement(mediaElement, MEDIA_CONTROLS_TIMELINE, "range")
+{
+}
+
+PassRefPtr<MediaControlTimelineElement> MediaControlTimelineElement::create(HTMLMediaElement* mediaElement)
+{
+    return adoptRef(new MediaControlTimelineElement(mediaElement));
+}
+
+void MediaControlTimelineElement::defaultEventHandler(Event* event)
+{
+    // Left button is 0. Rejects mouse events not from left button.
+    if (event->isMouseEvent() && static_cast<MouseEvent*>(event)->button())
+        return;
+
+    if (!attached())
+        return;
+
+    if (event->type() == eventNames().mousedownEvent)
+        mediaElement()->beginScrubbing();
+
+    MediaControlInputElement::defaultEventHandler(event);
+
+    if (event->type() == eventNames().mouseoverEvent || event->type() == eventNames().mouseoutEvent || event->type() == eventNames().mousemoveEvent)
+        return;
+
+    float time = narrowPrecisionToFloat(value().toDouble());
+    if (time != mediaElement()->currentTime()) {
+        ExceptionCode ec;
+        mediaElement()->setCurrentTime(time, ec);
+    }
+
+    RenderSlider* slider = toRenderSlider(renderer());
+    if (slider && slider->inDragMode())
+        toRenderMedia(mediaElement()->renderer())->updateTimeDisplay();
+
+    if (event->type() == eventNames().mouseupEvent)
+        mediaElement()->endScrubbing();
+}
+
+void MediaControlTimelineElement::update(bool updateDuration) 
+{
+    if (updateDuration) {
+        float duration = mediaElement()->duration();
+        setAttribute(maxAttr, String::number(isfinite(duration) ? duration : 0));
+    }
+    setValue(String::number(mediaElement()->currentTime()));
+    MediaControlInputElement::update();
+}
+
+// ----------------------------
+
+inline MediaControlVolumeSliderElement::MediaControlVolumeSliderElement(HTMLMediaElement* mediaElement)
+    : MediaControlInputElement(mediaElement, MEDIA_CONTROLS_VOLUME_SLIDER, "range")
+{
+}
+
+PassRefPtr<MediaControlVolumeSliderElement> MediaControlVolumeSliderElement::create(HTMLMediaElement* mediaElement)
+{
+    return adoptRef(new MediaControlVolumeSliderElement(mediaElement));
+}
+
+void MediaControlVolumeSliderElement::defaultEventHandler(Event* event)
+{
+    // Left button is 0. Rejects mouse events not from left button.
+    if (event->isMouseEvent() && static_cast<MouseEvent*>(event)->button())
+        return;
+
+    if (!attached())
+        return;
+
+    MediaControlInputElement::defaultEventHandler(event);
+
+    if (event->type() == eventNames().mouseoverEvent || event->type() == eventNames().mouseoutEvent || event->type() == eventNames().mousemoveEvent)
+        return;
+
+    float volume = narrowPrecisionToFloat(value().toDouble());
+    if (volume != mediaElement()->volume()) {
+        ExceptionCode ec = 0;
+        mediaElement()->setVolume(volume, ec);
+        ASSERT(!ec);
+    }
+}
+
+void MediaControlVolumeSliderElement::update()
+{
+    float volume = mediaElement()->volume();
+    if (value().toFloat() != volume)
+        setValue(String::number(volume));
+    MediaControlInputElement::update();
+}
+
+// ----------------------------
+
+inline MediaControlFullscreenButtonElement::MediaControlFullscreenButtonElement(HTMLMediaElement* mediaElement)
+    : MediaControlInputElement(mediaElement, MEDIA_CONTROLS_FULLSCREEN_BUTTON, "button")
+{
+}
+
+PassRefPtr<MediaControlFullscreenButtonElement> MediaControlFullscreenButtonElement::create(HTMLMediaElement* mediaElement)
+{
+    return adoptRef(new MediaControlFullscreenButtonElement(mediaElement));
+}
+
+void MediaControlFullscreenButtonElement::defaultEventHandler(Event* event)
+{
+    if (event->type() == eventNames().clickEvent) {
+        mediaElement()->enterFullscreen();
+        event->setDefaultHandled();
+    }
+    HTMLInputElement::defaultEventHandler(event);
+}
+
+// ----------------------------
+
+inline MediaControlTimeDisplayElement::MediaControlTimeDisplayElement(HTMLMediaElement* mediaElement, PseudoId pseudo)
+    : MediaControlElement(mediaElement, pseudo)
+    , m_currentValue(0)
+    , m_isVisible(true)
+{
+}
+
+PassRefPtr<MediaControlTimeDisplayElement> MediaControlTimeDisplayElement::create(HTMLMediaElement* mediaElement, PseudoId pseudoStyleId)
+{
+    return adoptRef(new MediaControlTimeDisplayElement(mediaElement, pseudoStyleId));
+}
+
+PassRefPtr<RenderStyle> MediaControlTimeDisplayElement::styleForElement()
+{
+    RefPtr<RenderStyle> style = MediaControlElement::styleForElement();
+    if (!m_isVisible) {
+        style = RenderStyle::clone(style.get());
+        style->setWidth(Length(0, Fixed));
+    }
+    return style;
+}
+
+void MediaControlTimeDisplayElement::setVisible(bool visible)
+{
+    if (visible == m_isVisible)
+        return;
+    m_isVisible = visible;
+
+    // This function is used during the RenderMedia::layout()
+    // call, where we cannot change the renderer at this time.
+    if (!renderer() || !renderer()->style())
+        return;
+
+    RefPtr<RenderStyle> style = styleForElement();
+    renderer()->setStyle(style.get());
+}
+
+void MediaControlTimeDisplayElement::setCurrentValue(float time)
+{
+    m_currentValue = time;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(VIDEO)