2 * Copyright (C) 2011, 2012 Apple Inc. All rights reserved.
3 * Copyright (C) 2011, 2012 Google Inc. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
28 #include "core/html/shadow/MediaControls.h"
30 #include "bindings/v8/ExceptionStatePlaceholder.h"
32 #include "core/html/shadow/MediaControlsAndroid.h"
37 static const double timeWithoutMouseMovementBeforeHidingFullscreenControls = 3;
39 MediaControls::MediaControls(Document& document)
40 : HTMLDivElement(document)
41 , m_mediaController(0)
43 , m_textDisplayContainer(0)
45 , m_currentTimeDisplay(0)
47 , m_panelMuteButton(0)
49 , m_toggleClosedCaptionsButton(0)
50 , m_fullScreenButton(0)
51 , m_durationDisplay(0)
53 , m_hideFullscreenControlsTimer(this, &MediaControls::hideFullscreenControlsTimerFired)
54 , m_isFullscreen(false)
55 , m_isMouseOverControls(false)
59 PassRefPtr<MediaControls> MediaControls::create(Document& document)
64 RefPtr<MediaControls> controls;
66 controls = adoptRef(new MediaControlsAndroid(document));
68 controls = adoptRef(new MediaControls(document));
71 if (controls->initializeControls(document))
72 return controls.release();
77 bool MediaControls::initializeControls(Document& document)
79 // Create an enclosing element for the panel so we can visually offset the controls correctly.
80 RefPtr<MediaControlPanelEnclosureElement> enclosure = MediaControlPanelEnclosureElement::create(document);
82 RefPtr<MediaControlPanelElement> panel = MediaControlPanelElement::create(document);
84 TrackExceptionState exceptionState;
86 RefPtr<MediaControlPlayButtonElement> playButton = MediaControlPlayButtonElement::create(document);
87 m_playButton = playButton.get();
88 panel->appendChild(playButton.release(), exceptionState);
89 if (exceptionState.hadException())
92 RefPtr<MediaControlTimelineElement> timeline = MediaControlTimelineElement::create(document, this);
93 m_timeline = timeline.get();
94 panel->appendChild(timeline.release(), exceptionState);
95 if (exceptionState.hadException())
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())
105 RefPtr<MediaControlTimeRemainingDisplayElement> durationDisplay = MediaControlTimeRemainingDisplayElement::create(document);
106 m_durationDisplay = durationDisplay.get();
107 panel->appendChild(durationDisplay.release(), exceptionState);
108 if (exceptionState.hadException())
111 RefPtr<MediaControlPanelMuteButtonElement> panelMuteButton = MediaControlPanelMuteButtonElement::create(document, this);
112 m_panelMuteButton = panelMuteButton.get();
113 panel->appendChild(panelMuteButton.release(), exceptionState);
114 if (exceptionState.hadException())
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())
124 RefPtr<MediaControlToggleClosedCaptionsButtonElement> toggleClosedCaptionsButton = MediaControlToggleClosedCaptionsButtonElement::create(document, this);
125 m_toggleClosedCaptionsButton = toggleClosedCaptionsButton.get();
126 panel->appendChild(toggleClosedCaptionsButton.release(), exceptionState);
127 if (exceptionState.hadException())
130 RefPtr<MediaControlFullscreenButtonElement> fullscreenButton = MediaControlFullscreenButtonElement::create(document);
131 m_fullScreenButton = fullscreenButton.get();
132 panel->appendChild(fullscreenButton.release(), exceptionState);
133 if (exceptionState.hadException())
136 m_panel = panel.get();
137 enclosure->appendChild(panel.release(), exceptionState);
138 if (exceptionState.hadException())
141 m_enclosure = enclosure.get();
142 appendChild(enclosure.release(), exceptionState);
143 if (exceptionState.hadException())
149 void MediaControls::setMediaController(MediaControllerInterface* controller)
151 if (m_mediaController == controller)
153 m_mediaController = controller;
156 m_panel->setMediaController(controller);
157 if (m_textDisplayContainer)
158 m_textDisplayContainer->setMediaController(controller);
160 m_playButton->setMediaController(controller);
161 if (m_currentTimeDisplay)
162 m_currentTimeDisplay->setMediaController(controller);
164 m_timeline->setMediaController(controller);
165 if (m_panelMuteButton)
166 m_panelMuteButton->setMediaController(controller);
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);
176 m_enclosure->setMediaController(controller);
179 void MediaControls::reset()
181 Page* page = document().page();
185 double duration = m_mediaController->duration();
186 m_durationDisplay->setInnerText(RenderTheme::theme().formatMediaControlsTime(duration), ASSERT_NO_EXCEPTION);
187 m_durationDisplay->setCurrentValue(duration);
189 m_playButton->updateDisplayType();
191 updateCurrentTimeDisplay();
193 m_timeline->setDuration(m_mediaController->duration());
194 m_timeline->setPosition(m_mediaController->currentTime());
196 m_panelMuteButton->show();
198 if (m_volumeSlider) {
199 if (!m_mediaController->hasAudio())
200 m_volumeSlider->hide();
202 m_volumeSlider->show();
203 m_volumeSlider->setVolume(m_mediaController->volume());
207 refreshClosedCaptionsButtonVisibility();
209 if (m_fullScreenButton) {
210 if (m_mediaController->hasVideo())
211 m_fullScreenButton->show();
213 m_fullScreenButton->hide();
219 void MediaControls::show()
222 m_panel->setIsDisplayed(true);
226 void MediaControls::hide()
228 m_panel->setIsDisplayed(false);
232 void MediaControls::makeOpaque()
234 m_panel->makeOpaque();
237 void MediaControls::makeTransparent()
239 m_panel->makeTransparent();
242 bool MediaControls::shouldHideControls()
244 return !m_panel->hovered();
247 void MediaControls::bufferingProgressed()
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());
255 void MediaControls::playbackStarted()
257 m_currentTimeDisplay->show();
258 m_durationDisplay->hide();
260 m_playButton->updateDisplayType();
261 m_timeline->setPosition(m_mediaController->currentTime());
262 updateCurrentTimeDisplay();
265 startHideFullscreenControlsTimer();
268 void MediaControls::playbackProgressed()
270 m_timeline->setPosition(m_mediaController->currentTime());
271 updateCurrentTimeDisplay();
273 if (!m_isMouseOverControls && m_mediaController->hasVideo())
277 void MediaControls::playbackStopped()
279 m_playButton->updateDisplayType();
280 m_timeline->setPosition(m_mediaController->currentTime());
281 updateCurrentTimeDisplay();
284 stopHideFullscreenControlsTimer();
287 void MediaControls::updateCurrentTimeDisplay()
289 double now = m_mediaController->currentTime();
290 double duration = m_mediaController->duration();
292 Page* page = document().page();
296 // After seek, hide duration display and show current time.
298 m_currentTimeDisplay->show();
299 m_durationDisplay->hide();
302 // Allow the theme to format the time.
303 m_currentTimeDisplay->setInnerText(RenderTheme::theme().formatMediaControlsCurrentTime(now, duration), IGNORE_EXCEPTION);
304 m_currentTimeDisplay->setCurrentValue(now);
307 void MediaControls::showVolumeSlider()
309 if (!m_mediaController->hasAudio())
312 m_volumeSlider->show();
315 void MediaControls::changedMute()
317 m_panelMuteButton->changedMute();
319 if (m_mediaController->muted())
320 m_volumeSlider->setVolume(0);
322 m_volumeSlider->setVolume(m_mediaController->volume());
325 void MediaControls::changedVolume()
328 m_volumeSlider->setVolume(m_mediaController->volume());
329 if (m_panelMuteButton && m_panelMuteButton->renderer())
330 m_panelMuteButton->renderer()->repaint();
333 void MediaControls::changedClosedCaptionsVisibility()
335 if (m_toggleClosedCaptionsButton)
336 m_toggleClosedCaptionsButton->updateDisplayType();
339 void MediaControls::refreshClosedCaptionsButtonVisibility()
341 if (!m_toggleClosedCaptionsButton)
344 if (m_mediaController->hasClosedCaptions())
345 m_toggleClosedCaptionsButton->show();
347 m_toggleClosedCaptionsButton->hide();
350 void MediaControls::closedCaptionTracksChanged()
352 refreshClosedCaptionsButtonVisibility();
355 void MediaControls::enteredFullscreen()
357 m_isFullscreen = true;
358 m_fullScreenButton->setIsFullscreen(true);
359 startHideFullscreenControlsTimer();
362 void MediaControls::exitedFullscreen()
364 m_isFullscreen = false;
365 m_fullScreenButton->setIsFullscreen(false);
366 stopHideFullscreenControlsTimer();
369 void MediaControls::defaultEventHandler(Event* event)
371 HTMLDivElement::defaultEventHandler(event);
373 if (event->type() == EventTypeNames::mouseover) {
374 if (!containsRelatedTarget(event)) {
375 m_isMouseOverControls = true;
376 if (!m_mediaController->canPlay()) {
378 if (shouldHideControls())
379 startHideFullscreenControlsTimer();
385 if (event->type() == EventTypeNames::mouseout) {
386 if (!containsRelatedTarget(event)) {
387 m_isMouseOverControls = false;
388 stopHideFullscreenControlsTimer();
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.
398 if (shouldHideControls())
399 startHideFullscreenControlsTimer();
405 void MediaControls::hideFullscreenControlsTimerFired(Timer<MediaControls>*)
407 if (m_mediaController->paused())
413 if (!shouldHideControls())
419 void MediaControls::startHideFullscreenControlsTimer()
424 Page* page = document().page();
428 m_hideFullscreenControlsTimer.startOneShot(timeWithoutMouseMovementBeforeHidingFullscreenControls);
431 void MediaControls::stopHideFullscreenControlsTimer()
433 m_hideFullscreenControlsTimer.stop();
436 const AtomicString& MediaControls::shadowPseudoId() const
438 DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls"));
442 bool MediaControls::containsRelatedTarget(Event* event)
444 if (!event->isMouseEvent())
446 EventTarget* relatedTarget = toMouseEvent(event)->relatedTarget();
449 return contains(relatedTarget->toNode());
452 void MediaControls::createTextTrackDisplay()
454 if (m_textDisplayContainer)
457 RefPtr<MediaControlTextTrackContainerElement> textDisplayContainer = MediaControlTextTrackContainerElement::create(document());
458 m_textDisplayContainer = textDisplayContainer.get();
460 if (m_mediaController)
461 m_textDisplayContainer->setMediaController(m_mediaController);
463 insertTextTrackContainer(textDisplayContainer.release());
466 void MediaControls::showTextTrackDisplay()
468 if (!m_textDisplayContainer)
469 createTextTrackDisplay();
470 m_textDisplayContainer->show();
473 void MediaControls::hideTextTrackDisplay()
475 if (!m_textDisplayContainer)
476 createTextTrackDisplay();
477 m_textDisplayContainer->hide();
480 void MediaControls::updateTextTrackDisplay()
482 if (!m_textDisplayContainer)
483 createTextTrackDisplay();
485 m_textDisplayContainer->updateDisplay();
488 void MediaControls::insertTextTrackContainer(PassRefPtr<MediaControlTextTrackContainerElement> textTrackContainer)
490 // Insert it before the first controller element so it always displays behind the controls.
491 insertBefore(textTrackContainer, m_enclosure);