WebCore/rendering/RenderMedia.cpp
changeset 0 4f2f89ce4247
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebCore/rendering/RenderMedia.cpp	Fri Sep 17 09:02:29 2010 +0300
@@ -0,0 +1,642 @@
+/*
+ * Copyright (C) 2007, 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR
+ * 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 "RenderMedia.h"
+
+#include "EventNames.h"
+#include "FloatConversion.h"
+#include "HTMLNames.h"
+#include "MediaControlElements.h"
+#include "MouseEvent.h"
+#include "Page.h"
+#include "RenderTheme.h"
+#include <wtf/CurrentTime.h>
+#include <wtf/MathExtras.h>
+
+using namespace std;
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+static const double cTimeUpdateRepeatDelay = 0.2;
+static const double cOpacityAnimationRepeatDelay = 0.05;
+
+RenderMedia::RenderMedia(HTMLMediaElement* video)
+    : RenderImage(video)
+    , m_timeUpdateTimer(this, &RenderMedia::timeUpdateTimerFired)
+    , m_opacityAnimationTimer(this, &RenderMedia::opacityAnimationTimerFired)
+    , m_mouseOver(false)
+    , m_opacityAnimationStartTime(0)
+    , m_opacityAnimationDuration(0)
+    , m_opacityAnimationFrom(0)
+    , m_opacityAnimationTo(1.0f)
+{
+}
+
+RenderMedia::RenderMedia(HTMLMediaElement* video, const IntSize& intrinsicSize)
+    : RenderImage(video)
+    , m_timeUpdateTimer(this, &RenderMedia::timeUpdateTimerFired)
+    , m_opacityAnimationTimer(this, &RenderMedia::opacityAnimationTimerFired)
+    , m_mouseOver(false)
+    , m_opacityAnimationStartTime(0)
+    , m_opacityAnimationDuration(0)
+    , m_opacityAnimationFrom(0)
+    , m_opacityAnimationTo(1.0f)
+{
+    setIntrinsicSize(intrinsicSize);
+}
+
+RenderMedia::~RenderMedia()
+{
+}
+
+void RenderMedia::destroy()
+{
+    if (m_controlsShadowRoot && m_controlsShadowRoot->renderer()) {
+
+        // detach the panel before removing the shadow renderer to prevent a crash in m_controlsShadowRoot->detach() 
+        //  when display: style changes
+        m_panel->detach();
+
+        removeChild(m_controlsShadowRoot->renderer());
+        m_controlsShadowRoot->detach();
+        m_controlsShadowRoot = 0;
+    }
+    RenderImage::destroy();
+}
+
+HTMLMediaElement* RenderMedia::mediaElement() const
+{ 
+    return static_cast<HTMLMediaElement*>(node()); 
+}
+
+MediaPlayer* RenderMedia::player() const
+{
+    return mediaElement()->player();
+}
+
+void RenderMedia::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
+{
+    RenderImage::styleDidChange(diff, oldStyle);
+
+    if (m_controlsShadowRoot) {
+        if (m_panel)
+            m_panel->updateStyle();
+        if (m_muteButton)
+            m_muteButton->updateStyle();
+        if (m_playButton)
+            m_playButton->updateStyle();
+        if (m_seekBackButton)
+            m_seekBackButton->updateStyle();
+        if (m_seekForwardButton)
+            m_seekForwardButton->updateStyle();
+        if (m_rewindButton)
+            m_rewindButton->updateStyle();
+        if (m_returnToRealtimeButton)
+            m_returnToRealtimeButton->updateStyle();
+        if (m_toggleClosedCaptionsButton)
+            m_toggleClosedCaptionsButton->updateStyle();
+        if (m_statusDisplay)
+            m_statusDisplay->updateStyle();
+        if (m_timelineContainer)
+            m_timelineContainer->updateStyle();
+        if (m_timeline)
+            m_timeline->updateStyle();
+        if (m_fullscreenButton)
+            m_fullscreenButton->updateStyle();
+        if (m_currentTimeDisplay)
+            m_currentTimeDisplay->updateStyle();
+        if (m_timeRemainingDisplay)
+            m_timeRemainingDisplay->updateStyle();
+        if (m_volumeSliderContainer)
+            m_volumeSliderContainer->updateStyle();
+        if (m_volumeSliderMuteButton)
+            m_volumeSliderMuteButton->updateStyle();
+        if (m_volumeSlider)
+            m_volumeSlider->updateStyle();
+    }
+}
+
+void RenderMedia::layout()
+{
+    IntSize oldSize = contentBoxRect().size();
+
+    RenderImage::layout();
+
+    RenderBox* controlsRenderer = m_controlsShadowRoot ? m_controlsShadowRoot->renderBox() : 0;
+    if (!controlsRenderer)
+        return;
+    IntSize newSize = contentBoxRect().size();
+    if (newSize != oldSize || controlsRenderer->needsLayout()) {
+
+        if (m_currentTimeDisplay && m_timeRemainingDisplay) {
+            bool shouldShowTimeDisplays = shouldShowTimeDisplayControls();
+            m_currentTimeDisplay->setVisible(shouldShowTimeDisplays);
+            m_timeRemainingDisplay->setVisible(shouldShowTimeDisplays);
+        }
+
+        controlsRenderer->setLocation(borderLeft() + paddingLeft(), borderTop() + paddingTop());
+        controlsRenderer->style()->setHeight(Length(newSize.height(), Fixed));
+        controlsRenderer->style()->setWidth(Length(newSize.width(), Fixed));
+        controlsRenderer->setNeedsLayout(true, false);
+        controlsRenderer->layout();
+        setChildNeedsLayout(false);
+    }
+}
+
+void RenderMedia::createControlsShadowRoot()
+{
+    ASSERT(!m_controlsShadowRoot);
+    m_controlsShadowRoot = MediaControlShadowRootElement::create(mediaElement());
+    addChild(m_controlsShadowRoot->renderer());
+}
+
+void RenderMedia::createPanel()
+{
+    ASSERT(!m_panel);
+    m_panel = MediaControlElement::create(mediaElement(), MEDIA_CONTROLS_PANEL);
+    m_panel->attachToParent(m_controlsShadowRoot.get());
+}
+
+void RenderMedia::createMuteButton()
+{
+    ASSERT(!m_muteButton);
+    m_muteButton = MediaControlMuteButtonElement::create(mediaElement(), MediaControlMuteButtonElement::Controller);
+    m_muteButton->attachToParent(m_panel.get());
+}
+
+void RenderMedia::createPlayButton()
+{
+    ASSERT(!m_playButton);
+    m_playButton = MediaControlPlayButtonElement::create(mediaElement());
+    m_playButton->attachToParent(m_panel.get());
+}
+
+void RenderMedia::createSeekBackButton()
+{
+    ASSERT(!m_seekBackButton);
+    m_seekBackButton = MediaControlSeekButtonElement::create(mediaElement(), MEDIA_CONTROLS_SEEK_BACK_BUTTON);
+    m_seekBackButton->attachToParent(m_panel.get());
+}
+
+void RenderMedia::createSeekForwardButton()
+{
+    ASSERT(!m_seekForwardButton);
+    m_seekForwardButton = MediaControlSeekButtonElement::create(mediaElement(), MEDIA_CONTROLS_SEEK_FORWARD_BUTTON);
+    m_seekForwardButton->attachToParent(m_panel.get());
+}
+
+void RenderMedia::createRewindButton()
+{
+    ASSERT(!m_rewindButton);
+    m_rewindButton = MediaControlRewindButtonElement::create(mediaElement());
+    m_rewindButton->attachToParent(m_panel.get());
+}
+
+void RenderMedia::createReturnToRealtimeButton()
+{
+    ASSERT(!m_returnToRealtimeButton);
+    m_returnToRealtimeButton = MediaControlReturnToRealtimeButtonElement::create(mediaElement());
+    m_returnToRealtimeButton->attachToParent(m_panel.get());
+}
+
+void RenderMedia::createToggleClosedCaptionsButton()
+{
+    ASSERT(!m_toggleClosedCaptionsButton);
+    m_toggleClosedCaptionsButton = MediaControlToggleClosedCaptionsButtonElement::create(mediaElement());
+    m_toggleClosedCaptionsButton->attachToParent(m_panel.get());
+}
+
+void RenderMedia::createStatusDisplay()
+{
+    ASSERT(!m_statusDisplay);
+    m_statusDisplay = MediaControlStatusDisplayElement::create(mediaElement());
+    m_statusDisplay->attachToParent(m_panel.get());
+}
+
+void RenderMedia::createTimelineContainer()
+{
+    ASSERT(!m_timelineContainer);
+    m_timelineContainer = MediaControlTimelineContainerElement::create(mediaElement());
+    m_timelineContainer->attachToParent(m_panel.get());
+}
+
+void RenderMedia::createTimeline()
+{
+    ASSERT(!m_timeline);
+    m_timeline = MediaControlTimelineElement::create(mediaElement());
+    m_timeline->setAttribute(precisionAttr, "float");
+    m_timeline->attachToParent(m_timelineContainer.get());
+}
+
+void RenderMedia::createVolumeSliderContainer()
+{
+    ASSERT(!m_volumeSliderContainer);
+    m_volumeSliderContainer = MediaControlVolumeSliderContainerElement::create(mediaElement());
+    m_volumeSliderContainer->attachToParent(m_panel.get());
+}
+
+void RenderMedia::createVolumeSlider()
+{
+    ASSERT(!m_volumeSlider);
+    m_volumeSlider = MediaControlVolumeSliderElement::create(mediaElement());
+    m_volumeSlider->setAttribute(precisionAttr, "float");
+    m_volumeSlider->setAttribute(maxAttr, "1");
+    m_volumeSlider->setAttribute(valueAttr, String::number(mediaElement()->volume()));
+    m_volumeSlider->attachToParent(m_volumeSliderContainer.get());
+}
+
+void RenderMedia::createVolumeSliderMuteButton()
+{
+    ASSERT(!m_volumeSliderMuteButton);
+    m_volumeSliderMuteButton = MediaControlMuteButtonElement::create(mediaElement(), MediaControlMuteButtonElement::VolumeSlider);
+    m_volumeSliderMuteButton->attachToParent(m_volumeSliderContainer.get());
+    
+}
+
+void RenderMedia::createCurrentTimeDisplay()
+{
+    ASSERT(!m_currentTimeDisplay);
+    m_currentTimeDisplay = MediaControlTimeDisplayElement::create(mediaElement(), MEDIA_CONTROLS_CURRENT_TIME_DISPLAY);
+    m_currentTimeDisplay->attachToParent(m_timelineContainer.get());
+}
+
+void RenderMedia::createTimeRemainingDisplay()
+{
+    ASSERT(!m_timeRemainingDisplay);
+    m_timeRemainingDisplay = MediaControlTimeDisplayElement::create(mediaElement(), MEDIA_CONTROLS_TIME_REMAINING_DISPLAY);
+    m_timeRemainingDisplay->attachToParent(m_timelineContainer.get());
+}
+
+void RenderMedia::createFullscreenButton()
+{
+    ASSERT(!m_fullscreenButton);
+    m_fullscreenButton = MediaControlFullscreenButtonElement::create(mediaElement());
+    m_fullscreenButton->attachToParent(m_panel.get());
+}
+    
+void RenderMedia::updateFromElement()
+{
+    updateControls();
+}
+            
+void RenderMedia::updateControls()
+{
+    HTMLMediaElement* media = mediaElement();
+    if (!media->controls() || !media->inActiveDocument()) {
+        if (m_controlsShadowRoot) {
+            m_controlsShadowRoot->detach();
+            m_panel = 0;
+            m_muteButton = 0;
+            m_playButton = 0;
+            m_statusDisplay = 0;
+            m_timelineContainer = 0;
+            m_timeline = 0;
+            m_seekBackButton = 0;
+            m_seekForwardButton = 0;
+            m_rewindButton = 0;
+            m_returnToRealtimeButton = 0;
+            m_currentTimeDisplay = 0;
+            m_timeRemainingDisplay = 0;
+            m_fullscreenButton = 0;
+            m_volumeSliderContainer = 0;
+            m_volumeSlider = 0;
+            m_volumeSliderMuteButton = 0;
+            m_controlsShadowRoot = 0;
+            m_toggleClosedCaptionsButton = 0;
+        }
+        m_opacityAnimationTo = 1.0f;
+        m_opacityAnimationTimer.stop();
+        m_timeUpdateTimer.stop();
+        return;
+    }
+    
+    if (!m_controlsShadowRoot) {
+        createControlsShadowRoot();
+        createPanel();
+        if (m_panel) {
+            createRewindButton();
+            createPlayButton();
+            createReturnToRealtimeButton();
+            createStatusDisplay();
+            createTimelineContainer();
+            if (m_timelineContainer) {
+                createCurrentTimeDisplay();
+                createTimeline();
+                createTimeRemainingDisplay();
+            }
+            createSeekBackButton();
+            createSeekForwardButton();
+            createToggleClosedCaptionsButton();
+            createFullscreenButton();
+            createMuteButton();
+            createVolumeSliderContainer();
+            if (m_volumeSliderContainer) {
+                createVolumeSlider();
+                createVolumeSliderMuteButton();
+            }
+            m_panel->attach();
+        }
+    }
+
+    if (media->canPlay()) {
+        if (m_timeUpdateTimer.isActive())
+            m_timeUpdateTimer.stop();
+    } else if (style()->visibility() == VISIBLE && m_timeline && m_timeline->renderer() && m_timeline->renderer()->style()->display() != NONE) {
+        m_timeUpdateTimer.startRepeating(cTimeUpdateRepeatDelay);
+    }
+
+    
+    if (m_panel) {
+        // update() might alter the opacity of the element, especially if we are in the middle
+        // of an animation. This is the only element concerned as we animate only this element.
+        float opacityBeforeChangingStyle = m_panel->renderer() ? m_panel->renderer()->style()->opacity() : 0;
+        m_panel->update();
+        changeOpacity(m_panel.get(), opacityBeforeChangingStyle);
+    }
+    if (m_muteButton)
+        m_muteButton->update();
+    if (m_playButton)
+        m_playButton->update();
+    if (m_timelineContainer)
+        m_timelineContainer->update();
+    if (m_volumeSliderContainer)
+        m_volumeSliderContainer->update();
+    if (m_timeline)
+        m_timeline->update();
+    if (m_currentTimeDisplay)
+        m_currentTimeDisplay->update();
+    if (m_timeRemainingDisplay)
+        m_timeRemainingDisplay->update();
+    if (m_seekBackButton)
+        m_seekBackButton->update();
+    if (m_seekForwardButton)
+        m_seekForwardButton->update();
+    if (m_rewindButton)
+        m_rewindButton->update();
+    if (m_returnToRealtimeButton)
+        m_returnToRealtimeButton->update();
+    if (m_toggleClosedCaptionsButton)
+        m_toggleClosedCaptionsButton->update();
+    if (m_statusDisplay)
+        m_statusDisplay->update();
+    if (m_fullscreenButton)
+        m_fullscreenButton->update();
+    if (m_volumeSlider)
+        m_volumeSlider->update();
+    if (m_volumeSliderMuteButton)
+        m_volumeSliderMuteButton->update();
+
+    updateTimeDisplay();
+    updateControlVisibility();
+}
+
+void RenderMedia::timeUpdateTimerFired(Timer<RenderMedia>*)
+{
+    if (m_timeline)
+        m_timeline->update(false);
+    updateTimeDisplay();
+}
+    
+void RenderMedia::updateTimeDisplay()
+{
+    if (!m_currentTimeDisplay || !m_currentTimeDisplay->renderer() || m_currentTimeDisplay->renderer()->style()->display() == NONE || style()->visibility() != VISIBLE)
+        return;
+
+    float now = mediaElement()->currentTime();
+    float duration = mediaElement()->duration();
+
+    // Allow the theme to format the time
+    ExceptionCode ec;
+    m_currentTimeDisplay->setInnerText(theme()->formatMediaControlsCurrentTime(now, duration), ec);
+    m_currentTimeDisplay->setCurrentValue(now);
+    m_timeRemainingDisplay->setInnerText(theme()->formatMediaControlsRemainingTime(now, duration), ec);
+    m_timeRemainingDisplay->setCurrentValue(now - duration);
+}
+
+void RenderMedia::updateControlVisibility() 
+{
+    if (!m_panel || !m_panel->renderer())
+        return;
+
+    // Don't fade for audio controls.
+    HTMLMediaElement* media = mediaElement();
+    if (!media->hasVideo())
+        return;
+
+    // Don't fade if the media element is not visible
+    if (style()->visibility() != VISIBLE)
+        return;
+    
+    bool shouldHideController = !m_mouseOver && !media->canPlay();
+
+    // Do fading manually, css animations don't work with shadow trees
+
+    float animateFrom = m_panel->renderer()->style()->opacity();
+    float animateTo = shouldHideController ? 0.0f : 1.0f;
+
+    if (animateFrom == animateTo)
+        return;
+
+    if (m_opacityAnimationTimer.isActive()) {
+        if (m_opacityAnimationTo == animateTo)
+            return;
+        m_opacityAnimationTimer.stop();
+    }
+
+    if (animateFrom < animateTo)
+        m_opacityAnimationDuration = m_panel->renderer()->theme()->mediaControlsFadeInDuration();
+    else
+        m_opacityAnimationDuration = m_panel->renderer()->theme()->mediaControlsFadeOutDuration();
+
+    m_opacityAnimationFrom = animateFrom;
+    m_opacityAnimationTo = animateTo;
+
+    m_opacityAnimationStartTime = currentTime();
+    m_opacityAnimationTimer.startRepeating(cOpacityAnimationRepeatDelay);
+}
+    
+void RenderMedia::changeOpacity(HTMLElement* e, float opacity) 
+{
+    if (!e || !e->renderer() || !e->renderer()->style())
+        return;
+    RefPtr<RenderStyle> s = RenderStyle::clone(e->renderer()->style());
+    s->setOpacity(opacity);
+    // z-index can't be auto if opacity is used
+    s->setZIndex(0);
+    e->renderer()->setStyle(s.release());
+}
+    
+void RenderMedia::opacityAnimationTimerFired(Timer<RenderMedia>*)
+{
+    double time = currentTime() - m_opacityAnimationStartTime;
+    if (time >= m_opacityAnimationDuration) {
+        time = m_opacityAnimationDuration;
+        m_opacityAnimationTimer.stop();
+    }
+    float opacity = narrowPrecisionToFloat(m_opacityAnimationFrom + (m_opacityAnimationTo - m_opacityAnimationFrom) * time / m_opacityAnimationDuration);
+    changeOpacity(m_panel.get(), opacity);
+}
+
+void RenderMedia::updateVolumeSliderContainer(bool visible)
+{
+    if (!mediaElement()->hasAudio() || !m_volumeSliderContainer || !m_volumeSlider)
+        return;
+
+    if (visible && !m_volumeSliderContainer->isVisible()) {
+        if (!m_muteButton || !m_muteButton->renderer() || !m_muteButton->renderBox())
+            return;
+
+        RefPtr<RenderStyle> s = m_volumeSliderContainer->styleForElement();
+        int height = s->height().isPercent() ? 0 : s->height().value();
+        int width = s->width().isPercent() ? 0 : s->width().value();
+        IntPoint offset = document()->page()->theme()->volumeSliderOffsetFromMuteButton(m_muteButton->renderer()->node(), IntSize(width, height));
+        int x = offset.x() + m_muteButton->renderBox()->offsetLeft();
+        int y = offset.y() + m_muteButton->renderBox()->offsetTop();
+
+        m_volumeSliderContainer->setPosition(x, y);
+        m_volumeSliderContainer->setVisible(true);
+        m_volumeSliderContainer->update();
+        m_volumeSlider->update();
+    } else if (!visible && m_volumeSliderContainer->isVisible()) {
+        m_volumeSliderContainer->setVisible(false);
+        m_volumeSliderContainer->updateStyle();
+    }
+}
+
+void RenderMedia::forwardEvent(Event* event)
+{
+    if (event->isMouseEvent() && m_controlsShadowRoot) {
+        MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
+        IntPoint point(mouseEvent->absoluteLocation());
+
+        bool defaultHandled = false;
+        if (m_volumeSliderMuteButton && m_volumeSliderMuteButton->hitTest(point)) {
+            m_volumeSliderMuteButton->defaultEventHandler(event);
+            defaultHandled = event->defaultHandled();
+        }
+
+        bool showVolumeSlider = false;
+        if (!defaultHandled && m_muteButton && m_muteButton->hitTest(point)) {
+            m_muteButton->defaultEventHandler(event);
+            if (event->type() != eventNames().mouseoutEvent)
+                showVolumeSlider = true;
+        }
+
+        if (m_volumeSliderContainer && m_volumeSliderContainer->hitTest(point))
+            showVolumeSlider = true;
+
+        if (m_volumeSlider && m_volumeSlider->hitTest(point)) {
+            m_volumeSlider->defaultEventHandler(event);
+            showVolumeSlider = true;
+        }
+
+        updateVolumeSliderContainer(showVolumeSlider);
+
+        if (m_playButton && m_playButton->hitTest(point))
+            m_playButton->defaultEventHandler(event);
+
+        if (m_seekBackButton && m_seekBackButton->hitTest(point))
+            m_seekBackButton->defaultEventHandler(event);
+
+        if (m_seekForwardButton && m_seekForwardButton->hitTest(point))
+            m_seekForwardButton->defaultEventHandler(event);
+
+        if (m_rewindButton && m_rewindButton->hitTest(point))
+            m_rewindButton->defaultEventHandler(event);
+
+        if (m_returnToRealtimeButton && m_returnToRealtimeButton->hitTest(point))
+            m_returnToRealtimeButton->defaultEventHandler(event);
+
+       if (m_toggleClosedCaptionsButton && m_toggleClosedCaptionsButton->hitTest(point))
+            m_toggleClosedCaptionsButton->defaultEventHandler(event);
+
+        if (m_timeline && m_timeline->hitTest(point))
+            m_timeline->defaultEventHandler(event);
+
+        if (m_fullscreenButton && m_fullscreenButton->hitTest(point))
+            m_fullscreenButton->defaultEventHandler(event);
+        
+        if (event->type() == eventNames().mouseoverEvent) {
+            m_mouseOver = true;
+            updateControlVisibility();
+        }
+        if (event->type() == eventNames().mouseoutEvent) {
+            // When the scrollbar thumb captures mouse events, we should treat the mouse as still being over our renderer if the new target is a descendant
+            Node* mouseOverNode = mouseEvent->relatedTarget() ? mouseEvent->relatedTarget()->toNode() : 0;
+            RenderObject* mouseOverRenderer = mouseOverNode ? mouseOverNode->renderer() : 0;
+            m_mouseOver = mouseOverRenderer && mouseOverRenderer->isDescendantOf(this);
+            updateControlVisibility();
+        }
+    }
+}
+
+int RenderMedia::lowestPosition(bool includeOverflowInterior, bool includeSelf) const
+{
+    int bottom = RenderImage::lowestPosition(includeOverflowInterior, includeSelf);
+    if (!m_controlsShadowRoot || !m_controlsShadowRoot->renderer())
+        return bottom;
+    
+    return max(bottom,  m_controlsShadowRoot->renderBox()->y() + m_controlsShadowRoot->renderBox()->lowestPosition(includeOverflowInterior, includeSelf));
+}
+
+int RenderMedia::rightmostPosition(bool includeOverflowInterior, bool includeSelf) const
+{
+    int right = RenderImage::rightmostPosition(includeOverflowInterior, includeSelf);
+    if (!m_controlsShadowRoot || !m_controlsShadowRoot->renderer())
+        return right;
+    
+    return max(right, m_controlsShadowRoot->renderBox()->x() + m_controlsShadowRoot->renderBox()->rightmostPosition(includeOverflowInterior, includeSelf));
+}
+
+int RenderMedia::leftmostPosition(bool includeOverflowInterior, bool includeSelf) const
+{
+    int left = RenderImage::leftmostPosition(includeOverflowInterior, includeSelf);
+    if (!m_controlsShadowRoot || !m_controlsShadowRoot->renderer())
+        return left;
+    
+    return min(left, m_controlsShadowRoot->renderBox()->x() +  m_controlsShadowRoot->renderBox()->leftmostPosition(includeOverflowInterior, includeSelf));
+}
+
+
+// We want the timeline slider to be at least 100 pixels wide.
+static const int minWidthToDisplayTimeDisplays = 16 + 16 + 45 + 100 + 45 + 16 + 1;
+
+bool RenderMedia::shouldShowTimeDisplayControls() const
+{
+    if (!m_currentTimeDisplay && !m_timeRemainingDisplay)
+        return false;
+
+    int width = mediaElement()->renderBox()->width();
+    return width >= minWidthToDisplayTimeDisplays * style()->effectiveZoom();
+}
+
+} // namespace WebCore
+
+#endif