Index: third_party/WebKit/Source/core/html/shadow/MediaControls.cpp |
diff --git a/third_party/WebKit/Source/core/html/shadow/MediaControls.cpp b/third_party/WebKit/Source/core/html/shadow/MediaControls.cpp |
deleted file mode 100644 |
index 811996a545f6752fcf5a449b696119c926d17b1c..0000000000000000000000000000000000000000 |
--- a/third_party/WebKit/Source/core/html/shadow/MediaControls.cpp |
+++ /dev/null |
@@ -1,1110 +0,0 @@ |
-/* |
- * Copyright (C) 2011, 2012 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 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 "core/html/shadow/MediaControls.h" |
- |
-#include "bindings/core/v8/ExceptionState.h" |
-#include "core/dom/ClientRect.h" |
-#include "core/dom/Fullscreen.h" |
-#include "core/dom/ResizeObserver.h" |
-#include "core/dom/ResizeObserverCallback.h" |
-#include "core/dom/ResizeObserverEntry.h" |
-#include "core/dom/TaskRunnerHelper.h" |
-#include "core/events/MouseEvent.h" |
-#include "core/frame/Settings.h" |
-#include "core/frame/UseCounter.h" |
-#include "core/html/HTMLMediaElement.h" |
-#include "core/html/HTMLVideoElement.h" |
-#include "core/html/media/HTMLMediaElementControlsList.h" |
-#include "core/html/shadow/MediaControlsMediaEventListener.h" |
-#include "core/html/shadow/MediaControlsOrientationLockDelegate.h" |
-#include "core/html/shadow/MediaControlsWindowEventListener.h" |
-#include "core/html/track/TextTrackContainer.h" |
-#include "core/html/track/TextTrackList.h" |
-#include "core/layout/LayoutObject.h" |
-#include "core/layout/LayoutTheme.h" |
-#include "platform/EventDispatchForbiddenScope.h" |
- |
-namespace blink { |
- |
-namespace { |
- |
-// TODO(steimel): should have better solution than hard-coding pixel values. |
-// Defined in core/css/mediaControls.css, core/css/mediaControlsAndroid.css, |
-// and core/paint/MediaControlsPainter.cpp. |
-constexpr int kOverlayPlayButtonWidth = 48; |
-constexpr int kOverlayPlayButtonHeight = 48; |
-constexpr int kOverlayBottomMargin = 10; |
-constexpr int kAndroidMediaPanelHeight = 48; |
- |
-constexpr int kMinWidthForOverlayPlayButton = kOverlayPlayButtonWidth; |
-constexpr int kMinHeightForOverlayPlayButton = kOverlayPlayButtonHeight + |
- kAndroidMediaPanelHeight + |
- (2 * kOverlayBottomMargin); |
- |
-} // anonymous namespace |
- |
-// If you change this value, then also update the corresponding value in |
-// LayoutTests/media/media-controls.js. |
-static const double timeWithoutMouseMovementBeforeHidingMediaControls = 3; |
- |
-static bool shouldShowFullscreenButton(const HTMLMediaElement& mediaElement) { |
- // Unconditionally allow the user to exit fullscreen if we are in it |
- // now. Especially on android, when we might not yet know if |
- // fullscreen is supported, we sometimes guess incorrectly and show |
- // the button earlier, and we don't want to remove it here if the |
- // user chose to enter fullscreen. crbug.com/500732 . |
- if (mediaElement.isFullscreen()) |
- return true; |
- |
- if (!mediaElement.isHTMLVideoElement()) |
- return false; |
- |
- if (!mediaElement.hasVideo()) |
- return false; |
- |
- if (!Fullscreen::fullscreenEnabled(mediaElement.document())) |
- return false; |
- |
- if (mediaElement.controlsListInternal()->shouldHideFullscreen()) { |
- UseCounter::count(mediaElement.document(), |
- UseCounter::HTMLMediaElementControlsListNoFullscreen); |
- return false; |
- } |
- |
- return true; |
-} |
- |
-static bool shouldShowCastButton(HTMLMediaElement& mediaElement) { |
- if (mediaElement.fastHasAttribute(HTMLNames::disableremoteplaybackAttr)) |
- return false; |
- |
- // Explicitly do not show cast button when the mediaControlsEnabled setting is |
- // false to make sure the overlay does not appear. |
- Document& document = mediaElement.document(); |
- if (document.settings() && !document.settings()->getMediaControlsEnabled()) |
- return false; |
- |
- // The page disabled the button via the attribute. |
- if (mediaElement.controlsListInternal()->shouldHideRemotePlayback()) { |
- UseCounter::count(mediaElement.document(), |
- UseCounter::HTMLMediaElementControlsListNoRemotePlayback); |
- return false; |
- } |
- |
- return mediaElement.hasRemoteRoutes(); |
-} |
- |
-static bool preferHiddenVolumeControls(const Document& document) { |
- return !document.settings() || |
- document.settings()->getPreferHiddenVolumeControls(); |
-} |
- |
-class MediaControls::BatchedControlUpdate { |
- WTF_MAKE_NONCOPYABLE(BatchedControlUpdate); |
- STACK_ALLOCATED(); |
- |
- public: |
- explicit BatchedControlUpdate(MediaControls* controls) |
- : m_controls(controls) { |
- DCHECK(isMainThread()); |
- DCHECK_GE(s_batchDepth, 0); |
- ++s_batchDepth; |
- } |
- ~BatchedControlUpdate() { |
- DCHECK(isMainThread()); |
- DCHECK_GT(s_batchDepth, 0); |
- if (!(--s_batchDepth)) |
- m_controls->computeWhichControlsFit(); |
- } |
- |
- private: |
- Member<MediaControls> m_controls; |
- static int s_batchDepth; |
-}; |
- |
-// Count of number open batches for controls visibility. |
-int MediaControls::BatchedControlUpdate::s_batchDepth = 0; |
- |
-class MediaControls::MediaControlsResizeObserverCallback final |
- : public ResizeObserverCallback { |
- public: |
- explicit MediaControlsResizeObserverCallback(MediaControls* controls) |
- : m_controls(controls) { |
- DCHECK(controls); |
- } |
- ~MediaControlsResizeObserverCallback() override = default; |
- |
- void handleEvent(const HeapVector<Member<ResizeObserverEntry>>& entries, |
- ResizeObserver* observer) override { |
- DCHECK_EQ(1u, entries.size()); |
- DCHECK_EQ(entries[0]->target(), m_controls->m_mediaElement); |
- m_controls->notifyElementSizeChanged(entries[0]->contentRect()); |
- } |
- |
- DEFINE_INLINE_TRACE() { |
- visitor->trace(m_controls); |
- ResizeObserverCallback::trace(visitor); |
- } |
- |
- private: |
- Member<MediaControls> m_controls; |
-}; |
- |
-MediaControls::MediaControls(HTMLMediaElement& mediaElement) |
- : HTMLDivElement(mediaElement.document()), |
- m_mediaElement(&mediaElement), |
- m_overlayEnclosure(nullptr), |
- m_overlayPlayButton(nullptr), |
- m_overlayCastButton(nullptr), |
- m_enclosure(nullptr), |
- m_panel(nullptr), |
- m_playButton(nullptr), |
- m_timeline(nullptr), |
- m_currentTimeDisplay(nullptr), |
- m_durationDisplay(nullptr), |
- m_muteButton(nullptr), |
- m_volumeSlider(nullptr), |
- m_toggleClosedCaptionsButton(nullptr), |
- m_textTrackList(nullptr), |
- m_overflowList(nullptr), |
- m_castButton(nullptr), |
- m_fullscreenButton(nullptr), |
- m_downloadButton(nullptr), |
- m_mediaEventListener(new MediaControlsMediaEventListener(this)), |
- m_windowEventListener(MediaControlsWindowEventListener::create( |
- this, |
- WTF::bind(&MediaControls::hideAllMenus, wrapWeakPersistent(this)))), |
- m_orientationLockDelegate(nullptr), |
- m_hideMediaControlsTimer(TaskRunnerHelper::get(TaskType::UnspecedTimer, |
- &mediaElement.document()), |
- this, |
- &MediaControls::hideMediaControlsTimerFired), |
- m_hideTimerBehaviorFlags(IgnoreNone), |
- m_isMouseOverControls(false), |
- m_isPausedForScrubbing(false), |
- m_resizeObserver(ResizeObserver::create( |
- mediaElement.document(), |
- new MediaControlsResizeObserverCallback(this))), |
- m_elementSizeChangedTimer(TaskRunnerHelper::get(TaskType::UnspecedTimer, |
- &mediaElement.document()), |
- this, |
- &MediaControls::elementSizeChangedTimerFired), |
- m_keepShowingUntilTimerFires(false) { |
- m_resizeObserver->observe(m_mediaElement); |
-} |
- |
-MediaControls* MediaControls::create(HTMLMediaElement& mediaElement, |
- ShadowRoot& shadowRoot) { |
- MediaControls* controls = new MediaControls(mediaElement); |
- controls->setShadowPseudoId(AtomicString("-webkit-media-controls")); |
- controls->initializeControls(); |
- controls->reset(); |
- |
- // Initialize the orientation lock when going fullscreen feature. |
- if (RuntimeEnabledFeatures::videoFullscreenOrientationLockEnabled() && |
- mediaElement.isHTMLVideoElement()) { |
- controls->m_orientationLockDelegate = |
- new MediaControlsOrientationLockDelegate( |
- toHTMLVideoElement(mediaElement)); |
- } |
- |
- shadowRoot.appendChild(controls); |
- return controls; |
-} |
- |
-// The media controls DOM structure looks like: |
-// |
-// MediaControls |
-// (-webkit-media-controls) |
-// +-MediaControlOverlayEnclosureElement |
-// | (-webkit-media-controls-overlay-enclosure) |
-// | +-MediaControlOverlayPlayButtonElement |
-// | | (-webkit-media-controls-overlay-play-button) |
-// | | {if mediaControlsOverlayPlayButtonEnabled} |
-// | \-MediaControlCastButtonElement |
-// | (-internal-media-controls-overlay-cast-button) |
-// \-MediaControlPanelEnclosureElement |
-// | (-webkit-media-controls-enclosure) |
-// \-MediaControlPanelElement |
-// | (-webkit-media-controls-panel) |
-// +-MediaControlPlayButtonElement |
-// | (-webkit-media-controls-play-button) |
-// +-MediaControlCurrentTimeDisplayElement |
-// | (-webkit-media-controls-current-time-display) |
-// +-MediaControlTimeRemainingDisplayElement |
-// | (-webkit-media-controls-time-remaining-display) |
-// +-MediaControlTimelineElement |
-// | (-webkit-media-controls-timeline) |
-// +-MediaControlMuteButtonElement |
-// | (-webkit-media-controls-mute-button) |
-// +-MediaControlVolumeSliderElement |
-// | (-webkit-media-controls-volume-slider) |
-// +-MediaControlFullscreenButtonElement |
-// | (-webkit-media-controls-fullscreen-button) |
-// +-MediaControlDownloadButtonElement |
-// | (-internal-media-controls-download-button) |
-// +-MediaControlToggleClosedCaptionsButtonElement |
-// | (-webkit-media-controls-toggle-closed-captions-button) |
-// \-MediaControlCastButtonElement |
-// (-internal-media-controls-cast-button) |
-// +-MediaControlTextTrackListElement |
-// | (-internal-media-controls-text-track-list) |
-// | {for each renderable text track} |
-// \-MediaControlTextTrackListItem |
-// | (-internal-media-controls-text-track-list-item) |
-// +-MediaControlTextTrackListItemInput |
-// | (-internal-media-controls-text-track-list-item-input) |
-// +-MediaControlTextTrackListItemCaptions |
-// | (-internal-media-controls-text-track-list-kind-captions) |
-// +-MediaControlTextTrackListItemSubtitles |
-// (-internal-media-controls-text-track-list-kind-subtitles) |
-void MediaControls::initializeControls() { |
- MediaControlOverlayEnclosureElement* overlayEnclosure = |
- MediaControlOverlayEnclosureElement::create(*this); |
- |
- if (RuntimeEnabledFeatures::mediaControlsOverlayPlayButtonEnabled()) { |
- MediaControlOverlayPlayButtonElement* overlayPlayButton = |
- MediaControlOverlayPlayButtonElement::create(*this); |
- m_overlayPlayButton = overlayPlayButton; |
- overlayEnclosure->appendChild(overlayPlayButton); |
- } |
- |
- MediaControlCastButtonElement* overlayCastButton = |
- MediaControlCastButtonElement::create(*this, true); |
- m_overlayCastButton = overlayCastButton; |
- overlayEnclosure->appendChild(overlayCastButton); |
- |
- m_overlayEnclosure = overlayEnclosure; |
- appendChild(overlayEnclosure); |
- |
- // Create an enclosing element for the panel so we can visually offset the |
- // controls correctly. |
- MediaControlPanelEnclosureElement* enclosure = |
- MediaControlPanelEnclosureElement::create(*this); |
- |
- MediaControlPanelElement* panel = MediaControlPanelElement::create(*this); |
- |
- MediaControlPlayButtonElement* playButton = |
- MediaControlPlayButtonElement::create(*this); |
- m_playButton = playButton; |
- panel->appendChild(playButton); |
- |
- MediaControlCurrentTimeDisplayElement* currentTimeDisplay = |
- MediaControlCurrentTimeDisplayElement::create(*this); |
- m_currentTimeDisplay = currentTimeDisplay; |
- m_currentTimeDisplay->setIsWanted(true); |
- panel->appendChild(currentTimeDisplay); |
- |
- MediaControlTimeRemainingDisplayElement* durationDisplay = |
- MediaControlTimeRemainingDisplayElement::create(*this); |
- m_durationDisplay = durationDisplay; |
- panel->appendChild(durationDisplay); |
- |
- MediaControlTimelineElement* timeline = |
- MediaControlTimelineElement::create(*this); |
- m_timeline = timeline; |
- panel->appendChild(timeline); |
- |
- MediaControlMuteButtonElement* muteButton = |
- MediaControlMuteButtonElement::create(*this); |
- m_muteButton = muteButton; |
- panel->appendChild(muteButton); |
- |
- MediaControlVolumeSliderElement* slider = |
- MediaControlVolumeSliderElement::create(*this); |
- m_volumeSlider = slider; |
- panel->appendChild(slider); |
- if (preferHiddenVolumeControls(document())) |
- m_volumeSlider->setIsWanted(false); |
- |
- MediaControlFullscreenButtonElement* fullscreenButton = |
- MediaControlFullscreenButtonElement::create(*this); |
- m_fullscreenButton = fullscreenButton; |
- panel->appendChild(fullscreenButton); |
- |
- MediaControlDownloadButtonElement* downloadButton = |
- MediaControlDownloadButtonElement::create(*this); |
- m_downloadButton = downloadButton; |
- panel->appendChild(downloadButton); |
- |
- MediaControlCastButtonElement* castButton = |
- MediaControlCastButtonElement::create(*this, false); |
- m_castButton = castButton; |
- panel->appendChild(castButton); |
- |
- MediaControlToggleClosedCaptionsButtonElement* toggleClosedCaptionsButton = |
- MediaControlToggleClosedCaptionsButtonElement::create(*this); |
- m_toggleClosedCaptionsButton = toggleClosedCaptionsButton; |
- panel->appendChild(toggleClosedCaptionsButton); |
- |
- m_panel = panel; |
- enclosure->appendChild(panel); |
- |
- m_enclosure = enclosure; |
- appendChild(enclosure); |
- |
- MediaControlTextTrackListElement* textTrackList = |
- MediaControlTextTrackListElement::create(*this); |
- m_textTrackList = textTrackList; |
- appendChild(textTrackList); |
- |
- MediaControlOverflowMenuButtonElement* overflowMenu = |
- MediaControlOverflowMenuButtonElement::create(*this); |
- m_overflowMenu = overflowMenu; |
- panel->appendChild(overflowMenu); |
- |
- MediaControlOverflowMenuListElement* overflowList = |
- MediaControlOverflowMenuListElement::create(*this); |
- m_overflowList = overflowList; |
- appendChild(overflowList); |
- |
- // The order in which we append elements to the overflow list is significant |
- // because it determines how the elements show up in the overflow menu |
- // relative to each other. The first item appended appears at the top of the |
- // overflow menu. |
- m_overflowList->appendChild(m_playButton->createOverflowElement( |
- *this, MediaControlPlayButtonElement::create(*this))); |
- m_overflowList->appendChild(m_fullscreenButton->createOverflowElement( |
- *this, MediaControlFullscreenButtonElement::create(*this))); |
- m_overflowList->appendChild(m_downloadButton->createOverflowElement( |
- *this, MediaControlDownloadButtonElement::create(*this))); |
- m_overflowList->appendChild(m_muteButton->createOverflowElement( |
- *this, MediaControlMuteButtonElement::create(*this))); |
- m_overflowList->appendChild(m_castButton->createOverflowElement( |
- *this, MediaControlCastButtonElement::create(*this, false))); |
- m_overflowList->appendChild( |
- m_toggleClosedCaptionsButton->createOverflowElement( |
- *this, MediaControlToggleClosedCaptionsButtonElement::create(*this))); |
-} |
- |
-Node::InsertionNotificationRequest MediaControls::insertedInto( |
- ContainerNode* root) { |
- if (!mediaElement().isConnected()) |
- return HTMLDivElement::insertedInto(root); |
- |
- // TODO(mlamouri): we should show the controls instead of having |
- // HTMLMediaElement do it. |
- |
- // m_windowEventListener doesn't need to be re-attached as it's only needed |
- // when a menu is visible. |
- m_mediaEventListener->attach(); |
- if (m_orientationLockDelegate) |
- m_orientationLockDelegate->attach(); |
- |
- if (!m_resizeObserver) { |
- m_resizeObserver = |
- ResizeObserver::create(m_mediaElement->document(), |
- new MediaControlsResizeObserverCallback(this)); |
- m_resizeObserver->observe(m_mediaElement); |
- } |
- |
- return HTMLDivElement::insertedInto(root); |
-} |
- |
-void MediaControls::removedFrom(ContainerNode*) { |
- DCHECK(!mediaElement().isConnected()); |
- |
- // TODO(mlamouri): we hide show the controls instead of having |
- // HTMLMediaElement do it. |
- |
- m_windowEventListener->stop(); |
- m_mediaEventListener->detach(); |
- if (m_orientationLockDelegate) |
- m_orientationLockDelegate->detach(); |
- |
- m_resizeObserver.clear(); |
-} |
- |
-void MediaControls::reset() { |
- EventDispatchForbiddenScope::AllowUserAgentEvents allowEventsInShadow; |
- BatchedControlUpdate batch(this); |
- |
- const double duration = mediaElement().duration(); |
- m_durationDisplay->setTextContent( |
- LayoutTheme::theme().formatMediaControlsTime(duration)); |
- m_durationDisplay->setCurrentValue(duration); |
- |
- // Show everything that we might hide. |
- // If we don't have a duration, then mark it to be hidden. For the |
- // old UI case, want / don't want is the same as show / hide since |
- // it is never marked as not fitting. |
- m_durationDisplay->setIsWanted(std::isfinite(duration)); |
- m_currentTimeDisplay->setIsWanted(true); |
- m_timeline->setIsWanted(true); |
- |
- // If the player has entered an error state, force it into the paused state. |
- if (mediaElement().error()) |
- mediaElement().pause(); |
- |
- updatePlayState(); |
- |
- updateCurrentTimeDisplay(); |
- |
- m_timeline->setDuration(duration); |
- m_timeline->setPosition(mediaElement().currentTime()); |
- |
- onVolumeChange(); |
- onTextTracksAddedOrRemoved(); |
- |
- onControlsListUpdated(); |
-} |
- |
-void MediaControls::onControlsListUpdated() { |
- BatchedControlUpdate batch(this); |
- |
- m_fullscreenButton->setIsWanted(shouldShowFullscreenButton(mediaElement())); |
- |
- refreshCastButtonVisibilityWithoutUpdate(); |
- |
- m_downloadButton->setIsWanted( |
- m_downloadButton->shouldDisplayDownloadButton()); |
-} |
- |
-LayoutObject* MediaControls::layoutObjectForTextTrackLayout() { |
- return m_panel->layoutObject(); |
-} |
- |
-void MediaControls::show() { |
- makeOpaque(); |
- m_panel->setIsWanted(true); |
- m_panel->setIsDisplayed(true); |
- if (m_overlayPlayButton) |
- m_overlayPlayButton->updateDisplayType(); |
-} |
- |
-void MediaControls::hide() { |
- m_panel->setIsWanted(false); |
- m_panel->setIsDisplayed(false); |
- if (m_overlayPlayButton) |
- m_overlayPlayButton->setIsWanted(false); |
-} |
- |
-bool MediaControls::isVisible() const { |
- return m_panel->isOpaque(); |
-} |
- |
-void MediaControls::makeOpaque() { |
- m_panel->makeOpaque(); |
-} |
- |
-void MediaControls::makeTransparent() { |
- m_panel->makeTransparent(); |
-} |
- |
-bool MediaControls::shouldHideMediaControls(unsigned behaviorFlags) const { |
- // Never hide for a media element without visual representation. |
- if (!mediaElement().isHTMLVideoElement() || !mediaElement().hasVideo() || |
- mediaElement().isPlayingRemotely()) { |
- return false; |
- } |
- |
- // Keep the controls visible as long as the timer is running. |
- const bool ignoreWaitForTimer = behaviorFlags & IgnoreWaitForTimer; |
- if (!ignoreWaitForTimer && m_keepShowingUntilTimerFires) |
- return false; |
- |
- // Don't hide if the mouse is over the controls. |
- const bool ignoreControlsHover = behaviorFlags & IgnoreControlsHover; |
- if (!ignoreControlsHover && m_panel->isHovered()) |
- return false; |
- |
- // Don't hide if the mouse is over the video area. |
- const bool ignoreVideoHover = behaviorFlags & IgnoreVideoHover; |
- if (!ignoreVideoHover && m_isMouseOverControls) |
- return false; |
- |
- // Don't hide if focus is on the HTMLMediaElement or within the |
- // controls/shadow tree. (Perform the checks separately to avoid going |
- // through all the potential ancestor hosts for the focused element.) |
- const bool ignoreFocus = behaviorFlags & IgnoreFocus; |
- if (!ignoreFocus && |
- (mediaElement().isFocused() || contains(document().focusedElement()))) { |
- return false; |
- } |
- |
- // Don't hide the media controls when a panel is showing. |
- if (m_textTrackList->isWanted() || m_overflowList->isWanted()) |
- return false; |
- |
- return true; |
-} |
- |
-void MediaControls::updatePlayState() { |
- if (m_isPausedForScrubbing) |
- return; |
- |
- if (m_overlayPlayButton) |
- m_overlayPlayButton->updateDisplayType(); |
- m_playButton->updateDisplayType(); |
-} |
- |
-void MediaControls::beginScrubbing() { |
- if (!mediaElement().paused()) { |
- m_isPausedForScrubbing = true; |
- mediaElement().pause(); |
- } |
-} |
- |
-void MediaControls::endScrubbing() { |
- if (m_isPausedForScrubbing) { |
- m_isPausedForScrubbing = false; |
- if (mediaElement().paused()) |
- mediaElement().play(); |
- } |
-} |
- |
-void MediaControls::updateCurrentTimeDisplay() { |
- double now = mediaElement().currentTime(); |
- double duration = mediaElement().duration(); |
- |
- // Allow the theme to format the time. |
- m_currentTimeDisplay->setInnerText( |
- LayoutTheme::theme().formatMediaControlsCurrentTime(now, duration), |
- IGNORE_EXCEPTION_FOR_TESTING); |
- m_currentTimeDisplay->setCurrentValue(now); |
-} |
- |
-void MediaControls::toggleTextTrackList() { |
- if (!mediaElement().hasClosedCaptions()) { |
- m_textTrackList->setVisible(false); |
- return; |
- } |
- |
- if (!m_textTrackList->isWanted()) |
- m_windowEventListener->start(); |
- |
- m_textTrackList->setVisible(!m_textTrackList->isWanted()); |
-} |
- |
-void MediaControls::showTextTrackAtIndex(unsigned indexToEnable) { |
- TextTrackList* trackList = mediaElement().textTracks(); |
- if (indexToEnable >= trackList->length()) |
- return; |
- TextTrack* track = trackList->anonymousIndexedGetter(indexToEnable); |
- if (track && track->canBeRendered()) |
- track->setMode(TextTrack::showingKeyword()); |
-} |
- |
-void MediaControls::disableShowingTextTracks() { |
- TextTrackList* trackList = mediaElement().textTracks(); |
- for (unsigned i = 0; i < trackList->length(); ++i) { |
- TextTrack* track = trackList->anonymousIndexedGetter(i); |
- if (track->mode() == TextTrack::showingKeyword()) |
- track->setMode(TextTrack::disabledKeyword()); |
- } |
-} |
- |
-void MediaControls::refreshCastButtonVisibility() { |
- refreshCastButtonVisibilityWithoutUpdate(); |
- BatchedControlUpdate batch(this); |
-} |
- |
-void MediaControls::refreshCastButtonVisibilityWithoutUpdate() { |
- if (!shouldShowCastButton(mediaElement())) { |
- m_castButton->setIsWanted(false); |
- m_overlayCastButton->setIsWanted(false); |
- return; |
- } |
- |
- // The reason for the autoplay test is that some pages (e.g. vimeo.com) have |
- // an autoplay background video, which doesn't autoplay on Chrome for Android |
- // (we prevent it) so starts paused. In such cases we don't want to |
- // automatically show the cast button, since it looks strange and is unlikely |
- // to correspond with anything the user wants to do. If a user does want to |
- // cast a paused autoplay video then they can still do so by touching or |
- // clicking on the video, which will cause the cast button to appear. |
- if (!mediaElement().shouldShowControls() && !mediaElement().autoplay() && |
- mediaElement().paused()) { |
- // Note that this is a case where we add the overlay cast button |
- // without wanting the panel cast button. We depend on the fact |
- // that computeWhichControlsFit() won't change overlay cast button |
- // visibility in the case where the cast button isn't wanted. |
- // We don't call compute...() here, but it will be called as |
- // non-cast changes (e.g., resize) occur. If the panel button |
- // is shown, however, compute...() will take control of the |
- // overlay cast button if it needs to hide it from the panel. |
- m_overlayCastButton->tryShowOverlay(); |
- m_castButton->setIsWanted(false); |
- } else if (mediaElement().shouldShowControls()) { |
- m_overlayCastButton->setIsWanted(false); |
- m_castButton->setIsWanted(true); |
- } |
-} |
- |
-void MediaControls::showOverlayCastButtonIfNeeded() { |
- if (mediaElement().shouldShowControls() || |
- !shouldShowCastButton(mediaElement())) |
- return; |
- |
- m_overlayCastButton->tryShowOverlay(); |
- resetHideMediaControlsTimer(); |
-} |
- |
-void MediaControls::enterFullscreen() { |
- Fullscreen::requestFullscreen(mediaElement()); |
-} |
- |
-void MediaControls::exitFullscreen() { |
- Fullscreen::exitFullscreen(document()); |
-} |
- |
-void MediaControls::startedCasting() { |
- m_castButton->setIsPlayingRemotely(true); |
- m_overlayCastButton->setIsPlayingRemotely(true); |
-} |
- |
-void MediaControls::stoppedCasting() { |
- m_castButton->setIsPlayingRemotely(false); |
- m_overlayCastButton->setIsPlayingRemotely(false); |
-} |
- |
-void MediaControls::defaultEventHandler(Event* event) { |
- HTMLDivElement::defaultEventHandler(event); |
- |
- // Do not handle events to not interfere with the rest of the page if no |
- // controls should be visible. |
- if (!mediaElement().shouldShowControls()) |
- return; |
- |
- // Add IgnoreControlsHover to m_hideTimerBehaviorFlags when we see a touch |
- // event, to allow the hide-timer to do the right thing when it fires. |
- // FIXME: Preferably we would only do this when we're actually handling the |
- // event here ourselves. |
- bool isTouchEvent = |
- event->isTouchEvent() || event->isGestureEvent() || |
- (event->isMouseEvent() && toMouseEvent(event)->fromTouch()); |
- m_hideTimerBehaviorFlags |= isTouchEvent ? IgnoreControlsHover : IgnoreNone; |
- |
- // Touch events are treated differently to avoid fake mouse events to trigger |
- // random behavior. The expect behaviour for touch is that a tap will show the |
- // controls and they will hide when the timer to hide fires. |
- if (isTouchEvent) { |
- if (event->type() != EventTypeNames::gesturetap) |
- return; |
- |
- if (!containsRelatedTarget(event)) { |
- if (!mediaElement().paused()) { |
- if (!isVisible()) { |
- makeOpaque(); |
- // When the panel switches from invisible to visible, we need to mark |
- // the event handled to avoid buttons below the tap to be activated. |
- event->setDefaultHandled(); |
- } |
- if (shouldHideMediaControls(IgnoreWaitForTimer)) { |
- m_keepShowingUntilTimerFires = true; |
- startHideMediaControlsTimer(); |
- } |
- } |
- } |
- |
- return; |
- } |
- |
- if (event->type() == EventTypeNames::mouseover) { |
- if (!containsRelatedTarget(event)) { |
- m_isMouseOverControls = true; |
- if (!mediaElement().paused()) { |
- makeOpaque(); |
- if (shouldHideMediaControls()) |
- startHideMediaControlsTimer(); |
- } |
- } |
- return; |
- } |
- |
- if (event->type() == EventTypeNames::mouseout) { |
- if (!containsRelatedTarget(event)) { |
- m_isMouseOverControls = false; |
- stopHideMediaControlsTimer(); |
- } |
- return; |
- } |
- |
- if (event->type() == EventTypeNames::mousemove) { |
- // When we get a mouse move, show the media controls, and start a timer |
- // that will hide the media controls after a 3 seconds without a mouse move. |
- makeOpaque(); |
- if (shouldHideMediaControls(IgnoreVideoHover)) |
- startHideMediaControlsTimer(); |
- return; |
- } |
-} |
- |
-void MediaControls::hideMediaControlsTimerFired(TimerBase*) { |
- unsigned behaviorFlags = |
- m_hideTimerBehaviorFlags | IgnoreFocus | IgnoreVideoHover; |
- m_hideTimerBehaviorFlags = IgnoreNone; |
- m_keepShowingUntilTimerFires = false; |
- |
- if (mediaElement().paused()) |
- return; |
- |
- if (!shouldHideMediaControls(behaviorFlags)) |
- return; |
- |
- makeTransparent(); |
- m_overlayCastButton->setIsWanted(false); |
-} |
- |
-void MediaControls::startHideMediaControlsTimer() { |
- m_hideMediaControlsTimer.startOneShot( |
- timeWithoutMouseMovementBeforeHidingMediaControls, BLINK_FROM_HERE); |
-} |
- |
-void MediaControls::stopHideMediaControlsTimer() { |
- m_keepShowingUntilTimerFires = false; |
- m_hideMediaControlsTimer.stop(); |
-} |
- |
-void MediaControls::resetHideMediaControlsTimer() { |
- stopHideMediaControlsTimer(); |
- if (!mediaElement().paused()) |
- startHideMediaControlsTimer(); |
-} |
- |
-bool MediaControls::containsRelatedTarget(Event* event) { |
- if (!event->isMouseEvent()) |
- return false; |
- EventTarget* relatedTarget = toMouseEvent(event)->relatedTarget(); |
- if (!relatedTarget) |
- return false; |
- return contains(relatedTarget->toNode()); |
-} |
- |
-void MediaControls::onVolumeChange() { |
- m_muteButton->updateDisplayType(); |
- m_volumeSlider->setVolume(mediaElement().muted() ? 0 |
- : mediaElement().volume()); |
- |
- // Update visibility of volume controls. |
- // TODO(mlamouri): it should not be part of the volumechange handling because |
- // it is using audio availability as input. |
- BatchedControlUpdate batch(this); |
- m_volumeSlider->setIsWanted(mediaElement().hasAudio() && |
- !preferHiddenVolumeControls(document())); |
- m_muteButton->setIsWanted(mediaElement().hasAudio()); |
-} |
- |
-void MediaControls::onFocusIn() { |
- if (!mediaElement().shouldShowControls()) |
- return; |
- |
- show(); |
- resetHideMediaControlsTimer(); |
-} |
- |
-void MediaControls::onTimeUpdate() { |
- m_timeline->setPosition(mediaElement().currentTime()); |
- updateCurrentTimeDisplay(); |
- |
- // 'timeupdate' might be called in a paused state. The controls should not |
- // become transparent in that case. |
- if (mediaElement().paused()) { |
- makeOpaque(); |
- return; |
- } |
- |
- if (isVisible() && shouldHideMediaControls()) |
- makeTransparent(); |
-} |
- |
-void MediaControls::onDurationChange() { |
- const double duration = mediaElement().duration(); |
- |
- // Update the displayed current time/duration. |
- m_durationDisplay->setTextContent( |
- LayoutTheme::theme().formatMediaControlsTime(duration)); |
- m_durationDisplay->setCurrentValue(duration); |
- updateCurrentTimeDisplay(); |
- |
- // Update the timeline (the UI with the seek marker). |
- m_timeline->setDuration(duration); |
-} |
- |
-void MediaControls::onPlay() { |
- updatePlayState(); |
- m_timeline->setPosition(mediaElement().currentTime()); |
- updateCurrentTimeDisplay(); |
- |
- startHideMediaControlsTimer(); |
-} |
- |
-void MediaControls::onPause() { |
- updatePlayState(); |
- m_timeline->setPosition(mediaElement().currentTime()); |
- updateCurrentTimeDisplay(); |
- makeOpaque(); |
- |
- stopHideMediaControlsTimer(); |
-} |
- |
-void MediaControls::onTextTracksAddedOrRemoved() { |
- m_toggleClosedCaptionsButton->setIsWanted(mediaElement().hasClosedCaptions()); |
- BatchedControlUpdate batch(this); |
-} |
- |
-void MediaControls::onTextTracksChanged() { |
- m_toggleClosedCaptionsButton->updateDisplayType(); |
-} |
- |
-void MediaControls::onError() { |
- // TODO(mlamouri): we should only change the aspects of the control that need |
- // to be changed. |
- reset(); |
-} |
- |
-void MediaControls::onLoadedMetadata() { |
- // TODO(mlamouri): we should only change the aspects of the control that need |
- // to be changed. |
- reset(); |
-} |
- |
-void MediaControls::onEnteredFullscreen() { |
- m_fullscreenButton->setIsFullscreen(true); |
- stopHideMediaControlsTimer(); |
- startHideMediaControlsTimer(); |
-} |
- |
-void MediaControls::onExitedFullscreen() { |
- m_fullscreenButton->setIsFullscreen(false); |
- stopHideMediaControlsTimer(); |
- startHideMediaControlsTimer(); |
-} |
- |
-void MediaControls::notifyElementSizeChanged(ClientRect* newSize) { |
- // Note that this code permits a bad frame on resize, since it is |
- // run after the relayout / paint happens. It would be great to improve |
- // this, but it would be even greater to move this code entirely to |
- // JS and fix it there. |
- |
- IntSize oldSize = m_size; |
- m_size.setWidth(newSize->width()); |
- m_size.setHeight(newSize->height()); |
- |
- // Adjust for effective zoom. |
- if (m_panel->layoutObject() && m_panel->layoutObject()->style()) { |
- m_size.setWidth(ceil(m_size.width() / |
- m_panel->layoutObject()->style()->effectiveZoom())); |
- m_size.setHeight(ceil(m_size.height() / |
- m_panel->layoutObject()->style()->effectiveZoom())); |
- } |
- |
- // Don't bother to do any work if this matches the most recent size. |
- if (oldSize != m_size) |
- m_elementSizeChangedTimer.startOneShot(0, BLINK_FROM_HERE); |
-} |
- |
-void MediaControls::elementSizeChangedTimerFired(TimerBase*) { |
- computeWhichControlsFit(); |
-} |
- |
-void MediaControls::computeWhichControlsFit() { |
- // Hide all controls that don't fit, and show the ones that do. |
- // This might be better suited for a layout, but since JS media controls |
- // won't benefit from that anwyay, we just do it here like JS will. |
- |
- // Controls that we'll hide / show, in order of decreasing priority. |
- MediaControlElement* elements[] = { |
- // Exclude m_overflowMenu; we handle it specially. |
- m_playButton.get(), |
- m_fullscreenButton.get(), |
- m_downloadButton.get(), |
- m_timeline.get(), |
- m_muteButton.get(), |
- m_volumeSlider.get(), |
- m_toggleClosedCaptionsButton.get(), |
- m_castButton.get(), |
- m_currentTimeDisplay.get(), |
- m_durationDisplay.get(), |
- }; |
- |
- // TODO(mlamouri): we need a more dynamic way to find out the width of an |
- // element. |
- const int sliderMargin = 36; // Sliders have 18px margin on each side. |
- |
- if (!m_size.width()) { |
- // No layout yet -- hide everything, then make them show up later. |
- // This prevents the wrong controls from being shown briefly |
- // immediately after the first layout and paint, but before we have |
- // a chance to revise them. |
- for (MediaControlElement* element : elements) { |
- if (element) |
- element->setDoesFit(false); |
- } |
- return; |
- } |
- |
- // Assume that all controls require 48px, unless we can get the computed |
- // style for a button. The minimumWidth is recorded and re-use for future |
- // MediaControls instances and future calls to this method given that at the |
- // moment the controls button width is per plataform. |
- // TODO(mlamouri): improve the mechanism without bandaid. |
- static int minimumWidth = 48; |
- if (m_playButton->layoutObject() && m_playButton->layoutObject()->style()) { |
- const ComputedStyle* style = m_playButton->layoutObject()->style(); |
- minimumWidth = ceil(style->width().pixels() / style->effectiveZoom()); |
- } else if (m_overflowMenu->layoutObject() && |
- m_overflowMenu->layoutObject()->style()) { |
- const ComputedStyle* style = m_overflowMenu->layoutObject()->style(); |
- minimumWidth = ceil(style->width().pixels() / style->effectiveZoom()); |
- } |
- |
- // Insert an overflow menu. However, if we see that the overflow menu |
- // doesn't end up containing at least two elements, we will not display it |
- // but instead make place for the first element that was dropped. |
- m_overflowMenu->setDoesFit(true); |
- m_overflowMenu->setIsWanted(true); |
- int usedWidth = minimumWidth; |
- |
- std::list<MediaControlElement*> overflowElements; |
- MediaControlElement* firstDisplacedElement = nullptr; |
- // For each control that fits, enable it in order of decreasing priority. |
- for (MediaControlElement* element : elements) { |
- if (!element) |
- continue; |
- int width = minimumWidth; |
- if ((element == m_timeline.get()) || (element == m_volumeSlider.get())) |
- width += sliderMargin; |
- element->shouldShowButtonInOverflowMenu(false); |
- if (element->isWanted()) { |
- if (usedWidth + width <= m_size.width()) { |
- element->setDoesFit(true); |
- usedWidth += width; |
- } else { |
- element->setDoesFit(false); |
- element->shouldShowButtonInOverflowMenu(true); |
- if (element->hasOverflowButton()) |
- overflowElements.push_front(element); |
- // We want a way to access the first media element that was |
- // removed. If we don't end up needing an overflow menu, we can |
- // use the space the overflow menu would have taken up to |
- // instead display that media element. |
- if (!element->hasOverflowButton() && !firstDisplacedElement) |
- firstDisplacedElement = element; |
- } |
- } |
- } |
- |
- // If we don't have at least two overflow elements, we will not show the |
- // overflow menu. |
- if (overflowElements.empty()) { |
- m_overflowMenu->setIsWanted(false); |
- usedWidth -= minimumWidth; |
- if (firstDisplacedElement) { |
- int width = minimumWidth; |
- if ((firstDisplacedElement == m_timeline.get()) || |
- (firstDisplacedElement == m_volumeSlider.get())) |
- width += sliderMargin; |
- if (usedWidth + width <= m_size.width()) |
- firstDisplacedElement->setDoesFit(true); |
- } |
- } else if (overflowElements.size() == 1) { |
- m_overflowMenu->setIsWanted(false); |
- overflowElements.front()->setDoesFit(true); |
- } |
- |
- // Decide if the overlay play button fits. |
- if (m_overlayPlayButton) { |
- bool doesFit = m_size.width() >= kMinWidthForOverlayPlayButton && |
- m_size.height() >= kMinHeightForOverlayPlayButton; |
- m_overlayPlayButton->setDoesFit(doesFit); |
- } |
-} |
- |
-void MediaControls::invalidate(Element* element) { |
- if (!element) |
- return; |
- |
- if (LayoutObject* layoutObject = element->layoutObject()) |
- layoutObject |
- ->setShouldDoFullPaintInvalidationIncludingNonCompositingDescendants(); |
-} |
- |
-void MediaControls::networkStateChanged() { |
- invalidate(m_playButton); |
- invalidate(m_overlayPlayButton); |
- invalidate(m_muteButton); |
- invalidate(m_fullscreenButton); |
- invalidate(m_downloadButton); |
- invalidate(m_timeline); |
- invalidate(m_volumeSlider); |
- |
- // Update the display state of the download button in case we now have a |
- // source or no longer have a source. |
- m_downloadButton->setIsWanted( |
- m_downloadButton->shouldDisplayDownloadButton()); |
-} |
- |
-bool MediaControls::overflowMenuVisible() { |
- return m_overflowList ? m_overflowList->isWanted() : false; |
-} |
- |
-void MediaControls::toggleOverflowMenu() { |
- DCHECK(m_overflowList); |
- |
- if (!m_overflowList->isWanted()) |
- m_windowEventListener->start(); |
- m_overflowList->setIsWanted(!m_overflowList->isWanted()); |
-} |
- |
-void MediaControls::hideAllMenus() { |
- m_windowEventListener->stop(); |
- |
- if (m_overflowList->isWanted()) |
- m_overflowList->setIsWanted(false); |
- if (m_textTrackList->isWanted()) |
- m_textTrackList->setVisible(false); |
-} |
- |
-DEFINE_TRACE(MediaControls) { |
- visitor->trace(m_resizeObserver); |
- visitor->trace(m_mediaElement); |
- visitor->trace(m_panel); |
- visitor->trace(m_overlayPlayButton); |
- visitor->trace(m_overlayEnclosure); |
- visitor->trace(m_playButton); |
- visitor->trace(m_currentTimeDisplay); |
- visitor->trace(m_timeline); |
- visitor->trace(m_muteButton); |
- visitor->trace(m_volumeSlider); |
- visitor->trace(m_toggleClosedCaptionsButton); |
- visitor->trace(m_fullscreenButton); |
- visitor->trace(m_downloadButton); |
- visitor->trace(m_durationDisplay); |
- visitor->trace(m_enclosure); |
- visitor->trace(m_textTrackList); |
- visitor->trace(m_overflowMenu); |
- visitor->trace(m_overflowList); |
- visitor->trace(m_castButton); |
- visitor->trace(m_overlayCastButton); |
- visitor->trace(m_mediaEventListener); |
- visitor->trace(m_windowEventListener); |
- visitor->trace(m_orientationLockDelegate); |
- HTMLDivElement::trace(visitor); |
-} |
- |
-} // namespace blink |