2011-06-10 Darin Adler <darin@apple.com>
[webkit:qtwebkit.git] / Source / WebCore / html / shadow / MediaControlRootElement.cpp
1 /*
2  * Copyright (C) 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
3  * Copyright (C) 2011 Google Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "config.h"
28
29 #if ENABLE(VIDEO)
30 #include "MediaControlRootElement.h"
31
32 #include "MediaControlElements.h"
33 #include "MouseEvent.h"
34 #include "Page.h"
35 #include "RenderTheme.h"
36
37 using namespace std;
38
39 namespace WebCore {
40
41 static const double timeWithoutMouseMovementBeforeHidingControls = 3;
42
43 MediaControlRootElement::MediaControlRootElement(HTMLMediaElement* mediaElement)
44     : MediaControls(mediaElement)
45     , m_mediaElement(mediaElement)
46     , m_rewindButton(0)
47     , m_playButton(0)
48     , m_returnToRealTimeButton(0)
49     , m_statusDisplay(0)
50     , m_currentTimeDisplay(0)
51     , m_timeline(0)
52     , m_timeRemainingDisplay(0)
53     , m_timelineContainer(0)
54     , m_seekBackButton(0)
55     , m_seekForwardButton(0)
56     , m_toggleClosedCaptionsButton(0)
57     , m_panelMuteButton(0)
58     , m_volumeSlider(0)
59     , m_volumeSliderMuteButton(0)
60     , m_volumeSliderContainer(0)
61     , m_fullScreenButton(0)
62     , m_fullScreenMinVolumeButton(0)
63     , m_fullScreenVolumeSlider(0)
64     , m_fullScreenMaxVolumeButton(0)
65     , m_panel(0)
66     , m_opaque(true)
67     , m_isMouseOverControls(false)
68     , m_hideFullscreenControlsTimer(this, &MediaControlRootElement::hideFullscreenControlsTimerFired)
69 {
70 }
71
72 PassRefPtr<MediaControls> MediaControls::create(HTMLMediaElement* mediaElement)
73 {
74     return MediaControlRootElement::create(mediaElement);
75 }
76
77 PassRefPtr<MediaControlRootElement> MediaControlRootElement::create(HTMLMediaElement* mediaElement)
78 {
79     if (!mediaElement->document()->page())
80         return 0;
81
82     RefPtr<MediaControlRootElement> controls = adoptRef(new MediaControlRootElement(mediaElement));
83
84     RefPtr<MediaControlPanelElement> panel = MediaControlPanelElement::create(mediaElement);
85
86     ExceptionCode ec;
87
88     RefPtr<MediaControlRewindButtonElement> rewindButton = MediaControlRewindButtonElement::create(mediaElement);
89     controls->m_rewindButton = rewindButton.get();
90     panel->appendChild(rewindButton.release(), ec, true);
91     if (ec)
92         return 0;
93
94     RefPtr<MediaControlPlayButtonElement> playButton = MediaControlPlayButtonElement::create(mediaElement);
95     controls->m_playButton = playButton.get();
96     panel->appendChild(playButton.release(), ec, true);
97     if (ec)
98         return 0;
99
100     RefPtr<MediaControlReturnToRealtimeButtonElement> returnToRealtimeButton = MediaControlReturnToRealtimeButtonElement::create(mediaElement);
101     controls->m_returnToRealTimeButton = returnToRealtimeButton.get();
102     panel->appendChild(returnToRealtimeButton.release(), ec, true);
103     if (ec)
104         return 0;
105
106     if (mediaElement->document()->page()->theme()->usesMediaControlStatusDisplay()) {
107         RefPtr<MediaControlStatusDisplayElement> statusDisplay = MediaControlStatusDisplayElement::create(mediaElement);
108         controls->m_statusDisplay = statusDisplay.get();
109         panel->appendChild(statusDisplay.release(), ec, true);
110         if (ec)
111             return 0;
112     }
113
114     RefPtr<MediaControlTimelineContainerElement> timelineContainer = MediaControlTimelineContainerElement::create(mediaElement);
115
116     RefPtr<MediaControlCurrentTimeDisplayElement> currentTimeDisplay = MediaControlCurrentTimeDisplayElement::create(mediaElement);
117     controls->m_currentTimeDisplay = currentTimeDisplay.get();
118     timelineContainer->appendChild(currentTimeDisplay.release(), ec, true);
119     if (ec)
120         return 0;
121
122     RefPtr<MediaControlTimelineElement> timeline = MediaControlTimelineElement::create(mediaElement, controls.get());
123     controls->m_timeline = timeline.get();
124     timelineContainer->appendChild(timeline.release(), ec, true);
125     if (ec)
126         return 0;
127
128     RefPtr<MediaControlTimeRemainingDisplayElement> timeRemainingDisplay = MediaControlTimeRemainingDisplayElement::create(mediaElement);
129     controls->m_timeRemainingDisplay = timeRemainingDisplay.get();
130     timelineContainer->appendChild(timeRemainingDisplay.release(), ec, true);
131     if (ec)
132         return 0;
133
134     controls->m_timelineContainer = timelineContainer.get();
135     panel->appendChild(timelineContainer.release(), ec, true);
136     if (ec)
137         return 0;
138
139     // FIXME: Only create when needed <http://webkit.org/b/57163>
140     RefPtr<MediaControlSeekBackButtonElement> seekBackButton = MediaControlSeekBackButtonElement::create(mediaElement);
141     controls->m_seekBackButton = seekBackButton.get();
142     panel->appendChild(seekBackButton.release(), ec, true);
143     if (ec)
144         return 0;
145
146     // FIXME: Only create when needed <http://webkit.org/b/57163>
147     RefPtr<MediaControlSeekForwardButtonElement> seekForwardButton = MediaControlSeekForwardButtonElement::create(mediaElement);
148     controls->m_seekForwardButton = seekForwardButton.get();
149     panel->appendChild(seekForwardButton.release(), ec, true);
150     if (ec)
151         return 0;
152
153     if (mediaElement->document()->page()->theme()->supportsClosedCaptioning()) {
154         RefPtr<MediaControlToggleClosedCaptionsButtonElement> toggleClosedCaptionsButton = MediaControlToggleClosedCaptionsButtonElement::create(mediaElement);
155         controls->m_toggleClosedCaptionsButton = toggleClosedCaptionsButton.get();
156         panel->appendChild(toggleClosedCaptionsButton.release(), ec, true);
157         if (ec)
158             return 0;
159     }
160
161     // FIXME: Only create when needed <http://webkit.org/b/57163>
162     RefPtr<MediaControlFullscreenButtonElement> fullScreenButton = MediaControlFullscreenButtonElement::create(mediaElement, controls.get());
163     controls->m_fullScreenButton = fullScreenButton.get();
164     panel->appendChild(fullScreenButton.release(), ec, true);
165
166     RefPtr<MediaControlPanelMuteButtonElement> panelMuteButton = MediaControlPanelMuteButtonElement::create(mediaElement, controls.get());
167     controls->m_panelMuteButton = panelMuteButton.get();
168     panel->appendChild(panelMuteButton.release(), ec, true);
169     if (ec)
170         return 0;
171
172     if (mediaElement->document()->page()->theme()->usesMediaControlVolumeSlider()) {
173         RefPtr<MediaControlVolumeSliderContainerElement> volumeSliderContainer = MediaControlVolumeSliderContainerElement::create(mediaElement);
174
175         RefPtr<MediaControlVolumeSliderElement> slider = MediaControlVolumeSliderElement::create(mediaElement);
176         controls->m_volumeSlider = slider.get();
177         volumeSliderContainer->appendChild(slider.release(), ec, true);
178         if (ec)
179             return 0;
180
181         RefPtr<MediaControlVolumeSliderMuteButtonElement> volumeSliderMuteButton = MediaControlVolumeSliderMuteButtonElement::create(mediaElement);
182         controls->m_volumeSliderMuteButton = volumeSliderMuteButton.get();
183         volumeSliderContainer->appendChild(volumeSliderMuteButton.release(), ec, true);
184         if (ec)
185             return 0;
186
187         controls->m_volumeSliderContainer = volumeSliderContainer.get();
188         panel->appendChild(volumeSliderContainer.release(), ec, true);
189         if (ec)
190             return 0;
191     }
192
193     // FIXME: Only create when needed <http://webkit.org/b/57163>
194     RefPtr<MediaControlFullscreenVolumeMinButtonElement> fullScreenMinVolumeButton = MediaControlFullscreenVolumeMinButtonElement::create(mediaElement);
195     controls->m_fullScreenMinVolumeButton = fullScreenMinVolumeButton.get();
196     panel->appendChild(fullScreenMinVolumeButton.release(), ec, true);
197     if (ec)
198         return 0;
199
200     RefPtr<MediaControlFullscreenVolumeSliderElement> fullScreenVolumeSlider = MediaControlFullscreenVolumeSliderElement::create(mediaElement);
201     controls->m_fullScreenVolumeSlider = fullScreenVolumeSlider.get();
202     panel->appendChild(fullScreenVolumeSlider.release(), ec, true);
203     if (ec)
204         return 0;
205
206     RefPtr<MediaControlFullscreenVolumeMaxButtonElement> fullScreenMaxVolumeButton = MediaControlFullscreenVolumeMaxButtonElement::create(mediaElement);
207     controls->m_fullScreenMaxVolumeButton = fullScreenMaxVolumeButton.get();
208     panel->appendChild(fullScreenMaxVolumeButton.release(), ec, true);
209     if (ec)
210         return 0;
211
212     controls->m_panel = panel.get();
213     controls->appendChild(panel.release(), ec, true);
214     if (ec)
215         return 0;
216
217     return controls.release();
218 }
219
220 void MediaControlRootElement::show()
221 {
222     m_panel->show();
223 }
224
225 void MediaControlRootElement::hide()
226 {
227     m_panel->hide();
228 }
229
230 static const String& webkitTransitionString()
231 {
232     DEFINE_STATIC_LOCAL(String, s, ("-webkit-transition"));
233     return s;
234 }
235
236 static const String& opacityString()
237 {
238     DEFINE_STATIC_LOCAL(String, s, ("opacity"));
239     return s;
240 }
241
242 void MediaControlRootElement::makeOpaque()
243 {
244     if (m_opaque)
245         return;
246
247     DEFINE_STATIC_LOCAL(String, transitionValue, ());
248     if (transitionValue.isNull())
249         transitionValue = String::format("opacity %.1gs", document()->page()->theme()->mediaControlsFadeInDuration());
250     DEFINE_STATIC_LOCAL(String, opacityValue, ("1"));
251
252     ExceptionCode ec;
253     // FIXME: Make more efficient <http://webkit.org/b/58157>
254     m_panel->style()->setProperty(webkitTransitionString(), transitionValue, ec);
255     m_panel->style()->setProperty(opacityString(), opacityValue, ec);
256     m_opaque = true;
257 }
258
259 void MediaControlRootElement::makeTransparent()
260 {
261     if (!m_opaque)
262         return;
263
264     DEFINE_STATIC_LOCAL(String, transitionValue, ());
265     if (transitionValue.isNull())
266         transitionValue = String::format("opacity %.1gs", document()->page()->theme()->mediaControlsFadeOutDuration());
267     DEFINE_STATIC_LOCAL(String, opacityValue, ("0"));
268
269     ExceptionCode ec;
270     // FIXME: Make more efficient <http://webkit.org/b/58157>
271     m_panel->style()->setProperty(webkitTransitionString(), transitionValue, ec);
272     m_panel->style()->setProperty(opacityString(), opacityValue, ec);
273     m_opaque = false;
274 }
275
276 void MediaControlRootElement::reset()
277 {
278     Page* page = document()->page();
279     if (!page)
280         return;
281
282     updateStatusDisplay();
283
284     if (m_mediaElement->supportsFullscreen())
285         m_fullScreenButton->show();
286     else
287         m_fullScreenButton->hide();
288
289     float duration = m_mediaElement->duration();
290     if (isfinite(duration) || page->theme()->hasOwnDisabledStateHandlingFor(MediaSliderPart)) {
291         m_timeline->setDuration(duration);
292         m_timelineContainer->show();
293         m_timeline->setPosition(m_mediaElement->currentTime());
294         updateTimeDisplay();
295     } else
296         m_timelineContainer->hide();
297
298     if (m_mediaElement->hasAudio() || page->theme()->hasOwnDisabledStateHandlingFor(MediaMuteButtonPart))
299         m_panelMuteButton->show();
300     else
301         m_panelMuteButton->hide();
302
303     if (m_volumeSlider)
304         m_volumeSlider->setVolume(m_mediaElement->volume());
305
306     if (m_toggleClosedCaptionsButton) {
307         if (m_mediaElement->hasClosedCaptions())
308             m_toggleClosedCaptionsButton->show();
309         else
310             m_toggleClosedCaptionsButton->hide();
311     }
312
313     if (m_mediaElement->movieLoadType() != MediaPlayer::LiveStream) {
314         m_returnToRealTimeButton->hide();
315         m_rewindButton->show();
316     } else {
317         m_returnToRealTimeButton->show();
318         m_rewindButton->hide();
319     }
320
321     makeOpaque();
322 }
323
324 void MediaControlRootElement::playbackStarted()
325 {
326     m_playButton->updateDisplayType();
327     m_timeline->setPosition(m_mediaElement->currentTime());
328     updateTimeDisplay();
329
330     if (m_mediaElement->isFullscreen())
331         startHideFullscreenControlsTimer();
332 }
333
334 void MediaControlRootElement::playbackProgressed()
335 {
336     m_timeline->setPosition(m_mediaElement->currentTime());
337     updateTimeDisplay();
338     
339     if (!m_isMouseOverControls && m_mediaElement->hasVideo())
340         makeTransparent();
341 }
342
343 void MediaControlRootElement::playbackStopped()
344 {
345     m_playButton->updateDisplayType();
346     m_timeline->setPosition(m_mediaElement->currentTime());
347     updateTimeDisplay();
348     makeOpaque();
349     
350     stopHideFullscreenControlsTimer();
351 }
352
353 void MediaControlRootElement::updateTimeDisplay()
354 {
355     float now = m_mediaElement->currentTime();
356     float duration = m_mediaElement->duration();
357
358     Page* page = document()->page();
359     if (!page)
360         return;
361
362     // Allow the theme to format the time.
363     ExceptionCode ec;
364     m_currentTimeDisplay->setInnerText(page->theme()->formatMediaControlsCurrentTime(now, duration), ec);
365     m_currentTimeDisplay->setCurrentValue(now);
366     m_timeRemainingDisplay->setInnerText(page->theme()->formatMediaControlsRemainingTime(now, duration), ec);
367     m_timeRemainingDisplay->setCurrentValue(now - duration);
368 }
369
370 void MediaControlRootElement::reportedError()
371 {
372     Page* page = document()->page();
373     if (!page)
374         return;
375
376     if (!page->theme()->hasOwnDisabledStateHandlingFor(MediaSliderPart))
377         m_timelineContainer->hide();
378
379     if (!page->theme()->hasOwnDisabledStateHandlingFor(MediaMuteButtonPart))
380         m_panelMuteButton->hide();
381
382      m_fullScreenButton->hide();
383
384     if (m_volumeSliderContainer)
385         m_volumeSliderContainer->hide();
386     if (m_toggleClosedCaptionsButton && !page->theme()->hasOwnDisabledStateHandlingFor(MediaToggleClosedCaptionsButtonPart))
387         m_toggleClosedCaptionsButton->hide();
388 }
389
390 void MediaControlRootElement::updateStatusDisplay()
391 {
392     if (m_statusDisplay)
393         m_statusDisplay->update();
394 }
395
396 void MediaControlRootElement::loadedMetadata()
397 {
398     if (m_statusDisplay && m_mediaElement->movieLoadType() != MediaPlayer::LiveStream)
399         m_statusDisplay->hide();
400
401     reset();
402 }
403
404 void MediaControlRootElement::changedClosedCaptionsVisibility()
405 {
406     if (m_toggleClosedCaptionsButton)
407         m_toggleClosedCaptionsButton->updateDisplayType();
408 }
409
410 void MediaControlRootElement::changedMute()
411 {
412     m_panelMuteButton->changedMute();
413     if (m_volumeSliderMuteButton)
414         m_volumeSliderMuteButton->changedMute();
415 }
416
417 void MediaControlRootElement::changedVolume()
418 {
419     if (m_volumeSlider)
420         m_volumeSlider->setVolume(m_mediaElement->volume());
421 }
422
423 void MediaControlRootElement::enteredFullscreen()
424 {
425     if (m_mediaElement->movieLoadType() == MediaPlayer::LiveStream || m_mediaElement->movieLoadType() == MediaPlayer::StoredStream) {
426         m_seekBackButton->hide();
427         m_seekForwardButton->hide();
428     } else
429         m_rewindButton->hide();
430
431     m_panel->setCanBeDragged(true);
432
433     startHideFullscreenControlsTimer();
434 }
435
436 void MediaControlRootElement::exitedFullscreen()
437 {
438     // "show" actually means removal of display:none style, so we are just clearing styles
439     // when exiting fullscreen.
440     // FIXME: Clarify naming of show/hide <http://webkit.org/b/58157>
441     m_rewindButton->show();
442     m_seekBackButton->show();
443     m_seekForwardButton->show();
444
445     m_panel->setCanBeDragged(false);
446
447     // We will keep using the panel, but we want it to go back to the standard position.
448     // This will matter right away because we use the panel even when not fullscreen.
449     // And if we reenter fullscreen we also want the panel in the standard position.
450     m_panel->resetPosition();
451
452     stopHideFullscreenControlsTimer();    
453 }
454
455 void MediaControlRootElement::showVolumeSlider()
456 {
457     if (!m_mediaElement->hasAudio())
458         return;
459
460     if (m_volumeSliderContainer)
461         m_volumeSliderContainer->show();
462 }
463
464 bool MediaControlRootElement::shouldHideControls()
465 {
466     return !m_panel->hovered();
467 }
468
469 bool MediaControlRootElement::containsRelatedTarget(Event* event)
470 {
471     if (!event->isMouseEvent())
472         return false;
473     EventTarget* relatedTarget = static_cast<MouseEvent*>(event)->relatedTarget();
474     if (!relatedTarget)
475         return false;
476     return contains(relatedTarget->toNode());
477 }
478
479 void MediaControlRootElement::defaultEventHandler(Event* event)
480 {
481     MediaControls::defaultEventHandler(event);
482
483     if (event->type() == eventNames().mouseoverEvent) {
484         if (!containsRelatedTarget(event)) {
485             m_isMouseOverControls = true;
486             if (!m_mediaElement->canPlay()) {
487                 makeOpaque();
488                 if (shouldHideControls())
489                     startHideFullscreenControlsTimer();
490             }
491         }
492     } else if (event->type() == eventNames().mouseoutEvent) {
493         if (!containsRelatedTarget(event)) {
494             m_isMouseOverControls = false;
495             stopHideFullscreenControlsTimer();
496         }
497     } else if (event->type() == eventNames().mousemoveEvent) {
498         if (m_mediaElement->isFullscreen()) {
499             // When we get a mouse move in fullscreen mode, show the media controls, and start a timer
500             // that will hide the media controls after a 3 seconds without a mouse move.
501             makeOpaque();
502             if (shouldHideControls())
503                 startHideFullscreenControlsTimer();
504         }
505     }
506 }
507
508 void MediaControlRootElement::startHideFullscreenControlsTimer()
509 {
510     if (!m_mediaElement->isFullscreen())
511         return;
512     
513     m_hideFullscreenControlsTimer.startOneShot(timeWithoutMouseMovementBeforeHidingControls);
514 }
515
516 void MediaControlRootElement::hideFullscreenControlsTimerFired(Timer<MediaControlRootElement>*)
517 {
518     if (!m_mediaElement->isPlaying())
519         return;
520     
521     if (!m_mediaElement->isFullscreen())
522         return;
523     
524     if (!shouldHideControls())
525         return;
526     
527     makeTransparent();
528 }
529
530 void MediaControlRootElement::stopHideFullscreenControlsTimer()
531 {
532     m_hideFullscreenControlsTimer.stop();
533 }
534
535 const AtomicString& MediaControlRootElement::shadowPseudoId() const
536 {
537     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls"));
538     return id;
539 }
540
541 }
542
543 #endif