987056635e4e23a77f6df98f20f9d0a2d4f71c58
[WebKit.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_toggleClosedCaptionsButton)
125             m_toggleClosedCaptionsButton->updateStyle();
126         if (m_statusDisplay)
127             m_statusDisplay->updateStyle();
128         if (m_timelineContainer)
129             m_timelineContainer->updateStyle();
130         if (m_timeline)
131             m_timeline->updateStyle();
132         if (m_fullscreenButton)
133             m_fullscreenButton->updateStyle();
134         if (m_currentTimeDisplay)
135             m_currentTimeDisplay->updateStyle();
136         if (m_timeRemainingDisplay)
137             m_timeRemainingDisplay->updateStyle();
138         if (m_volumeSliderContainer)
139             m_volumeSliderContainer->updateStyle();
140         if (m_volumeSlider)
141             m_volumeSlider->updateStyle();
142     }
143 }
144
145 void RenderMedia::layout()
146 {
147     IntSize oldSize = contentBoxRect().size();
148
149     RenderReplaced::layout();
150
151     RenderBox* controlsRenderer = m_controlsShadowRoot ? m_controlsShadowRoot->renderBox() : 0;
152     if (!controlsRenderer)
153         return;
154     IntSize newSize = contentBoxRect().size();
155     if (newSize != oldSize || controlsRenderer->needsLayout()) {
156
157         if (m_currentTimeDisplay && m_timeRemainingDisplay) {
158             bool shouldShowTimeDisplays = shouldShowTimeDisplayControls();
159             m_currentTimeDisplay->setVisible(shouldShowTimeDisplays);
160             m_timeRemainingDisplay->setVisible(shouldShowTimeDisplays);
161         }
162
163         controlsRenderer->setLocation(borderLeft() + paddingLeft(), borderTop() + paddingTop());
164         controlsRenderer->style()->setHeight(Length(newSize.height(), Fixed));
165         controlsRenderer->style()->setWidth(Length(newSize.width(), Fixed));
166         controlsRenderer->setNeedsLayout(true, false);
167         controlsRenderer->layout();
168         setChildNeedsLayout(false);
169     }
170 }
171
172 void RenderMedia::createControlsShadowRoot()
173 {
174     ASSERT(!m_controlsShadowRoot);
175     m_controlsShadowRoot = new MediaControlShadowRootElement(document(), mediaElement());
176     addChild(m_controlsShadowRoot->renderer());
177 }
178
179 void RenderMedia::createPanel()
180 {
181     ASSERT(!m_panel);
182     m_panel = new MediaControlElement(document(), MEDIA_CONTROLS_PANEL, mediaElement());
183     m_panel->attachToParent(m_controlsShadowRoot.get());
184 }
185
186 void RenderMedia::createMuteButton()
187 {
188     ASSERT(!m_muteButton);
189     m_muteButton = new MediaControlMuteButtonElement(document(), mediaElement());
190     m_muteButton->attachToParent(m_panel.get());
191 }
192
193 void RenderMedia::createPlayButton()
194 {
195     ASSERT(!m_playButton);
196     m_playButton = new MediaControlPlayButtonElement(document(), mediaElement());
197     m_playButton->attachToParent(m_panel.get());
198 }
199
200 void RenderMedia::createSeekBackButton()
201 {
202     ASSERT(!m_seekBackButton);
203     m_seekBackButton = new MediaControlSeekButtonElement(document(), mediaElement(), false);
204     m_seekBackButton->attachToParent(m_panel.get());
205 }
206
207 void RenderMedia::createSeekForwardButton()
208 {
209     ASSERT(!m_seekForwardButton);
210     m_seekForwardButton = new MediaControlSeekButtonElement(document(), mediaElement(), true);
211     m_seekForwardButton->attachToParent(m_panel.get());
212 }
213
214 void RenderMedia::createRewindButton()
215 {
216     ASSERT(!m_rewindButton);
217     m_rewindButton = new MediaControlRewindButtonElement(document(), mediaElement());
218     m_rewindButton->attachToParent(m_panel.get());
219 }
220
221 void RenderMedia::createReturnToRealtimeButton()
222 {
223     ASSERT(!m_returnToRealtimeButton);
224     m_returnToRealtimeButton = new MediaControlReturnToRealtimeButtonElement(document(), mediaElement());
225     m_returnToRealtimeButton->attachToParent(m_panel.get());
226 }
227
228 void RenderMedia::createToggleClosedCaptionsButton()
229 {
230     ASSERT(!m_toggleClosedCaptionsButton);
231     m_toggleClosedCaptionsButton = new MediaControlToggleClosedCaptionsButtonElement(document(), mediaElement());
232     m_toggleClosedCaptionsButton->attachToParent(m_panel.get());
233 }
234
235 void RenderMedia::createStatusDisplay()
236 {
237     ASSERT(!m_statusDisplay);
238     m_statusDisplay = new MediaControlStatusDisplayElement(document(), mediaElement());
239     m_statusDisplay->attachToParent(m_panel.get());
240 }
241
242 void RenderMedia::createTimelineContainer()
243 {
244     ASSERT(!m_timelineContainer);
245     m_timelineContainer = new MediaControlTimelineContainerElement(document(), mediaElement());
246     m_timelineContainer->attachToParent(m_panel.get());
247 }
248
249 void RenderMedia::createTimeline()
250 {
251     ASSERT(!m_timeline);
252     m_timeline = new MediaControlTimelineElement(document(), mediaElement());
253     m_timeline->setAttribute(precisionAttr, "float");
254     m_timeline->attachToParent(m_timelineContainer.get());
255 }
256
257 void RenderMedia::createVolumeSliderContainer()
258 {
259     ASSERT(!m_volumeSliderContainer);
260     m_volumeSliderContainer = new MediaControlVolumeSliderContainerElement(document(), mediaElement());
261     m_volumeSliderContainer->attachToParent(m_panel.get());
262 }
263
264 void RenderMedia::createVolumeSlider()
265 {
266     ASSERT(!m_volumeSlider);
267     m_volumeSlider = new MediaControlVolumeSliderElement(document(), mediaElement());
268     m_volumeSlider->setAttribute(precisionAttr, "float");
269     m_volumeSlider->setAttribute(maxAttr, "1");
270     m_volumeSlider->setAttribute(valueAttr, String::number(mediaElement()->volume()));
271     m_volumeSlider->attachToParent(m_volumeSliderContainer.get());
272 }
273
274 void RenderMedia::createCurrentTimeDisplay()
275 {
276     ASSERT(!m_currentTimeDisplay);
277     m_currentTimeDisplay = new MediaControlTimeDisplayElement(document(), MEDIA_CONTROLS_CURRENT_TIME_DISPLAY, mediaElement());
278     m_currentTimeDisplay->attachToParent(m_timelineContainer.get());
279 }
280
281 void RenderMedia::createTimeRemainingDisplay()
282 {
283     ASSERT(!m_timeRemainingDisplay);
284     m_timeRemainingDisplay = new MediaControlTimeDisplayElement(document(), MEDIA_CONTROLS_TIME_REMAINING_DISPLAY, mediaElement());
285     m_timeRemainingDisplay->attachToParent(m_timelineContainer.get());
286 }
287
288 void RenderMedia::createFullscreenButton()
289 {
290     ASSERT(!m_fullscreenButton);
291     m_fullscreenButton = new MediaControlFullscreenButtonElement(document(), mediaElement());
292     m_fullscreenButton->attachToParent(m_panel.get());
293 }
294     
295 void RenderMedia::updateFromElement()
296 {
297     updateControls();
298 }
299             
300 void RenderMedia::updateControls()
301 {
302     HTMLMediaElement* media = mediaElement();
303     if (!media->controls() || !media->inActiveDocument()) {
304         if (m_controlsShadowRoot) {
305             m_controlsShadowRoot->detach();
306             m_panel = 0;
307             m_muteButton = 0;
308             m_playButton = 0;
309             m_statusDisplay = 0;
310             m_timelineContainer = 0;
311             m_timeline = 0;
312             m_seekBackButton = 0;
313             m_seekForwardButton = 0;
314             m_rewindButton = 0;
315             m_returnToRealtimeButton = 0;
316             m_currentTimeDisplay = 0;
317             m_timeRemainingDisplay = 0;
318             m_fullscreenButton = 0;
319             m_volumeSliderContainer = 0;
320             m_volumeSlider = 0;
321             m_controlsShadowRoot = 0;
322             m_toggleClosedCaptionsButton = 0;
323         }
324         m_opacityAnimationTo = 1.0f;
325         m_opacityAnimationTimer.stop();
326         m_timeUpdateTimer.stop();
327         return;
328     }
329     
330     if (!m_controlsShadowRoot) {
331         createControlsShadowRoot();
332         createPanel();
333         if (m_panel) {
334             createRewindButton();
335             createPlayButton();
336             createReturnToRealtimeButton();
337             createToggleClosedCaptionsButton();
338             createStatusDisplay();
339             createTimelineContainer();
340             if (m_timelineContainer) {
341                 createCurrentTimeDisplay();
342                 createTimeline();
343                 createTimeRemainingDisplay();
344             }
345             createSeekBackButton();
346             createSeekForwardButton();
347             createFullscreenButton();
348             createMuteButton();
349             createVolumeSliderContainer();
350             if (m_volumeSliderContainer)
351                 createVolumeSlider();
352             m_panel->attach();
353         }
354     }
355
356     if (media->canPlay()) {
357         if (m_timeUpdateTimer.isActive())
358             m_timeUpdateTimer.stop();
359     } else if (style()->visibility() == VISIBLE && m_timeline && m_timeline->renderer() && m_timeline->renderer()->style()->display() != NONE) {
360         m_timeUpdateTimer.startRepeating(cTimeUpdateRepeatDelay);
361     }
362
363     
364     if (m_panel) {
365         // update() might alter the opacity of the element, especially if we are in the middle
366         // of an animation. This is the only element concerned as we animate only this element.
367         float opacityBeforeChangingStyle = m_panel->renderer() ? m_panel->renderer()->style()->opacity() : 0;
368         m_panel->update();
369         changeOpacity(m_panel.get(), opacityBeforeChangingStyle);
370     }
371     if (m_muteButton)
372         m_muteButton->update();
373     if (m_playButton)
374         m_playButton->update();
375     if (m_timelineContainer)
376         m_timelineContainer->update();
377     if (m_volumeSliderContainer)
378         m_volumeSliderContainer->update();
379     if (m_timeline)
380         m_timeline->update();
381     if (m_currentTimeDisplay)
382         m_currentTimeDisplay->update();
383     if (m_timeRemainingDisplay)
384         m_timeRemainingDisplay->update();
385     if (m_seekBackButton)
386         m_seekBackButton->update();
387     if (m_seekForwardButton)
388         m_seekForwardButton->update();
389     if (m_rewindButton)
390         m_rewindButton->update();
391     if (m_returnToRealtimeButton)
392         m_returnToRealtimeButton->update();
393     if (m_toggleClosedCaptionsButton)
394         m_toggleClosedCaptionsButton->update();
395     if (m_statusDisplay)
396         m_statusDisplay->update();
397     if (m_fullscreenButton)
398         m_fullscreenButton->update();
399     if (m_volumeSlider)
400         m_volumeSlider->update();
401
402     updateTimeDisplay();
403     updateControlVisibility();
404 }
405
406 void RenderMedia::timeUpdateTimerFired(Timer<RenderMedia>*)
407 {
408     if (m_timeline)
409         m_timeline->update(false);
410     updateTimeDisplay();
411 }
412     
413 void RenderMedia::updateTimeDisplay()
414 {
415     if (!m_currentTimeDisplay || !m_currentTimeDisplay->renderer() || m_currentTimeDisplay->renderer()->style()->display() == NONE || style()->visibility() != VISIBLE)
416         return;
417     float now = mediaElement()->currentTime();
418     float duration = mediaElement()->duration();
419
420     m_currentTimeDisplay->setCurrentValue(now);
421     m_timeRemainingDisplay->setCurrentValue(now - duration);
422 }
423
424 void RenderMedia::updateControlVisibility() 
425 {
426     if (!m_panel || !m_panel->renderer())
427         return;
428
429     // Don't fade for audio controls.
430     HTMLMediaElement* media = mediaElement();
431     if (!media->hasVideo())
432         return;
433
434     // Don't fade if the media element is not visible
435     if (style()->visibility() != VISIBLE)
436         return;
437     
438     bool shouldHideController = !m_mouseOver && !media->canPlay();
439
440     // Do fading manually, css animations don't work with shadow trees
441
442     float animateFrom = m_panel->renderer()->style()->opacity();
443     float animateTo = shouldHideController ? 0.0f : 1.0f;
444
445     if (animateFrom == animateTo)
446         return;
447
448     if (m_opacityAnimationTimer.isActive()) {
449         if (m_opacityAnimationTo == animateTo)
450             return;
451         m_opacityAnimationTimer.stop();
452     }
453
454     if (animateFrom < animateTo)
455         m_opacityAnimationDuration = cOpacityAnimationDurationFadeIn;
456     else
457         m_opacityAnimationDuration = cOpacityAnimationDurationFadeOut;
458
459     m_opacityAnimationFrom = animateFrom;
460     m_opacityAnimationTo = animateTo;
461
462     m_opacityAnimationStartTime = currentTime();
463     m_opacityAnimationTimer.startRepeating(cOpacityAnimationRepeatDelay);
464 }
465     
466 void RenderMedia::changeOpacity(HTMLElement* e, float opacity) 
467 {
468     if (!e || !e->renderer() || !e->renderer()->style())
469         return;
470     RefPtr<RenderStyle> s = RenderStyle::clone(e->renderer()->style());
471     s->setOpacity(opacity);
472     // z-index can't be auto if opacity is used
473     s->setZIndex(0);
474     e->renderer()->setStyle(s.release());
475 }
476     
477 void RenderMedia::opacityAnimationTimerFired(Timer<RenderMedia>*)
478 {
479     double time = currentTime() - m_opacityAnimationStartTime;
480     if (time >= m_opacityAnimationDuration) {
481         time = m_opacityAnimationDuration;
482         m_opacityAnimationTimer.stop();
483     }
484     float opacity = narrowPrecisionToFloat(m_opacityAnimationFrom + (m_opacityAnimationTo - m_opacityAnimationFrom) * time / m_opacityAnimationDuration);
485     changeOpacity(m_panel.get(), opacity);
486 }
487
488 void RenderMedia::updateVolumeSliderContainer(bool visible)
489 {
490     if (!mediaElement()->hasAudio() || !m_volumeSliderContainer || !m_volumeSlider)
491         return;
492
493     if (visible && !m_volumeSliderContainer->isVisible()) {
494         if (!m_muteButton || !m_muteButton->renderer() || !m_muteButton->renderBox())
495             return;
496
497         RefPtr<RenderStyle> s = m_volumeSliderContainer->styleForElement();
498         int height = s->height().isPercent() ? 0 : s->height().value();
499         int x = m_muteButton->renderBox()->offsetLeft();
500         int y = m_muteButton->renderBox()->offsetTop() - height;
501         FloatPoint absPoint = m_muteButton->renderer()->localToAbsolute(FloatPoint(x, y), true, true);
502         if (absPoint.y() < 0)
503             y = m_muteButton->renderBox()->offsetTop() + m_muteButton->renderBox()->height();
504         m_volumeSliderContainer->setVisible(true);
505         m_volumeSliderContainer->setPosition(x, y);
506         m_volumeSliderContainer->update();
507         m_volumeSlider->update();
508     } else if (!visible && m_volumeSliderContainer->isVisible()) {
509         m_volumeSliderContainer->setVisible(false);
510         m_volumeSliderContainer->updateStyle();
511     }
512 }
513
514 void RenderMedia::forwardEvent(Event* event)
515 {
516     if (event->isMouseEvent() && m_controlsShadowRoot) {
517         MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
518         IntPoint point(mouseEvent->absoluteLocation());
519         bool showVolumeSlider = false;
520         if (m_muteButton && m_muteButton->hitTest(point)) {
521             m_muteButton->defaultEventHandler(event);
522             if (event->type() != eventNames().mouseoutEvent)
523                 showVolumeSlider = true;
524         }
525
526         if (m_volumeSliderContainer && m_volumeSliderContainer->hitTest(point))
527             showVolumeSlider = true;
528
529         if (m_volumeSlider && m_volumeSlider->hitTest(point)) {
530             m_volumeSlider->defaultEventHandler(event);
531             showVolumeSlider = true;
532         }
533
534         updateVolumeSliderContainer(showVolumeSlider);
535
536         if (m_playButton && m_playButton->hitTest(point))
537             m_playButton->defaultEventHandler(event);
538
539         if (m_seekBackButton && m_seekBackButton->hitTest(point))
540             m_seekBackButton->defaultEventHandler(event);
541
542         if (m_seekForwardButton && m_seekForwardButton->hitTest(point))
543             m_seekForwardButton->defaultEventHandler(event);
544
545         if (m_rewindButton && m_rewindButton->hitTest(point))
546             m_rewindButton->defaultEventHandler(event);
547
548         if (m_returnToRealtimeButton && m_returnToRealtimeButton->hitTest(point))
549             m_returnToRealtimeButton->defaultEventHandler(event);
550
551        if (m_toggleClosedCaptionsButton && m_toggleClosedCaptionsButton->hitTest(point))
552             m_toggleClosedCaptionsButton->defaultEventHandler(event);
553
554         if (m_timeline && m_timeline->hitTest(point))
555             m_timeline->defaultEventHandler(event);
556
557         if (m_fullscreenButton && m_fullscreenButton->hitTest(point))
558             m_fullscreenButton->defaultEventHandler(event);
559         
560         if (event->type() == eventNames().mouseoverEvent) {
561             m_mouseOver = true;
562             updateControlVisibility();
563         }
564         if (event->type() == eventNames().mouseoutEvent) {
565             // 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
566             Node* mouseOverNode = mouseEvent->relatedTarget() ? mouseEvent->relatedTarget()->toNode() : 0;
567             RenderObject* mouseOverRenderer = mouseOverNode ? mouseOverNode->renderer() : 0;
568             m_mouseOver = mouseOverRenderer && mouseOverRenderer->isDescendantOf(this);
569             updateControlVisibility();
570         }
571     }
572 }
573
574 int RenderMedia::lowestPosition(bool includeOverflowInterior, bool includeSelf) const
575 {
576     int bottom = RenderReplaced::lowestPosition(includeOverflowInterior, includeSelf);
577     if (!m_controlsShadowRoot || !m_controlsShadowRoot->renderer())
578         return bottom;
579     
580     return max(bottom,  m_controlsShadowRoot->renderBox()->y() + m_controlsShadowRoot->renderBox()->lowestPosition(includeOverflowInterior, includeSelf));
581 }
582
583 int RenderMedia::rightmostPosition(bool includeOverflowInterior, bool includeSelf) const
584 {
585     int right = RenderReplaced::rightmostPosition(includeOverflowInterior, includeSelf);
586     if (!m_controlsShadowRoot || !m_controlsShadowRoot->renderer())
587         return right;
588     
589     return max(right, m_controlsShadowRoot->renderBox()->x() + m_controlsShadowRoot->renderBox()->rightmostPosition(includeOverflowInterior, includeSelf));
590 }
591
592 int RenderMedia::leftmostPosition(bool includeOverflowInterior, bool includeSelf) const
593 {
594     int left = RenderReplaced::leftmostPosition(includeOverflowInterior, includeSelf);
595     if (!m_controlsShadowRoot || !m_controlsShadowRoot->renderer())
596         return left;
597     
598     return min(left, m_controlsShadowRoot->renderBox()->x() +  m_controlsShadowRoot->renderBox()->leftmostPosition(includeOverflowInterior, includeSelf));
599 }
600
601
602 // We want the timeline slider to be at least 100 pixels wide.
603 static const int minWidthToDisplayTimeDisplays = 16 + 16 + 45 + 100 + 45 + 16 + 1;
604
605 bool RenderMedia::shouldShowTimeDisplayControls() const
606 {
607     if (!m_currentTimeDisplay && !m_timeRemainingDisplay)
608         return false;
609
610     int width = mediaElement()->renderBox()->width();
611     return width >= minWidthToDisplayTimeDisplays * style()->effectiveZoom();
612 }
613
614 } // namespace WebCore
615
616 #endif