Chromium Code Reviews| 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..a3320eb2efe7396ec3bbd3f0b35bd3f8a87f4280 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); |
| + |
|
philipj_slow
2015/07/21 12:02:21
Revert extra blank line
liberato (no reviews please)
2015/07/27 20:26:08
Done.
|
| 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); |
|
philipj_slow
2015/07/21 12:02:21
Can fold this into a single setIsWanted call.
liberato (no reviews please)
2015/07/27 20:26:08
done. don't know why this wasn't one line to begi
|
| + 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()) { |
|
philipj_slow
2015/07/21 12:02:21
Drop the extra {}, presumably left as part of a no
liberato (no reviews please)
2015/07/27 20:26:08
Done.
|
| 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. |
|
philipj_slow
2015/07/21 12:02:21
s/let/leave/
liberato (no reviews please)
2015/07/27 20:26:08
Done.
|
| + // 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,108 @@ 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. |
| + // 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. |
| + if (panelWidth == 0) |
|
philipj_slow
2015/07/21 12:02:21
Repeat of earlier issue, why not update m_panelWid
liberato (no reviews please)
2015/07/27 20:26:08
this isn't the same. this is the callback that no
philipj_slow
2015/07/28 15:23:49
Oh, OK. In this case, would it break anything if m
liberato (no reviews please)
2015/07/30 05:54:23
sure, i'll set it here unconditionally and let cha
philipj_slow
2015/08/03 13:10:45
Acknowledged.
|
| + return; |
| + |
| + if (!RuntimeEnabledFeatures::newMediaPlaybackUiEnabled()) |
| + return; |
| + |
| + m_panelWidth = panelWidth; |
| + |
| + // Adjust for effective zoom. |
| + if (!m_panel->layoutObject() || !m_panel->layoutObject()->style()) |
| + return; |
| + m_panelWidth = ceil(m_panelWidth / m_panel->layoutObject()->style()->effectiveZoom()); |
| + |
| + m_panelWidthChangedTimer.startOneShot(0, FROM_HERE); |
| +} |
| + |
| +void MediaControls::changedControlSelections() |
| +{ |
| + int panelWidth = m_panel->clientWidth(); |
|
philipj_slow
2015/07/21 12:02:21
When is this needed, is it not redundant with noti
liberato (no reviews please)
2015/07/27 20:26:08
when visibility changes, one should call changedCo
philipj_slow
2015/07/28 15:23:49
Hmm, so it seems that if this helps avoid a bad fr
liberato (no reviews please)
2015/07/30 05:54:23
i removed this function. the only case where the
philipj_slow
2015/08/03 13:10:45
What was the source of the appendChild() call here
|
| + // 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; |
| + |
| + const int minimumWidth = element->minimumWidth(); |
|
philipj_slow
2015/07/21 12:02:21
Move this inside the branch where it is used.
liberato (no reviews please)
2015/07/27 20:26:08
Done.
|
| + if (element->isWanted()) { |
| + if (usedWidth + minimumWidth <= m_panelWidth) { |
| + element->setDoesFit(true); |
| + usedWidth += 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; |
|
philipj_slow
2015/07/21 12:02:21
Do you also want it to be reset when loading a new
liberato (no reviews please)
2015/07/27 20:26:08
good point, will reset in reset().
philipj_slow
2015/08/03 13:10:45
Acknowledged.
|
| + // Update the controls visibility. |
| + updateVolume(); |
| +} |
| + |
| DEFINE_TRACE(MediaControls) |
| { |
| visitor->trace(m_mediaElement); |