Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / html / shadow / MediaControls.cpp
1 /*
2  * Copyright (C) 2011, 2012 Apple Inc. All rights reserved.
3  * Copyright (C) 2011, 2012 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 #include "core/html/shadow/MediaControls.h"
29
30 #include "bindings/v8/ExceptionStatePlaceholder.h"
31 #if OS(ANDROID)
32 #include "core/html/shadow/MediaControlsAndroid.h"
33 #endif
34
35 namespace WebCore {
36
37 static const double timeWithoutMouseMovementBeforeHidingFullscreenControls = 3;
38
39 MediaControls::MediaControls(Document& document)
40     : HTMLDivElement(document)
41     , m_mediaController(0)
42     , m_panel(0)
43     , m_textDisplayContainer(0)
44     , m_playButton(0)
45     , m_currentTimeDisplay(0)
46     , m_timeline(0)
47     , m_panelMuteButton(0)
48     , m_volumeSlider(0)
49     , m_toggleClosedCaptionsButton(0)
50     , m_fullScreenButton(0)
51     , m_durationDisplay(0)
52     , m_enclosure(0)
53     , m_hideFullscreenControlsTimer(this, &MediaControls::hideFullscreenControlsTimerFired)
54     , m_isFullscreen(false)
55     , m_isMouseOverControls(false)
56 {
57 }
58
59 PassRefPtr<MediaControls> MediaControls::create(Document& document)
60 {
61     if (!document.page())
62         return 0;
63
64     RefPtr<MediaControls> controls;
65 #if OS(ANDROID)
66     controls = adoptRef(new MediaControlsAndroid(document));
67 #else
68     controls = adoptRef(new MediaControls(document));
69 #endif
70
71     if (controls->initializeControls(document))
72         return controls.release();
73
74     return 0;
75 }
76
77 bool MediaControls::initializeControls(Document& document)
78 {
79     // Create an enclosing element for the panel so we can visually offset the controls correctly.
80     RefPtr<MediaControlPanelEnclosureElement> enclosure = MediaControlPanelEnclosureElement::create(document);
81
82     RefPtr<MediaControlPanelElement> panel = MediaControlPanelElement::create(document);
83
84     TrackExceptionState exceptionState;
85
86     RefPtr<MediaControlPlayButtonElement> playButton = MediaControlPlayButtonElement::create(document);
87     m_playButton = playButton.get();
88     panel->appendChild(playButton.release(), exceptionState);
89     if (exceptionState.hadException())
90         return false;
91
92     RefPtr<MediaControlTimelineElement> timeline = MediaControlTimelineElement::create(document, this);
93     m_timeline = timeline.get();
94     panel->appendChild(timeline.release(), exceptionState);
95     if (exceptionState.hadException())
96         return false;
97
98     RefPtr<MediaControlCurrentTimeDisplayElement> currentTimeDisplay = MediaControlCurrentTimeDisplayElement::create(document);
99     m_currentTimeDisplay = currentTimeDisplay.get();
100     m_currentTimeDisplay->hide();
101     panel->appendChild(currentTimeDisplay.release(), exceptionState);
102     if (exceptionState.hadException())
103         return false;
104
105     RefPtr<MediaControlTimeRemainingDisplayElement> durationDisplay = MediaControlTimeRemainingDisplayElement::create(document);
106     m_durationDisplay = durationDisplay.get();
107     panel->appendChild(durationDisplay.release(), exceptionState);
108     if (exceptionState.hadException())
109         return false;
110
111     RefPtr<MediaControlPanelMuteButtonElement> panelMuteButton = MediaControlPanelMuteButtonElement::create(document, this);
112     m_panelMuteButton = panelMuteButton.get();
113     panel->appendChild(panelMuteButton.release(), exceptionState);
114     if (exceptionState.hadException())
115         return false;
116
117     RefPtr<MediaControlPanelVolumeSliderElement> slider = MediaControlPanelVolumeSliderElement::create(document);
118     m_volumeSlider = slider.get();
119     m_volumeSlider->setClearMutedOnUserInteraction(true);
120     panel->appendChild(slider.release(), exceptionState);
121     if (exceptionState.hadException())
122         return false;
123
124     RefPtr<MediaControlToggleClosedCaptionsButtonElement> toggleClosedCaptionsButton = MediaControlToggleClosedCaptionsButtonElement::create(document, this);
125     m_toggleClosedCaptionsButton = toggleClosedCaptionsButton.get();
126     panel->appendChild(toggleClosedCaptionsButton.release(), exceptionState);
127     if (exceptionState.hadException())
128         return false;
129
130     RefPtr<MediaControlFullscreenButtonElement> fullscreenButton = MediaControlFullscreenButtonElement::create(document);
131     m_fullScreenButton = fullscreenButton.get();
132     panel->appendChild(fullscreenButton.release(), exceptionState);
133     if (exceptionState.hadException())
134         return false;
135
136     m_panel = panel.get();
137     enclosure->appendChild(panel.release(), exceptionState);
138     if (exceptionState.hadException())
139         return false;
140
141     m_enclosure = enclosure.get();
142     appendChild(enclosure.release(), exceptionState);
143     if (exceptionState.hadException())
144         return false;
145
146     return true;
147 }
148
149 void MediaControls::setMediaController(MediaControllerInterface* controller)
150 {
151     if (m_mediaController == controller)
152         return;
153     m_mediaController = controller;
154
155     if (m_panel)
156         m_panel->setMediaController(controller);
157     if (m_textDisplayContainer)
158         m_textDisplayContainer->setMediaController(controller);
159     if (m_playButton)
160         m_playButton->setMediaController(controller);
161     if (m_currentTimeDisplay)
162         m_currentTimeDisplay->setMediaController(controller);
163     if (m_timeline)
164         m_timeline->setMediaController(controller);
165     if (m_panelMuteButton)
166         m_panelMuteButton->setMediaController(controller);
167     if (m_volumeSlider)
168         m_volumeSlider->setMediaController(controller);
169     if (m_toggleClosedCaptionsButton)
170         m_toggleClosedCaptionsButton->setMediaController(controller);
171     if (m_fullScreenButton)
172         m_fullScreenButton->setMediaController(controller);
173     if (m_durationDisplay)
174         m_durationDisplay->setMediaController(controller);
175     if (m_enclosure)
176         m_enclosure->setMediaController(controller);
177 }
178
179 void MediaControls::reset()
180 {
181     Page* page = document().page();
182     if (!page)
183         return;
184
185     double duration = m_mediaController->duration();
186     m_durationDisplay->setInnerText(RenderTheme::theme().formatMediaControlsTime(duration), ASSERT_NO_EXCEPTION);
187     m_durationDisplay->setCurrentValue(duration);
188
189     m_playButton->updateDisplayType();
190
191     updateCurrentTimeDisplay();
192
193     m_timeline->setDuration(m_mediaController->duration());
194     m_timeline->setPosition(m_mediaController->currentTime());
195
196     m_panelMuteButton->show();
197
198     if (m_volumeSlider) {
199         if (!m_mediaController->hasAudio())
200             m_volumeSlider->hide();
201         else {
202             m_volumeSlider->show();
203             m_volumeSlider->setVolume(m_mediaController->volume());
204         }
205     }
206
207     refreshClosedCaptionsButtonVisibility();
208
209     if (m_fullScreenButton) {
210         if (m_mediaController->hasVideo())
211             m_fullScreenButton->show();
212         else
213             m_fullScreenButton->hide();
214     }
215
216     makeOpaque();
217 }
218
219 void MediaControls::show()
220 {
221     makeOpaque();
222     m_panel->setIsDisplayed(true);
223     m_panel->show();
224 }
225
226 void MediaControls::hide()
227 {
228     m_panel->setIsDisplayed(false);
229     m_panel->hide();
230 }
231
232 void MediaControls::makeOpaque()
233 {
234     m_panel->makeOpaque();
235 }
236
237 void MediaControls::makeTransparent()
238 {
239     m_panel->makeTransparent();
240 }
241
242 bool MediaControls::shouldHideControls()
243 {
244     return !m_panel->hovered();
245 }
246
247 void MediaControls::bufferingProgressed()
248 {
249     // We only need to update buffering progress when paused, during normal
250     // playback playbackProgressed() will take care of it.
251     if (m_mediaController->paused())
252         m_timeline->setPosition(m_mediaController->currentTime());
253 }
254
255 void MediaControls::playbackStarted()
256 {
257     m_currentTimeDisplay->show();
258     m_durationDisplay->hide();
259
260     m_playButton->updateDisplayType();
261     m_timeline->setPosition(m_mediaController->currentTime());
262     updateCurrentTimeDisplay();
263
264     if (m_isFullscreen)
265         startHideFullscreenControlsTimer();
266 }
267
268 void MediaControls::playbackProgressed()
269 {
270     m_timeline->setPosition(m_mediaController->currentTime());
271     updateCurrentTimeDisplay();
272
273     if (!m_isMouseOverControls && m_mediaController->hasVideo())
274         makeTransparent();
275 }
276
277 void MediaControls::playbackStopped()
278 {
279     m_playButton->updateDisplayType();
280     m_timeline->setPosition(m_mediaController->currentTime());
281     updateCurrentTimeDisplay();
282     makeOpaque();
283
284     stopHideFullscreenControlsTimer();
285 }
286
287 void MediaControls::updateCurrentTimeDisplay()
288 {
289     double now = m_mediaController->currentTime();
290     double duration = m_mediaController->duration();
291
292     Page* page = document().page();
293     if (!page)
294         return;
295
296     // After seek, hide duration display and show current time.
297     if (now > 0) {
298         m_currentTimeDisplay->show();
299         m_durationDisplay->hide();
300     }
301
302     // Allow the theme to format the time.
303     m_currentTimeDisplay->setInnerText(RenderTheme::theme().formatMediaControlsCurrentTime(now, duration), IGNORE_EXCEPTION);
304     m_currentTimeDisplay->setCurrentValue(now);
305 }
306
307 void MediaControls::showVolumeSlider()
308 {
309     if (!m_mediaController->hasAudio())
310         return;
311
312     m_volumeSlider->show();
313 }
314
315 void MediaControls::changedMute()
316 {
317     m_panelMuteButton->changedMute();
318
319     if (m_mediaController->muted())
320         m_volumeSlider->setVolume(0);
321     else
322         m_volumeSlider->setVolume(m_mediaController->volume());
323 }
324
325 void MediaControls::changedVolume()
326 {
327     if (m_volumeSlider)
328         m_volumeSlider->setVolume(m_mediaController->volume());
329     if (m_panelMuteButton && m_panelMuteButton->renderer())
330         m_panelMuteButton->renderer()->repaint();
331 }
332
333 void MediaControls::changedClosedCaptionsVisibility()
334 {
335     if (m_toggleClosedCaptionsButton)
336         m_toggleClosedCaptionsButton->updateDisplayType();
337 }
338
339 void MediaControls::refreshClosedCaptionsButtonVisibility()
340 {
341     if (!m_toggleClosedCaptionsButton)
342         return;
343
344     if (m_mediaController->hasClosedCaptions())
345         m_toggleClosedCaptionsButton->show();
346     else
347         m_toggleClosedCaptionsButton->hide();
348 }
349
350 void MediaControls::closedCaptionTracksChanged()
351 {
352     refreshClosedCaptionsButtonVisibility();
353 }
354
355 void MediaControls::enteredFullscreen()
356 {
357     m_isFullscreen = true;
358     m_fullScreenButton->setIsFullscreen(true);
359     startHideFullscreenControlsTimer();
360 }
361
362 void MediaControls::exitedFullscreen()
363 {
364     m_isFullscreen = false;
365     m_fullScreenButton->setIsFullscreen(false);
366     stopHideFullscreenControlsTimer();
367 }
368
369 void MediaControls::defaultEventHandler(Event* event)
370 {
371     HTMLDivElement::defaultEventHandler(event);
372
373     if (event->type() == EventTypeNames::mouseover) {
374         if (!containsRelatedTarget(event)) {
375             m_isMouseOverControls = true;
376             if (!m_mediaController->canPlay()) {
377                 makeOpaque();
378                 if (shouldHideControls())
379                     startHideFullscreenControlsTimer();
380             }
381         }
382         return;
383     }
384
385     if (event->type() == EventTypeNames::mouseout) {
386         if (!containsRelatedTarget(event)) {
387             m_isMouseOverControls = false;
388             stopHideFullscreenControlsTimer();
389         }
390         return;
391     }
392
393     if (event->type() == EventTypeNames::mousemove) {
394         if (m_isFullscreen) {
395             // When we get a mouse move in fullscreen mode, show the media controls, and start a timer
396             // that will hide the media controls after a 3 seconds without a mouse move.
397             makeOpaque();
398             if (shouldHideControls())
399                 startHideFullscreenControlsTimer();
400         }
401         return;
402     }
403 }
404
405 void MediaControls::hideFullscreenControlsTimerFired(Timer<MediaControls>*)
406 {
407     if (m_mediaController->paused())
408         return;
409
410     if (!m_isFullscreen)
411         return;
412
413     if (!shouldHideControls())
414         return;
415
416     makeTransparent();
417 }
418
419 void MediaControls::startHideFullscreenControlsTimer()
420 {
421     if (!m_isFullscreen)
422         return;
423
424     Page* page = document().page();
425     if (!page)
426         return;
427
428     m_hideFullscreenControlsTimer.startOneShot(timeWithoutMouseMovementBeforeHidingFullscreenControls);
429 }
430
431 void MediaControls::stopHideFullscreenControlsTimer()
432 {
433     m_hideFullscreenControlsTimer.stop();
434 }
435
436 const AtomicString& MediaControls::shadowPseudoId() const
437 {
438     DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls"));
439     return id;
440 }
441
442 bool MediaControls::containsRelatedTarget(Event* event)
443 {
444     if (!event->isMouseEvent())
445         return false;
446     EventTarget* relatedTarget = toMouseEvent(event)->relatedTarget();
447     if (!relatedTarget)
448         return false;
449     return contains(relatedTarget->toNode());
450 }
451
452 void MediaControls::createTextTrackDisplay()
453 {
454     if (m_textDisplayContainer)
455         return;
456
457     RefPtr<MediaControlTextTrackContainerElement> textDisplayContainer = MediaControlTextTrackContainerElement::create(document());
458     m_textDisplayContainer = textDisplayContainer.get();
459
460     if (m_mediaController)
461         m_textDisplayContainer->setMediaController(m_mediaController);
462
463     insertTextTrackContainer(textDisplayContainer.release());
464 }
465
466 void MediaControls::showTextTrackDisplay()
467 {
468     if (!m_textDisplayContainer)
469         createTextTrackDisplay();
470     m_textDisplayContainer->show();
471 }
472
473 void MediaControls::hideTextTrackDisplay()
474 {
475     if (!m_textDisplayContainer)
476         createTextTrackDisplay();
477     m_textDisplayContainer->hide();
478 }
479
480 void MediaControls::updateTextTrackDisplay()
481 {
482     if (!m_textDisplayContainer)
483         createTextTrackDisplay();
484
485     m_textDisplayContainer->updateDisplay();
486 }
487
488 void MediaControls::insertTextTrackContainer(PassRefPtr<MediaControlTextTrackContainerElement> textTrackContainer)
489 {
490     // Insert it before the first controller element so it always displays behind the controls.
491     insertBefore(textTrackContainer, m_enclosure);
492 }
493
494 }