Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(60)

Unified Diff: third_party/WebKit/Source/core/html/shadow/MediaControls.cpp

Issue 2795783004: Move core MediaControls implementation to modules/media_controls/. (Closed)
Patch Set: rebase Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698