| /* |
| * Copyright (C) 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. |
| * Copyright (C) 2011 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| |
| #if ENABLE(VIDEO) |
| #include "MediaControlRootElement.h" |
| |
| #include "MediaControlElements.h" |
| #include "Page.h" |
| #include "RenderTheme.h" |
| |
| using namespace std; |
| |
| namespace WebCore { |
| |
| MediaControlRootElement::MediaControlRootElement(HTMLMediaElement* mediaElement) |
| : MediaControls(mediaElement) |
| , m_mediaElement(mediaElement) |
| , m_rewindButton(0) |
| , m_playButton(0) |
| , m_returnToRealTimeButton(0) |
| , m_statusDisplay(0) |
| , m_currentTimeDisplay(0) |
| , m_timeline(0) |
| , m_timeRemainingDisplay(0) |
| , m_timelineContainer(0) |
| , m_seekBackButton(0) |
| , m_seekForwardButton(0) |
| , m_toggleClosedCaptionsButton(0) |
| , m_panelMuteButton(0) |
| , m_volumeSlider(0) |
| , m_volumeSliderMuteButton(0) |
| , m_volumeSliderContainer(0) |
| , m_fullScreenButton(0) |
| , m_fullScreenMinVolumeButton(0) |
| , m_fullScreenVolumeSlider(0) |
| , m_fullScreenMaxVolumeButton(0) |
| , m_panel(0) |
| , m_opaque(true) |
| { |
| } |
| |
| PassRefPtr<MediaControls> MediaControls::create(HTMLMediaElement* mediaElement) |
| { |
| return MediaControlRootElement::create(mediaElement); |
| } |
| |
| PassRefPtr<MediaControlRootElement> MediaControlRootElement::create(HTMLMediaElement* mediaElement) |
| { |
| if (!mediaElement->document()->page()) |
| return 0; |
| |
| RefPtr<MediaControlRootElement> controls = adoptRef(new MediaControlRootElement(mediaElement)); |
| |
| RefPtr<MediaControlPanelElement> panel = MediaControlPanelElement::create(mediaElement); |
| |
| ExceptionCode ec; |
| |
| RefPtr<MediaControlRewindButtonElement> rewindButton = MediaControlRewindButtonElement::create(mediaElement); |
| controls->m_rewindButton = rewindButton.get(); |
| panel->appendChild(rewindButton.release(), ec, true); |
| if (ec) |
| return 0; |
| |
| RefPtr<MediaControlPlayButtonElement> playButton = MediaControlPlayButtonElement::create(mediaElement); |
| controls->m_playButton = playButton.get(); |
| panel->appendChild(playButton.release(), ec, true); |
| if (ec) |
| return 0; |
| |
| RefPtr<MediaControlReturnToRealtimeButtonElement> returnToRealtimeButton = MediaControlReturnToRealtimeButtonElement::create(mediaElement); |
| controls->m_returnToRealTimeButton = returnToRealtimeButton.get(); |
| panel->appendChild(returnToRealtimeButton.release(), ec, true); |
| if (ec) |
| return 0; |
| |
| if (mediaElement->document()->page()->theme()->usesMediaControlStatusDisplay()) { |
| RefPtr<MediaControlStatusDisplayElement> statusDisplay = MediaControlStatusDisplayElement::create(mediaElement); |
| controls->m_statusDisplay = statusDisplay.get(); |
| panel->appendChild(statusDisplay.release(), ec, true); |
| if (ec) |
| return 0; |
| } |
| |
| RefPtr<MediaControlTimelineContainerElement> timelineContainer = MediaControlTimelineContainerElement::create(mediaElement); |
| |
| RefPtr<MediaControlCurrentTimeDisplayElement> currentTimeDisplay = MediaControlCurrentTimeDisplayElement::create(mediaElement); |
| controls->m_currentTimeDisplay = currentTimeDisplay.get(); |
| timelineContainer->appendChild(currentTimeDisplay.release(), ec, true); |
| if (ec) |
| return 0; |
| |
| RefPtr<MediaControlTimelineElement> timeline = MediaControlTimelineElement::create(mediaElement, controls.get()); |
| controls->m_timeline = timeline.get(); |
| timelineContainer->appendChild(timeline.release(), ec, true); |
| if (ec) |
| return 0; |
| |
| RefPtr<MediaControlTimeRemainingDisplayElement> timeRemainingDisplay = MediaControlTimeRemainingDisplayElement::create(mediaElement); |
| controls->m_timeRemainingDisplay = timeRemainingDisplay.get(); |
| timelineContainer->appendChild(timeRemainingDisplay.release(), ec, true); |
| if (ec) |
| return 0; |
| |
| controls->m_timelineContainer = timelineContainer.get(); |
| panel->appendChild(timelineContainer.release(), ec, true); |
| if (ec) |
| return 0; |
| |
| #if !PLATFORM(ANDROID) |
| // FIXME: Only create when needed <http://webkit.org/b/57163> |
| RefPtr<MediaControlSeekBackButtonElement> seekBackButton = MediaControlSeekBackButtonElement::create(mediaElement); |
| controls->m_seekBackButton = seekBackButton.get(); |
| panel->appendChild(seekBackButton.release(), ec, true); |
| if (ec) |
| return 0; |
| |
| // FIXME: Only create when needed <http://webkit.org/b/57163> |
| RefPtr<MediaControlSeekForwardButtonElement> seekForwardButton = MediaControlSeekForwardButtonElement::create(mediaElement); |
| controls->m_seekForwardButton = seekForwardButton.get(); |
| panel->appendChild(seekForwardButton.release(), ec, true); |
| if (ec) |
| return 0; |
| #endif |
| |
| if (mediaElement->document()->page()->theme()->supportsClosedCaptioning()) { |
| RefPtr<MediaControlToggleClosedCaptionsButtonElement> toggleClosedCaptionsButton = MediaControlToggleClosedCaptionsButtonElement::create(mediaElement); |
| controls->m_toggleClosedCaptionsButton = toggleClosedCaptionsButton.get(); |
| panel->appendChild(toggleClosedCaptionsButton.release(), ec, true); |
| if (ec) |
| return 0; |
| } |
| |
| // FIXME: Only create when needed <http://webkit.org/b/57163> |
| RefPtr<MediaControlFullscreenButtonElement> fullScreenButton = MediaControlFullscreenButtonElement::create(mediaElement, controls.get()); |
| controls->m_fullScreenButton = fullScreenButton.get(); |
| panel->appendChild(fullScreenButton.release(), ec, true); |
| |
| RefPtr<MediaControlPanelMuteButtonElement> panelMuteButton = MediaControlPanelMuteButtonElement::create(mediaElement, controls.get()); |
| controls->m_panelMuteButton = panelMuteButton.get(); |
| panel->appendChild(panelMuteButton.release(), ec, true); |
| if (ec) |
| return 0; |
| |
| if (mediaElement->document()->page()->theme()->usesMediaControlVolumeSlider()) { |
| RefPtr<MediaControlVolumeSliderContainerElement> volumeSliderContainer = MediaControlVolumeSliderContainerElement::create(mediaElement); |
| |
| RefPtr<MediaControlVolumeSliderElement> slider = MediaControlVolumeSliderElement::create(mediaElement); |
| controls->m_volumeSlider = slider.get(); |
| volumeSliderContainer->appendChild(slider.release(), ec, true); |
| if (ec) |
| return 0; |
| |
| RefPtr<MediaControlVolumeSliderMuteButtonElement> volumeSliderMuteButton = MediaControlVolumeSliderMuteButtonElement::create(mediaElement); |
| controls->m_volumeSliderMuteButton = volumeSliderMuteButton.get(); |
| volumeSliderContainer->appendChild(volumeSliderMuteButton.release(), ec, true); |
| if (ec) |
| return 0; |
| |
| controls->m_volumeSliderContainer = volumeSliderContainer.get(); |
| panel->appendChild(volumeSliderContainer.release(), ec, true); |
| if (ec) |
| return 0; |
| } |
| |
| // FIXME: Only create when needed <http://webkit.org/b/57163> |
| RefPtr<MediaControlFullscreenVolumeMinButtonElement> fullScreenMinVolumeButton = MediaControlFullscreenVolumeMinButtonElement::create(mediaElement); |
| controls->m_fullScreenMinVolumeButton = fullScreenMinVolumeButton.get(); |
| panel->appendChild(fullScreenMinVolumeButton.release(), ec, true); |
| if (ec) |
| return 0; |
| |
| RefPtr<MediaControlFullscreenVolumeSliderElement> fullScreenVolumeSlider = MediaControlFullscreenVolumeSliderElement::create(mediaElement); |
| controls->m_fullScreenVolumeSlider = fullScreenVolumeSlider.get(); |
| panel->appendChild(fullScreenVolumeSlider.release(), ec, true); |
| if (ec) |
| return 0; |
| |
| RefPtr<MediaControlFullscreenVolumeMaxButtonElement> fullScreenMaxVolumeButton = MediaControlFullscreenVolumeMaxButtonElement::create(mediaElement); |
| controls->m_fullScreenMaxVolumeButton = fullScreenMaxVolumeButton.get(); |
| panel->appendChild(fullScreenMaxVolumeButton.release(), ec, true); |
| if (ec) |
| return 0; |
| |
| controls->m_panel = panel.get(); |
| controls->appendChild(panel.release(), ec, true); |
| if (ec) |
| return 0; |
| |
| return controls.release(); |
| } |
| |
| void MediaControlRootElement::show() |
| { |
| m_panel->show(); |
| } |
| |
| void MediaControlRootElement::hide() |
| { |
| m_panel->hide(); |
| } |
| |
| static const String& webkitTransitionString() |
| { |
| DEFINE_STATIC_LOCAL(String, s, ("-webkit-transition")); |
| return s; |
| } |
| |
| static const String& opacityString() |
| { |
| DEFINE_STATIC_LOCAL(String, s, ("opacity")); |
| return s; |
| } |
| |
| void MediaControlRootElement::makeOpaque() |
| { |
| if (m_opaque) |
| return; |
| |
| DEFINE_STATIC_LOCAL(String, transitionValue, ()); |
| if (transitionValue.isNull()) |
| transitionValue = String::format("opacity %.1gs", document()->page()->theme()->mediaControlsFadeInDuration()); |
| DEFINE_STATIC_LOCAL(String, opacityValue, ("1")); |
| |
| ExceptionCode ec; |
| // FIXME: Make more efficient <http://webkit.org/b/58157> |
| m_panel->style()->setProperty(webkitTransitionString(), transitionValue, ec); |
| m_panel->style()->setProperty(opacityString(), opacityValue, ec); |
| m_opaque = true; |
| } |
| |
| void MediaControlRootElement::makeTransparent() |
| { |
| if (!m_opaque) |
| return; |
| |
| DEFINE_STATIC_LOCAL(String, transitionValue, ()); |
| if (transitionValue.isNull()) |
| transitionValue = String::format("opacity %.1gs", document()->page()->theme()->mediaControlsFadeOutDuration()); |
| DEFINE_STATIC_LOCAL(String, opacityValue, ("0")); |
| |
| ExceptionCode ec; |
| // FIXME: Make more efficient <http://webkit.org/b/58157> |
| m_panel->style()->setProperty(webkitTransitionString(), transitionValue, ec); |
| m_panel->style()->setProperty(opacityString(), opacityValue, ec); |
| m_opaque = false; |
| } |
| |
| void MediaControlRootElement::reset() |
| { |
| Page* page = document()->page(); |
| if (!page) |
| return; |
| |
| changedNetworkState(); |
| |
| if (m_mediaElement->supportsFullscreen()) |
| m_fullScreenButton->show(); |
| else |
| m_fullScreenButton->hide(); |
| |
| float duration = m_mediaElement->duration(); |
| if (isfinite(duration) || page->theme()->hasOwnDisabledStateHandlingFor(MediaSliderPart)) { |
| m_timeline->setDuration(duration); |
| m_timelineContainer->show(); |
| m_timeline->setPosition(m_mediaElement->currentTime()); |
| updateTimeDisplay(); |
| } else |
| m_timelineContainer->hide(); |
| |
| if (m_mediaElement->hasAudio() || page->theme()->hasOwnDisabledStateHandlingFor(MediaMuteButtonPart)) |
| m_panelMuteButton->show(); |
| else |
| m_panelMuteButton->hide(); |
| |
| if (m_volumeSlider) |
| m_volumeSlider->setVolume(m_mediaElement->volume()); |
| |
| if (m_toggleClosedCaptionsButton) { |
| if (m_mediaElement->hasClosedCaptions()) |
| m_toggleClosedCaptionsButton->show(); |
| else |
| m_toggleClosedCaptionsButton->hide(); |
| } |
| |
| if (m_mediaElement->movieLoadType() != MediaPlayer::LiveStream) { |
| m_returnToRealTimeButton->hide(); |
| m_rewindButton->show(); |
| } else { |
| m_returnToRealTimeButton->show(); |
| m_rewindButton->hide(); |
| } |
| |
| makeOpaque(); |
| } |
| |
| void MediaControlRootElement::playbackStarted() |
| { |
| m_playButton->updateDisplayType(); |
| m_timeline->setPosition(m_mediaElement->currentTime()); |
| updateTimeDisplay(); |
| } |
| |
| void MediaControlRootElement::playbackProgressed() |
| { |
| m_timeline->setPosition(m_mediaElement->currentTime()); |
| updateTimeDisplay(); |
| } |
| |
| void MediaControlRootElement::playbackStopped() |
| { |
| m_playButton->updateDisplayType(); |
| m_timeline->setPosition(m_mediaElement->currentTime()); |
| updateTimeDisplay(); |
| makeOpaque(); |
| } |
| |
| void MediaControlRootElement::updateTimeDisplay() |
| { |
| float now = m_mediaElement->currentTime(); |
| float duration = m_mediaElement->duration(); |
| |
| Page* page = document()->page(); |
| if (!page) |
| return; |
| |
| // Allow the theme to format the time. |
| ExceptionCode ec; |
| m_currentTimeDisplay->setInnerText(page->theme()->formatMediaControlsCurrentTime(now, duration), ec); |
| m_currentTimeDisplay->setCurrentValue(now); |
| m_timeRemainingDisplay->setInnerText(page->theme()->formatMediaControlsRemainingTime(now, duration), ec); |
| m_timeRemainingDisplay->setCurrentValue(now - duration); |
| } |
| |
| void MediaControlRootElement::reportedError() |
| { |
| Page* page = document()->page(); |
| if (!page) |
| return; |
| |
| if (!page->theme()->hasOwnDisabledStateHandlingFor(MediaSliderPart)) |
| m_timelineContainer->hide(); |
| |
| if (!page->theme()->hasOwnDisabledStateHandlingFor(MediaMuteButtonPart)) |
| m_panelMuteButton->hide(); |
| |
| m_fullScreenButton->hide(); |
| |
| if (m_volumeSliderContainer) |
| m_volumeSliderContainer->hide(); |
| if (m_toggleClosedCaptionsButton && !page->theme()->hasOwnDisabledStateHandlingFor(MediaToggleClosedCaptionsButtonPart)) |
| m_toggleClosedCaptionsButton->hide(); |
| } |
| |
| void MediaControlRootElement::changedNetworkState() |
| { |
| if (m_statusDisplay) |
| m_statusDisplay->update(); |
| } |
| |
| void MediaControlRootElement::loadedMetadata() |
| { |
| if (m_statusDisplay) |
| m_statusDisplay->hide(); |
| |
| reset(); |
| } |
| |
| void MediaControlRootElement::changedClosedCaptionsVisibility() |
| { |
| if (m_toggleClosedCaptionsButton) |
| m_toggleClosedCaptionsButton->updateDisplayType(); |
| } |
| |
| void MediaControlRootElement::changedMute() |
| { |
| m_panelMuteButton->changedMute(); |
| if (m_volumeSliderMuteButton) |
| m_volumeSliderMuteButton->changedMute(); |
| } |
| |
| void MediaControlRootElement::changedVolume() |
| { |
| if (m_volumeSlider) |
| m_volumeSlider->setVolume(m_mediaElement->volume()); |
| } |
| |
| void MediaControlRootElement::enteredFullscreen() |
| { |
| if (m_mediaElement->movieLoadType() == MediaPlayer::LiveStream || m_mediaElement->movieLoadType() == MediaPlayer::StoredStream) { |
| #if !PLATFORM(ANDROID) |
| m_seekBackButton->hide(); |
| m_seekForwardButton->hide(); |
| #endif |
| } else |
| m_rewindButton->hide(); |
| } |
| |
| void MediaControlRootElement::exitedFullscreen() |
| { |
| // "show" actually means removal of display:none style, so we are just clearing styles |
| // when exiting fullscreen. |
| // FIXME: Clarify naming of show/hide <http://webkit.org/b/58157> |
| m_rewindButton->show(); |
| #if !PLATFORM(ANDROID) |
| m_seekBackButton->show(); |
| m_seekForwardButton->show(); |
| #endif |
| } |
| |
| void MediaControlRootElement::showVolumeSlider() |
| { |
| if (!m_mediaElement->hasAudio()) |
| return; |
| |
| if (m_volumeSliderContainer) |
| m_volumeSliderContainer->show(); |
| } |
| |
| const AtomicString& MediaControlRootElement::shadowPseudoId() const |
| { |
| DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls")); |
| return id; |
| } |
| |
| } |
| |
| #endif |