78ae06ae42ec5f268317117663af22c0ddb53067
[WebKit-https.git] / WebCore / rendering / RenderMedia.cpp
1 /*
2  * Copyright (C) 2007, 2008, 2009 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  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27
28 #if ENABLE(VIDEO)
29 #include "RenderMedia.h"
30
31 #include "EventNames.h"
32 #include "FloatConversion.h"
33 #include "HTMLNames.h"
34 #include "MediaControlElements.h"
35 #include "MouseEvent.h"
36 #include "RenderTheme.h"
37 #include <wtf/CurrentTime.h>
38 #include <wtf/MathExtras.h>
39
40 using namespace std;
41
42 namespace WebCore {
43
44 using namespace HTMLNames;
45
46 static const double cTimeUpdateRepeatDelay = 0.2;
47 static const double cOpacityAnimationRepeatDelay = 0.05;
48 // FIXME get this from style
49 static const double cOpacityAnimationDurationFadeIn = 0.1;
50 static const double cOpacityAnimationDurationFadeOut = 0.3;
51
52 RenderMedia::RenderMedia(HTMLMediaElement* video)
53     : RenderReplaced(video)
54     , m_timeUpdateTimer(this, &RenderMedia::timeUpdateTimerFired)
55     , m_opacityAnimationTimer(this, &RenderMedia::opacityAnimationTimerFired)
56     , m_mouseOver(false)
57     , m_opacityAnimationStartTime(0)
58     , m_opacityAnimationDuration(cOpacityAnimationDurationFadeIn)
59     , m_opacityAnimationFrom(0)
60     , m_opacityAnimationTo(1.0f)
61 {
62 }
63
64 RenderMedia::RenderMedia(HTMLMediaElement* video, const IntSize& intrinsicSize)
65     : RenderReplaced(video, intrinsicSize)
66     , m_timeUpdateTimer(this, &RenderMedia::timeUpdateTimerFired)
67     , m_opacityAnimationTimer(this, &RenderMedia::opacityAnimationTimerFired)
68     , m_mouseOver(false)
69     , m_opacityAnimationStartTime(0)
70     , m_opacityAnimationDuration(cOpacityAnimationDurationFadeIn)
71     , m_opacityAnimationFrom(0)
72     , m_opacityAnimationTo(1.0f)
73 {
74 }
75
76 RenderMedia::~RenderMedia()
77 {
78 }
79
80 void RenderMedia::destroy()
81 {
82     if (m_controlsShadowRoot && m_controlsShadowRoot->renderer()) {
83
84         // detach the panel before removing the shadow renderer to prevent a crash in m_controlsShadowRoot->detach() 
85         //  when display: style changes
86         m_panel->detach();
87
88         removeChild(m_controlsShadowRoot->renderer());
89         m_controlsShadowRoot->detach();
90         m_controlsShadowRoot = 0;
91     }
92     RenderReplaced::destroy();
93 }
94
95 HTMLMediaElement* RenderMedia::mediaElement() const
96
97     return static_cast<HTMLMediaElement*>(node()); 
98 }
99
100 MediaPlayer* RenderMedia::player() const
101 {
102     return mediaElement()->player();
103 }
104
105 void RenderMedia::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
106 {
107     RenderReplaced::styleDidChange(diff, oldStyle);
108
109     if (m_controlsShadowRoot) {
110         if (m_panel)
111             m_panel->updateStyle();
112         if (m_muteButton)
113             m_muteButton->updateStyle();
114         if (m_playButton)
115             m_playButton->updateStyle();
116         if (m_seekBackButton)
117             m_seekBackButton->updateStyle();
118         if (m_seekForwardButton)
119             m_seekForwardButton->updateStyle();
120         if (m_rewindButton)
121             m_rewindButton->updateStyle();
122         if (m_returnToRealtimeButton)
123             m_returnToRealtimeButton->updateStyle();
124         if (m_statusDisplay)
125             m_statusDisplay->updateStyle();
126         if (m_timelineContainer)
127             m_timelineContainer->updateStyle();
128         if (m_timeline)
129             m_timeline->updateStyle();
130         if (m_fullscreenButton)
131             m_fullscreenButton->updateStyle();
132         if (m_currentTimeDisplay)
133             m_currentTimeDisplay->updateStyle();
134         if (m_timeRemainingDisplay)
135             m_timeRemainingDisplay->updateStyle();
136         if (m_volumeSliderContainer)
137             m_volumeSliderContainer->updateStyle();
138         if (m_volumeSlider)
139             m_volumeSlider->updateStyle();
140     }
141 }
142
143 void RenderMedia::layout()
144 {
145     IntSize oldSize = contentBoxRect().size();
146
147     RenderReplaced::layout();
148
149     RenderBox* controlsRenderer = m_controlsShadowRoot ? m_controlsShadowRoot->renderBox() : 0;
150     if (!controlsRenderer)
151         return;
152     IntSize newSize = contentBoxRect().size();
153     if (newSize != oldSize || controlsRenderer->needsLayout()) {
154
155         if (m_currentTimeDisplay && m_timeRemainingDisplay) {
156             bool shouldShowTimeDisplays = shouldShowTimeDisplayControls();
157             m_currentTimeDisplay->setVisible(shouldShowTimeDisplays);
158             m_timeRemainingDisplay->setVisible(shouldShowTimeDisplays);
159         }
160
161         controlsRenderer->setLocation(borderLeft() + paddingLeft(), borderTop() + paddingTop());
162         controlsRenderer->style()->setHeight(Length(newSize.height(), Fixed));
163         controlsRenderer->style()->setWidth(Length(newSize.width(), Fixed));
164         controlsRenderer->setNeedsLayout(true, false);
165         controlsRenderer->layout();
166         setChildNeedsLayout(false);
167     }
168 }
169
170 void RenderMedia::createControlsShadowRoot()
171 {
172     ASSERT(!m_controlsShadowRoot);
173     m_controlsShadowRoot = new MediaControlShadowRootElement(document(), mediaElement());
174     addChild(m_controlsShadowRoot->renderer());
175 }
176
177 void RenderMedia::createPanel()
178 {
179     ASSERT(!m_panel);
180     m_panel = new MediaControlElement(document(), MEDIA_CONTROLS_PANEL, mediaElement());
181     m_panel->attachToParent(m_controlsShadowRoot.get());
182 }
183
184 void RenderMedia::createMuteButton()
185 {
186     ASSERT(!m_muteButton);
187     m_muteButton = new MediaControlMuteButtonElement(document(), mediaElement());
188     m_muteButton->attachToParent(m_panel.get());
189 }
190
191 void RenderMedia::createPlayButton()
192 {
193     ASSERT(!m_playButton);
194     m_playButton = new MediaControlPlayButtonElement(document(), mediaElement());
195     m_playButton->attachToParent(m_panel.get());
196 }
197
198 void RenderMedia::createSeekBackButton()
199 {
200     ASSERT(!m_seekBackButton);
201     m_seekBackButton = new MediaControlSeekButtonElement(document(), mediaElement(), false);
202     m_seekBackButton->attachToParent(m_panel.get());
203 }
204
205 void RenderMedia::createSeekForwardButton()
206 {
207     ASSERT(!m_seekForwardButton);
208     m_seekForwardButton = new MediaControlSeekButtonElement(document(), mediaElement(), true);
209     m_seekForwardButton->attachToParent(m_panel.get());
210 }
211
212 void RenderMedia::createRewindButton()
213 {
214     ASSERT(!m_rewindButton);
215     m_rewindButton = new MediaControlRewindButtonElement(document(), mediaElement());
216     m_rewindButton->attachToParent(m_panel.get());
217 }
218
219 void RenderMedia::createReturnToRealtimeButton()
220 {
221     ASSERT(!m_returnToRealtimeButton);
222     m_returnToRealtimeButton = new MediaControlReturnToRealtimeButtonElement(document(), mediaElement());
223     m_returnToRealtimeButton->attachToParent(m_panel.get());
224 }
225
226 void RenderMedia::createStatusDisplay()
227 {
228     ASSERT(!m_statusDisplay);
229     m_statusDisplay = new MediaControlStatusDisplayElement(document(), mediaElement());
230     m_statusDisplay->attachToParent(m_panel.get());
231 }
232
233 void RenderMedia::createTimelineContainer()
234 {
235     ASSERT(!m_timelineContainer);
236     m_timelineContainer = new MediaControlTimelineContainerElement(document(), mediaElement());
237     m_timelineContainer->attachToParent(m_panel.get());
238 }
239
240 void RenderMedia::createTimeline()
241 {
242     ASSERT(!m_timeline);
243     m_timeline = new MediaControlTimelineElement(document(), mediaElement());
244     m_timeline->setAttribute(precisionAttr, "float");
245     m_timeline->attachToParent(m_timelineContainer.get());
246 }
247
248 void RenderMedia::createVolumeSliderContainer()
249 {
250     ASSERT(!m_volumeSliderContainer);
251     m_volumeSliderContainer = new MediaControlVolumeSliderContainerElement(document(), mediaElement());
252     m_volumeSliderContainer->attachToParent(m_panel.get());
253 }
254
255 void RenderMedia::createVolumeSlider()
256 {
257     ASSERT(!m_volumeSlider);
258     m_volumeSlider = new MediaControlVolumeSliderElement(document(), mediaElement());
259     m_volumeSlider->setAttribute(precisionAttr, "float");
260     m_volumeSlider->attachToParent(m_volumeSliderContainer.get());
261 }
262
263 void RenderMedia::createCurrentTimeDisplay()
264 {
265     ASSERT(!m_currentTimeDisplay);
266     m_currentTimeDisplay = new MediaControlTimeDisplayElement(document(), MEDIA_CONTROLS_CURRENT_TIME_DISPLAY, mediaElement());
267     m_currentTimeDisplay->attachToParent(m_timelineContainer.get());
268 }
269
270 void RenderMedia::createTimeRemainingDisplay()
271 {
272     ASSERT(!m_timeRemainingDisplay);
273     m_timeRemainingDisplay = new MediaControlTimeDisplayElement(document(), MEDIA_CONTROLS_TIME_REMAINING_DISPLAY, mediaElement());
274     m_timeRemainingDisplay->attachToParent(m_timelineContainer.get());
275 }
276
277 void RenderMedia::createFullscreenButton()
278 {
279     ASSERT(!m_fullscreenButton);
280     m_fullscreenButton = new MediaControlFullscreenButtonElement(document(), mediaElement());
281     m_fullscreenButton->attachToParent(m_panel.get());
282 }
283     
284 void RenderMedia::updateFromElement()
285 {
286     updateControls();
287 }
288             
289 void RenderMedia::updateControls()
290 {
291     HTMLMediaElement* media = mediaElement();
292     if (!media->controls() || !media->inActiveDocument()) {
293         if (m_controlsShadowRoot) {
294             m_controlsShadowRoot->detach();
295             m_panel = 0;
296             m_muteButton = 0;
297             m_playButton = 0;
298             m_statusDisplay = 0;
299             m_timelineContainer = 0;
300             m_timeline = 0;
301             m_seekBackButton = 0;
302             m_seekForwardButton = 0;
303             m_rewindButton = 0;
304             m_returnToRealtimeButton = 0;
305             m_currentTimeDisplay = 0;
306             m_timeRemainingDisplay = 0;
307             m_fullscreenButton = 0;
308             m_volumeSliderContainer = 0;
309             m_volumeSlider = 0;
310             m_controlsShadowRoot = 0;
311         }
312         m_opacityAnimationTo = 1.0f;
313         m_opacityAnimationTimer.stop();
314         m_timeUpdateTimer.stop();
315         return;
316     }
317     
318     if (!m_controlsShadowRoot) {
319         createControlsShadowRoot();
320         createPanel();
321         if (m_panel) {
322             createRewindButton();
323             createMuteButton();
324             createPlayButton();
325             createReturnToRealtimeButton();
326             createStatusDisplay();
327             createTimelineContainer();
328             createVolumeSliderContainer();
329             createSeekBackButton();
330             createSeekForwardButton();
331             createFullscreenButton();
332             if (m_volumeSliderContainer)
333                 createVolumeSlider();
334             if (m_timelineContainer) {
335                 createCurrentTimeDisplay();
336                 createTimeline();
337                 createTimeRemainingDisplay();
338             }
339             m_panel->attach();
340         }
341     }
342
343     if (media->canPlay()) {
344         if (m_timeUpdateTimer.isActive())
345             m_timeUpdateTimer.stop();
346     } else if (style()->visibility() == VISIBLE && m_timeline && m_timeline->renderer() && m_timeline->renderer()->style()->display() != NONE) {
347         m_timeUpdateTimer.startRepeating(cTimeUpdateRepeatDelay);
348     }
349
350     
351     if (m_panel) {
352         // update() might alter the opacity of the element, especially if we are in the middle
353         // of an animation. This is the only element concerned as we animate only this element.
354         float opacityBeforeChangingStyle = m_panel->renderer() ? m_panel->renderer()->style()->opacity() : 0;
355         m_panel->update();
356         changeOpacity(m_panel.get(), opacityBeforeChangingStyle);
357     }
358     if (m_muteButton)
359         m_muteButton->update();
360     if (m_playButton)
361         m_playButton->update();
362     if (m_timelineContainer)
363         m_timelineContainer->update();
364     if (m_volumeSliderContainer)
365         m_volumeSliderContainer->update();
366     if (m_timeline)
367         m_timeline->update();
368     if (m_currentTimeDisplay)
369         m_currentTimeDisplay->update();
370     if (m_timeRemainingDisplay)
371         m_timeRemainingDisplay->update();
372     if (m_seekBackButton)
373         m_seekBackButton->update();
374     if (m_seekForwardButton)
375         m_seekForwardButton->update();
376     if (m_rewindButton)
377         m_rewindButton->update();
378     if (m_returnToRealtimeButton)
379         m_returnToRealtimeButton->update();
380     if (m_statusDisplay)
381         m_statusDisplay->update();
382     if (m_fullscreenButton)
383         m_fullscreenButton->update();
384     if (m_volumeSlider)
385         m_volumeSlider->update();
386
387     updateTimeDisplay();
388     updateControlVisibility();
389 }
390
391 void RenderMedia::timeUpdateTimerFired(Timer<RenderMedia>*)
392 {
393     if (m_timeline)
394         m_timeline->update(false);
395     updateTimeDisplay();
396 }
397     
398 String RenderMedia::formatTime(float time)
399 {
400     if (!isfinite(time))
401         time = 0;
402     int seconds = (int)fabsf(time); 
403     int hours = seconds / (60 * 60);
404     int minutes = (seconds / 60) % 60;
405     seconds %= 60;
406     if (hours) {
407         if (hours > 9)
408             return String::format("%s%02d:%02d:%02d", (time < 0 ? "-" : ""), hours, minutes, seconds);
409         else
410             return String::format("%s%01d:%02d:%02d", (time < 0 ? "-" : ""), hours, minutes, seconds);
411     }
412     else
413         return String::format("%s%02d:%02d", (time < 0 ? "-" : ""), minutes, seconds);
414 }
415
416 void RenderMedia::updateTimeDisplay()
417 {
418     if (!m_currentTimeDisplay || !m_currentTimeDisplay->renderer() || m_currentTimeDisplay->renderer()->style()->display() == NONE || style()->visibility() != VISIBLE)
419         return;
420     float now = mediaElement()->currentTime();
421     float duration = mediaElement()->duration();
422
423     String timeString = formatTime(now);
424     ExceptionCode ec;
425     m_currentTimeDisplay->setInnerText(timeString, ec);
426     
427     timeString = formatTime(now - duration);
428     m_timeRemainingDisplay->setInnerText(timeString, ec);
429 }
430
431 void RenderMedia::updateControlVisibility() 
432 {
433     if (!m_panel || !m_panel->renderer())
434         return;
435
436     // Don't fade for audio controls.
437     HTMLMediaElement* media = mediaElement();
438     if (!media->hasVideo())
439         return;
440
441     // Don't fade if the media element is not visible
442     if (style()->visibility() != VISIBLE)
443         return;
444     
445     bool shouldHideController = !m_mouseOver && !media->canPlay();
446
447     // Do fading manually, css animations don't work with shadow trees
448
449     float animateFrom = m_panel->renderer()->style()->opacity();
450     float animateTo = shouldHideController ? 0.0f : 1.0f;
451
452     if (animateFrom == animateTo)
453         return;
454
455     if (m_opacityAnimationTimer.isActive()) {
456         if (m_opacityAnimationTo == animateTo)
457             return;
458         m_opacityAnimationTimer.stop();
459     }
460
461     if (animateFrom < animateTo)
462         m_opacityAnimationDuration = cOpacityAnimationDurationFadeIn;
463     else
464         m_opacityAnimationDuration = cOpacityAnimationDurationFadeOut;
465
466     m_opacityAnimationFrom = animateFrom;
467     m_opacityAnimationTo = animateTo;
468
469     m_opacityAnimationStartTime = currentTime();
470     m_opacityAnimationTimer.startRepeating(cOpacityAnimationRepeatDelay);
471 }
472     
473 void RenderMedia::changeOpacity(HTMLElement* e, float opacity) 
474 {
475     if (!e || !e->renderer() || !e->renderer()->style())
476         return;
477     RefPtr<RenderStyle> s = RenderStyle::clone(e->renderer()->style());
478     s->setOpacity(opacity);
479     // z-index can't be auto if opacity is used
480     s->setZIndex(0);
481     e->renderer()->setStyle(s.release());
482 }
483     
484 void RenderMedia::opacityAnimationTimerFired(Timer<RenderMedia>*)
485 {
486     double time = currentTime() - m_opacityAnimationStartTime;
487     if (time >= m_opacityAnimationDuration) {
488         time = m_opacityAnimationDuration;
489         m_opacityAnimationTimer.stop();
490     }
491     float opacity = narrowPrecisionToFloat(m_opacityAnimationFrom + (m_opacityAnimationTo - m_opacityAnimationFrom) * time / m_opacityAnimationDuration);
492     changeOpacity(m_panel.get(), opacity);
493 }
494
495 void RenderMedia::updateVolumeSliderContainer(bool visible)
496 {
497     if (!mediaElement()->hasAudio() || !m_volumeSliderContainer || !m_volumeSlider)
498         return;
499
500     if (visible && !m_volumeSliderContainer->isVisible()) {
501         if (!m_muteButton || !m_muteButton->renderer() || !m_muteButton->renderBox())
502             return;
503
504         RefPtr<RenderStyle> s = m_volumeSliderContainer->styleForElement();
505         int height = s->height().isPercent() ? 0 : s->height().value();
506         int x = m_muteButton->renderBox()->offsetLeft();
507         int y = m_muteButton->renderBox()->offsetTop() - height;
508         FloatPoint absPoint = m_muteButton->renderer()->localToAbsolute(FloatPoint(x, y), true, true);
509         if (absPoint.y() < 0)
510             y = m_muteButton->renderBox()->offsetTop() + m_muteButton->renderBox()->height();
511         m_volumeSliderContainer->setVisible(true);
512         m_volumeSliderContainer->setPosition(x, y);
513         m_volumeSliderContainer->update();
514         m_volumeSlider->update();
515     } else if (!visible && m_volumeSliderContainer->isVisible()) {
516         m_volumeSliderContainer->setVisible(false);
517         m_volumeSliderContainer->updateStyle();
518     }
519 }
520
521 void RenderMedia::forwardEvent(Event* event)
522 {
523     if (event->isMouseEvent() && m_controlsShadowRoot) {
524         MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
525         IntPoint point(mouseEvent->absoluteLocation());
526         bool showVolumeSlider = false;
527         if (m_muteButton && m_muteButton->hitTest(point)) {
528             m_muteButton->defaultEventHandler(event);
529             if (event->type() != eventNames().mouseoutEvent)
530                 showVolumeSlider = true;
531         }
532
533         if (m_volumeSliderContainer && m_volumeSliderContainer->hitTest(point))
534             showVolumeSlider = true;
535
536         if (m_volumeSlider && m_volumeSlider->hitTest(point)) {
537             m_volumeSlider->defaultEventHandler(event);
538             showVolumeSlider = true;
539         }
540
541         updateVolumeSliderContainer(showVolumeSlider);
542
543         if (m_playButton && m_playButton->hitTest(point))
544             m_playButton->defaultEventHandler(event);
545
546         if (m_seekBackButton && m_seekBackButton->hitTest(point))
547             m_seekBackButton->defaultEventHandler(event);
548
549         if (m_seekForwardButton && m_seekForwardButton->hitTest(point))
550             m_seekForwardButton->defaultEventHandler(event);
551
552         if (m_rewindButton && m_rewindButton->hitTest(point))
553             m_rewindButton->defaultEventHandler(event);
554
555         if (m_returnToRealtimeButton && m_returnToRealtimeButton->hitTest(point))
556             m_returnToRealtimeButton->defaultEventHandler(event);
557
558         if (m_timeline && m_timeline->hitTest(point))
559             m_timeline->defaultEventHandler(event);
560
561         if (m_fullscreenButton && m_fullscreenButton->hitTest(point))
562             m_fullscreenButton->defaultEventHandler(event);
563         
564         if (event->type() == eventNames().mouseoverEvent) {
565             m_mouseOver = true;
566             updateControlVisibility();
567         }
568         if (event->type() == eventNames().mouseoutEvent) {
569             // 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
570             Node* mouseOverNode = mouseEvent->relatedTarget() ? mouseEvent->relatedTarget()->toNode() : 0;
571             RenderObject* mouseOverRenderer = mouseOverNode ? mouseOverNode->renderer() : 0;
572             m_mouseOver = mouseOverRenderer && mouseOverRenderer->isDescendantOf(this);
573             updateControlVisibility();
574         }
575     }
576 }
577
578 int RenderMedia::lowestPosition(bool includeOverflowInterior, bool includeSelf) const
579 {
580     int bottom = RenderReplaced::lowestPosition(includeOverflowInterior, includeSelf);
581     if (!m_controlsShadowRoot || !m_controlsShadowRoot->renderer())
582         return bottom;
583     
584     return max(bottom,  m_controlsShadowRoot->renderBox()->y() + m_controlsShadowRoot->renderBox()->lowestPosition(includeOverflowInterior, includeSelf));
585 }
586
587 int RenderMedia::rightmostPosition(bool includeOverflowInterior, bool includeSelf) const
588 {
589     int right = RenderReplaced::rightmostPosition(includeOverflowInterior, includeSelf);
590     if (!m_controlsShadowRoot || !m_controlsShadowRoot->renderer())
591         return right;
592     
593     return max(right, m_controlsShadowRoot->renderBox()->x() + m_controlsShadowRoot->renderBox()->rightmostPosition(includeOverflowInterior, includeSelf));
594 }
595
596 int RenderMedia::leftmostPosition(bool includeOverflowInterior, bool includeSelf) const
597 {
598     int left = RenderReplaced::leftmostPosition(includeOverflowInterior, includeSelf);
599     if (!m_controlsShadowRoot || !m_controlsShadowRoot->renderer())
600         return left;
601     
602     return min(left, m_controlsShadowRoot->renderBox()->x() +  m_controlsShadowRoot->renderBox()->leftmostPosition(includeOverflowInterior, includeSelf));
603 }
604
605
606 // We want the timeline slider to be at least 100 pixels wide.
607 static const int minWidthToDisplayTimeDisplays = 16 + 16 + 45 + 100 + 45 + 16 + 1;
608
609 bool RenderMedia::shouldShowTimeDisplayControls() const
610 {
611     if (!m_currentTimeDisplay && !m_timeRemainingDisplay)
612         return false;
613
614     int width = mediaElement()->renderBox()->width();
615     return width >= minWidthToDisplayTimeDisplays * style()->effectiveZoom();
616 }
617
618 } // namespace WebCore
619
620 #endif