blob: 7b013d6909dfb6c3b1884ef14dc5e493789cf083 [file] [log] [blame]
/*
* 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.
*/
#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIA_CONTROLS_MEDIA_CONTROLS_IMPL_H_
#define THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIA_CONTROLS_MEDIA_CONTROLS_IMPL_H_
#include "third_party/blink/renderer/core/geometry/dom_rect_read_only.h"
#include "third_party/blink/renderer/core/html/html_div_element.h"
#include "third_party/blink/renderer/core/html/media/media_controls.h"
#include "third_party/blink/renderer/modules/modules_export.h"
#include "third_party/blink/renderer/platform/timer.h"
namespace blink {
class Event;
class HTMLVideoElement;
class MediaControlsMediaEventListener;
class MediaControlsOrientationLockDelegate;
class MediaControlsRotateToFullscreenDelegate;
class MediaControlsWindowEventListener;
class MediaControlButtonPanelElement;
class MediaControlCastButtonElement;
class MediaControlCurrentTimeDisplayElement;
class MediaControlDownloadButtonElement;
class MediaControlFullscreenButtonElement;
class MediaControlLoadingPanelElement;
class MediaControlMuteButtonElement;
class MediaControlOverflowMenuButtonElement;
class MediaControlOverflowMenuListElement;
class MediaControlOverlayEnclosureElement;
class MediaControlOverlayPlayButtonElement;
class MediaControlPanelElement;
class MediaControlPanelEnclosureElement;
class MediaControlPictureInPictureButtonElement;
class MediaControlPlayButtonElement;
class MediaControlRemainingTimeDisplayElement;
class MediaControlScrubbingMessageElement;
class MediaControlTextTrackListElement;
class MediaControlTimelineElement;
class MediaControlToggleClosedCaptionsButtonElement;
class MediaControlVolumeSliderElement;
class MediaDownloadInProductHelpManager;
class ShadowRoot;
class TextTrack;
// Default implementation of the core/ MediaControls interface used by
// HTMLMediaElement.
class MODULES_EXPORT MediaControlsImpl final : public HTMLDivElement,
public MediaControls {
USING_GARBAGE_COLLECTED_MIXIN(MediaControlsImpl);
WTF_MAKE_NONCOPYABLE(MediaControlsImpl);
public:
static MediaControlsImpl* Create(HTMLMediaElement&, ShadowRoot&);
~MediaControlsImpl() override = default;
// Returns whether the ModernMediaControlsEnabled runtime flag is on.
static bool IsModern();
// Returns whether the event is considered a touch event.
static bool IsTouchEvent(Event*);
// Node override.
Node::InsertionNotificationRequest InsertedInto(ContainerNode*) override;
void RemovedFrom(ContainerNode*) override;
// MediaControls implementation.
void MaybeShow() override;
void Hide() override;
void Reset() override;
void OnControlsListUpdated() override;
// TODO(mlamouri): this is temporary to notify the controls that an
// HTMLTrackElement failed to load because there is no web exposed way to
// be notified on the TextTrack object. See https://crbug.com/669977
void OnTrackElementFailedToLoad() override { OnTextTracksAddedOrRemoved(); }
// Notify us that the media element's network state has changed.
void NetworkStateChanged() override;
LayoutObject* PanelLayoutObject() override;
LayoutObject* TimelineLayoutObject() override;
LayoutObject* ButtonPanelLayoutObject() override;
LayoutObject* ContainerLayoutObject() override;
void SetTestMode(bool) override;
// Return the internal elements, which is used by registering clicking
// EventHandlers from MediaControlsWindowEventListener.
HTMLDivElement* PanelElement() override;
// TODO(mlamouri): this method is needed in order to notify the controls that
// the `MediaControlsEnabled` setting has changed.
void OnMediaControlsEnabledChange() override {
// There is no update because only the overlay is expected to change.
RefreshCastButtonVisibilityWithoutUpdate();
}
// Called by the fullscreen buttons to toggle fulllscreen on/off.
void EnterFullscreen();
void ExitFullscreen();
// Called by the MediaControlOverlayPlayButtonElement to check if toggling
// fullscreen is allowed.
bool IsFullscreenEnabled() const;
// Text track related methods exposed to components handling closed captions.
void ToggleTextTrackList();
void ShowTextTrackAtIndex(unsigned);
void DisableShowingTextTracks();
// Returns the label for the track when a valid track is passed in and "Off"
// when the parameter is null.
String GetTextTrackLabel(TextTrack*) const;
// Methods related to the overflow menu.
void ToggleOverflowMenu();
bool OverflowMenuVisible();
void ShowOverlayCastButtonIfNeeded();
// Methods call by the scrubber.
void BeginScrubbing(bool);
void EndScrubbing();
void UpdateCurrentTimeDisplay();
// Methods used for Download In-product help.
const MediaControlDownloadButtonElement& DownloadButton() const;
const MediaControlOverflowMenuButtonElement& OverflowButton() const;
MediaControlOverflowMenuButtonElement& OverflowButton();
void DidDismissDownloadInProductHelp();
MediaDownloadInProductHelpManager* DownloadInProductHelp();
// Accessors for UI elements.
const MediaControlCurrentTimeDisplayElement& CurrentTimeDisplay() const;
MediaControlToggleClosedCaptionsButtonElement& ToggleClosedCaptions();
void Trace(blink::Visitor*) override;
// Track the state of the controls.
enum ControlsState {
// There is no video source.
kNoSource,
// Metadata has not been loaded.
kNotLoaded,
// Metadata is being loaded.
kLoadingMetadata,
// Metadata is loaded and the media is ready to play. This can be when the
// media is paused, when it has ended or before the media has started
// playing.
kStopped,
// The media is playing.
kPlaying,
// Playback has stopped to buffer.
kBuffering,
// The media is being scrubbed.
kScrubbing,
};
ControlsState State() const;
void MaybeToggleControlsFromTap();
// Called by accessibility code to notify that the controls was focused by an
// accessibility tool. This is meant to be replaced by AOM when the event will
// be exposed to the platform.
void OnAccessibleFocus();
private:
// MediaControlsMediaEventListener is a component that is listening to events
// and calling the appropriate callback on MediaControlsImpl. The object is
// split from MedaiControlsImpl to reduce boilerplate and ease reading. In
// order to not expose accessors only for this component, a friendship is
// declared.
friend class MediaControlsMediaEventListener;
// Same as above but handles the menus hiding when the window is interacted
// with, allowing MediaControlsImpl to not have the boilerplate.
friend class MediaControlsWindowEventListener;
// For tests.
friend class MediaControlsOrientationLockDelegateTest;
friend class MediaControlsOrientationLockAndRotateToFullscreenDelegateTest;
friend class MediaControlsRotateToFullscreenDelegateTest;
friend class MediaControlsImplTest;
friend class MediaControlsImplInProductHelpTest;
friend class MediaControlTimelineElementTest;
// Need to be members of MediaControls for private member access.
class BatchedControlUpdate;
class MediaControlsResizeObserverDelegate;
class MediaElementMutationCallback;
// Notify us that our controls enclosure has changed size.
void NotifyElementSizeChanged(DOMRectReadOnly* new_size);
// Update the CSS class when we think the state has updated.
void UpdateCSSClassFromState();
// Sets/removes a CSS class from this element based on |should_have_class|.
void SetClass(const AtomicString& class_name, bool should_have_class);
// Get the HTMLVideoElement that the controls are attached to. The caller must
// check that the element is a video element first.
HTMLVideoElement& VideoElement();
explicit MediaControlsImpl(HTMLMediaElement&);
void InitializeControls();
void PopulatePanel();
void MakeOpaque();
void MakeOpaqueFromPointerEvent();
void MakeTransparent();
bool IsVisible() const;
// If the overlay play button is present then make sure it is displayed.
void MaybeShowOverlayPlayButton();
void UpdatePlayState();
enum HideBehaviorFlags {
kIgnoreNone = 0,
kIgnoreVideoHover = 1 << 0,
kIgnoreFocus = 1 << 1,
kIgnoreControlsHover = 1 << 2,
kIgnoreWaitForTimer = 1 << 3,
};
bool ShouldHideMediaControls(unsigned behavior_flags = 0) const;
bool AreVideoControlsHovered() const;
void HideMediaControlsTimerFired(TimerBase*);
void StartHideMediaControlsIfNecessary();
void StartHideMediaControlsTimer();
void StopHideMediaControlsTimer();
void ResetHideMediaControlsTimer();
void HideCursor();
void ShowCursor();
void ElementSizeChangedTimerFired(TimerBase*);
// Hide elements that don't fit, and show those things that we want which
// do fit. This requires that m_effectiveWidth and m_effectiveHeight are
// current.
void ComputeWhichControlsFit();
void UpdateOverflowMenuWanted() const;
void UpdateScrubbingMessageFits() const;
void UpdateSizingCSSClass();
void UpdateOverlayPlayButtonWidthCSSVar();
void MaybeRecordElementsDisplayed() const;
// Takes a popup menu (caption, overflow) and position on the screen. This is
// used because these menus use a fixed position in order to appear over all
// content.
void PositionPopupMenu(Element*);
// When a video element has an audio track but no video track, we modify the
// controls to display like audio controls.
bool ShouldActAsAudioControls() const;
void StartActingAsAudioControls();
void StopActingAsAudioControls();
void UpdateActingAsAudioControls();
// Returns true/false based on which set of controls to display.
bool ShouldShowAudioControls() const;
bool ShouldShowVideoControls() const;
// Node
bool IsMediaControls() const override { return true; }
bool WillRespondToMouseMoveEvents() override { return true; }
void DefaultEventHandler(Event*) override;
bool ContainsRelatedTarget(Event*);
void HandlePointerEvent(Event*);
void HandleTouchEvent(Event*);
// Internal cast related methods.
void RemotePlaybackStateChanged();
void RefreshCastButtonVisibility();
void RefreshCastButtonVisibilityWithoutUpdate();
// Methods called by MediaControlsMediaEventListener.
void OnVolumeChange();
void OnFocusIn();
void OnTimeUpdate();
void OnDurationChange();
void OnPlay();
void OnPlaying();
void OnPause();
void OnTextTracksAddedOrRemoved();
void OnTextTracksChanged();
void OnError();
void OnLoadedMetadata();
void OnEnteredFullscreen();
void OnExitedFullscreen();
void OnPanelKeypress();
void OnMediaKeyboardEvent(Event* event) { DefaultEventHandler(event); }
void OnWaiting();
void OnLoadingProgress();
void OnLoadedData();
// Media control elements.
Member<MediaControlOverlayEnclosureElement> overlay_enclosure_;
Member<MediaControlOverlayPlayButtonElement> overlay_play_button_;
Member<MediaControlCastButtonElement> overlay_cast_button_;
Member<MediaControlPanelEnclosureElement> enclosure_;
Member<MediaControlPanelElement> panel_;
Member<MediaControlPlayButtonElement> play_button_;
Member<MediaControlTimelineElement> timeline_;
Member<MediaControlScrubbingMessageElement> scrubbing_message_;
Member<MediaControlCurrentTimeDisplayElement> current_time_display_;
Member<MediaControlRemainingTimeDisplayElement> duration_display_;
Member<MediaControlMuteButtonElement> mute_button_;
Member<MediaControlVolumeSliderElement> volume_slider_;
Member<MediaControlToggleClosedCaptionsButtonElement>
toggle_closed_captions_button_;
Member<MediaControlTextTrackListElement> text_track_list_;
Member<MediaControlOverflowMenuButtonElement> overflow_menu_;
Member<MediaControlOverflowMenuListElement> overflow_list_;
Member<MediaControlButtonPanelElement> media_button_panel_;
Member<MediaControlLoadingPanelElement> loading_panel_;
Member<MediaControlPictureInPictureButtonElement> picture_in_picture_button_;
Member<MediaControlCastButtonElement> cast_button_;
Member<MediaControlFullscreenButtonElement> fullscreen_button_;
Member<MediaControlDownloadButtonElement> download_button_;
Member<MediaControlsMediaEventListener> media_event_listener_;
Member<MediaControlsOrientationLockDelegate> orientation_lock_delegate_;
Member<MediaControlsRotateToFullscreenDelegate>
rotate_to_fullscreen_delegate_;
TaskRunnerTimer<MediaControlsImpl> hide_media_controls_timer_;
unsigned hide_timer_behavior_flags_;
bool is_mouse_over_controls_ : 1;
bool is_paused_for_scrubbing_ : 1;
bool is_scrubbing_ = false;
// Watches the video element for resize and updates media controls as
// necessary.
Member<ResizeObserver> resize_observer_;
// Watches the media element for attribute changes and updates media controls
// as necessary.
Member<MediaElementMutationCallback> element_mutation_callback_;
TaskRunnerTimer<MediaControlsImpl> element_size_changed_timer_;
IntSize size_;
bool keep_showing_until_timer_fires_ : 1;
Member<MediaDownloadInProductHelpManager> download_iph_manager_;
bool is_acting_as_audio_controls_ = false;
// Our best guess on whether the user is interacting with the controls via
// touch (as opposed to mouse). This is used to determine how to handle
// certain pointer events. In particular, when the user is interacting via
// touch events, we want to ignore pointerover/pointerout/pointermove events.
bool is_touch_interaction_ = false;
// Holds the currently set --overlay-play-button-width value. Used to check if
// we need to update.
base::Optional<double> overlay_play_button_width_;
bool is_test_mode_ = false;
};
DEFINE_ELEMENT_TYPE_CASTS(MediaControlsImpl, IsMediaControls());
} // namespace blink
#endif