summaryrefslogtreecommitdiff
path: root/Source/WebCore/html/shadow/MediaControlsApple.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/html/shadow/MediaControlsApple.cpp')
-rw-r--r--Source/WebCore/html/shadow/MediaControlsApple.cpp568
1 files changed, 568 insertions, 0 deletions
diff --git a/Source/WebCore/html/shadow/MediaControlsApple.cpp b/Source/WebCore/html/shadow/MediaControlsApple.cpp
new file mode 100644
index 000000000..61092524c
--- /dev/null
+++ b/Source/WebCore/html/shadow/MediaControlsApple.cpp
@@ -0,0 +1,568 @@
+/*
+ * Copyright (C) 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2011, 2012 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 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 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 "MediaControlsApple.h"
+
+#include "CSSValueKeywords.h"
+#include "EventNames.h"
+#include "Page.h"
+#include "WheelEvent.h"
+
+namespace WebCore {
+
+MediaControlsApple::MediaControlsApple(Document& document)
+ : MediaControls(document)
+{
+}
+
+RefPtr<MediaControls> MediaControls::tryCreate(Document& document)
+{
+ return MediaControlsApple::tryCreateControls(document);
+}
+
+RefPtr<MediaControlsApple> MediaControlsApple::tryCreateControls(Document& document)
+{
+ if (!document.page())
+ return nullptr;
+
+ auto controls = adoptRef(*new MediaControlsApple(document));
+
+ auto panel = MediaControlPanelElement::create(document);
+
+ auto rewindButton = MediaControlRewindButtonElement::create(document);
+ controls->m_rewindButton = rewindButton.ptr();
+ if (panel->appendChild(rewindButton).hasException())
+ return nullptr;
+
+ auto playButton = MediaControlPlayButtonElement::create(document);
+ controls->m_playButton = playButton.ptr();
+ if (panel->appendChild(playButton).hasException())
+ return nullptr;
+
+ auto returnToRealtimeButton = MediaControlReturnToRealtimeButtonElement::create(document);
+ controls->m_returnToRealTimeButton = returnToRealtimeButton.ptr();
+ if (panel->appendChild(returnToRealtimeButton).hasException())
+ return nullptr;
+
+ if (document.page()->theme().usesMediaControlStatusDisplay()) {
+ auto statusDisplay = MediaControlStatusDisplayElement::create(document);
+ controls->m_statusDisplay = statusDisplay.ptr();
+ if (panel->appendChild(statusDisplay).hasException())
+ return nullptr;
+ }
+
+ auto timelineContainer = MediaControlTimelineContainerElement::create(document);
+
+ auto currentTimeDisplay = MediaControlCurrentTimeDisplayElement::create(document);
+ controls->m_currentTimeDisplay = currentTimeDisplay.ptr();
+ if (timelineContainer->appendChild(currentTimeDisplay).hasException())
+ return nullptr;
+
+ auto timeline = MediaControlTimelineElement::create(document, controls.ptr());
+ controls->m_timeline = timeline.ptr();
+ if (timelineContainer->appendChild(timeline).hasException())
+ return nullptr;
+
+ auto timeRemainingDisplay = MediaControlTimeRemainingDisplayElement::create(document);
+ controls->m_timeRemainingDisplay = timeRemainingDisplay.ptr();
+ if (timelineContainer->appendChild(timeRemainingDisplay).hasException())
+ return nullptr;
+
+ controls->m_timelineContainer = timelineContainer.ptr();
+ if (panel->appendChild(timelineContainer).hasException())
+ return nullptr;
+
+ // FIXME: Only create when needed <http://webkit.org/b/57163>
+ auto seekBackButton = MediaControlSeekBackButtonElement::create(document);
+ controls->m_seekBackButton = seekBackButton.ptr();
+ if (panel->appendChild(seekBackButton).hasException())
+ return nullptr;
+
+ // FIXME: Only create when needed <http://webkit.org/b/57163>
+ auto seekForwardButton = MediaControlSeekForwardButtonElement::create(document);
+ controls->m_seekForwardButton = seekForwardButton.ptr();
+ if (panel->appendChild(seekForwardButton).hasException())
+ return nullptr;
+
+ if (document.page()->theme().supportsClosedCaptioning()) {
+ auto closedCaptionsContainer = MediaControlClosedCaptionsContainerElement::create(document);
+
+ auto closedCaptionsTrackList = MediaControlClosedCaptionsTrackListElement::create(document, controls.ptr());
+ controls->m_closedCaptionsTrackList = closedCaptionsTrackList.ptr();
+ if (closedCaptionsContainer->appendChild(closedCaptionsTrackList).hasException())
+ return nullptr;
+
+ auto toggleClosedCaptionsButton = MediaControlToggleClosedCaptionsButtonElement::create(document, controls.ptr());
+ controls->m_toggleClosedCaptionsButton = toggleClosedCaptionsButton.ptr();
+ if (panel->appendChild(toggleClosedCaptionsButton).hasException())
+ return nullptr;
+
+ controls->m_closedCaptionsContainer = closedCaptionsContainer.ptr();
+ if (controls->appendChild(closedCaptionsContainer).hasException())
+ return nullptr;
+ }
+
+ // FIXME: Only create when needed <http://webkit.org/b/57163>
+ auto fullScreenButton = MediaControlFullscreenButtonElement::create(document);
+ controls->m_fullScreenButton = fullScreenButton.ptr();
+ panel->appendChild(fullScreenButton);
+
+ // The mute button and the slider element should be in the same div.
+ auto panelVolumeControlContainer = HTMLDivElement::create(document);
+
+ if (document.page()->theme().usesMediaControlVolumeSlider()) {
+ auto volumeSliderContainer = MediaControlVolumeSliderContainerElement::create(document);
+
+ auto slider = MediaControlPanelVolumeSliderElement::create(document);
+ controls->m_volumeSlider = slider.ptr();
+ if (volumeSliderContainer->appendChild(slider).hasException())
+ return nullptr;
+
+ // This is a duplicate mute button, which is visible in some ports at the bottom of the volume bar.
+ // It's important only when the volume bar is displayed below the controls.
+ auto volumeSliderMuteButton = MediaControlVolumeSliderMuteButtonElement::create(document);
+ controls->m_volumeSliderMuteButton = volumeSliderMuteButton.ptr();
+ if (volumeSliderContainer->appendChild(volumeSliderMuteButton).hasException())
+ return nullptr;
+
+ controls->m_volumeSliderContainer = volumeSliderContainer.ptr();
+ if (panelVolumeControlContainer->appendChild(volumeSliderContainer).hasException())
+ return nullptr;
+ }
+
+ auto panelMuteButton = MediaControlPanelMuteButtonElement::create(document, controls.ptr());
+ controls->m_panelMuteButton = panelMuteButton.ptr();
+ if (panelVolumeControlContainer->appendChild(panelMuteButton).hasException())
+ return nullptr;
+
+ if (panel->appendChild(panelVolumeControlContainer).hasException())
+ return nullptr;
+
+ // FIXME: Only create when needed <http://webkit.org/b/57163>
+ auto fullScreenMinVolumeButton = MediaControlFullscreenVolumeMinButtonElement::create(document);
+ controls->m_fullScreenMinVolumeButton = fullScreenMinVolumeButton.ptr();
+ if (panel->appendChild(fullScreenMinVolumeButton).hasException())
+ return nullptr;
+
+ auto fullScreenVolumeSlider = MediaControlFullscreenVolumeSliderElement::create(document);
+ controls->m_fullScreenVolumeSlider = fullScreenVolumeSlider.ptr();
+ if (panel->appendChild(fullScreenVolumeSlider).hasException())
+ return nullptr;
+
+ auto fullScreenMaxVolumeButton = MediaControlFullscreenVolumeMaxButtonElement::create(document);
+ controls->m_fullScreenMaxVolumeButton = fullScreenMaxVolumeButton.ptr();
+ if (panel->appendChild(fullScreenMaxVolumeButton).hasException())
+ return nullptr;
+
+ controls->m_panel = panel.ptr();
+ if (controls->appendChild(panel).hasException())
+ return nullptr;
+
+ return WTFMove(controls);
+}
+
+void MediaControlsApple::setMediaController(MediaControllerInterface* controller)
+{
+ if (m_mediaController == controller)
+ return;
+
+ MediaControls::setMediaController(controller);
+
+ if (m_rewindButton)
+ m_rewindButton->setMediaController(controller);
+ if (m_returnToRealTimeButton)
+ m_returnToRealTimeButton->setMediaController(controller);
+ if (m_statusDisplay)
+ m_statusDisplay->setMediaController(controller);
+ if (m_timeRemainingDisplay)
+ m_timeRemainingDisplay->setMediaController(controller);
+ if (m_timelineContainer)
+ m_timelineContainer->setMediaController(controller);
+ if (m_seekBackButton)
+ m_seekBackButton->setMediaController(controller);
+ if (m_seekForwardButton)
+ m_seekForwardButton->setMediaController(controller);
+ if (m_volumeSliderMuteButton)
+ m_volumeSliderMuteButton->setMediaController(controller);
+ if (m_volumeSliderContainer)
+ m_volumeSliderContainer->setMediaController(controller);
+ if (m_fullScreenMinVolumeButton)
+ m_fullScreenMinVolumeButton->setMediaController(controller);
+ if (m_fullScreenVolumeSlider)
+ m_fullScreenVolumeSlider->setMediaController(controller);
+ if (m_fullScreenMaxVolumeButton)
+ m_fullScreenMaxVolumeButton->setMediaController(controller);
+ if (m_closedCaptionsTrackList)
+ m_closedCaptionsTrackList->setMediaController(controller);
+ if (m_closedCaptionsContainer)
+ m_closedCaptionsContainer->setMediaController(controller);
+}
+
+void MediaControlsApple::defaultEventHandler(Event& event)
+{
+ if (event.type() == eventNames().clickEvent) {
+ if (m_closedCaptionsContainer && m_closedCaptionsContainer->isShowing()) {
+ hideClosedCaptionTrackList();
+ event.setDefaultHandled();
+ }
+ }
+
+ MediaControls::defaultEventHandler(event);
+}
+
+void MediaControlsApple::hide()
+{
+ MediaControls::hide();
+ m_volumeSliderContainer->hide();
+ if (m_closedCaptionsContainer)
+ hideClosedCaptionTrackList();
+}
+
+void MediaControlsApple::makeTransparent()
+{
+ MediaControls::makeTransparent();
+ m_volumeSliderContainer->hide();
+ if (m_closedCaptionsContainer)
+ hideClosedCaptionTrackList();
+}
+
+void MediaControlsApple::changedClosedCaptionsVisibility()
+{
+ MediaControls::changedClosedCaptionsVisibility();
+ if (m_closedCaptionsContainer && m_closedCaptionsContainer->isShowing())
+ hideClosedCaptionTrackList();
+
+}
+
+void MediaControlsApple::reset()
+{
+ Page* page = document().page();
+ if (!page)
+ return;
+
+ updateStatusDisplay();
+
+ if (m_mediaController->supportsFullscreen(HTMLMediaElementEnums::VideoFullscreenModeStandard))
+ m_fullScreenButton->show();
+ else
+ m_fullScreenButton->hide();
+
+ double duration = m_mediaController->duration();
+ if (std::isfinite(duration) || page->theme().hasOwnDisabledStateHandlingFor(MediaSliderPart)) {
+ m_timeline->setDuration(duration);
+ m_timelineContainer->show();
+ m_timeline->setPosition(m_mediaController->currentTime());
+ updateCurrentTimeDisplay();
+ } else
+ m_timelineContainer->hide();
+
+ if (m_mediaController->hasAudio() || page->theme().hasOwnDisabledStateHandlingFor(MediaMuteButtonPart))
+ m_panelMuteButton->show();
+ else
+ m_panelMuteButton->hide();
+
+ if (m_volumeSlider)
+ setSliderVolume();
+
+ if (m_toggleClosedCaptionsButton) {
+ if (m_mediaController->hasClosedCaptions())
+ m_toggleClosedCaptionsButton->show();
+ else
+ m_toggleClosedCaptionsButton->hide();
+ }
+
+ if (m_playButton)
+ m_playButton->updateDisplayType();
+
+#if ENABLE(FULLSCREEN_API)
+ if (m_fullScreenVolumeSlider)
+ setFullscreenSliderVolume();
+
+ if (m_isFullscreen) {
+ if (m_mediaController->isLiveStream()) {
+ m_seekBackButton->hide();
+ m_seekForwardButton->hide();
+ m_rewindButton->show();
+ m_returnToRealTimeButton->show();
+ } else {
+ m_seekBackButton->show();
+ m_seekForwardButton->show();
+ m_rewindButton->hide();
+ m_returnToRealTimeButton->hide();
+ }
+ } else
+#endif
+ if (!m_mediaController->isLiveStream()) {
+ m_returnToRealTimeButton->hide();
+ m_rewindButton->show();
+ } else {
+ m_returnToRealTimeButton->show();
+ m_rewindButton->hide();
+ }
+
+ makeOpaque();
+}
+
+void MediaControlsApple::updateCurrentTimeDisplay()
+{
+ double now = m_mediaController->currentTime();
+ double duration = m_mediaController->duration();
+
+ Page* page = document().page();
+ if (!page)
+ return;
+
+ // Allow the theme to format the time.
+ m_currentTimeDisplay->setInnerText(page->theme().formatMediaControlsCurrentTime(now, duration));
+ m_currentTimeDisplay->setCurrentValue(now);
+ m_timeRemainingDisplay->setInnerText(page->theme().formatMediaControlsRemainingTime(now, duration));
+ m_timeRemainingDisplay->setCurrentValue(now - duration);
+}
+
+void MediaControlsApple::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();
+ if (m_closedCaptionsContainer)
+ hideClosedCaptionTrackList();
+}
+
+void MediaControlsApple::updateStatusDisplay()
+{
+ if (m_statusDisplay)
+ m_statusDisplay->update();
+}
+
+void MediaControlsApple::loadedMetadata()
+{
+ if (m_statusDisplay && !m_mediaController->isLiveStream())
+ m_statusDisplay->hide();
+
+ MediaControls::loadedMetadata();
+}
+
+void MediaControlsApple::changedMute()
+{
+ MediaControls::changedMute();
+
+ if (m_volumeSliderMuteButton)
+ m_volumeSliderMuteButton->changedMute();
+}
+
+void MediaControlsApple::changedVolume()
+{
+ MediaControls::changedVolume();
+
+ if (m_fullScreenVolumeSlider)
+ setFullscreenSliderVolume();
+}
+
+void MediaControlsApple::enteredFullscreen()
+{
+ MediaControls::enteredFullscreen();
+ m_panel->setCanBeDragged(true);
+
+ if (m_mediaController->isLiveStream()) {
+ m_seekBackButton->hide();
+ m_seekForwardButton->hide();
+ m_rewindButton->show();
+ m_returnToRealTimeButton->show();
+ } else {
+ m_seekBackButton->show();
+ m_seekForwardButton->show();
+ m_rewindButton->hide();
+ m_returnToRealTimeButton->hide();
+ }
+}
+
+void MediaControlsApple::exitedFullscreen()
+{
+ m_rewindButton->show();
+ m_seekBackButton->show();
+ m_seekForwardButton->show();
+ m_returnToRealTimeButton->show();
+
+ m_panel->setCanBeDragged(false);
+
+ // We will keep using the panel, but we want it to go back to the standard position.
+ // This will matter right away because we use the panel even when not fullscreen.
+ // And if we reenter fullscreen we also want the panel in the standard position.
+ m_panel->resetPosition();
+
+ MediaControls::exitedFullscreen();
+}
+
+void MediaControlsApple::showVolumeSlider()
+{
+ if (!m_mediaController->hasAudio())
+ return;
+
+ if (m_volumeSliderContainer)
+ m_volumeSliderContainer->show();
+}
+
+void MediaControlsApple::toggleClosedCaptionTrackList()
+{
+ if (!m_mediaController->hasClosedCaptions())
+ return;
+
+ if (m_closedCaptionsContainer) {
+ if (m_closedCaptionsContainer->isShowing())
+ hideClosedCaptionTrackList();
+ else {
+ if (m_closedCaptionsTrackList)
+ m_closedCaptionsTrackList->updateDisplay();
+ showClosedCaptionTrackList();
+ }
+ }
+}
+
+void MediaControlsApple::showClosedCaptionTrackList()
+{
+ if (!m_closedCaptionsContainer || m_closedCaptionsContainer->isShowing())
+ return;
+
+ m_closedCaptionsContainer->show();
+
+ // Ensure the controls panel does not receive any events while the captions
+ // track list is visible as all events now need to be captured by the
+ // track list.
+ m_panel->setInlineStyleProperty(CSSPropertyPointerEvents, CSSValueNone);
+
+ EventListener& listener = eventListener();
+ m_closedCaptionsContainer->addEventListener(eventNames().wheelEvent, listener, true);
+
+ // Track click events in the capture phase at two levels, first at the document level
+ // such that a click outside of the <video> may dismiss the track list, second at the
+ // media controls level such that a click anywhere outside of the track list hides the
+ // track list. These two levels are necessary since it would not be possible to get a
+ // reference to the track list when handling the event outside of the shadow tree.
+ document().addEventListener(eventNames().clickEvent, listener, true);
+ addEventListener(eventNames().clickEvent, listener, true);
+}
+
+void MediaControlsApple::hideClosedCaptionTrackList()
+{
+ if (!m_closedCaptionsContainer || !m_closedCaptionsContainer->isShowing())
+ return;
+
+ m_closedCaptionsContainer->hide();
+
+ // Buttons in the controls panel may now be interactive.
+ m_panel->removeInlineStyleProperty(CSSPropertyPointerEvents);
+
+ EventListener& listener = eventListener();
+ m_closedCaptionsContainer->removeEventListener(eventNames().wheelEvent, listener, true);
+ document().removeEventListener(eventNames().clickEvent, listener, true);
+ removeEventListener(eventNames().clickEvent, listener, true);
+}
+
+void MediaControlsApple::setFullscreenSliderVolume()
+{
+ m_fullScreenVolumeSlider->setVolume(m_mediaController->muted() ? 0.0 : m_mediaController->volume());
+}
+
+bool MediaControlsApple::shouldClosedCaptionsContainerPreventPageScrolling(int wheelDeltaY)
+{
+ int scrollTop = m_closedCaptionsContainer->scrollTop();
+ // Scrolling down.
+ if (wheelDeltaY < 0 && (scrollTop + m_closedCaptionsContainer->offsetHeight()) >= m_closedCaptionsContainer->scrollHeight())
+ return true;
+ // Scrolling up.
+ if (wheelDeltaY > 0 && scrollTop <= 0)
+ return true;
+ return false;
+}
+
+void MediaControlsApple::handleClickEvent(Event& event)
+{
+ Node* currentTarget = event.currentTarget()->toNode();
+ Node* target = event.target()->toNode();
+
+ if ((currentTarget == &document() && !shadowHost()->contains(target)) || (currentTarget == this && !m_closedCaptionsContainer->contains(target))) {
+ hideClosedCaptionTrackList();
+ event.stopImmediatePropagation();
+ event.setDefaultHandled();
+ }
+}
+
+void MediaControlsApple::closedCaptionTracksChanged()
+{
+ if (m_toggleClosedCaptionsButton) {
+ if (m_mediaController->hasClosedCaptions())
+ m_toggleClosedCaptionsButton->show();
+ else
+ m_toggleClosedCaptionsButton->hide();
+ }
+}
+
+MediaControlsAppleEventListener& MediaControlsApple::eventListener()
+{
+ if (!m_eventListener)
+ m_eventListener = MediaControlsAppleEventListener::create(this);
+ return *m_eventListener;
+}
+
+// --------
+
+void MediaControlsAppleEventListener::handleEvent(ScriptExecutionContext*, Event* event)
+{
+ if (event->type() == eventNames().clickEvent)
+ m_mediaControls->handleClickEvent(*event);
+ else if (eventNames().isWheelEventType(event->type()) && is<WheelEvent>(*event)) {
+ WheelEvent& wheelEvent = downcast<WheelEvent>(*event);
+ if (m_mediaControls->shouldClosedCaptionsContainerPreventPageScrolling(wheelEvent.wheelDeltaY()))
+ wheelEvent.preventDefault();
+ }
+}
+
+bool MediaControlsAppleEventListener::operator==(const EventListener& listener) const
+{
+ if (const MediaControlsAppleEventListener* mediaControlsAppleEventListener = MediaControlsAppleEventListener::cast(&listener))
+ return m_mediaControls == mediaControlsAppleEventListener->m_mediaControls;
+ return false;
+}
+
+}
+
+#endif