/*
 * Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 Apple 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. 
 */

#pragma once

#if ENABLE(VIDEO)

#include "HTMLElement.h"
#include "ActiveDOMObject.h"
#include "GenericEventQueue.h"
#include "GenericTaskQueue.h"
#include "JSDOMPromiseDeferred.h"
#include "MediaCanStartListener.h"
#include "MediaControllerInterface.h"
#include "MediaPlayer.h"
#include <wtf/Deque.h>

#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
#include "MediaPlayerProxy.h"
#endif

#if ENABLE(VIDEO_TRACK)
#include "AudioTrack.h"
#include "PODIntervalTree.h"
#include "TextTrack.h"
#include "TextTrackCue.h"
#include "VideoTrack.h"
#endif

namespace WebCore {

#if ENABLE(WEB_AUDIO)
class AudioSourceProvider;
class MediaElementAudioSourceNode;
#endif
class Event;
class HTMLSourceElement;
class HTMLTrackElement;
class MediaController;
class MediaControls;
class MediaError;
#if ENABLE(ENCRYPTED_MEDIA)
class MediaKeys;
#endif
class KURL;
class TimeRanges;
#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
class Widget;
#endif
#if PLATFORM(MAC)
class DisplaySleepDisabler;
#endif
#if ENABLE(LEGACY_ENCRYPTED_MEDIA_V2)
class WebKitMediaKeys;
#endif
#if ENABLE(MEDIA_SOURCE)
class VideoPlaybackQuality;
#endif

#if ENABLE(VIDEO_TRACK)
class AudioTrackList;
class AudioTrackPrivate;
class InbandTextTrackPrivate;
class TextTrackList;
class VideoTrackList;
class VideoTrackPrivate;

typedef PODIntervalTree<double, TextTrackCue*> CueIntervalTree;
typedef CueIntervalTree::IntervalType CueInterval;
typedef Vector<CueInterval> CueList;
#endif

#if ENABLE(MEDIA_STREAM)
class MediaStream;
#endif

String urlForLogging(const KURL&);

// FIXME: The inheritance from MediaPlayerClient here should be private inheritance.
// But it can't be until the Chromium WebMediaPlayerClientImpl class is fixed so it
// no longer depends on typecasting a MediaPlayerClient to an HTMLMediaElement.

class HTMLMediaElement
	: public HTMLElement
	, public MediaPlayerClient
	, public MediaPlayerSupportsTypeClient
	, private MediaCanStartListener
	, public ActiveDOMObject
	, public MediaControllerInterface
#if ENABLE(VIDEO_TRACK)
	, public AudioTrackClient
	, public TextTrackClient
	, public VideoTrackClient
#endif
{
public:
	ALWAYS_INLINE RefPtr<MediaPlayer> player() const { return m_player; }

    int mediaPlayerID() const;
    
    virtual void documentWillBecomeInactive() override;

    virtual bool isVideo() const = 0;
    virtual bool hasVideo() const override { return false; }
    virtual bool hasAudio() const override;

    void rewind(double timeDelta);
    void returnToRealtime() override;

    // Eventually overloaded in HTMLVideoElement
    virtual bool supportsFullscreen() const override { return false; };

    virtual bool supportsSave() const;
    virtual bool supportsScanning() const override;
    
    PlatformMedia platformMedia() const;
#if USE(ACCELERATED_COMPOSITING)
    PlatformLayer* platformLayer() const;
#endif

    enum DelayedActionType {
        LoadMediaResource = 1 << 0,
        LoadTextTrackResource = 1 << 1,
        TextTrackChangesNotification = 1 << 2
    };
    void scheduleDelayedAction(DelayedActionType);
    
    MediaPlayer::MovieLoadType movieLoadType() const;
    
    bool inActiveDocument() const { return m_inActiveDocument; }
    
// DOM API
// error state
    PassRefPtr<MediaError> error() const;

    void setSrc(const String&);
    const KURL& currentSrc() const { return m_currentSrc; }

#if ENABLE(MEDIA_STREAM)
    MediaStream* srcObject() const { return m_mediaStreamSrcObject.get(); }
    void setSrcObject(MediaStream*);
#endif

// network state
    enum NetworkState { NETWORK_EMPTY, NETWORK_IDLE, NETWORK_LOADING, NETWORK_NO_SOURCE };
    NetworkState networkState() const;

    String preload() const;    
    void setPreload(const String&);

    PassRefPtr<TimeRanges> buffered() const;
    void load(ExceptionCode&);
    String canPlayType(const String& mimeType, const String& keySystem = String(), const KURL& = KURL()) const;

// ready state
    ReadyState readyState() const;
    bool seeking() const;

// playback state
    double currentTime() const;
    void setCurrentTime(double);
    virtual void setCurrentTime(double, ExceptionCode&);
    virtual double getStartDate() const;
    double duration() const;
    bool paused() const;
    double defaultPlaybackRate() const;
    void setDefaultPlaybackRate(double);
    double playbackRate() const;
    void setPlaybackRate(double);

    // MediaTime versions of playback state
    MediaTime currentMediaTime() const;
    void setCurrentTime(const MediaTime&);
    MediaTime durationMediaTime() const;
    void fastSeek(const MediaTime&);

    void updatePlaybackRate();
    bool webkitPreservesPitch() const;
    void setWebkitPreservesPitch(bool);
    PassRefPtr<TimeRanges> played();
    PassRefPtr<TimeRanges> seekable() const;
    bool ended() const;
    bool autoplay() const;    
    void setAutoplay(bool b);
    bool loop() const;    
    void setLoop(bool b);
    void play();
    void pause();
    virtual void setShouldBufferData(bool);
    void fastSeek(double);

// captions
    bool webkitHasClosedCaptions() const;
    bool webkitClosedCaptionsVisible() const;
    void setWebkitClosedCaptionsVisible(bool);

#if ENABLE(MEDIA_STATISTICS)
// Statistics
    unsigned webkitAudioDecodedByteCount() const;
    unsigned webkitVideoDecodedByteCount() const;
#endif

#if ENABLE(MEDIA_SOURCE)
    void detachMediaSource();
    void incrementDroppedFrameCount() { ++m_droppedVideoFrames; }
#endif 

#if ENABLE(LEGACY_ENCRYPTED_MEDIA)
    void generateKeyRequest(const String& keySystem, PassRefPtr<Uint8Array> initData, ExceptionCode&);
    void addKey(const String& keySystem, PassRefPtr<Uint8Array> key, PassRefPtr<Uint8Array> initData, const String& sessionId, ExceptionCode&);
    void cancelKeyRequest(const String& keySystem, const String& sessionId, ExceptionCode&);
#endif

#if ENABLE(LEGACY_ENCRYPTED_MEDIA_V2)
    ALWAYS_INLINE WebKitMediaKeys* keys() const { return m_webKitMediaKeys.get(); }
    ALWAYS_INLINE WebKitMediaKeys* webkitKeys() const { return m_webKitMediaKeys.get(); }
    void webkitSetMediaKeys(WebKitMediaKeys* mediaKeys);
#endif

#if ENABLE(ENCRYPTED_MEDIA)
    MediaKeys* mediaKeys() const { return m_mediaKeys.get(); }

    typedef DOMPromiseDeferred<std::nullptr_t, ExceptionCode> PromiseVoid;
    void setMediaKeys(MediaKeys*, PromiseVoid);
#endif

// controls
    bool controls() const;
    void setControls(bool);
    double volume() const;
    void setVolume(double, ExceptionCode&);
    bool muted() const;
    void setMuted(bool);

    void togglePlayState();
    void beginScrubbing();
    void endScrubbing();
    
    bool canPlay() const;

    double percentLoaded() const;

#if ENABLE(VIDEO_TRACK)
    PassRefPtr<TextTrack> addTextTrack(const String& kind, const String& label, const String& language, ExceptionCode&);

    AudioTrackList* audioTracks();
    TextTrackList* textTracks();
    VideoTrackList* videoTracks();

    CueList currentlyActiveCues() const { return m_currentlyActiveCues; }

    void addAudioTrack(PassRefPtr<AudioTrack>);
    void addTextTrack(PassRefPtr<TextTrack>);
    void addVideoTrack(PassRefPtr<VideoTrack>);
    void removeAudioTrack(AudioTrack*);
    void removeTextTrack(TextTrack*);
    void removeVideoTrack(VideoTrack*);
    void removeAllInbandTracks();

    virtual void didAddTextTrack(HTMLTrackElement*);
    virtual void didRemoveTextTrack(HTMLTrackElement*);

    virtual void mediaPlayerDidAddAudioTrack(PassRefPtr<AudioTrackPrivate>) final;
    virtual void mediaPlayerDidAddTextTrack(PassRefPtr<InbandTextTrackPrivate>) final;
    virtual void mediaPlayerDidAddVideoTrack(PassRefPtr<VideoTrackPrivate>) final;
    virtual void mediaPlayerDidRemoveAudioTrack(PassRefPtr<AudioTrackPrivate>) final;
    virtual void mediaPlayerDidRemoveTextTrack(PassRefPtr<InbandTextTrackPrivate>) final;
    virtual void mediaPlayerDidRemoveVideoTrack(PassRefPtr<VideoTrackPrivate>) final;

    virtual void mediaPlayerDidAddAudioTrack(PassRefPtr<AudioTrack>) final;
    virtual void mediaPlayerDidRemoveAllAudioTracks() final;

    struct TrackGroup {
        enum GroupKind { CaptionsAndSubtitles, Description, Chapter, Metadata, Other };

        TrackGroup(GroupKind kind)
            : kind(kind)
            , hasSrcLang(false)
        {
        }

        Vector<RefPtr<TextTrack> > tracks;
        RefPtr<TextTrack> visibleTrack;
        RefPtr<TextTrack> defaultTrack;
        GroupKind kind;
        bool hasSrcLang;
    };

    void configureTextTrackGroupForLanguage(const TrackGroup&) const;
    void configureTextTracks();
    void configureTextTrackGroup(const TrackGroup&);

    void toggleTrackAtIndex(int index, bool exclusive = true);
    static int textTracksOffIndex() { return -1; }
    static int textTracksIndexNotFound() { return -2; }

    bool userPrefersCaptions() const;
    bool userIsInterestedInThisTrackKind(String) const;
    bool textTracksAreReady() const;
    void configureTextTrackDisplay();
    void updateTextTrackDisplay();

    // AudioTrackClient
    virtual void audioTrackEnabledChanged(AudioTrack*) final;

    // TextTrackClient
    virtual void textTrackReadyStateChanged(TextTrack*) final;
    virtual void textTrackKindChanged(TextTrack*) final;
    virtual void textTrackModeChanged(TextTrack*) final;
    virtual void textTrackAddCues(TextTrack*, const TextTrackCueList*) final;
    virtual void textTrackRemoveCues(TextTrack*, const TextTrackCueList*) final;
    virtual void textTrackAddCue(TextTrack*, PassRefPtr<TextTrackCue>) final;
    virtual void textTrackRemoveCue(TextTrack*, PassRefPtr<TextTrackCue>) final;

    // VideoTrackClient
    virtual void videoTrackSelectedChanged(VideoTrack*) final;
#endif

#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
    void allocateMediaPlayerIfNecessary();
    void setNeedWidgetUpdate(bool needWidgetUpdate) { m_needWidgetUpdate = needWidgetUpdate; }
    void deliverNotification(MediaPlayerProxyNotificationType notification);
    void setMediaPlayerProxy(WebMediaPlayerProxy* proxy);
    void getPluginProxyParams(KURL& url, Vector<String>& names, Vector<String>& values);
    void createMediaPlayerProxy();
    void updateWidget(PluginCreationOption);
#endif

    // EventTarget function.
    // Both Node (via HTMLElement) and ActiveDOMObject define this method, which
    // causes an ambiguity error at compile time. This class's constructor
    // ensures that both implementations return document, so return the result
    // of one of them here.
    using HTMLElement::scriptExecutionContext;

    bool hasSingleSecurityOrigin() const { return !m_player || m_player->hasSingleSecurityOrigin(); }
    
    bool isFullscreen() const;
    void enterFullscreen();
    void exitFullscreen();

    void uncheckedSeek(double time);
    void uncheckedSeek(MediaTime time);

    bool hasClosedCaptions() const;
    bool closedCaptionsVisible() const;
    void setClosedCaptionsVisible(bool);

    MediaControls* mediaControls() const;

    void sourceWasRemoved(HTMLSourceElement*);
    void sourceWasAdded(HTMLSourceElement*);

    void privateBrowsingStateDidChange();

    // Media cache management.
    static void getSitesInMediaCache(Vector<String>&);
    static void clearMediaCache();
    static void clearMediaCacheForSite(const String&);

    bool isPlaying() const { return m_playing; }

#if ENABLE(WEB_AUDIO)
    MediaElementAudioSourceNode* audioSourceNode() { return m_audioSourceNode; }
    void setAudioSourceNode(MediaElementAudioSourceNode*);

    AudioSourceProvider* audioSourceProvider();
#endif

    enum InvalidURLAction { DoNothing, Complain };
    bool isSafeToLoadURL(const KURL&, InvalidURLAction);

    const String& mediaGroup() const;
    void setMediaGroup(const String&);

    MediaController* controller() const;
    void setController(PassRefPtr<MediaController>);

    MediaController* controllerForBindings() const { return controller(); }
    void setControllerForBindings(MediaController*);

    virtual void dispatchEvent(PassRefPtr<Event>) final;

    virtual bool willRespondToMouseClickEvents() final;

    void enteredOrExitedFullscreen() { configureMediaControls(); }

#if ENABLE(MEDIA_SOURCE)
    RefPtr<VideoPlaybackQuality> getVideoPlaybackQuality();
#endif

    void mediaVolumeDidChange();

#if ENABLE(VIDEO_TRACK)
    void captionPreferencesChanged();
#endif

protected:
    HTMLMediaElement(const QualifiedName&, Document*, bool);
    virtual ~HTMLMediaElement();

    virtual void attributeChanged(const QualifiedName&, const AtomString& oldValue, const AtomString& newValue, AttributeModificationReason) override;
    virtual void finishParsingChildren() override;
    virtual bool isURLAttribute(const Attribute&) const OVERRIDE;
    virtual void willAttachRenderers() OVERRIDE;
    virtual void didAttachRenderers() OVERRIDE;

    virtual void didMoveToNewDocument(Document& oldDocument, Document& newDocument) OVERRIDE;

    enum DisplayMode { Unknown, None, Poster, PosterWaitingForVideo, Video };
    DisplayMode displayMode() const { return m_displayMode; }
    virtual void setDisplayMode(DisplayMode mode) { m_displayMode = mode; }
    
    virtual bool isMediaElement() const final { return true; }

    // Restrictions to change default behaviors.
    enum BehaviorRestrictionFlags {
        NoRestrictions = 0,
        RequireUserGestureForLoadRestriction = 1 << 0,
        RequireUserGestureForRateChangeRestriction = 1 << 1,
        RequireUserGestureForFullscreenRestriction = 1 << 2,
        RequirePageConsentToLoadMediaRestriction = 1 << 3,
    };
    typedef unsigned BehaviorRestrictions;
    
    bool userGestureRequiredForLoad() const { return m_restrictions & RequireUserGestureForLoadRestriction; }
    bool userGestureRequiredForRateChange() const { return m_restrictions & RequireUserGestureForRateChangeRestriction; }
    bool userGestureRequiredForFullscreen() const { return m_restrictions & RequireUserGestureForFullscreenRestriction; }
    bool pageConsentRequiredForLoad() const { return m_restrictions & RequirePageConsentToLoadMediaRestriction; }
    
    void addBehaviorRestriction(BehaviorRestrictions restriction) { m_restrictions |= restriction; }
    void removeBehaviorRestriction(BehaviorRestrictions restriction) { m_restrictions &= ~restriction; }

private:
    void createMediaPlayer();

    virtual bool alwaysCreateUserAgentShadowRoot() const OVERRIDE { return true; }

    // FIXME: Shadow DOM spec says we should be able to create shadow root on audio and video elements
    virtual bool canHaveUserAgentShadowRoot() const OVERRIDE FINAL { return true; }

    virtual bool supportsFocus() const OVERRIDE;
    virtual bool isMouseFocusable() const OVERRIDE;
    virtual bool rendererIsNeeded(const RenderStyle&) OVERRIDE;
    virtual RenderPtr<RenderElement> createElementRenderer(PassOwnPtr<RenderStyle>, const RenderTreePosition&) OVERRIDE;
    virtual bool childShouldCreateRenderer(const Node*) const OVERRIDE;
    virtual InsertedIntoResult insertedIntoAncestor(const InsertionType&, ContainerNode&) OVERRIDE;
    virtual void didFinishInsertingNode() OVERRIDE;
    virtual void removedFromAncestor(const RemovalType&, ContainerNode&) OVERRIDE;
    virtual void didRecalcStyle(Style::Change) OVERRIDE;
    bool isInteractiveContent() const override;
    
    virtual void defaultEventHandler(Event*) OVERRIDE;

    virtual void didBecomeFullscreenElement() override;
    virtual void willStopBeingFullscreenElement() override;

    // ActiveDOMObject functions.
    virtual const char* activeDOMObjectName() const OVERRIDE { return "HTMLMediaElement"; }
	virtual bool canSuspendForDocumentSuspension() const override;
    virtual void suspend(ReasonForSuspension) override;
    virtual void resume() override;
    virtual void stop() override;
    virtual bool virtualHasPendingActivity() const override;

    void stopWithoutDestroyingMediaPlayer();

    virtual void updateDisplayState() { }
    
    void setReadyState(MediaPlayer::ReadyState);
    void setNetworkState(MediaPlayer::NetworkState);

    double effectivePlaybackRate() const;

    virtual HTMLMediaElement* mediaPlayerOwningElement() final { return this;}
    virtual void mediaPlayerNetworkStateChanged(MediaPlayer*) final;
    virtual void mediaPlayerReadyStateChanged(MediaPlayer*) final;
    virtual void mediaPlayerTimeChanged(MediaPlayer*, bool playEnded) final;
    virtual void mediaPlayerVolumeChanged(MediaPlayer*) final;
    virtual void mediaPlayerMuteChanged(MediaPlayer*) final;
    virtual void mediaPlayerDurationChanged(MediaPlayer*) final;
    virtual void mediaPlayerRateChanged(MediaPlayer*) final;
    virtual void mediaPlayerPlaybackStateChanged(MediaPlayer*) final;
    virtual void mediaPlayerSawUnsupportedTracks(MediaPlayer*) final;
    virtual void mediaPlayerResourceNotSupported(MediaPlayer*) final;
    virtual void mediaPlayerRepaint(MediaPlayer*) final;
    virtual void mediaPlayerPlatformError(MediaPlayer*, unsigned short errorCode) final;
    virtual void mediaPlayerSizeChanged(MediaPlayer*) final;
#if USE(ACCELERATED_COMPOSITING)
    virtual bool mediaPlayerRenderingCanBeAccelerated(MediaPlayer*) final;
    virtual void mediaPlayerRenderingModeChanged(MediaPlayer*) final;
#endif
    virtual void mediaPlayerEngineUpdated(MediaPlayer*) final;
    virtual void mediaPlayerFirstVideoFrameAvailable(MediaPlayer*) final;
    virtual void mediaPlayerCharacteristicChanged(MediaPlayer*) final;
    virtual void mediaPlayerScheduleEvent(MediaPlayer*, const String&) final;

#if ENABLE(LEGACY_ENCRYPTED_MEDIA)
    virtual void mediaPlayerKeyAdded(MediaPlayer*, const String& keySystem, const String& sessionId) final;
    virtual void mediaPlayerKeyError(MediaPlayer*, const String& keySystem, const String& sessionId, MediaPlayerClient::MediaKeyErrorCode, unsigned short systemCode) final;
    virtual void mediaPlayerKeyMessage(MediaPlayer*, const String& keySystem, const String& sessionId, const unsigned char* message, unsigned messageLength) final;
    virtual void mediaPlayerKeyNeeded(MediaPlayer*, const String& keySystem, const String& sessionId, const unsigned char* initData, unsigned initDataLength, const String& initDataType) final;
#endif

#if ENABLE(LEGACY_ENCRYPTED_MEDIA_V2)
    virtual void mediaPlayerKeyNeeded(MediaPlayer*, Uint8Array*, const String& initDataType) final;
#endif

    virtual String mediaPlayerReferrer() const final;
    virtual String mediaPlayerUserAgent() const final;
    virtual CORSMode mediaPlayerCORSMode() const final;

    virtual bool mediaPlayerNeedsSiteSpecificHacks() const final;
    virtual String mediaPlayerDocumentHost() const final;

#if PLATFORM(WIN) && USE(AVFOUNDATION)
    virtual GraphicsDeviceAdapter* mediaPlayerGraphicsDeviceAdapter(const MediaPlayer*) const OVERRIDE;
#endif

    void loadTimerFired();
    void progressEventTimerFired();
    void playbackProgressTimerFired();
    bool seekTimerFiredInternal();
    void seekTimerFired();
    void startPlaybackProgressTimer();
    void startProgressEventTimer();
    void stopPeriodicTimers();

    void seek(const MediaTime&);
    bool seekInternal(const MediaTime&);
    bool seekWithTolerance(const MediaTime&, const MediaTime& negativeTolerance, const MediaTime& positiveTolerance, bool fromDOM);
    void finishSeek();
    void checkIfSeekNeeded();
    void addPlayedRange(const MediaTime& start, const MediaTime& end);

    void scheduleTimeupdateEvent(bool periodicEvent);
    void scheduleEvent(const AtomicString& eventName);
    
    // loading
    void selectMediaResource();
    void loadResource(const KURL&, ContentType&, const String& keySystem);
    void scheduleNextSourceChild();
    void loadNextSourceChild();
    void userCancelledLoad();
    void clearMediaPlayer(int flags, bool willLoadNewURL);
    bool havePotentialSourceChild();
    void noneSupported();
    void mediaEngineError(PassRefPtr<MediaError> err);
    void cancelPendingEventsAndCallbacks();
    void waitForSourceChange();
    void prepareToPlay();

    KURL selectNextSourceChild(ContentType*, String* keySystem, InvalidURLAction);

    void mediaLoadingFailed(MediaPlayer::NetworkState);

#if ENABLE(VIDEO_TRACK)
    void updateActiveTextTrackCues(double);
    HTMLTrackElement* showingTrackWithSameKind(HTMLTrackElement*) const;

    bool ignoreTrackDisplayUpdateRequests() const { return m_ignoreTrackDisplayUpdate > 0; }
    void beginIgnoringTrackDisplayUpdateRequests() { ++m_ignoreTrackDisplayUpdate; }
    void endIgnoringTrackDisplayUpdateRequests() { ASSERT(m_ignoreTrackDisplayUpdate); --m_ignoreTrackDisplayUpdate; }

    void markCaptionAndSubtitleTracksAsUnconfigured();
#endif

    // These "internal" functions do not check user gesture restrictions.
    void loadInternal();
    void playInternal();
    void pauseInternal();

    void prepareForLoad();
    void allowVideoRendering();

    bool processingMediaPlayerCallback() const { return m_processingMediaPlayerCallback > 0; }
    void beginProcessingMediaPlayerCallback() { ++m_processingMediaPlayerCallback; }
    void endProcessingMediaPlayerCallback() { ASSERT(m_processingMediaPlayerCallback); --m_processingMediaPlayerCallback; }

    void updateVolume();
    void updatePlayState(bool shouldLog = true);
    bool potentiallyPlaying() const;
    bool endedPlayback() const;
    bool stoppedDueToErrors() const;
    bool pausedForUserInteraction() const;
    bool couldPlayIfEnoughData() const;

    MediaTime minTimeSeekable() const;
    MediaTime maxTimeSeekable() const;

    void setPlaying(bool);

    // Pauses playback without changing any states or generating events
    void setPausedInternal(bool);

    void setPlaybackRateInternal(double);

    virtual void mediaCanStart();

    void setShouldDelayLoadEvent(bool);
    void invalidateCachedTime();
    void refreshCachedTime() const;

    bool hasMediaControls() const;
    bool createMediaControls();
    void configureMediaControls();

    void prepareMediaFragmentURI();
    void applyMediaFragmentURI();

    void changeNetworkStateFromLoadingToIdle();

    void removeBehaviorsRestrictionsAfterFirstUserGesture();

#if ENABLE(MICRODATA)
    virtual String itemValueText() const;
    virtual void setItemValueText(const String&, ExceptionCode&);
#endif

    void updateMediaController();
    bool isBlocked() const;
    bool isBlockedOnMediaController() const;
    bool hasCurrentSrc() const { return !m_currentSrc.isEmpty(); }
    bool isLiveStream() const { return movieLoadType() == MediaPlayer::LiveStream; }
    bool isAutoplaying() const { return m_autoplaying; }

#if PLATFORM(MAC)
    void updateDisableSleep();
    bool shouldDisableSleep() const;
#endif

    void pauseAfterDetachedTimerFired();

    Timer<HTMLMediaElement> m_loadTimer;
    Timer<HTMLMediaElement> m_progressEventTimer;
    Timer<HTMLMediaElement> m_playbackProgressTimer;
    Timer<HTMLMediaElement> m_pauseAfterDetachedTimer;
    Timer<HTMLMediaElement> m_seekTimer;
    RefPtr<TimeRanges> m_playedTimeRanges;
    OwnPtr<GenericEventQueue> m_asyncEventQueue;

    GenericTaskQueue<HTMLMediaElement> m_taskQueue;

    double m_playbackRate;
    double m_defaultPlaybackRate;
    bool m_webkitPreservesPitch;
    NetworkState m_networkState;
    ReadyState m_readyState;
    ReadyState m_readyStateMaximum;
    KURL m_currentSrc;

    RefPtr<MediaError> m_error;

    struct PendingSeek {
        PendingSeek(MediaTime now, MediaTime targetTime, MediaTime negativeTolerance, MediaTime positiveTolerance)
            : now(now)
            , targetTime(targetTime)
            , negativeTolerance(negativeTolerance)
            , positiveTolerance(positiveTolerance)
        {
        }
        MediaTime now;
        MediaTime targetTime;
        MediaTime negativeTolerance;
        MediaTime positiveTolerance;
    };
    OwnPtr<PendingSeek> m_pendingSeek;

    double m_volume;
    MediaTime m_lastSeekTime;
    
    unsigned m_previousProgress;
    double m_previousProgressTime;

    // The last time a timeupdate event was sent (based on monotonic clock).
    double m_clockTimeAtLastUpdateEvent;

    // The last time a timeupdate event was sent in movie time.
    MediaTime m_lastTimeUpdateEventMovieTime;
    
    // Loading state.
    enum LoadState { WaitingForSource, LoadingFromSrcAttr, LoadingFromSourceElement };
    LoadState m_loadState;
    RefPtr<HTMLSourceElement> m_currentSourceNode;
    RefPtr<Node> m_nextChildNodeToConsider;

    RefPtr<MediaPlayer> m_player;
#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
    RefPtr<Widget> m_proxyWidget;
#endif

    BehaviorRestrictions m_restrictions;
    
    MediaPlayer::Preload m_preload;

    DisplayMode m_displayMode;

    // Counter incremented while processing a callback from the media player, so we can avoid
    // calling the media engine recursively.
    int m_processingMediaPlayerCallback;

#if ENABLE(MEDIA_SOURCE)
    RefPtr<HTMLMediaSource> m_mediaSource;
    unsigned long m_droppedVideoFrames;
#endif

    mutable MediaTime m_cachedTime;
    mutable double m_clockTimeAtLastCachedTimeUpdate;
    mutable double m_minimumClockTimeToUpdateCachedTime;

    MediaTime m_fragmentStartTime;
    MediaTime m_fragmentEndTime;

    typedef unsigned PendingActionFlags;
    PendingActionFlags m_pendingActionFlags;

    bool m_playing : 1;
    bool m_isWaitingUntilMediaCanStart : 1;
    bool m_shouldDelayLoadEvent : 1;
    bool m_haveFiredLoadedData : 1;
    bool m_inActiveDocument : 1;
    bool m_autoplaying : 1;
    bool m_muted : 1;
    bool m_paused : 1;
    bool m_seeking : 1;

    // data has not been loaded since sending a "stalled" event
    bool m_sentStalledEvent : 1;

    // time has not changed since sending an "ended" event
    bool m_sentEndEvent : 1;

    bool m_pausedInternal : 1;

    // Not all media engines provide enough information about a file to be able to
    // support progress events so setting m_sendProgressEvents disables them 
    bool m_sendProgressEvents : 1;

    bool m_isFullscreen : 1;
    bool m_closedCaptionsVisible : 1;

#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
    bool m_needWidgetUpdate : 1;
#endif

    bool m_dispatchingCanPlayEvent : 1;
    bool m_loadInitiatedByUserGesture : 1;
    bool m_completelyLoaded : 1;
    bool m_havePreparedToPlay : 1;
    bool m_parsingInProgress : 1;

#if ENABLE(VIDEO_TRACK)
    bool m_tracksAreReady : 1;
    bool m_haveVisibleTextTrack : 1;
    double m_lastTextTrackUpdateTime;

    RefPtr<AudioTrackList> m_audioTracks;
    RefPtr<TextTrackList> m_textTracks;
    RefPtr<VideoTrackList> m_videoTracks;
    Vector<RefPtr<TextTrack> > m_textTracksWhenResourceSelectionBegan;

    CueIntervalTree m_cueTree;

    CueList m_currentlyActiveCues;
    int m_ignoreTrackDisplayUpdate;
    bool m_disableCaptions;
#endif

#if ENABLE(WEB_AUDIO)
    // This is a weak reference, since m_audioSourceNode holds a reference to us.
    // The value is set just after the MediaElementAudioSourceNode is created.
    // The value is cleared in MediaElementAudioSourceNode::~MediaElementAudioSourceNode().
    MediaElementAudioSourceNode* m_audioSourceNode;
#endif

    String m_mediaGroup;
    friend class MediaController;
    RefPtr<MediaController> m_mediaController;

#if PLATFORM(MAC)
    OwnPtr<DisplaySleepDisabler> m_sleepDisabler;
#endif

#if ENABLE(LEGACY_ENCRYPTED_MEDIA_V2)
    RefPtr<WebKitMediaKeys> m_webKitMediaKeys;
#endif

#if ENABLE(ENCRYPTED_MEDIA)
    RefPtr<MediaKeys> m_mediaKeys;
#endif

#if ENABLE(LEGACY_ENCRYPTED_MEDIA)
    void keyRequestTimerFired();

    struct PendingKeyRequest {
        PendingKeyRequest(const String& keySystem, Uint8Array* initData);
        ~PendingKeyRequest();
        String keySystem;
        RefPtr<Uint8Array> initData;
    };
    Deque<PendingKeyRequest> m_pendingKeyRequests;
    Timer<HTMLMediaElement> m_keyRequestTimer;
#endif

#if ENABLE(MEDIA_STREAM)
    RefPtr<MediaStream> m_mediaStreamSrcObject;
#endif
};

#if ENABLE(VIDEO_TRACK)
#ifndef NDEBUG
// Template specializations required by PodIntervalTree in debug mode.
template <>
struct ValueToString<double> {
    static String string(const double value)
    {
        return String::number(value);
    }
};

template <>
struct ValueToString<TextTrackCue*> {
    static String string(TextTrackCue* const& cue)
    {
        return String::format("%p id=%s interval=%f-->%f cue=%s)", cue, cue->id().utf8().data(), cue->startTime(), cue->endTime(), cue->text().utf8().data());
    }
};
#endif
#endif

ALWAYS_INLINE bool isMediaElement(const Node* node)
{
    return node && node->isElementNode() && toElement(node)->isMediaElement();
}

ALWAYS_INLINE bool isMediaElement(const Node& node)
{
    return node.isElementNode() && toElement(node).isMediaElement();
}

ALWAYS_INLINE bool isMediaElement(const Element* element)
{
    return element && element->isMediaElement();
}

ALWAYS_INLINE bool isMediaElement(const Element& element)
{
    return element.isMediaElement();
}

ALWAYS_INLINE HTMLMediaElement* toMediaElement(Node* node)
{
    ASSERT(!node || isMediaElement(node));
    return static_cast<HTMLMediaElement*>(node);
}

ALWAYS_INLINE HTMLMediaElement& toMediaElement(Node& node)
{
    ASSERT(!node || isMediaElement(node));
    return static_cast<HTMLMediaElement&>(node);
}

ALWAYS_INLINE HTMLMediaElement* toHTMLMediaElement(Node* node)
{
    ASSERT(!node || isHTMLMediaElement(node));
    return static_cast<HTMLMediaElement*>(node);
}

ALWAYS_INLINE const HTMLMediaElement* toHTMLMediaElement(const Node* node)
{
    ASSERT(!node || isHTMLMediaElement(node));
    return static_cast<const HTMLMediaElement*>(node);
}

ALWAYS_INLINE bool isHTMLMediaElement(const Node& node)
{
    return node.isElementNode() && toElement(node).isMediaElement();
}

ALWAYS_INLINE bool isHTMLMediaElement(const Element& element)
{
    return element.isMediaElement();
}

ALWAYS_INLINE HTMLMediaElement& toHTMLMediaElement(Node& node)
{
    ASSERT(isHTMLMediaElement(node));
    return static_cast<HTMLMediaElement&>(node);
}

template <typename ArgType>
struct ElementTypeCastTraits<const HTMLMediaElement, ArgType> {
    static bool is(ArgType& node) { return isHTMLMediaElement(node); }
};

#ifndef NDEBUG
template<>
struct ValueToString<MediaTime> {
    static String string(const MediaTime& time)
    {
        return toString(time);
    }
};
#endif

} //namespace

#endif
