| Index: Source/core/html/shadow/MediaControls.cpp
|
| diff --git a/Source/core/html/shadow/MediaControls.cpp b/Source/core/html/shadow/MediaControls.cpp
|
| index 17d1096113fffeb9acbde1c6aaa10e83a405b48e..0e262e60995af9f2f54258e11e48abf70f513cfd 100644
|
| --- a/Source/core/html/shadow/MediaControls.cpp
|
| +++ b/Source/core/html/shadow/MediaControls.cpp
|
| @@ -47,6 +47,11 @@ static bool fullscreenIsSupported(const Document& document)
|
| return !document.settings() || document.settings()->fullscreenSupported();
|
| }
|
|
|
| +static bool preferHiddenAudioElements(const Document& document)
|
| +{
|
| + return !document.settings() || document.settings()->preferHiddenAudioElements();
|
| +}
|
| +
|
| MediaControls::MediaControls(HTMLMediaElement& mediaElement)
|
| : HTMLDivElement(mediaElement.document())
|
| , m_mediaElement(&mediaElement)
|
| @@ -68,6 +73,10 @@ MediaControls::MediaControls(HTMLMediaElement& mediaElement)
|
| , m_hideTimerBehaviorFlags(IgnoreNone)
|
| , m_isMouseOverControls(false)
|
| , m_isPausedForScrubbing(false)
|
| + , m_panelWidthChangedTimer(this, &MediaControls::panelWidthChangedTimerFired)
|
| + , m_panelWidth(0)
|
| + , m_allowHiddenAudioElements(RuntimeEnabledFeatures::newMediaPlaybackUiEnabled())
|
| + , m_keepMuteButton(false)
|
| {
|
| }
|
|
|
| @@ -89,9 +98,12 @@ PassRefPtrWillBeRawPtr<MediaControls> MediaControls::create(HTMLMediaElement& me
|
| // \-MediaControlPanelEnclosureElement (-webkit-media-controls-enclosure)
|
| // \-MediaControlPanelElement (-webkit-media-controls-panel)
|
| // +-MediaControlPlayButtonElement (-webkit-media-controls-play-button)
|
| +// | {if !RTE::newMediaPlaybackUi()}
|
| // +-MediaControlTimelineElement (-webkit-media-controls-timeline)
|
| // +-MediaControlCurrentTimeDisplayElement (-webkit-media-controls-current-time-display)
|
| // +-MediaControlTimeRemainingDisplayElement (-webkit-media-controls-time-remaining-display)
|
| +// | {if RTE::newMediaPlaybackUi()}
|
| +// +-MediaControlTimelineElement (-webkit-media-controls-timeline)
|
| // +-MediaControlMuteButtonElement (-webkit-media-controls-mute-button)
|
| // +-MediaControlVolumeSliderElement (-webkit-media-controls-volume-slider)
|
| // +-MediaControlToggleClosedCaptionsButtonElement (-webkit-media-controls-toggle-closed-captions-button)
|
| @@ -99,6 +111,7 @@ PassRefPtrWillBeRawPtr<MediaControls> MediaControls::create(HTMLMediaElement& me
|
| // \-MediaControlFullscreenButtonElement (-webkit-media-controls-fullscreen-button)
|
| void MediaControls::initializeControls()
|
| {
|
| + const bool useNewUi = RuntimeEnabledFeatures::newMediaPlaybackUiEnabled();
|
| RefPtrWillBeRawPtr<MediaControlOverlayEnclosureElement> overlayEnclosure = MediaControlOverlayEnclosureElement::create(*this);
|
|
|
| if (document().settings() && document().settings()->mediaControlsOverlayPlayButtonEnabled()) {
|
| @@ -125,24 +138,36 @@ void MediaControls::initializeControls()
|
|
|
| RefPtrWillBeRawPtr<MediaControlTimelineElement> timeline = MediaControlTimelineElement::create(*this);
|
| m_timeline = timeline.get();
|
| - panel->appendChild(timeline.release());
|
| + // In old UX, timeline is before the time / duration text.
|
| + if (!useNewUi)
|
| + panel->appendChild(timeline.release());
|
| + // else we will attach it later.
|
|
|
| RefPtrWillBeRawPtr<MediaControlCurrentTimeDisplayElement> currentTimeDisplay = MediaControlCurrentTimeDisplayElement::create(*this);
|
| m_currentTimeDisplay = currentTimeDisplay.get();
|
| - m_currentTimeDisplay->hide();
|
| + m_currentTimeDisplay->setIsWanted(useNewUi);
|
| +
|
| panel->appendChild(currentTimeDisplay.release());
|
|
|
| RefPtrWillBeRawPtr<MediaControlTimeRemainingDisplayElement> durationDisplay = MediaControlTimeRemainingDisplayElement::create(*this);
|
| m_durationDisplay = durationDisplay.get();
|
| panel->appendChild(durationDisplay.release());
|
|
|
| + // Timeline is after the time / duration text if newMediaPlaybackUiEnabled.
|
| + if (useNewUi)
|
| + panel->appendChild(timeline.release());
|
| +
|
| RefPtrWillBeRawPtr<MediaControlMuteButtonElement> muteButton = MediaControlMuteButtonElement::create(*this);
|
| m_muteButton = muteButton.get();
|
| panel->appendChild(muteButton.release());
|
| + if (m_allowHiddenAudioElements && preferHiddenAudioElements(document()))
|
| + m_muteButton->setIsWanted(false);
|
|
|
| RefPtrWillBeRawPtr<MediaControlVolumeSliderElement> slider = MediaControlVolumeSliderElement::create(*this);
|
| m_volumeSlider = slider.get();
|
| panel->appendChild(slider.release());
|
| + if (m_allowHiddenAudioElements && preferHiddenAudioElements(document()))
|
| + m_volumeSlider->setIsWanted(false);
|
|
|
| RefPtrWillBeRawPtr<MediaControlToggleClosedCaptionsButtonElement> toggleClosedCaptionsButton = MediaControlToggleClosedCaptionsButtonElement::create(*this);
|
| m_toggleClosedCaptionsButton = toggleClosedCaptionsButton.get();
|
| @@ -165,10 +190,24 @@ void MediaControls::initializeControls()
|
|
|
| void MediaControls::reset()
|
| {
|
| - double duration = mediaElement().duration();
|
| + const bool useNewUi = RuntimeEnabledFeatures::newMediaPlaybackUiEnabled();
|
| + const double duration = mediaElement().duration();
|
| m_durationDisplay->setInnerText(LayoutTheme::theme().formatMediaControlsTime(duration), ASSERT_NO_EXCEPTION);
|
| m_durationDisplay->setCurrentValue(duration);
|
|
|
| + if (useNewUi) {
|
| + // 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.
|
| + if (std::isfinite(duration))
|
| + m_durationDisplay->setIsWanted(true);
|
| + else
|
| + m_durationDisplay->setIsWanted(false);
|
| + m_currentTimeDisplay->setIsWanted(true);
|
| + m_timeline->setIsWanted(true);
|
| + }
|
| +
|
| updatePlayState();
|
|
|
| updateCurrentTimeDisplay();
|
| @@ -176,10 +215,6 @@ void MediaControls::reset()
|
| m_timeline->setDuration(duration);
|
| m_timeline->setPosition(mediaElement().currentTime());
|
|
|
| - if (!mediaElement().hasAudio())
|
| - m_volumeSlider->hide();
|
| - else
|
| - m_volumeSlider->show();
|
| updateVolume();
|
|
|
| refreshClosedCaptionsButtonVisibility();
|
| @@ -189,14 +224,13 @@ void MediaControls::reset()
|
| // 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().hasVideo() && fullscreenIsSupported(document()))
|
| - || mediaElement().isFullscreen())
|
| - m_fullScreenButton->show();
|
| - else
|
| - m_fullScreenButton->hide();
|
| + m_fullScreenButton->setIsWanted((mediaElement().hasVideo()
|
| + && fullscreenIsSupported(document()))
|
| + || mediaElement().isFullscreen());
|
|
|
| - refreshCastButtonVisibility();
|
| + refreshCastButtonVisibilityWithoutUpdate();
|
| makeOpaque();
|
| + changedControlSelections();
|
| }
|
|
|
| LayoutObject* MediaControls::layoutObjectForTextTrackLayout()
|
| @@ -207,7 +241,7 @@ LayoutObject* MediaControls::layoutObjectForTextTrackLayout()
|
| void MediaControls::show()
|
| {
|
| makeOpaque();
|
| - m_panel->show();
|
| + m_panel->setIsWanted(true);
|
| m_panel->setIsDisplayed(true);
|
| if (m_overlayPlayButton)
|
| m_overlayPlayButton->updateDisplayType();
|
| @@ -223,10 +257,10 @@ void MediaControls::mediaElementFocused()
|
|
|
| void MediaControls::hide()
|
| {
|
| - m_panel->hide();
|
| + m_panel->setIsWanted(false);
|
| m_panel->setIsDisplayed(false);
|
| if (m_overlayPlayButton)
|
| - m_overlayPlayButton->hide();
|
| + m_overlayPlayButton->setIsWanted(false);
|
| }
|
|
|
| void MediaControls::makeOpaque()
|
| @@ -237,6 +271,7 @@ void MediaControls::makeOpaque()
|
| void MediaControls::makeTransparent()
|
| {
|
| m_panel->makeTransparent();
|
| + m_overlayCastButton->setIsWanted(false);
|
| }
|
|
|
| bool MediaControls::shouldHideMediaControls(unsigned behaviorFlags) const
|
| @@ -263,8 +298,10 @@ bool MediaControls::shouldHideMediaControls(unsigned behaviorFlags) const
|
|
|
| void MediaControls::playbackStarted()
|
| {
|
| - m_currentTimeDisplay->show();
|
| - m_durationDisplay->hide();
|
| + if (!RuntimeEnabledFeatures::newMediaPlaybackUiEnabled()) {
|
| + m_currentTimeDisplay->setIsWanted(true);
|
| + m_durationDisplay->setIsWanted(false);
|
| + }
|
|
|
| updatePlayState();
|
| m_timeline->setPosition(mediaElement().currentTime());
|
| @@ -325,9 +362,9 @@ void MediaControls::updateCurrentTimeDisplay()
|
| double duration = mediaElement().duration();
|
|
|
| // After seek, hide duration display and show current time.
|
| - if (now > 0) {
|
| - m_currentTimeDisplay->show();
|
| - m_durationDisplay->hide();
|
| + if (!RuntimeEnabledFeatures::newMediaPlaybackUiEnabled() && now > 0) {
|
| + m_currentTimeDisplay->setIsWanted(true);
|
| + m_durationDisplay->setIsWanted(false);
|
| }
|
|
|
| // Allow the theme to format the time.
|
| @@ -342,10 +379,36 @@ void MediaControls::updateVolume()
|
| if (LayoutObject* layoutObject = m_muteButton->layoutObject())
|
| layoutObject->setShouldDoFullPaintInvalidation();
|
|
|
| - if (mediaElement().muted())
|
| + if (mediaElement().muted()) {
|
| m_volumeSlider->setVolume(0);
|
| - else
|
| + } else {
|
| m_volumeSlider->setVolume(mediaElement().volume());
|
| + }
|
| +
|
| + // Update the visibility of our audio elements.
|
| + // We never want the volume slider if there's no audio.
|
| + // If there is audio, then we want it unless hiding audio is enabled and
|
| + // we prefer to hide it.
|
| + m_volumeSlider->setIsWanted(mediaElement().hasAudio()
|
| + && !(m_allowHiddenAudioElements && preferHiddenAudioElements(document())));
|
| +
|
| + // The mute button is a little more complicated. If enableNewMediaPlaybackUi
|
| + // is true, then we choose to hide or show the mute button to save space.
|
| + // If enableNew* is not set, then we never touch the mute button, and
|
| + // instead let it up to the CSS.
|
| + // Note that this is why m_allowHiddenAudioElements isn't rolled into prefer...().
|
| + if (m_allowHiddenAudioElements) {
|
| + // If there is no audio track, then hide the mute button. If there
|
| + // is an audio track, then we always show the mute button unless
|
| + // we prefer to hide it and the media isn't muted. If it's muted,
|
| + // then we show it to let the user unmute it. In this case, we don't
|
| + // want to re-hide the mute button later.
|
| + m_keepMuteButton |= mediaElement().muted();
|
| + m_muteButton->setIsWanted(mediaElement().hasAudio()
|
| + && (!preferHiddenAudioElements(document()) || m_keepMuteButton));
|
| + }
|
| +
|
| +
|
| // Invalidate the volume slider because it paints differently according to volume.
|
| if (LayoutObject* layoutObject = m_volumeSlider->layoutObject())
|
| layoutObject->setShouldDoFullPaintInvalidation();
|
| @@ -358,10 +421,7 @@ void MediaControls::changedClosedCaptionsVisibility()
|
|
|
| void MediaControls::refreshClosedCaptionsButtonVisibility()
|
| {
|
| - if (mediaElement().hasClosedCaptions())
|
| - m_toggleClosedCaptionsButton->show();
|
| - else
|
| - m_toggleClosedCaptionsButton->hide();
|
| + m_toggleClosedCaptionsButton->setIsWanted(mediaElement().hasClosedCaptions());
|
| }
|
|
|
| static Element* elementFromCenter(Element& element)
|
| @@ -376,14 +436,19 @@ static Element* elementFromCenter(Element& element)
|
| void MediaControls::tryShowOverlayCastButton()
|
| {
|
| // The element needs to be shown to have its dimensions and position.
|
| - m_overlayCastButton->show();
|
| -
|
| + m_overlayCastButton->setIsWanted(true);
|
| if (elementFromCenter(*m_overlayCastButton) != &mediaElement())
|
| - m_overlayCastButton->hide();
|
| + m_overlayCastButton->setIsWanted(false);
|
| }
|
|
|
| void MediaControls::refreshCastButtonVisibility()
|
| {
|
| + refreshCastButtonVisibilityWithoutUpdate();
|
| + changedControlSelections();
|
| +}
|
| +
|
| +void MediaControls::refreshCastButtonVisibilityWithoutUpdate()
|
| +{
|
| if (mediaElement().hasRemoteRoutes()) {
|
| // 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
|
| @@ -391,19 +456,31 @@ void MediaControls::refreshCastButtonVisibility()
|
| // 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()) {
|
| - showOverlayCastButton();
|
| + // 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.
|
| + tryShowOverlayCastButton();
|
| + m_castButton->setIsWanted(false);
|
| } else if (mediaElement().shouldShowControls()) {
|
| - m_overlayCastButton->hide();
|
| - m_castButton->show();
|
| - // Check that the cast button actually fits on the bar.
|
| - if (m_fullScreenButton->getBoundingClientRect()->right() > m_panel->getBoundingClientRect()->right()) {
|
| - m_castButton->hide();
|
| + m_overlayCastButton->setIsWanted(false);
|
| + m_castButton->setIsWanted(true);
|
| + // Check that the cast button actually fits on the bar. For the
|
| + // newMediaPlaybackUiEnabled case, we let changedControlSelections()
|
| + // handle this.
|
| + if ( !RuntimeEnabledFeatures::newMediaPlaybackUiEnabled()
|
| + && m_fullScreenButton->getBoundingClientRect()->right() > m_panel->getBoundingClientRect()->right()) {
|
| + m_castButton->setIsWanted(false);
|
| tryShowOverlayCastButton();
|
| }
|
| }
|
| } else {
|
| - m_castButton->hide();
|
| - m_overlayCastButton->hide();
|
| + m_castButton->setIsWanted(false);
|
| + m_overlayCastButton->setIsWanted(false);
|
| }
|
| }
|
|
|
| @@ -494,7 +571,6 @@ void MediaControls::hideMediaControlsTimerFired(Timer<MediaControls>*)
|
| return;
|
|
|
| makeTransparent();
|
| - m_overlayCastButton->hide();
|
| }
|
|
|
| void MediaControls::startHideMediaControlsTimer()
|
| @@ -525,6 +601,97 @@ bool MediaControls::containsRelatedTarget(Event* event)
|
| return contains(relatedTarget->toNode());
|
| }
|
|
|
| +void MediaControls::notifyPanelWidthChanged(int panelWidth)
|
| +{
|
| + // Don't bother to do any work if this matches the most recent panel
|
| + // width, since we're called after layout.
|
| + if (panelWidth == 0)
|
| + return;
|
| +
|
| + if (!RuntimeEnabledFeatures::newMediaPlaybackUiEnabled())
|
| + return;
|
| +
|
| + m_panelWidth = panelWidth;
|
| + m_panelWidthChangedTimer.startOneShot(0, FROM_HERE);
|
| +}
|
| +
|
| +void MediaControls::changedControlSelections()
|
| +{
|
| + int panelWidth = m_panel->clientWidth();
|
| + // Don't short-circuit update if the panel hasn't changed. The fit state
|
| + // is a function of the controls we want, so recompute if we can.
|
| + if (panelWidth == 0)
|
| + return;
|
| +
|
| + m_panelWidth = panelWidth;
|
| + computeWhichControlsFit();
|
| +}
|
| +
|
| +void MediaControls::panelWidthChangedTimerFired(Timer<MediaControls>*)
|
| +{
|
| + 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.
|
| + // The order, in order of decreasing droppiness:
|
| + // Volume, time, seek bar, cast.
|
| +
|
| + if (!RuntimeEnabledFeatures::newMediaPlaybackUiEnabled())
|
| + return;
|
| +
|
| + if (!m_panelWidth)
|
| + return;
|
| +
|
| + // Controls that we'll hide / show, in order of decreasing priority.
|
| + MediaControlElement* elements[] = {
|
| + m_playButton.get(),
|
| + m_toggleClosedCaptionsButton.get(),
|
| + m_fullScreenButton.get(),
|
| + m_timeline.get(),
|
| + m_currentTimeDisplay.get(),
|
| + m_volumeSlider.get(),
|
| + m_castButton.get(),
|
| + m_muteButton.get(),
|
| + m_durationDisplay.get(),
|
| + };
|
| +
|
| + int usedWidth = 0;
|
| + bool droppedCastButton = false;
|
| + for (MediaControlElement* element : elements) {
|
| + if (!element)
|
| + continue;
|
| +
|
| + if (element->isWanted()) {
|
| + if (usedWidth + element->minimumWidth() <= m_panelWidth) {
|
| + element->setDoesFit(true);
|
| + usedWidth += element->minimumWidth();
|
| + } else {
|
| + element->setDoesFit(false);
|
| + if (element == m_castButton.get())
|
| + droppedCastButton = true;
|
| + }
|
| + }
|
| + }
|
| +
|
| + // Special case for cast: if we want a cast button but dropped it, then
|
| + // show the overlay cast button instead.
|
| + if (m_castButton->isWanted())
|
| + m_overlayCastButton->setIsWanted(droppedCastButton);
|
| +}
|
| +
|
| +void MediaControls::setAllowHiddenAudioElements(bool allow)
|
| +{
|
| + m_allowHiddenAudioElements = allow;
|
| + // Clear the 'keep muted flag', for tests.
|
| + m_keepMuteButton = false;
|
| + // Update the controls visibility.
|
| + updateVolume();
|
| +}
|
| +
|
| DEFINE_TRACE(MediaControls)
|
| {
|
| visitor->trace(m_mediaElement);
|
|
|