1/*
2 * Copyright (C) 2007-2014 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#if ENABLE(VIDEO)
28#include "HTMLMediaElement.h"
29
30#include "ApplicationCacheHost.h"
31#include "ApplicationCacheResource.h"
32#include "Attribute.h"
33#include "CSSPropertyNames.h"
34#include "CSSValueKeywords.h"
35#include "ChromeClient.h"
36#include "ClientRect.h"
37#include "ClientRectList.h"
38#include "ContentSecurityPolicy.h"
39#include "ContentType.h"
40#include "CookieJar.h"
41#include "DiagnosticLoggingKeys.h"
42#include "DisplaySleepDisabler.h"
43#include "DocumentLoader.h"
44#include "ElementIterator.h"
45#include "EventNames.h"
46#include "ExceptionCodePlaceholder.h"
47#include "FrameLoader.h"
48#include "FrameLoaderClient.h"
49#include "FrameView.h"
50#include "HTMLSourceElement.h"
51#include "HTMLVideoElement.h"
52#include "JSHTMLMediaElement.h"
53#include "Language.h"
54#include "Logging.h"
55#include "MIMETypeRegistry.h"
56#include "MainFrame.h"
57#include "MediaController.h"
58#include "MediaControls.h"
59#include "MediaDocument.h"
60#include "MediaError.h"
61#include "MediaFragmentURIParser.h"
62#include "MediaKeyEvent.h"
63#include "MediaList.h"
64#include "MediaQueryEvaluator.h"
65#include "MediaSessionManager.h"
66#include "NetworkingContext.h"
67#include "PageActivityAssertionToken.h"
68#include "PageGroup.h"
69#include "PageThrottler.h"
70#include "ProgressTracker.h"
71#include "RenderLayerCompositor.h"
72#include "RenderVideo.h"
73#include "RenderView.h"
74#include "ScriptController.h"
75#include "ScriptSourceCode.h"
76#include "SecurityPolicy.h"
77#include "SessionID.h"
78#include "Settings.h"
79#include "ShadowRoot.h"
80#include "TimeRanges.h"
81#include <limits>
82#include <runtime/Uint8Array.h>
83#include <wtf/CurrentTime.h>
84#include <wtf/MathExtras.h>
85#include <wtf/Ref.h>
86#include <wtf/text/CString.h>
87
88#if ENABLE(VIDEO_TRACK)
89#include "AudioTrackList.h"
90#include "HTMLTrackElement.h"
91#include "InbandGenericTextTrack.h"
92#include "InbandTextTrackPrivate.h"
93#include "InbandWebVTTTextTrack.h"
94#include "RuntimeEnabledFeatures.h"
95#include "TextTrackCueList.h"
96#include "TextTrackList.h"
97#include "VideoTrackList.h"
98#endif
99
100#if ENABLE(WEB_AUDIO)
101#include "AudioSourceProvider.h"
102#include "MediaElementAudioSourceNode.h"
103#endif
104
105#if PLATFORM(IOS)
106#include "RuntimeApplicationChecksIOS.h"
107#endif
108
109#if ENABLE(IOS_AIRPLAY)
110#include "WebKitPlaybackTargetAvailabilityEvent.h"
111#endif
112
113#if ENABLE(MEDIA_SOURCE)
114#include "DOMWindow.h"
115#include "MediaSource.h"
116#include "Performance.h"
117#include "VideoPlaybackQuality.h"
118#endif
119
120#if ENABLE(MEDIA_STREAM)
121#include "MediaStream.h"
122#include "MediaStreamRegistry.h"
123#endif
124
125#if ENABLE(ENCRYPTED_MEDIA_V2)
126#include "MediaKeyNeededEvent.h"
127#include "MediaKeys.h"
128#endif
129
130#if USE(PLATFORM_TEXT_TRACK_MENU)
131#include "PlatformTextTrack.h"
132#endif
133
134#if ENABLE(MEDIA_CONTROLS_SCRIPT)
135#include "JSMediaControlsHost.h"
136#include "MediaControlsHost.h"
137#include "ScriptGlobalObject.h"
138#include <bindings/ScriptObject.h>
139#endif
140
141namespace WebCore {
142
143static const double SeekRepeatDelay = 0.1;
144static const double SeekTime = 0.2;
145static const double ScanRepeatDelay = 1.5;
146static const double ScanMaximumRate = 8;
147
148static void setFlags(unsigned& value, unsigned flags)
149{
150    value |= flags;
151}
152
153static void clearFlags(unsigned& value, unsigned flags)
154{
155    value &= ~flags;
156}
157
158#if !LOG_DISABLED
159static String urlForLoggingMedia(const URL& url)
160{
161    static const unsigned maximumURLLengthForLogging = 128;
162
163    if (url.string().length() < maximumURLLengthForLogging)
164        return url.string();
165    return url.string().substring(0, maximumURLLengthForLogging) + "...";
166}
167
168static const char* boolString(bool val)
169{
170    return val ? "true" : "false";
171}
172#endif
173
174#ifndef LOG_MEDIA_EVENTS
175// Default to not logging events because so many are generated they can overwhelm the rest of
176// the logging.
177#define LOG_MEDIA_EVENTS 0
178#endif
179
180#ifndef LOG_CACHED_TIME_WARNINGS
181// Default to not logging warnings about excessive drift in the cached media time because it adds a
182// fair amount of overhead and logging.
183#define LOG_CACHED_TIME_WARNINGS 0
184#endif
185
186#if ENABLE(MEDIA_SOURCE)
187// URL protocol used to signal that the media source API is being used.
188static const char* mediaSourceBlobProtocol = "blob";
189#endif
190
191using namespace HTMLNames;
192
193typedef HashMap<Document*, HashSet<HTMLMediaElement*>> DocumentElementSetMap;
194static DocumentElementSetMap& documentToElementSetMap()
195{
196    DEPRECATED_DEFINE_STATIC_LOCAL(DocumentElementSetMap, map, ());
197    return map;
198}
199
200static void addElementToDocumentMap(HTMLMediaElement& element, Document& document)
201{
202    DocumentElementSetMap& map = documentToElementSetMap();
203    HashSet<HTMLMediaElement*> set = map.take(&document);
204    set.add(&element);
205    map.add(&document, set);
206}
207
208static void removeElementFromDocumentMap(HTMLMediaElement& element, Document& document)
209{
210    DocumentElementSetMap& map = documentToElementSetMap();
211    HashSet<HTMLMediaElement*> set = map.take(&document);
212    set.remove(&element);
213    if (!set.isEmpty())
214        map.add(&document, set);
215}
216
217#if ENABLE(ENCRYPTED_MEDIA)
218static ExceptionCode exceptionCodeForMediaKeyException(MediaPlayer::MediaKeyException exception)
219{
220    switch (exception) {
221    case MediaPlayer::NoError:
222        return 0;
223    case MediaPlayer::InvalidPlayerState:
224        return INVALID_STATE_ERR;
225    case MediaPlayer::KeySystemNotSupported:
226        return NOT_SUPPORTED_ERR;
227    }
228
229    ASSERT_NOT_REACHED();
230    return INVALID_STATE_ERR;
231}
232#endif
233
234#if ENABLE(VIDEO_TRACK)
235class TrackDisplayUpdateScope {
236public:
237    TrackDisplayUpdateScope(HTMLMediaElement* mediaElement)
238    {
239        m_mediaElement = mediaElement;
240        m_mediaElement->beginIgnoringTrackDisplayUpdateRequests();
241    }
242    ~TrackDisplayUpdateScope()
243    {
244        ASSERT(m_mediaElement);
245        m_mediaElement->endIgnoringTrackDisplayUpdateRequests();
246    }
247
248private:
249    HTMLMediaElement* m_mediaElement;
250};
251#endif
252
253HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document& document, bool createdByParser)
254    : HTMLElement(tagName, document)
255    , ActiveDOMObject(&document)
256    , m_loadTimer(this, &HTMLMediaElement::loadTimerFired)
257    , m_progressEventTimer(this, &HTMLMediaElement::progressEventTimerFired)
258    , m_playbackProgressTimer(this, &HTMLMediaElement::playbackProgressTimerFired)
259    , m_scanTimer(this, &HTMLMediaElement::scanTimerFired)
260    , m_seekTimer(this, &HTMLMediaElement::seekTimerFired)
261    , m_playedTimeRanges()
262    , m_asyncEventQueue(*this)
263    , m_playbackRate(1.0f)
264    , m_defaultPlaybackRate(1.0f)
265    , m_webkitPreservesPitch(true)
266    , m_networkState(NETWORK_EMPTY)
267    , m_readyState(HAVE_NOTHING)
268    , m_readyStateMaximum(HAVE_NOTHING)
269    , m_volume(1.0f)
270    , m_volumeInitialized(false)
271    , m_lastSeekTime(0)
272    , m_previousProgressTime(std::numeric_limits<double>::max())
273    , m_clockTimeAtLastUpdateEvent(0)
274    , m_lastTimeUpdateEventMovieTime(std::numeric_limits<double>::max())
275    , m_loadState(WaitingForSource)
276#if PLATFORM(IOS)
277    , m_videoFullscreenGravity(MediaPlayer::VideoGravityResizeAspect)
278#endif
279    , m_preload(MediaPlayer::Auto)
280    , m_displayMode(Unknown)
281    , m_processingMediaPlayerCallback(0)
282#if ENABLE(MEDIA_SOURCE)
283    , m_droppedVideoFrames(0)
284#endif
285    , m_cachedTime(MediaPlayer::invalidTime())
286    , m_clockTimeAtLastCachedTimeUpdate(0)
287    , m_minimumClockTimeToUpdateCachedTime(0)
288    , m_fragmentStartTime(MediaPlayer::invalidTime())
289    , m_fragmentEndTime(MediaPlayer::invalidTime())
290    , m_pendingActionFlags(0)
291    , m_actionAfterScan(Nothing)
292    , m_scanType(Scan)
293    , m_scanDirection(Forward)
294    , m_playing(false)
295    , m_isWaitingUntilMediaCanStart(false)
296    , m_shouldDelayLoadEvent(false)
297    , m_haveFiredLoadedData(false)
298    , m_inActiveDocument(true)
299    , m_autoplaying(true)
300    , m_muted(false)
301    , m_explicitlyMuted(false)
302    , m_paused(true)
303    , m_seeking(false)
304    , m_sentStalledEvent(false)
305    , m_sentEndEvent(false)
306    , m_pausedInternal(false)
307    , m_sendProgressEvents(true)
308    , m_isFullscreen(false)
309    , m_closedCaptionsVisible(false)
310    , m_webkitLegacyClosedCaptionOverride(false)
311    , m_completelyLoaded(false)
312    , m_havePreparedToPlay(false)
313    , m_parsingInProgress(createdByParser)
314    , m_elementIsHidden(document.hidden())
315#if PLATFORM(IOS)
316    , m_requestingPlay(false)
317#endif
318#if ENABLE(MEDIA_CONTROLS_SCRIPT)
319    , m_mediaControlsDependOnPageScaleFactor(false)
320#endif
321#if ENABLE(VIDEO_TRACK)
322    , m_tracksAreReady(true)
323    , m_haveVisibleTextTrack(false)
324    , m_processingPreferenceChange(false)
325    , m_lastTextTrackUpdateTime(-1)
326    , m_captionDisplayMode(CaptionUserPreferences::Automatic)
327    , m_audioTracks(0)
328    , m_textTracks(0)
329    , m_videoTracks(0)
330    , m_ignoreTrackDisplayUpdate(0)
331#endif
332#if ENABLE(WEB_AUDIO)
333    , m_audioSourceNode(0)
334#endif
335    , m_mediaSession(HTMLMediaSession::create(*this))
336    , m_reportedExtraMemoryCost(0)
337#if ENABLE(MEDIA_STREAM)
338    , m_mediaStreamSrcObject(nullptr)
339#endif
340{
341    LOG(Media, "HTMLMediaElement::HTMLMediaElement");
342    setHasCustomStyleResolveCallbacks();
343
344    m_mediaSession->addBehaviorRestriction(HTMLMediaSession::RequireUserGestureForFullscreen);
345    m_mediaSession->addBehaviorRestriction(HTMLMediaSession::RequirePageConsentToLoadMedia);
346
347    // FIXME: We should clean up and look to better merge the iOS and non-iOS code below.
348    Settings* settings = document.settings();
349#if !PLATFORM(IOS)
350    if (settings && settings->mediaPlaybackRequiresUserGesture()) {
351        m_mediaSession->addBehaviorRestriction(HTMLMediaSession::RequireUserGestureForRateChange);
352        m_mediaSession->addBehaviorRestriction(HTMLMediaSession::RequireUserGestureForLoad);
353    }
354#else
355    m_sendProgressEvents = false;
356    if (!settings || settings->mediaPlaybackRequiresUserGesture()) {
357        // Allow autoplay in a MediaDocument that is not in an iframe.
358        if (document.ownerElement() || !document.isMediaDocument())
359            m_mediaSession->addBehaviorRestriction(HTMLMediaSession::RequireUserGestureForRateChange);
360#if ENABLE(IOS_AIRPLAY)
361        m_mediaSession->addBehaviorRestriction(HTMLMediaSession::RequireUserGestureToShowPlaybackTargetPicker);
362#endif
363    } else {
364        // Relax RequireUserGestureForFullscreen when mediaPlaybackRequiresUserGesture is not set:
365        m_mediaSession->removeBehaviorRestriction(HTMLMediaSession::RequireUserGestureForFullscreen);
366    }
367#endif // !PLATFORM(IOS)
368
369#if ENABLE(VIDEO_TRACK)
370    if (document.page())
371        m_captionDisplayMode = document.page()->group().captionPreferences()->captionDisplayMode();
372#endif
373
374    registerWithDocument(document);
375}
376
377HTMLMediaElement::~HTMLMediaElement()
378{
379    LOG(Media, "HTMLMediaElement::~HTMLMediaElement");
380
381    m_asyncEventQueue.close();
382
383    setShouldDelayLoadEvent(false);
384    unregisterWithDocument(document());
385
386#if ENABLE(VIDEO_TRACK)
387    if (m_audioTracks) {
388        m_audioTracks->clearElement();
389        for (unsigned i = 0; i < m_audioTracks->length(); ++i)
390            m_audioTracks->item(i)->clearClient();
391    }
392    if (m_textTracks)
393        m_textTracks->clearElement();
394    if (m_textTracks) {
395        for (unsigned i = 0; i < m_textTracks->length(); ++i)
396            m_textTracks->item(i)->clearClient();
397    }
398    if (m_videoTracks) {
399        m_videoTracks->clearElement();
400        for (unsigned i = 0; i < m_videoTracks->length(); ++i)
401            m_videoTracks->item(i)->clearClient();
402    }
403#endif
404
405#if ENABLE(IOS_AIRPLAY)
406    if (!hasEventListeners(eventNames().webkitplaybacktargetavailabilitychangedEvent))
407        m_mediaSession->setHasPlaybackTargetAvailabilityListeners(*this, false);
408#endif
409
410    if (m_mediaController) {
411        m_mediaController->removeMediaElement(this);
412        m_mediaController = 0;
413    }
414
415#if ENABLE(MEDIA_SOURCE)
416    closeMediaSource();
417#endif
418
419#if ENABLE(ENCRYPTED_MEDIA_V2)
420    setMediaKeys(0);
421#endif
422
423#if ENABLE(MEDIA_CONTROLS_SCRIPT)
424    if (m_isolatedWorld)
425        m_isolatedWorld->clearWrappers();
426#endif
427
428    m_completelyLoaded = true;
429    if (m_player)
430        m_player->clearMediaPlayerClient();
431}
432
433void HTMLMediaElement::registerWithDocument(Document& document)
434{
435    if (m_isWaitingUntilMediaCanStart)
436        document.addMediaCanStartListener(this);
437
438#if !PLATFORM(IOS)
439    document.registerForMediaVolumeCallbacks(this);
440    document.registerForPrivateBrowsingStateChangedCallbacks(this);
441#endif
442
443    document.registerForVisibilityStateChangedCallbacks(this);
444
445#if ENABLE(VIDEO_TRACK)
446    document.registerForCaptionPreferencesChangedCallbacks(this);
447#endif
448
449#if ENABLE(MEDIA_CONTROLS_SCRIPT)
450    if (m_mediaControlsDependOnPageScaleFactor)
451        document.registerForPageScaleFactorChangedCallbacks(this);
452#endif
453
454    addElementToDocumentMap(*this, document);
455}
456
457void HTMLMediaElement::unregisterWithDocument(Document& document)
458{
459    if (m_isWaitingUntilMediaCanStart)
460        document.removeMediaCanStartListener(this);
461
462#if !PLATFORM(IOS)
463    document.unregisterForMediaVolumeCallbacks(this);
464    document.unregisterForPrivateBrowsingStateChangedCallbacks(this);
465#endif
466
467    document.unregisterForVisibilityStateChangedCallbacks(this);
468
469#if ENABLE(VIDEO_TRACK)
470    document.unregisterForCaptionPreferencesChangedCallbacks(this);
471#endif
472
473#if ENABLE(MEDIA_CONTROLS_SCRIPT)
474    if (m_mediaControlsDependOnPageScaleFactor)
475        document.unregisterForPageScaleFactorChangedCallbacks(this);
476#endif
477
478    removeElementFromDocumentMap(*this, document);
479}
480
481void HTMLMediaElement::didMoveToNewDocument(Document* oldDocument)
482{
483    if (m_shouldDelayLoadEvent) {
484        if (oldDocument)
485            oldDocument->decrementLoadEventDelayCount();
486        document().incrementLoadEventDelayCount();
487    }
488
489    if (oldDocument) {
490        unregisterWithDocument(*oldDocument);
491    }
492
493    registerWithDocument(document());
494
495    HTMLElement::didMoveToNewDocument(oldDocument);
496}
497
498bool HTMLMediaElement::hasCustomFocusLogic() const
499{
500    return true;
501}
502
503bool HTMLMediaElement::supportsFocus() const
504{
505    if (document().isMediaDocument())
506        return false;
507
508    // If no controls specified, we should still be able to focus the element if it has tabIndex.
509    return controls() ||  HTMLElement::supportsFocus();
510}
511
512bool HTMLMediaElement::isMouseFocusable() const
513{
514    return false;
515}
516
517#if PLATFORM(IOS)
518bool HTMLMediaElement::parseMediaPlayerAttribute(const QualifiedName& name, const AtomicString& value)
519{
520    ASSERT(m_player);
521    if (name == data_youtube_idAttr) {
522        m_player->attributeChanged(name.toString(), value);
523        return true;
524    }
525    if (name == titleAttr) {
526        m_player->attributeChanged(name.toString(), value);
527        return true;
528    }
529
530    if (Settings* settings = document().settings()) {
531#if ENABLE(IOS_AIRPLAY)
532        if (name == webkitairplayAttr && settings->mediaPlaybackAllowsAirPlay()) {
533            m_player->attributeChanged(name.toString(), value);
534            return true;
535        }
536#endif
537        if (name == webkit_playsinlineAttr && settings->mediaPlaybackAllowsInline()) {
538            m_player->attributeChanged(name.toString(), ASCIILiteral(value.isNull() ? "false" : "true"));
539            return true;
540        }
541    }
542    return false;
543}
544#endif
545
546void HTMLMediaElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
547{
548    if (name == srcAttr) {
549#if PLATFORM(IOS)
550        // Note, unless the restriction on requiring user action has been removed,
551        // do not begin downloading data on iOS.
552        if (!value.isNull() && m_mediaSession->dataLoadingPermitted(*this)) {
553#else
554        // Trigger a reload, as long as the 'src' attribute is present.
555        if (!value.isNull()) {
556#endif
557            clearMediaPlayer(LoadMediaResource);
558            scheduleDelayedAction(LoadMediaResource);
559        }
560    } else if (name == controlsAttr)
561        configureMediaControls();
562    else if (name == loopAttr)
563        updateSleepDisabling();
564    else if (name == preloadAttr) {
565        if (equalIgnoringCase(value, "none"))
566            m_preload = MediaPlayer::None;
567        else if (equalIgnoringCase(value, "metadata"))
568            m_preload = MediaPlayer::MetaData;
569        else {
570            // The spec does not define an "invalid value default" but "auto" is suggested as the
571            // "missing value default", so use it for everything except "none" and "metadata"
572            m_preload = MediaPlayer::Auto;
573        }
574
575        // The attribute must be ignored if the autoplay attribute is present
576        if (!autoplay() && m_player)
577            m_player->setPreload(m_mediaSession->effectivePreloadForElement(*this));
578
579    } else if (name == mediagroupAttr)
580        setMediaGroup(value);
581    else if (name == onabortAttr)
582        setAttributeEventListener(eventNames().abortEvent, name, value);
583    else if (name == onbeforeloadAttr)
584        setAttributeEventListener(eventNames().beforeloadEvent, name, value);
585    else if (name == oncanplayAttr)
586        setAttributeEventListener(eventNames().canplayEvent, name, value);
587    else if (name == oncanplaythroughAttr)
588        setAttributeEventListener(eventNames().canplaythroughEvent, name, value);
589    else if (name == ondurationchangeAttr)
590        setAttributeEventListener(eventNames().durationchangeEvent, name, value);
591    else if (name == onemptiedAttr)
592        setAttributeEventListener(eventNames().emptiedEvent, name, value);
593    else if (name == onendedAttr)
594        setAttributeEventListener(eventNames().endedEvent, name, value);
595    else if (name == onerrorAttr)
596        setAttributeEventListener(eventNames().errorEvent, name, value);
597    else if (name == onloadeddataAttr)
598        setAttributeEventListener(eventNames().loadeddataEvent, name, value);
599    else if (name == onloadedmetadataAttr)
600        setAttributeEventListener(eventNames().loadedmetadataEvent, name, value);
601    else if (name == onloadstartAttr)
602        setAttributeEventListener(eventNames().loadstartEvent, name, value);
603    else if (name == onpauseAttr)
604        setAttributeEventListener(eventNames().pauseEvent, name, value);
605    else if (name == onplayAttr)
606        setAttributeEventListener(eventNames().playEvent, name, value);
607    else if (name == onplayingAttr)
608        setAttributeEventListener(eventNames().playingEvent, name, value);
609    else if (name == onprogressAttr)
610        setAttributeEventListener(eventNames().progressEvent, name, value);
611    else if (name == onratechangeAttr)
612        setAttributeEventListener(eventNames().ratechangeEvent, name, value);
613    else if (name == onseekedAttr)
614        setAttributeEventListener(eventNames().seekedEvent, name, value);
615    else if (name == onseekingAttr)
616        setAttributeEventListener(eventNames().seekingEvent, name, value);
617    else if (name == onstalledAttr)
618        setAttributeEventListener(eventNames().stalledEvent, name, value);
619    else if (name == onsuspendAttr)
620        setAttributeEventListener(eventNames().suspendEvent, name, value);
621    else if (name == ontimeupdateAttr)
622        setAttributeEventListener(eventNames().timeupdateEvent, name, value);
623    else if (name == onvolumechangeAttr)
624        setAttributeEventListener(eventNames().volumechangeEvent, name, value);
625    else if (name == onwaitingAttr)
626        setAttributeEventListener(eventNames().waitingEvent, name, value);
627    else if (name == onwebkitbeginfullscreenAttr)
628        setAttributeEventListener(eventNames().webkitbeginfullscreenEvent, name, value);
629    else if (name == onwebkitendfullscreenAttr)
630        setAttributeEventListener(eventNames().webkitendfullscreenEvent, name, value);
631#if ENABLE(IOS_AIRPLAY)
632    else if (name == onwebkitcurrentplaybacktargetiswirelesschangedAttr)
633        setAttributeEventListener(eventNames().webkitcurrentplaybacktargetiswirelesschangedEvent, name, value);
634    else if (name == onwebkitplaybacktargetavailabilitychangedAttr)
635        setAttributeEventListener(eventNames().webkitplaybacktargetavailabilitychangedEvent, name, value);
636#endif
637#if PLATFORM(IOS)
638    else if (m_player && parseMediaPlayerAttribute(name, value))
639        return;
640#endif
641    else
642        HTMLElement::parseAttribute(name, value);
643}
644
645void HTMLMediaElement::finishParsingChildren()
646{
647    HTMLElement::finishParsingChildren();
648    m_parsingInProgress = false;
649
650#if ENABLE(VIDEO_TRACK)
651    if (!RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled())
652        return;
653
654    if (descendantsOfType<HTMLTrackElement>(*this).first())
655        scheduleDelayedAction(ConfigureTextTracks);
656#endif
657}
658
659bool HTMLMediaElement::rendererIsNeeded(const RenderStyle& style)
660{
661    return controls() && HTMLElement::rendererIsNeeded(style);
662}
663
664RenderPtr<RenderElement> HTMLMediaElement::createElementRenderer(PassRef<RenderStyle> style)
665{
666    return createRenderer<RenderMedia>(*this, WTF::move(style));
667}
668
669bool HTMLMediaElement::childShouldCreateRenderer(const Node& child) const
670{
671#if ENABLE(MEDIA_CONTROLS_SCRIPT)
672    return hasShadowRootParent(child) && HTMLElement::childShouldCreateRenderer(child);
673#else
674    if (!hasMediaControls())
675        return false;
676    // <media> doesn't allow its content, including shadow subtree, to
677    // be rendered. So this should return false for most of the children.
678    // One exception is a shadow tree built for rendering controls which should be visible.
679    // So we let them go here by comparing its subtree root with one of the controls.
680    return &mediaControls()->treeScope() == &child.treeScope()
681        && hasShadowRootParent(child)
682        && HTMLElement::childShouldCreateRenderer(child);
683#endif
684}
685
686Node::InsertionNotificationRequest HTMLMediaElement::insertedInto(ContainerNode& insertionPoint)
687{
688    LOG(Media, "HTMLMediaElement::insertedInto");
689
690    HTMLElement::insertedInto(insertionPoint);
691    if (insertionPoint.inDocument()) {
692        m_inActiveDocument = true;
693
694#if PLATFORM(IOS)
695        if (m_networkState == NETWORK_EMPTY && !fastGetAttribute(srcAttr).isEmpty() && m_mediaSession->dataLoadingPermitted(*this))
696#else
697        if (m_networkState == NETWORK_EMPTY && !fastGetAttribute(srcAttr).isEmpty())
698#endif
699            scheduleDelayedAction(LoadMediaResource);
700    }
701
702    if (!m_explicitlyMuted) {
703        m_explicitlyMuted = true;
704        m_muted = fastHasAttribute(mutedAttr);
705    }
706
707    configureMediaControls();
708    return InsertionDone;
709}
710
711void HTMLMediaElement::removedFrom(ContainerNode& insertionPoint)
712{
713    LOG(Media, "HTMLMediaElement::removedFrom");
714
715    m_inActiveDocument = false;
716    if (insertionPoint.inDocument()) {
717        if (hasMediaControls())
718            mediaControls()->hide();
719        if (m_networkState > NETWORK_EMPTY)
720            pause();
721        if (m_isFullscreen)
722            exitFullscreen();
723
724        if (m_player) {
725            JSC::VM& vm = JSDOMWindowBase::commonVM();
726            JSC::JSLockHolder lock(vm);
727
728            size_t extraMemoryCost = m_player->extraMemoryCost();
729            size_t extraMemoryCostDelta = extraMemoryCost - m_reportedExtraMemoryCost;
730            m_reportedExtraMemoryCost = extraMemoryCost;
731
732            if (extraMemoryCostDelta > 0)
733                vm.heap.reportExtraMemoryCost(extraMemoryCostDelta);
734        }
735    }
736
737    HTMLElement::removedFrom(insertionPoint);
738}
739
740void HTMLMediaElement::willAttachRenderers()
741{
742    ASSERT(!renderer());
743}
744
745void HTMLMediaElement::didAttachRenderers()
746{
747    if (renderer())
748        renderer()->updateFromElement();
749}
750
751void HTMLMediaElement::didRecalcStyle(Style::Change)
752{
753    if (renderer())
754        renderer()->updateFromElement();
755}
756
757void HTMLMediaElement::scheduleDelayedAction(DelayedActionType actionType)
758{
759    LOG(Media, "HTMLMediaElement::scheduleLoad");
760
761    if ((actionType & LoadMediaResource) && !(m_pendingActionFlags & LoadMediaResource)) {
762        prepareForLoad();
763        setFlags(m_pendingActionFlags, LoadMediaResource);
764    }
765
766#if ENABLE(VIDEO_TRACK)
767    if (RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled() && (actionType & ConfigureTextTracks))
768        setFlags(m_pendingActionFlags, ConfigureTextTracks);
769#endif
770
771#if USE(PLATFORM_TEXT_TRACK_MENU)
772    if (actionType & TextTrackChangesNotification)
773        setFlags(m_pendingActionFlags, TextTrackChangesNotification);
774#endif
775
776    m_loadTimer.startOneShot(0);
777}
778
779void HTMLMediaElement::scheduleNextSourceChild()
780{
781    // Schedule the timer to try the next <source> element WITHOUT resetting state ala prepareForLoad.
782    setFlags(m_pendingActionFlags, LoadMediaResource);
783    m_loadTimer.startOneShot(0);
784}
785
786void HTMLMediaElement::scheduleEvent(const AtomicString& eventName)
787{
788#if LOG_MEDIA_EVENTS
789    LOG(Media, "HTMLMediaElement::scheduleEvent - scheduling '%s'", eventName.string().ascii().data());
790#endif
791    RefPtr<Event> event = Event::create(eventName, false, true);
792
793    // Don't set the event target, the event queue will set it in GenericEventQueue::timerFired and setting it here
794    // will trigger an ASSERT if this element has been marked for deletion.
795
796    m_asyncEventQueue.enqueueEvent(event.release());
797}
798
799void HTMLMediaElement::loadTimerFired(Timer<HTMLMediaElement>&)
800{
801    Ref<HTMLMediaElement> protect(*this); // loadNextSourceChild may fire 'beforeload', which can make arbitrary DOM mutations.
802
803#if ENABLE(VIDEO_TRACK)
804    if (RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled() && (m_pendingActionFlags & ConfigureTextTracks))
805        configureTextTracks();
806#endif
807
808    if (m_pendingActionFlags & LoadMediaResource) {
809        if (m_loadState == LoadingFromSourceElement)
810            loadNextSourceChild();
811        else
812            loadInternal();
813    }
814
815#if USE(PLATFORM_TEXT_TRACK_MENU)
816    if (RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled() && (m_pendingActionFlags & TextTrackChangesNotification))
817        notifyMediaPlayerOfTextTrackChanges();
818#endif
819
820    m_pendingActionFlags = 0;
821}
822
823PassRefPtr<MediaError> HTMLMediaElement::error() const
824{
825    return m_error;
826}
827
828void HTMLMediaElement::setSrc(const String& url)
829{
830    setAttribute(srcAttr, url);
831}
832
833#if ENABLE(MEDIA_STREAM)
834void HTMLMediaElement::setSrcObject(MediaStream* mediaStream)
835{
836    // FIXME: Setting the srcObject attribute may cause other changes to the media element's internal state:
837    // Specifically, if srcObject is specified, the UA must use it as the source of media, even if the src
838    // attribute is also set or children are present. If the value of srcObject is replaced or set to null
839    // the UA must re-run the media element load algorithm.
840    //
841    // https://bugs.webkit.org/show_bug.cgi?id=124896
842
843    m_mediaStreamSrcObject = mediaStream;
844}
845#endif
846
847HTMLMediaElement::NetworkState HTMLMediaElement::networkState() const
848{
849    return m_networkState;
850}
851
852String HTMLMediaElement::canPlayType(const String& mimeType, const String& keySystem, const URL& url) const
853{
854    MediaEngineSupportParameters parameters;
855    ContentType contentType(mimeType);
856    parameters.type = contentType.type().lower();
857    parameters.codecs = contentType.parameter(ASCIILiteral("codecs"));
858    parameters.url = url;
859#if ENABLE(ENCRYPTED_MEDIA)
860    parameters.keySystem = keySystem;
861#else
862    UNUSED_PARAM(keySystem);
863#endif
864    MediaPlayer::SupportsType support = MediaPlayer::supportsType(parameters, this);
865    String canPlay;
866
867    // 4.8.10.3
868    switch (support)
869    {
870        case MediaPlayer::IsNotSupported:
871            canPlay = emptyString();
872            break;
873        case MediaPlayer::MayBeSupported:
874            canPlay = ASCIILiteral("maybe");
875            break;
876        case MediaPlayer::IsSupported:
877            canPlay = ASCIILiteral("probably");
878            break;
879    }
880
881    LOG(Media, "HTMLMediaElement::canPlayType(%s, %s, %s) -> %s", mimeType.utf8().data(), keySystem.utf8().data(), url.stringCenterEllipsizedToLength().utf8().data(), canPlay.utf8().data());
882
883    return canPlay;
884}
885
886void HTMLMediaElement::load()
887{
888    Ref<HTMLMediaElement> protect(*this); // loadInternal may result in a 'beforeload' event, which can make arbitrary DOM mutations.
889
890    LOG(Media, "HTMLMediaElement::load()");
891
892    if (!m_mediaSession->dataLoadingPermitted(*this))
893        return;
894    if (ScriptController::processingUserGesture())
895        removeBehaviorsRestrictionsAfterFirstUserGesture();
896
897    prepareForLoad();
898    loadInternal();
899    prepareToPlay();
900}
901
902void HTMLMediaElement::prepareForLoad()
903{
904    LOG(Media, "HTMLMediaElement::prepareForLoad");
905
906    // Perform the cleanup required for the resource load algorithm to run.
907    stopPeriodicTimers();
908    m_loadTimer.stop();
909    // FIXME: Figure out appropriate place to reset LoadTextTrackResource if necessary and set m_pendingActionFlags to 0 here.
910    m_pendingActionFlags &= ~LoadMediaResource;
911    m_sentEndEvent = false;
912    m_sentStalledEvent = false;
913    m_haveFiredLoadedData = false;
914    m_completelyLoaded = false;
915    m_havePreparedToPlay = false;
916    m_displayMode = Unknown;
917    m_currentSrc = URL();
918
919    // 1 - Abort any already-running instance of the resource selection algorithm for this element.
920    m_loadState = WaitingForSource;
921    m_currentSourceNode = 0;
922
923    // 2 - If there are any tasks from the media element's media element event task source in
924    // one of the task queues, then remove those tasks.
925    cancelPendingEventsAndCallbacks();
926
927    // 3 - If the media element's networkState is set to NETWORK_LOADING or NETWORK_IDLE, queue
928    // a task to fire a simple event named abort at the media element.
929    if (m_networkState == NETWORK_LOADING || m_networkState == NETWORK_IDLE)
930        scheduleEvent(eventNames().abortEvent);
931
932#if ENABLE(MEDIA_SOURCE)
933    closeMediaSource();
934#endif
935
936    createMediaPlayer();
937
938    // 4 - If the media element's networkState is not set to NETWORK_EMPTY, then run these substeps
939    if (m_networkState != NETWORK_EMPTY) {
940        // 4.1 - Queue a task to fire a simple event named emptied at the media element.
941        scheduleEvent(eventNames().emptiedEvent);
942
943        // 4.2 - If a fetching process is in progress for the media element, the user agent should stop it.
944        m_networkState = NETWORK_EMPTY;
945
946        // 4.3 - Forget the media element's media-resource-specific tracks.
947        forgetResourceSpecificTracks();
948
949        // 4.4 - If readyState is not set to HAVE_NOTHING, then set it to that state.
950        m_readyState = HAVE_NOTHING;
951        m_readyStateMaximum = HAVE_NOTHING;
952
953        // 4.5 - If the paused attribute is false, then set it to true.
954        m_paused = true;
955
956        // 4.6 - If seeking is true, set it to false.
957        m_seeking = false;
958
959        // 4.7 - Set the current playback position to 0.
960        //       Set the official playback position to 0.
961        //       If this changed the official playback position, then queue a task to fire a simple event named timeupdate at the media element.
962        // FIXME: Add support for firing this event. e.g., scheduleEvent(eventNames().timeUpdateEvent);
963
964        // 4.8 - Set the initial playback position to 0.
965        // FIXME: Make this less subtle. The position only becomes 0 because of the createMediaPlayer() call
966        // above.
967        refreshCachedTime();
968
969        invalidateCachedTime();
970
971        // 4.9 - Set the timeline offset to Not-a-Number (NaN).
972        // 4.10 - Update the duration attribute to Not-a-Number (NaN).
973
974        updateMediaController();
975#if ENABLE(VIDEO_TRACK)
976        if (RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled())
977            updateActiveTextTrackCues(0);
978#endif
979    }
980
981    // 5 - Set the playbackRate attribute to the value of the defaultPlaybackRate attribute.
982    setPlaybackRate(defaultPlaybackRate());
983
984    // 6 - Set the error attribute to null and the autoplaying flag to true.
985    m_error = 0;
986    m_autoplaying = true;
987
988    // 7 - Invoke the media element's resource selection algorithm.
989
990    // 8 - Note: Playback of any previously playing media resource for this element stops.
991
992    // The resource selection algorithm
993    // 1 - Set the networkState to NETWORK_NO_SOURCE
994    m_networkState = NETWORK_NO_SOURCE;
995
996    // 2 - Asynchronously await a stable state.
997
998    m_playedTimeRanges = TimeRanges::create();
999
1000    // FIXME: Investigate whether these can be moved into m_networkState != NETWORK_EMPTY block above
1001    // so they are closer to the relevant spec steps.
1002    m_lastSeekTime = 0;
1003
1004    // The spec doesn't say to block the load event until we actually run the asynchronous section
1005    // algorithm, but do it now because we won't start that until after the timer fires and the
1006    // event may have already fired by then.
1007    MediaPlayer::Preload effectivePreload = m_mediaSession->effectivePreloadForElement(*this);
1008    if (effectivePreload != MediaPlayer::None)
1009        setShouldDelayLoadEvent(true);
1010
1011#if PLATFORM(IOS)
1012    Settings* settings = document().settings();
1013    if (effectivePreload != MediaPlayer::None && settings && settings->mediaDataLoadsAutomatically())
1014        prepareToPlay();
1015#endif
1016
1017    configureMediaControls();
1018}
1019
1020void HTMLMediaElement::loadInternal()
1021{
1022    // Some of the code paths below this function dispatch the BeforeLoad event. This ASSERT helps
1023    // us catch those bugs more quickly without needing all the branches to align to actually
1024    // trigger the event.
1025    ASSERT(!NoEventDispatchAssertion::isEventDispatchForbidden());
1026
1027    // If we can't start a load right away, start it later.
1028    if (!m_mediaSession->pageAllowsDataLoading(*this)) {
1029        setShouldDelayLoadEvent(false);
1030        if (m_isWaitingUntilMediaCanStart)
1031            return;
1032        document().addMediaCanStartListener(this);
1033        m_isWaitingUntilMediaCanStart = true;
1034        return;
1035    }
1036
1037    clearFlags(m_pendingActionFlags, LoadMediaResource);
1038
1039    // Once the page has allowed an element to load media, it is free to load at will. This allows a
1040    // playlist that starts in a foreground tab to continue automatically if the tab is subsequently
1041    // put into the background.
1042    m_mediaSession->removeBehaviorRestriction(HTMLMediaSession::RequirePageConsentToLoadMedia);
1043
1044#if ENABLE(VIDEO_TRACK)
1045    if (hasMediaControls())
1046        mediaControls()->changedClosedCaptionsVisibility();
1047
1048    // HTMLMediaElement::textTracksAreReady will need "... the text tracks whose mode was not in the
1049    // disabled state when the element's resource selection algorithm last started".
1050    if (RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled()) {
1051        m_textTracksWhenResourceSelectionBegan.clear();
1052        if (m_textTracks) {
1053            for (unsigned i = 0; i < m_textTracks->length(); ++i) {
1054                TextTrack* track = m_textTracks->item(i);
1055                if (track->mode() != TextTrack::disabledKeyword())
1056                    m_textTracksWhenResourceSelectionBegan.append(track);
1057            }
1058        }
1059    }
1060#endif
1061
1062    selectMediaResource();
1063}
1064
1065void HTMLMediaElement::selectMediaResource()
1066{
1067    LOG(Media, "HTMLMediaElement::selectMediaResource");
1068
1069    enum Mode { attribute, children };
1070
1071    // 3 - If the media element has a src attribute, then let mode be attribute.
1072    Mode mode = attribute;
1073    if (!fastHasAttribute(srcAttr)) {
1074        // Otherwise, if the media element does not have a src attribute but has a source
1075        // element child, then let mode be children and let candidate be the first such
1076        // source element child in tree order.
1077        if (auto firstSource = childrenOfType<HTMLSourceElement>(*this).first()) {
1078            mode = children;
1079            m_nextChildNodeToConsider = firstSource;
1080            m_currentSourceNode = 0;
1081        } else {
1082            // Otherwise the media element has neither a src attribute nor a source element
1083            // child: set the networkState to NETWORK_EMPTY, and abort these steps; the
1084            // synchronous section ends.
1085            m_loadState = WaitingForSource;
1086            setShouldDelayLoadEvent(false);
1087            m_networkState = NETWORK_EMPTY;
1088
1089            LOG(Media, "HTMLMediaElement::selectMediaResource, nothing to load");
1090            return;
1091        }
1092    }
1093
1094    // 4 - Set the media element's delaying-the-load-event flag to true (this delays the load event),
1095    // and set its networkState to NETWORK_LOADING.
1096    setShouldDelayLoadEvent(true);
1097    m_networkState = NETWORK_LOADING;
1098
1099    // 5 - Queue a task to fire a simple event named loadstart at the media element.
1100    scheduleEvent(eventNames().loadstartEvent);
1101
1102    // 6 - If mode is attribute, then run these substeps
1103    if (mode == attribute) {
1104        m_loadState = LoadingFromSrcAttr;
1105
1106        // If the src attribute's value is the empty string ... jump down to the failed step below
1107        URL mediaURL = getNonEmptyURLAttribute(srcAttr);
1108        if (mediaURL.isEmpty()) {
1109            mediaLoadingFailed(MediaPlayer::FormatError);
1110            LOG(Media, "HTMLMediaElement::selectMediaResource, empty 'src'");
1111            return;
1112        }
1113
1114        if (!isSafeToLoadURL(mediaURL, Complain) || !dispatchBeforeLoadEvent(mediaURL.string())) {
1115            mediaLoadingFailed(MediaPlayer::FormatError);
1116            return;
1117        }
1118
1119        // No type or key system information is available when the url comes
1120        // from the 'src' attribute so MediaPlayer
1121        // will have to pick a media engine based on the file extension.
1122        ContentType contentType((String()));
1123        loadResource(mediaURL, contentType, String());
1124        LOG(Media, "HTMLMediaElement::selectMediaResource, using 'src' attribute url");
1125        return;
1126    }
1127
1128    // Otherwise, the source elements will be used
1129    loadNextSourceChild();
1130}
1131
1132void HTMLMediaElement::loadNextSourceChild()
1133{
1134    ContentType contentType((String()));
1135    String keySystem;
1136    URL mediaURL = selectNextSourceChild(&contentType, &keySystem, Complain);
1137    if (!mediaURL.isValid()) {
1138        waitForSourceChange();
1139        return;
1140    }
1141
1142    // Recreate the media player for the new url
1143    createMediaPlayer();
1144
1145    m_loadState = LoadingFromSourceElement;
1146    loadResource(mediaURL, contentType, keySystem);
1147}
1148
1149static URL createFileURLForApplicationCacheResource(const String& path)
1150{
1151    // URL should have a function to create a url from a path, but it does not. This function
1152    // is not suitable because URL::setPath uses encodeWithURLEscapeSequences, which it notes
1153    // does not correctly escape '#' and '?'. This function works for our purposes because
1154    // app cache media files are always created with encodeForFileName(createCanonicalUUIDString()).
1155
1156#if USE(CF) && PLATFORM(WIN)
1157    RetainPtr<CFURLRef> cfURL = adoptCF(CFURLCreateWithFileSystemPath(0, path.createCFString().get(), kCFURLWindowsPathStyle, false));
1158    URL url(cfURL.get());
1159#else
1160    URL url;
1161
1162    url.setProtocol(ASCIILiteral("file"));
1163    url.setPath(path);
1164#endif
1165    return url;
1166}
1167
1168void HTMLMediaElement::loadResource(const URL& initialURL, ContentType& contentType, const String& keySystem)
1169{
1170    ASSERT(isSafeToLoadURL(initialURL, Complain));
1171
1172    LOG(Media, "HTMLMediaElement::loadResource(%s, %s, %s)", urlForLoggingMedia(initialURL).utf8().data(), contentType.raw().utf8().data(), keySystem.utf8().data());
1173
1174    Frame* frame = document().frame();
1175    if (!frame) {
1176        mediaLoadingFailed(MediaPlayer::FormatError);
1177        return;
1178    }
1179
1180    URL url = initialURL;
1181    if (!frame->loader().willLoadMediaElementURL(url)) {
1182        mediaLoadingFailed(MediaPlayer::FormatError);
1183        return;
1184    }
1185
1186    // The resource fetch algorithm
1187    m_networkState = NETWORK_LOADING;
1188
1189    // If the url should be loaded from the application cache, pass the url of the cached file
1190    // to the media engine.
1191    ApplicationCacheHost* cacheHost = frame->loader().documentLoader()->applicationCacheHost();
1192    ApplicationCacheResource* resource = 0;
1193    if (cacheHost && cacheHost->shouldLoadResourceFromApplicationCache(ResourceRequest(url), resource)) {
1194        // Resources that are not present in the manifest will always fail to load (at least, after the
1195        // cache has been primed the first time), making the testing of offline applications simpler.
1196        if (!resource || resource->path().isEmpty()) {
1197            mediaLoadingFailed(MediaPlayer::NetworkError);
1198            return;
1199        }
1200    }
1201
1202    // Set m_currentSrc *before* changing to the cache url, the fact that we are loading from the app
1203    // cache is an internal detail not exposed through the media element API.
1204    m_currentSrc = url;
1205
1206    if (resource) {
1207        url = createFileURLForApplicationCacheResource(resource->path());
1208        LOG(Media, "HTMLMediaElement::loadResource - will load from app cache -> %s", urlForLoggingMedia(url).utf8().data());
1209    }
1210
1211    LOG(Media, "HTMLMediaElement::loadResource - m_currentSrc -> %s", urlForLoggingMedia(m_currentSrc).utf8().data());
1212
1213#if ENABLE(MEDIA_STREAM)
1214    if (MediaStreamRegistry::registry().lookup(url.string()))
1215        m_mediaSession->removeBehaviorRestriction(HTMLMediaSession::RequireUserGestureForRateChange);
1216#endif
1217
1218    if (m_sendProgressEvents)
1219        startProgressEventTimer();
1220
1221    bool privateMode = document().page() && document().page()->usesEphemeralSession();
1222    m_player->setPrivateBrowsingMode(privateMode);
1223
1224    // Reset display mode to force a recalculation of what to show because we are resetting the player.
1225    setDisplayMode(Unknown);
1226
1227    if (!autoplay())
1228        m_player->setPreload(m_mediaSession->effectivePreloadForElement(*this));
1229    m_player->setPreservesPitch(m_webkitPreservesPitch);
1230
1231    if (!m_explicitlyMuted) {
1232        m_explicitlyMuted = true;
1233        m_muted = fastHasAttribute(mutedAttr);
1234    }
1235
1236    updateVolume();
1237
1238#if ENABLE(MEDIA_SOURCE)
1239    ASSERT(!m_mediaSource);
1240
1241    if (url.protocolIs(mediaSourceBlobProtocol))
1242        m_mediaSource = MediaSource::lookup(url.string());
1243
1244    if (m_mediaSource) {
1245        if (m_mediaSource->attachToElement(this))
1246            m_player->load(url, contentType, m_mediaSource.get());
1247        else {
1248            // Forget our reference to the MediaSource, so we leave it alone
1249            // while processing remainder of load failure.
1250            m_mediaSource = 0;
1251            mediaLoadingFailed(MediaPlayer::FormatError);
1252        }
1253    } else
1254#endif
1255    if (!m_player->load(url, contentType, keySystem))
1256        mediaLoadingFailed(MediaPlayer::FormatError);
1257
1258    m_mediaSession->applyMediaPlayerRestrictions(*this);
1259
1260    // If there is no poster to display, allow the media engine to render video frames as soon as
1261    // they are available.
1262    updateDisplayState();
1263
1264    if (renderer())
1265        renderer()->updateFromElement();
1266}
1267
1268#if ENABLE(VIDEO_TRACK)
1269static bool trackIndexCompare(TextTrack* a,
1270                              TextTrack* b)
1271{
1272    return a->trackIndex() - b->trackIndex() < 0;
1273}
1274
1275static bool eventTimeCueCompare(const std::pair<double, TextTrackCue*>& a,
1276                                const std::pair<double, TextTrackCue*>& b)
1277{
1278    // 12 - Sort the tasks in events in ascending time order (tasks with earlier
1279    // times first).
1280    if (a.first != b.first)
1281        return a.first - b.first < 0;
1282
1283    // If the cues belong to different text tracks, it doesn't make sense to
1284    // compare the two tracks by the relative cue order, so return the relative
1285    // track order.
1286    if (a.second->track() != b.second->track())
1287        return trackIndexCompare(a.second->track(), b.second->track());
1288
1289    // 12 - Further sort tasks in events that have the same time by the
1290    // relative text track cue order of the text track cues associated
1291    // with these tasks.
1292    return a.second->cueIndex() - b.second->cueIndex() < 0;
1293}
1294
1295static bool compareCueInterval(const CueInterval& one, const CueInterval& two)
1296{
1297    return one.data()->isOrderedBefore(two.data());
1298};
1299
1300
1301void HTMLMediaElement::updateActiveTextTrackCues(double movieTime)
1302{
1303    // 4.8.10.8 Playing the media resource
1304
1305    //  If the current playback position changes while the steps are running,
1306    //  then the user agent must wait for the steps to complete, and then must
1307    //  immediately rerun the steps.
1308    if (ignoreTrackDisplayUpdateRequests())
1309        return;
1310
1311    LOG(Media, "HTMLMediaElement::updateActiveTextTracks");
1312
1313    // 1 - Let current cues be a list of cues, initialized to contain all the
1314    // cues of all the hidden, showing, or showing by default text tracks of the
1315    // media element (not the disabled ones) whose start times are less than or
1316    // equal to the current playback position and whose end times are greater
1317    // than the current playback position.
1318    CueList currentCues;
1319
1320    // The user agent must synchronously unset [the text track cue active] flag
1321    // whenever ... the media element's readyState is changed back to HAVE_NOTHING.
1322    if (m_readyState != HAVE_NOTHING && m_player) {
1323        currentCues = m_cueTree.allOverlaps(m_cueTree.createInterval(movieTime, movieTime));
1324        if (currentCues.size() > 1)
1325            std::sort(currentCues.begin(), currentCues.end(), &compareCueInterval);
1326    }
1327
1328    CueList previousCues;
1329    CueList missedCues;
1330
1331    // 2 - Let other cues be a list of cues, initialized to contain all the cues
1332    // of hidden, showing, and showing by default text tracks of the media
1333    // element that are not present in current cues.
1334    previousCues = m_currentlyActiveCues;
1335
1336    // 3 - Let last time be the current playback position at the time this
1337    // algorithm was last run for this media element, if this is not the first
1338    // time it has run.
1339    double lastTime = m_lastTextTrackUpdateTime;
1340
1341    // 4 - If the current playback position has, since the last time this
1342    // algorithm was run, only changed through its usual monotonic increase
1343    // during normal playback, then let missed cues be the list of cues in other
1344    // cues whose start times are greater than or equal to last time and whose
1345    // end times are less than or equal to the current playback position.
1346    // Otherwise, let missed cues be an empty list.
1347    if (lastTime >= 0 && m_lastSeekTime < movieTime) {
1348        CueList potentiallySkippedCues =
1349            m_cueTree.allOverlaps(m_cueTree.createInterval(lastTime, movieTime));
1350
1351        for (size_t i = 0; i < potentiallySkippedCues.size(); ++i) {
1352            double cueStartTime = potentiallySkippedCues[i].low();
1353            double cueEndTime = potentiallySkippedCues[i].high();
1354
1355            // Consider cues that may have been missed since the last seek time.
1356            if (cueStartTime > std::max(m_lastSeekTime, lastTime) && cueEndTime < movieTime)
1357                missedCues.append(potentiallySkippedCues[i]);
1358        }
1359    }
1360
1361    m_lastTextTrackUpdateTime = movieTime;
1362
1363    // 5 - If the time was reached through the usual monotonic increase of the
1364    // current playback position during normal playback, and if the user agent
1365    // has not fired a timeupdate event at the element in the past 15 to 250ms
1366    // and is not still running event handlers for such an event, then the user
1367    // agent must queue a task to fire a simple event named timeupdate at the
1368    // element. (In the other cases, such as explicit seeks, relevant events get
1369    // fired as part of the overall process of changing the current playback
1370    // position.)
1371    if (!m_paused && m_lastSeekTime <= lastTime)
1372        scheduleTimeupdateEvent(false);
1373
1374    // Explicitly cache vector sizes, as their content is constant from here.
1375    size_t currentCuesSize = currentCues.size();
1376    size_t missedCuesSize = missedCues.size();
1377    size_t previousCuesSize = previousCues.size();
1378
1379    // 6 - If all of the cues in current cues have their text track cue active
1380    // flag set, none of the cues in other cues have their text track cue active
1381    // flag set, and missed cues is empty, then abort these steps.
1382    bool activeSetChanged = missedCuesSize;
1383
1384    for (size_t i = 0; !activeSetChanged && i < previousCuesSize; ++i)
1385        if (!currentCues.contains(previousCues[i]) && previousCues[i].data()->isActive())
1386            activeSetChanged = true;
1387
1388    for (size_t i = 0; i < currentCuesSize; ++i) {
1389        TextTrackCue* cue = currentCues[i].data();
1390
1391        if (cue->isRenderable())
1392            toVTTCue(cue)->updateDisplayTree(movieTime);
1393
1394        if (!cue->isActive())
1395            activeSetChanged = true;
1396    }
1397
1398    if (!activeSetChanged)
1399        return;
1400
1401    // 7 - If the time was reached through the usual monotonic increase of the
1402    // current playback position during normal playback, and there are cues in
1403    // other cues that have their text track cue pause-on-exi flag set and that
1404    // either have their text track cue active flag set or are also in missed
1405    // cues, then immediately pause the media element.
1406    for (size_t i = 0; !m_paused && i < previousCuesSize; ++i) {
1407        if (previousCues[i].data()->pauseOnExit()
1408            && previousCues[i].data()->isActive()
1409            && !currentCues.contains(previousCues[i]))
1410            pause();
1411    }
1412
1413    for (size_t i = 0; !m_paused && i < missedCuesSize; ++i) {
1414        if (missedCues[i].data()->pauseOnExit())
1415            pause();
1416    }
1417
1418    // 8 - Let events be a list of tasks, initially empty. Each task in this
1419    // list will be associated with a text track, a text track cue, and a time,
1420    // which are used to sort the list before the tasks are queued.
1421    Vector<std::pair<double, TextTrackCue*>> eventTasks;
1422
1423    // 8 - Let affected tracks be a list of text tracks, initially empty.
1424    Vector<TextTrack*> affectedTracks;
1425
1426    for (size_t i = 0; i < missedCuesSize; ++i) {
1427        // 9 - For each text track cue in missed cues, prepare an event named enter
1428        // for the TextTrackCue object with the text track cue start time.
1429        eventTasks.append(std::make_pair(missedCues[i].data()->startTime(),
1430                                         missedCues[i].data()));
1431
1432        // 10 - For each text track [...] in missed cues, prepare an event
1433        // named exit for the TextTrackCue object with the  with the later of
1434        // the text track cue end time and the text track cue start time.
1435
1436        // Note: An explicit task is added only if the cue is NOT a zero or
1437        // negative length cue. Otherwise, the need for an exit event is
1438        // checked when these tasks are actually queued below. This doesn't
1439        // affect sorting events before dispatch either, because the exit
1440        // event has the same time as the enter event.
1441        if (missedCues[i].data()->startTime() < missedCues[i].data()->endTime())
1442            eventTasks.append(std::make_pair(missedCues[i].data()->endTime(),
1443                                             missedCues[i].data()));
1444    }
1445
1446    for (size_t i = 0; i < previousCuesSize; ++i) {
1447        // 10 - For each text track cue in other cues that has its text
1448        // track cue active flag set prepare an event named exit for the
1449        // TextTrackCue object with the text track cue end time.
1450        if (!currentCues.contains(previousCues[i]))
1451            eventTasks.append(std::make_pair(previousCues[i].data()->endTime(),
1452                                             previousCues[i].data()));
1453    }
1454
1455    for (size_t i = 0; i < currentCuesSize; ++i) {
1456        // 11 - For each text track cue in current cues that does not have its
1457        // text track cue active flag set, prepare an event named enter for the
1458        // TextTrackCue object with the text track cue start time.
1459        if (!previousCues.contains(currentCues[i]))
1460            eventTasks.append(std::make_pair(currentCues[i].data()->startTime(),
1461                                             currentCues[i].data()));
1462    }
1463
1464    // 12 - Sort the tasks in events in ascending time order (tasks with earlier
1465    // times first).
1466    std::sort(eventTasks.begin(), eventTasks.end(), eventTimeCueCompare);
1467
1468    for (size_t i = 0; i < eventTasks.size(); ++i) {
1469        if (!affectedTracks.contains(eventTasks[i].second->track()))
1470            affectedTracks.append(eventTasks[i].second->track());
1471
1472        // 13 - Queue each task in events, in list order.
1473        RefPtr<Event> event;
1474
1475        // Each event in eventTasks may be either an enterEvent or an exitEvent,
1476        // depending on the time that is associated with the event. This
1477        // correctly identifies the type of the event, if the startTime is
1478        // less than the endTime in the cue.
1479        if (eventTasks[i].second->startTime() >= eventTasks[i].second->endTime()) {
1480            event = Event::create(eventNames().enterEvent, false, false);
1481            event->setTarget(eventTasks[i].second);
1482            m_asyncEventQueue.enqueueEvent(event.release());
1483
1484            event = Event::create(eventNames().exitEvent, false, false);
1485            event->setTarget(eventTasks[i].second);
1486            m_asyncEventQueue.enqueueEvent(event.release());
1487        } else {
1488            if (eventTasks[i].first == eventTasks[i].second->startTime())
1489                event = Event::create(eventNames().enterEvent, false, false);
1490            else
1491                event = Event::create(eventNames().exitEvent, false, false);
1492
1493            event->setTarget(eventTasks[i].second);
1494            m_asyncEventQueue.enqueueEvent(event.release());
1495        }
1496    }
1497
1498    // 14 - Sort affected tracks in the same order as the text tracks appear in
1499    // the media element's list of text tracks, and remove duplicates.
1500    std::sort(affectedTracks.begin(), affectedTracks.end(), trackIndexCompare);
1501
1502    // 15 - For each text track in affected tracks, in the list order, queue a
1503    // task to fire a simple event named cuechange at the TextTrack object, and, ...
1504    for (size_t i = 0; i < affectedTracks.size(); ++i) {
1505        RefPtr<Event> event = Event::create(eventNames().cuechangeEvent, false, false);
1506        event->setTarget(affectedTracks[i]);
1507
1508        m_asyncEventQueue.enqueueEvent(event.release());
1509
1510        // ... if the text track has a corresponding track element, to then fire a
1511        // simple event named cuechange at the track element as well.
1512        if (affectedTracks[i]->trackType() == TextTrack::TrackElement) {
1513            RefPtr<Event> event = Event::create(eventNames().cuechangeEvent, false, false);
1514            HTMLTrackElement* trackElement = static_cast<LoadableTextTrack*>(affectedTracks[i])->trackElement();
1515            ASSERT(trackElement);
1516            event->setTarget(trackElement);
1517
1518            m_asyncEventQueue.enqueueEvent(event.release());
1519        }
1520    }
1521
1522    // 16 - Set the text track cue active flag of all the cues in the current
1523    // cues, and unset the text track cue active flag of all the cues in the
1524    // other cues.
1525    for (size_t i = 0; i < currentCuesSize; ++i)
1526        currentCues[i].data()->setIsActive(true);
1527
1528    for (size_t i = 0; i < previousCuesSize; ++i)
1529        if (!currentCues.contains(previousCues[i]))
1530            previousCues[i].data()->setIsActive(false);
1531
1532    // Update the current active cues.
1533    m_currentlyActiveCues = currentCues;
1534
1535    if (activeSetChanged)
1536        updateTextTrackDisplay();
1537}
1538
1539bool HTMLMediaElement::textTracksAreReady() const
1540{
1541    // 4.8.10.12.1 Text track model
1542    // ...
1543    // The text tracks of a media element are ready if all the text tracks whose mode was not
1544    // in the disabled state when the element's resource selection algorithm last started now
1545    // have a text track readiness state of loaded or failed to load.
1546    for (unsigned i = 0; i < m_textTracksWhenResourceSelectionBegan.size(); ++i) {
1547        if (m_textTracksWhenResourceSelectionBegan[i]->readinessState() == TextTrack::Loading
1548            || m_textTracksWhenResourceSelectionBegan[i]->readinessState() == TextTrack::NotLoaded)
1549            return false;
1550    }
1551
1552    return true;
1553}
1554
1555void HTMLMediaElement::textTrackReadyStateChanged(TextTrack* track)
1556{
1557    if (m_player && m_textTracksWhenResourceSelectionBegan.contains(track)) {
1558        if (track->readinessState() != TextTrack::Loading)
1559            setReadyState(m_player->readyState());
1560    } else {
1561        // The track readiness state might have changed as a result of the user
1562        // clicking the captions button. In this case, a check whether all the
1563        // resources have failed loading should be done in order to hide the CC button.
1564        if (hasMediaControls() && track->readinessState() == TextTrack::FailedToLoad)
1565            mediaControls()->refreshClosedCaptionsButtonVisibility();
1566    }
1567}
1568
1569void HTMLMediaElement::audioTrackEnabledChanged(AudioTrack* track)
1570{
1571    if (!RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled())
1572        return;
1573    if (m_audioTracks && m_audioTracks->contains(track))
1574        m_audioTracks->scheduleChangeEvent();
1575}
1576
1577void HTMLMediaElement::textTrackModeChanged(TextTrack* track)
1578{
1579    bool trackIsLoaded = true;
1580    if (track->trackType() == TextTrack::TrackElement) {
1581        trackIsLoaded = false;
1582        for (auto& trackElement : childrenOfType<HTMLTrackElement>(*this)) {
1583            if (trackElement.track() == track) {
1584                if (trackElement.readyState() == HTMLTrackElement::LOADING || trackElement.readyState() == HTMLTrackElement::LOADED)
1585                    trackIsLoaded = true;
1586                break;
1587            }
1588        }
1589    }
1590
1591    // If this is the first added track, create the list of text tracks.
1592    if (!m_textTracks)
1593        m_textTracks = TextTrackList::create(this, ActiveDOMObject::scriptExecutionContext());
1594
1595    // Mark this track as "configured" so configureTextTracks won't change the mode again.
1596    track->setHasBeenConfigured(true);
1597
1598    if (track->mode() != TextTrack::disabledKeyword() && trackIsLoaded)
1599        textTrackAddCues(track, track->cues());
1600
1601#if USE(PLATFORM_TEXT_TRACK_MENU)
1602    if (platformTextTrackMenu())
1603        platformTextTrackMenu()->trackWasSelected(track->platformTextTrack());
1604#endif
1605
1606    configureTextTrackDisplay(AssumeTextTrackVisibilityChanged);
1607
1608    if (m_textTracks && m_textTracks->contains(track))
1609        m_textTracks->scheduleChangeEvent();
1610
1611#if ENABLE(AVF_CAPTIONS)
1612    if (track->trackType() == TextTrack::TrackElement && m_player)
1613        m_player->notifyTrackModeChanged();
1614#endif
1615}
1616
1617void HTMLMediaElement::videoTrackSelectedChanged(VideoTrack* track)
1618{
1619    if (!RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled())
1620        return;
1621    if (m_videoTracks && m_videoTracks->contains(track))
1622        m_videoTracks->scheduleChangeEvent();
1623}
1624
1625void HTMLMediaElement::textTrackKindChanged(TextTrack* track)
1626{
1627    if (track->kind() != TextTrack::captionsKeyword() && track->kind() != TextTrack::subtitlesKeyword() && track->mode() == TextTrack::showingKeyword())
1628        track->setMode(TextTrack::hiddenKeyword());
1629}
1630
1631void HTMLMediaElement::beginIgnoringTrackDisplayUpdateRequests()
1632{
1633    ++m_ignoreTrackDisplayUpdate;
1634}
1635
1636void HTMLMediaElement::endIgnoringTrackDisplayUpdateRequests()
1637{
1638    ASSERT(m_ignoreTrackDisplayUpdate);
1639    --m_ignoreTrackDisplayUpdate;
1640    if (!m_ignoreTrackDisplayUpdate && m_inActiveDocument)
1641        updateActiveTextTrackCues(currentTime());
1642}
1643
1644void HTMLMediaElement::textTrackAddCues(TextTrack* track, const TextTrackCueList* cues)
1645{
1646    if (track->mode() == TextTrack::disabledKeyword())
1647        return;
1648
1649    TrackDisplayUpdateScope scope(this);
1650    for (size_t i = 0; i < cues->length(); ++i)
1651        textTrackAddCue(track, cues->item(i));
1652}
1653
1654void HTMLMediaElement::textTrackRemoveCues(TextTrack*, const TextTrackCueList* cues)
1655{
1656    TrackDisplayUpdateScope scope(this);
1657    for (size_t i = 0; i < cues->length(); ++i)
1658        textTrackRemoveCue(cues->item(i)->track(), cues->item(i));
1659}
1660
1661void HTMLMediaElement::textTrackAddCue(TextTrack* track, PassRefPtr<TextTrackCue> cue)
1662{
1663    if (track->mode() == TextTrack::disabledKeyword())
1664        return;
1665
1666    // Negative duration cues need be treated in the interval tree as
1667    // zero-length cues.
1668    double endTime = std::max(cue->startTime(), cue->endTime());
1669
1670    CueInterval interval = m_cueTree.createInterval(cue->startTime(), endTime, cue.get());
1671    if (!m_cueTree.contains(interval))
1672        m_cueTree.add(interval);
1673    updateActiveTextTrackCues(currentTime());
1674}
1675
1676void HTMLMediaElement::textTrackRemoveCue(TextTrack*, PassRefPtr<TextTrackCue> cue)
1677{
1678    // Negative duration cues need to be treated in the interval tree as
1679    // zero-length cues.
1680    double endTime = std::max(cue->startTime(), cue->endTime());
1681
1682    CueInterval interval = m_cueTree.createInterval(cue->startTime(), endTime, cue.get());
1683    m_cueTree.remove(interval);
1684
1685#if ENABLE(WEBVTT_REGIONS)
1686    // Since the cue will be removed from the media element and likely the
1687    // TextTrack might also be destructed, notifying the region of the cue
1688    // removal shouldn't be done.
1689    if (cue->isRenderable())
1690        toVTTCue(cue.get())->notifyRegionWhenRemovingDisplayTree(false);
1691#endif
1692
1693    size_t index = m_currentlyActiveCues.find(interval);
1694    if (index != notFound) {
1695        cue->setIsActive(false);
1696        m_currentlyActiveCues.remove(index);
1697    }
1698
1699    if (cue->isRenderable())
1700        toVTTCue(cue.get())->removeDisplayTree();
1701    updateActiveTextTrackCues(currentTime());
1702
1703#if ENABLE(WEBVTT_REGIONS)
1704    if (cue->isRenderable())
1705        toVTTCue(cue.get())->notifyRegionWhenRemovingDisplayTree(true);
1706#endif
1707}
1708
1709#endif
1710
1711bool HTMLMediaElement::isSafeToLoadURL(const URL& url, InvalidURLAction actionIfInvalid)
1712{
1713    if (!url.isValid()) {
1714        LOG(Media, "HTMLMediaElement::isSafeToLoadURL(%s) -> FALSE because url is invalid", urlForLoggingMedia(url).utf8().data());
1715        return false;
1716    }
1717
1718    Frame* frame = document().frame();
1719    if (!frame || !document().securityOrigin()->canDisplay(url)) {
1720        if (actionIfInvalid == Complain)
1721            FrameLoader::reportLocalLoadFailed(frame, url.stringCenterEllipsizedToLength());
1722        LOG(Media, "HTMLMediaElement::isSafeToLoadURL(%s) -> FALSE rejected by SecurityOrigin", urlForLoggingMedia(url).utf8().data());
1723        return false;
1724    }
1725
1726    if (!document().contentSecurityPolicy()->allowMediaFromSource(url)) {
1727        LOG(Media, "HTMLMediaElement::isSafeToLoadURL(%s) -> rejected by Content Security Policy", urlForLoggingMedia(url).utf8().data());
1728        return false;
1729    }
1730
1731    return true;
1732}
1733
1734void HTMLMediaElement::startProgressEventTimer()
1735{
1736    if (m_progressEventTimer.isActive())
1737        return;
1738
1739    m_previousProgressTime = monotonicallyIncreasingTime();
1740    // 350ms is not magic, it is in the spec!
1741    m_progressEventTimer.startRepeating(0.350);
1742}
1743
1744void HTMLMediaElement::waitForSourceChange()
1745{
1746    LOG(Media, "HTMLMediaElement::waitForSourceChange");
1747
1748    stopPeriodicTimers();
1749    m_loadState = WaitingForSource;
1750
1751    // 6.17 - Waiting: Set the element's networkState attribute to the NETWORK_NO_SOURCE value
1752    m_networkState = NETWORK_NO_SOURCE;
1753
1754    // 6.18 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
1755    setShouldDelayLoadEvent(false);
1756
1757    updateDisplayState();
1758
1759    if (renderer())
1760        renderer()->updateFromElement();
1761}
1762
1763void HTMLMediaElement::noneSupported()
1764{
1765    LOG(Media, "HTMLMediaElement::noneSupported");
1766
1767    stopPeriodicTimers();
1768    m_loadState = WaitingForSource;
1769    m_currentSourceNode = 0;
1770
1771    // 4.8.10.5
1772    // 6 - Reaching this step indicates that the media resource failed to load or that the given
1773    // URL could not be resolved. In one atomic operation, run the following steps:
1774
1775    // 6.1 - Set the error attribute to a new MediaError object whose code attribute is set to
1776    // MEDIA_ERR_SRC_NOT_SUPPORTED.
1777    m_error = MediaError::create(MediaError::MEDIA_ERR_SRC_NOT_SUPPORTED);
1778
1779    // 6.2 - Forget the media element's media-resource-specific text tracks.
1780    forgetResourceSpecificTracks();
1781
1782    // 6.3 - Set the element's networkState attribute to the NETWORK_NO_SOURCE value.
1783    m_networkState = NETWORK_NO_SOURCE;
1784
1785    // 7 - Queue a task to fire a simple event named error at the media element.
1786    scheduleEvent(eventNames().errorEvent);
1787
1788#if ENABLE(MEDIA_SOURCE)
1789    closeMediaSource();
1790#endif
1791
1792    // 8 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
1793    setShouldDelayLoadEvent(false);
1794
1795    // 9 - Abort these steps. Until the load() method is invoked or the src attribute is changed,
1796    // the element won't attempt to load another resource.
1797
1798    updateDisplayState();
1799
1800    if (renderer())
1801        renderer()->updateFromElement();
1802}
1803
1804void HTMLMediaElement::mediaLoadingFailedFatally(MediaPlayer::NetworkState error)
1805{
1806    LOG(Media, "HTMLMediaElement::mediaLoadingFailedFatally(%d)", static_cast<int>(error));
1807
1808    // 1 - The user agent should cancel the fetching process.
1809    stopPeriodicTimers();
1810    m_loadState = WaitingForSource;
1811
1812    // 2 - Set the error attribute to a new MediaError object whose code attribute is
1813    // set to MEDIA_ERR_NETWORK/MEDIA_ERR_DECODE.
1814    if (error == MediaPlayer::NetworkError)
1815        m_error = MediaError::create(MediaError::MEDIA_ERR_NETWORK);
1816    else if (error == MediaPlayer::DecodeError)
1817        m_error = MediaError::create(MediaError::MEDIA_ERR_DECODE);
1818    else
1819        ASSERT_NOT_REACHED();
1820
1821    // 3 - Queue a task to fire a simple event named error at the media element.
1822    scheduleEvent(eventNames().errorEvent);
1823
1824#if ENABLE(MEDIA_SOURCE)
1825    closeMediaSource();
1826#endif
1827
1828    // 4 - Set the element's networkState attribute to the NETWORK_EMPTY value and queue a
1829    // task to fire a simple event called emptied at the element.
1830    m_networkState = NETWORK_EMPTY;
1831    scheduleEvent(eventNames().emptiedEvent);
1832
1833    // 5 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
1834    setShouldDelayLoadEvent(false);
1835
1836    // 6 - Abort the overall resource selection algorithm.
1837    m_currentSourceNode = 0;
1838
1839#if PLATFORM(COCOA)
1840    if (document().isMediaDocument())
1841        toMediaDocument(document()).mediaElementSawUnsupportedTracks();
1842#endif
1843}
1844
1845void HTMLMediaElement::cancelPendingEventsAndCallbacks()
1846{
1847    LOG(Media, "HTMLMediaElement::cancelPendingEventsAndCallbacks");
1848    m_asyncEventQueue.cancelAllEvents();
1849
1850    for (auto& source : childrenOfType<HTMLSourceElement>(*this))
1851        source.cancelPendingErrorEvent();
1852}
1853
1854Document* HTMLMediaElement::mediaPlayerOwningDocument()
1855{
1856    return &document();
1857}
1858
1859void HTMLMediaElement::mediaPlayerNetworkStateChanged(MediaPlayer*)
1860{
1861    beginProcessingMediaPlayerCallback();
1862    setNetworkState(m_player->networkState());
1863    endProcessingMediaPlayerCallback();
1864}
1865
1866static void logMediaLoadRequest(Page* page, const String& mediaEngine, const String& errorMessage, bool succeeded)
1867{
1868    if (!page || !page->settings().diagnosticLoggingEnabled())
1869        return;
1870
1871    ChromeClient& chromeClient = page->chrome().client();
1872
1873    if (!succeeded) {
1874        chromeClient.logDiagnosticMessage(DiagnosticLoggingKeys::mediaLoadingFailedKey(), errorMessage, DiagnosticLoggingKeys::failKey());
1875        return;
1876    }
1877
1878    chromeClient.logDiagnosticMessage(DiagnosticLoggingKeys::mediaLoadedKey(), mediaEngine, DiagnosticLoggingKeys::noopKey());
1879
1880    if (!page->hasSeenAnyMediaEngine())
1881        chromeClient.logDiagnosticMessage(DiagnosticLoggingKeys::pageContainsAtLeastOneMediaEngineKey(), emptyString(), DiagnosticLoggingKeys::noopKey());
1882
1883    if (!page->hasSeenMediaEngine(mediaEngine))
1884        chromeClient.logDiagnosticMessage(DiagnosticLoggingKeys::pageContainsMediaEngineKey(), mediaEngine, DiagnosticLoggingKeys::noopKey());
1885
1886    page->sawMediaEngine(mediaEngine);
1887}
1888
1889static String stringForNetworkState(MediaPlayer::NetworkState state)
1890{
1891    switch (state) {
1892    case MediaPlayer::Empty: return ASCIILiteral("Empty");
1893    case MediaPlayer::Idle: return ASCIILiteral("Idle");
1894    case MediaPlayer::Loading: return ASCIILiteral("Loading");
1895    case MediaPlayer::Loaded: return ASCIILiteral("Loaded");
1896    case MediaPlayer::FormatError: return ASCIILiteral("FormatError");
1897    case MediaPlayer::NetworkError: return ASCIILiteral("NetworkError");
1898    case MediaPlayer::DecodeError: return ASCIILiteral("DecodeError");
1899    default: return emptyString();
1900    }
1901}
1902
1903void HTMLMediaElement::mediaLoadingFailed(MediaPlayer::NetworkState error)
1904{
1905    stopPeriodicTimers();
1906
1907    // If we failed while trying to load a <source> element, the movie was never parsed, and there are more
1908    // <source> children, schedule the next one
1909    if (m_readyState < HAVE_METADATA && m_loadState == LoadingFromSourceElement) {
1910
1911        // resource selection algorithm
1912        // Step 9.Otherwise.9 - Failed with elements: Queue a task, using the DOM manipulation task source, to fire a simple event named error at the candidate element.
1913        if (m_currentSourceNode)
1914            m_currentSourceNode->scheduleErrorEvent();
1915        else
1916            LOG(Media, "HTMLMediaElement::setNetworkState - error event not sent, <source> was removed");
1917
1918        // 9.Otherwise.10 - Asynchronously await a stable state. The synchronous section consists of all the remaining steps of this algorithm until the algorithm says the synchronous section has ended.
1919
1920        // 9.Otherwise.11 - Forget the media element's media-resource-specific tracks.
1921        forgetResourceSpecificTracks();
1922
1923        if (havePotentialSourceChild()) {
1924            LOG(Media, "HTMLMediaElement::setNetworkState - scheduling next <source>");
1925            scheduleNextSourceChild();
1926        } else {
1927            LOG(Media, "HTMLMediaElement::setNetworkState - no more <source> elements, waiting");
1928            waitForSourceChange();
1929        }
1930
1931        return;
1932    }
1933
1934    if ((error == MediaPlayer::NetworkError && m_readyState >= HAVE_METADATA) || error == MediaPlayer::DecodeError)
1935        mediaLoadingFailedFatally(error);
1936    else if ((error == MediaPlayer::FormatError || error == MediaPlayer::NetworkError) && m_loadState == LoadingFromSrcAttr)
1937        noneSupported();
1938
1939    updateDisplayState();
1940    if (hasMediaControls()) {
1941        mediaControls()->reset();
1942        mediaControls()->reportedError();
1943    }
1944
1945    logMediaLoadRequest(document().page(), String(), stringForNetworkState(error), false);
1946}
1947
1948void HTMLMediaElement::setNetworkState(MediaPlayer::NetworkState state)
1949{
1950    LOG(Media, "HTMLMediaElement::setNetworkState(%d) - current state is %d", static_cast<int>(state), static_cast<int>(m_networkState));
1951
1952    if (state == MediaPlayer::Empty) {
1953        // Just update the cached state and leave, we can't do anything.
1954        m_networkState = NETWORK_EMPTY;
1955        return;
1956    }
1957
1958    if (state == MediaPlayer::FormatError || state == MediaPlayer::NetworkError || state == MediaPlayer::DecodeError) {
1959        mediaLoadingFailed(state);
1960        return;
1961    }
1962
1963    if (state == MediaPlayer::Idle) {
1964        if (m_networkState > NETWORK_IDLE) {
1965            changeNetworkStateFromLoadingToIdle();
1966            setShouldDelayLoadEvent(false);
1967        } else {
1968            m_networkState = NETWORK_IDLE;
1969        }
1970    }
1971
1972    if (state == MediaPlayer::Loading) {
1973        if (m_networkState < NETWORK_LOADING || m_networkState == NETWORK_NO_SOURCE)
1974            startProgressEventTimer();
1975        m_networkState = NETWORK_LOADING;
1976    }
1977
1978    if (state == MediaPlayer::Loaded) {
1979        if (m_networkState != NETWORK_IDLE)
1980            changeNetworkStateFromLoadingToIdle();
1981        m_completelyLoaded = true;
1982    }
1983
1984    if (hasMediaControls())
1985        mediaControls()->updateStatusDisplay();
1986}
1987
1988void HTMLMediaElement::changeNetworkStateFromLoadingToIdle()
1989{
1990    m_progressEventTimer.stop();
1991    if (hasMediaControls() && m_player->didLoadingProgress())
1992        mediaControls()->bufferingProgressed();
1993
1994    // Schedule one last progress event so we guarantee that at least one is fired
1995    // for files that load very quickly.
1996    scheduleEvent(eventNames().progressEvent);
1997    scheduleEvent(eventNames().suspendEvent);
1998    m_networkState = NETWORK_IDLE;
1999}
2000
2001void HTMLMediaElement::mediaPlayerReadyStateChanged(MediaPlayer*)
2002{
2003    beginProcessingMediaPlayerCallback();
2004
2005    setReadyState(m_player->readyState());
2006
2007    endProcessingMediaPlayerCallback();
2008}
2009
2010void HTMLMediaElement::setReadyState(MediaPlayer::ReadyState state)
2011{
2012    LOG(Media, "HTMLMediaElement::setReadyState(%d) - current state is %d,", static_cast<int>(state), static_cast<int>(m_readyState));
2013
2014    // Set "wasPotentiallyPlaying" BEFORE updating m_readyState, potentiallyPlaying() uses it
2015    bool wasPotentiallyPlaying = potentiallyPlaying();
2016
2017    ReadyState oldState = m_readyState;
2018    ReadyState newState = static_cast<ReadyState>(state);
2019
2020#if ENABLE(VIDEO_TRACK)
2021    bool tracksAreReady = !RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled() || textTracksAreReady();
2022
2023    if (newState == oldState && m_tracksAreReady == tracksAreReady)
2024        return;
2025
2026    m_tracksAreReady = tracksAreReady;
2027#else
2028    if (newState == oldState)
2029        return;
2030    bool tracksAreReady = true;
2031#endif
2032
2033    if (tracksAreReady)
2034        m_readyState = newState;
2035    else {
2036        // If a media file has text tracks the readyState may not progress beyond HAVE_FUTURE_DATA until
2037        // the text tracks are ready, regardless of the state of the media file.
2038        if (newState <= HAVE_METADATA)
2039            m_readyState = newState;
2040        else
2041            m_readyState = HAVE_CURRENT_DATA;
2042    }
2043
2044    if (oldState > m_readyStateMaximum)
2045        m_readyStateMaximum = oldState;
2046
2047    if (m_networkState == NETWORK_EMPTY)
2048        return;
2049
2050    if (m_seeking) {
2051        // 4.8.10.9, step 11
2052        if (wasPotentiallyPlaying && m_readyState < HAVE_FUTURE_DATA)
2053            scheduleEvent(eventNames().waitingEvent);
2054
2055        // 4.8.10.10 step 14 & 15.
2056        if (m_readyState >= HAVE_CURRENT_DATA)
2057            finishSeek();
2058    } else {
2059        if (wasPotentiallyPlaying && m_readyState < HAVE_FUTURE_DATA) {
2060            // 4.8.10.8
2061            invalidateCachedTime();
2062            scheduleTimeupdateEvent(false);
2063            scheduleEvent(eventNames().waitingEvent);
2064        }
2065    }
2066
2067    if (m_readyState >= HAVE_METADATA && oldState < HAVE_METADATA) {
2068        prepareMediaFragmentURI();
2069        scheduleEvent(eventNames().durationchangeEvent);
2070        scheduleEvent(eventNames().loadedmetadataEvent);
2071        if (hasMediaControls())
2072            mediaControls()->loadedMetadata();
2073        if (renderer())
2074            renderer()->updateFromElement();
2075
2076        logMediaLoadRequest(document().page(), m_player->engineDescription(), String(), true);
2077    }
2078
2079    bool shouldUpdateDisplayState = false;
2080
2081    if (m_readyState >= HAVE_CURRENT_DATA && oldState < HAVE_CURRENT_DATA && !m_haveFiredLoadedData) {
2082        m_haveFiredLoadedData = true;
2083        shouldUpdateDisplayState = true;
2084        scheduleEvent(eventNames().loadeddataEvent);
2085        setShouldDelayLoadEvent(false);
2086        applyMediaFragmentURI();
2087    }
2088
2089    bool isPotentiallyPlaying = potentiallyPlaying();
2090    if (m_readyState == HAVE_FUTURE_DATA && oldState <= HAVE_CURRENT_DATA && tracksAreReady) {
2091        scheduleEvent(eventNames().canplayEvent);
2092        if (isPotentiallyPlaying)
2093            scheduleEvent(eventNames().playingEvent);
2094        shouldUpdateDisplayState = true;
2095    }
2096
2097    if (m_readyState == HAVE_ENOUGH_DATA && oldState < HAVE_ENOUGH_DATA && tracksAreReady) {
2098        if (oldState <= HAVE_CURRENT_DATA)
2099            scheduleEvent(eventNames().canplayEvent);
2100
2101        scheduleEvent(eventNames().canplaythroughEvent);
2102
2103        if (isPotentiallyPlaying && oldState <= HAVE_CURRENT_DATA)
2104            scheduleEvent(eventNames().playingEvent);
2105
2106        if (m_autoplaying && m_paused && autoplay() && !document().isSandboxed(SandboxAutomaticFeatures) && m_mediaSession->playbackPermitted(*this)) {
2107            m_paused = false;
2108            invalidateCachedTime();
2109            scheduleEvent(eventNames().playEvent);
2110            scheduleEvent(eventNames().playingEvent);
2111        }
2112
2113        shouldUpdateDisplayState = true;
2114    }
2115
2116    if (shouldUpdateDisplayState) {
2117        updateDisplayState();
2118        if (hasMediaControls()) {
2119            mediaControls()->refreshClosedCaptionsButtonVisibility();
2120            mediaControls()->updateStatusDisplay();
2121        }
2122    }
2123
2124    updatePlayState();
2125    updateMediaController();
2126#if ENABLE(VIDEO_TRACK)
2127    if (RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled())
2128        updateActiveTextTrackCues(currentTime());
2129#endif
2130}
2131
2132#if ENABLE(ENCRYPTED_MEDIA)
2133void HTMLMediaElement::mediaPlayerKeyAdded(MediaPlayer*, const String& keySystem, const String& sessionId)
2134{
2135    MediaKeyEventInit initializer;
2136    initializer.keySystem = keySystem;
2137    initializer.sessionId = sessionId;
2138    initializer.bubbles = false;
2139    initializer.cancelable = false;
2140
2141    RefPtr<Event> event = MediaKeyEvent::create(eventNames().webkitkeyaddedEvent, initializer);
2142    event->setTarget(this);
2143    m_asyncEventQueue.enqueueEvent(event.release());
2144}
2145
2146void HTMLMediaElement::mediaPlayerKeyError(MediaPlayer*, const String& keySystem, const String& sessionId, MediaPlayerClient::MediaKeyErrorCode errorCode, unsigned short systemCode)
2147{
2148    MediaKeyError::Code mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_UNKNOWN;
2149    switch (errorCode) {
2150    case MediaPlayerClient::UnknownError:
2151        mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_UNKNOWN;
2152        break;
2153    case MediaPlayerClient::ClientError:
2154        mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_CLIENT;
2155        break;
2156    case MediaPlayerClient::ServiceError:
2157        mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_SERVICE;
2158        break;
2159    case MediaPlayerClient::OutputError:
2160        mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_OUTPUT;
2161        break;
2162    case MediaPlayerClient::HardwareChangeError:
2163        mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_HARDWARECHANGE;
2164        break;
2165    case MediaPlayerClient::DomainError:
2166        mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_DOMAIN;
2167        break;
2168    }
2169
2170    MediaKeyEventInit initializer;
2171    initializer.keySystem = keySystem;
2172    initializer.sessionId = sessionId;
2173    initializer.errorCode = MediaKeyError::create(mediaKeyErrorCode);
2174    initializer.systemCode = systemCode;
2175    initializer.bubbles = false;
2176    initializer.cancelable = false;
2177
2178    RefPtr<Event> event = MediaKeyEvent::create(eventNames().webkitkeyerrorEvent, initializer);
2179    event->setTarget(this);
2180    m_asyncEventQueue.enqueueEvent(event.release());
2181}
2182
2183void HTMLMediaElement::mediaPlayerKeyMessage(MediaPlayer*, const String& keySystem, const String& sessionId, const unsigned char* message, unsigned messageLength, const URL& defaultURL)
2184{
2185    MediaKeyEventInit initializer;
2186    initializer.keySystem = keySystem;
2187    initializer.sessionId = sessionId;
2188    initializer.message = Uint8Array::create(message, messageLength);
2189    initializer.defaultURL = defaultURL;
2190    initializer.bubbles = false;
2191    initializer.cancelable = false;
2192
2193    RefPtr<Event> event = MediaKeyEvent::create(eventNames().webkitkeymessageEvent, initializer);
2194    event->setTarget(this);
2195    m_asyncEventQueue.enqueueEvent(event.release());
2196}
2197
2198bool HTMLMediaElement::mediaPlayerKeyNeeded(MediaPlayer*, const String& keySystem, const String& sessionId, const unsigned char* initData, unsigned initDataLength)
2199{
2200    if (!hasEventListeners(eventNames().webkitneedkeyEvent)) {
2201        m_error = MediaError::create(MediaError::MEDIA_ERR_ENCRYPTED);
2202        scheduleEvent(eventNames().errorEvent);
2203        return false;
2204    }
2205
2206    MediaKeyEventInit initializer;
2207    initializer.keySystem = keySystem;
2208    initializer.sessionId = sessionId;
2209    initializer.initData = Uint8Array::create(initData, initDataLength);
2210    initializer.bubbles = false;
2211    initializer.cancelable = false;
2212
2213    RefPtr<Event> event = MediaKeyEvent::create(eventNames().webkitneedkeyEvent, initializer);
2214    event->setTarget(this);
2215    m_asyncEventQueue.enqueueEvent(event.release());
2216    return true;
2217}
2218#endif
2219
2220#if ENABLE(ENCRYPTED_MEDIA_V2)
2221bool HTMLMediaElement::mediaPlayerKeyNeeded(MediaPlayer*, Uint8Array* initData)
2222{
2223    if (!hasEventListeners("webkitneedkey")) {
2224        m_error = MediaError::create(MediaError::MEDIA_ERR_ENCRYPTED);
2225        scheduleEvent(eventNames().errorEvent);
2226        return false;
2227    }
2228
2229    MediaKeyNeededEventInit initializer;
2230    initializer.initData = initData;
2231    initializer.bubbles = false;
2232    initializer.cancelable = false;
2233
2234    RefPtr<Event> event = MediaKeyNeededEvent::create(eventNames().webkitneedkeyEvent, initializer);
2235    event->setTarget(this);
2236    m_asyncEventQueue.enqueueEvent(event.release());
2237
2238    return true;
2239}
2240
2241void HTMLMediaElement::setMediaKeys(MediaKeys* mediaKeys)
2242{
2243    if (m_mediaKeys == mediaKeys)
2244        return;
2245
2246    if (m_mediaKeys)
2247        m_mediaKeys->setMediaElement(0);
2248    m_mediaKeys = mediaKeys;
2249    if (m_mediaKeys)
2250        m_mediaKeys->setMediaElement(this);
2251}
2252#endif
2253
2254void HTMLMediaElement::progressEventTimerFired(Timer<HTMLMediaElement>&)
2255{
2256    ASSERT(m_player);
2257    if (m_networkState != NETWORK_LOADING)
2258        return;
2259
2260    double time = monotonicallyIncreasingTime();
2261    double timedelta = time - m_previousProgressTime;
2262
2263    if (m_player->didLoadingProgress()) {
2264        scheduleEvent(eventNames().progressEvent);
2265        m_previousProgressTime = time;
2266        m_sentStalledEvent = false;
2267        if (renderer())
2268            renderer()->updateFromElement();
2269        if (hasMediaControls())
2270            mediaControls()->bufferingProgressed();
2271    } else if (timedelta > 3.0 && !m_sentStalledEvent) {
2272        scheduleEvent(eventNames().stalledEvent);
2273        m_sentStalledEvent = true;
2274        setShouldDelayLoadEvent(false);
2275    }
2276}
2277
2278void HTMLMediaElement::rewind(double timeDelta)
2279{
2280    LOG(Media, "HTMLMediaElement::rewind(%f)", timeDelta);
2281    setCurrentTime(std::max(currentTime() - timeDelta, minTimeSeekable()));
2282}
2283
2284void HTMLMediaElement::returnToRealtime()
2285{
2286    LOG(Media, "HTMLMediaElement::returnToRealtime");
2287    setCurrentTime(maxTimeSeekable());
2288}
2289
2290void HTMLMediaElement::addPlayedRange(double start, double end)
2291{
2292    LOG(Media, "HTMLMediaElement::addPlayedRange(%f, %f)", start, end);
2293    if (!m_playedTimeRanges)
2294        m_playedTimeRanges = TimeRanges::create();
2295    m_playedTimeRanges->add(start, end);
2296}
2297
2298bool HTMLMediaElement::supportsSave() const
2299{
2300    return m_player ? m_player->supportsSave() : false;
2301}
2302
2303bool HTMLMediaElement::supportsScanning() const
2304{
2305    return m_player ? m_player->supportsScanning() : false;
2306}
2307
2308void HTMLMediaElement::prepareToPlay()
2309{
2310    LOG(Media, "HTMLMediaElement::prepareToPlay(%p)", this);
2311    if (m_havePreparedToPlay)
2312        return;
2313    m_havePreparedToPlay = true;
2314    m_player->prepareToPlay();
2315}
2316
2317void HTMLMediaElement::fastSeek(double time)
2318{
2319    LOG(Media, "HTMLMediaElement::fastSeek(%f)", time);
2320    // 4.7.10.9 Seeking
2321    // 9. If the approximate-for-speed flag is set, adjust the new playback position to a value that will
2322    // allow for playback to resume promptly. If new playback position before this step is before current
2323    // playback position, then the adjusted new playback position must also be before the current playback
2324    // position. Similarly, if the new playback position before this step is after current playback position,
2325    // then the adjusted new playback position must also be after the current playback position.
2326    refreshCachedTime();
2327    double delta = time - currentTime();
2328    double negativeTolerance = delta >= 0 ? delta : std::numeric_limits<double>::infinity();
2329    double positiveTolerance = delta < 0 ? -delta : std::numeric_limits<double>::infinity();
2330
2331    seekWithTolerance(time, negativeTolerance, positiveTolerance, true);
2332}
2333
2334void HTMLMediaElement::seek(double time)
2335{
2336    LOG(Media, "HTMLMediaElement::seek(%f)", time);
2337    seekWithTolerance(time, 0, 0, true);
2338}
2339
2340void HTMLMediaElement::seekInternal(double time)
2341{
2342    LOG(Media, "HTMLMediaElement::seekInternal(%f)", time);
2343    seekWithTolerance(time, 0, 0, false);
2344}
2345
2346void HTMLMediaElement::seekWithTolerance(double time, double negativeTolerance, double positiveTolerance, bool fromDOM)
2347{
2348    // 4.8.10.9 Seeking
2349
2350    // 1 - Set the media element's show poster flag to false.
2351    setDisplayMode(Video);
2352
2353    // 2 - If the media element's readyState is HAVE_NOTHING, abort these steps.
2354    if (m_readyState == HAVE_NOTHING || !m_player)
2355        return;
2356
2357    // If the media engine has been told to postpone loading data, let it go ahead now.
2358    if (m_preload < MediaPlayer::Auto && m_readyState < HAVE_FUTURE_DATA)
2359        prepareToPlay();
2360
2361    // Get the current time before setting m_seeking, m_lastSeekTime is returned once it is set.
2362    refreshCachedTime();
2363    double now = currentTime();
2364
2365    // 3 - If the element's seeking IDL attribute is true, then another instance of this algorithm is
2366    // already running. Abort that other instance of the algorithm without waiting for the step that
2367    // it is running to complete.
2368    if (m_seeking) {
2369        m_seekTimer.stop();
2370        m_pendingSeek = nullptr;
2371    }
2372
2373    // 4 - Set the seeking IDL attribute to true.
2374    // The flag will be cleared when the engine tells us the time has actually changed.
2375    m_seeking = true;
2376    if (m_playing) {
2377        if (m_lastSeekTime < now)
2378            addPlayedRange(m_lastSeekTime, now);
2379    }
2380    m_lastSeekTime = time;
2381
2382    // 5 - If the seek was in response to a DOM method call or setting of an IDL attribute, then continue
2383    // the script. The remainder of these steps must be run asynchronously.
2384    m_pendingSeek = std::make_unique<PendingSeek>(now, time, negativeTolerance, positiveTolerance);
2385    if (fromDOM)
2386        m_seekTimer.startOneShot(0);
2387    else
2388        seekTimerFired(m_seekTimer);
2389}
2390
2391void HTMLMediaElement::seekTimerFired(Timer<HTMLMediaElement>&)
2392{
2393    if (!m_player) {
2394        m_seeking = false;
2395        return;
2396    }
2397
2398    ASSERT(m_pendingSeek);
2399    double now = m_pendingSeek->now;
2400    double time = m_pendingSeek->targetTime;
2401    double negativeTolerance = m_pendingSeek->negativeTolerance;
2402    double positiveTolerance = m_pendingSeek->positiveTolerance;
2403    m_pendingSeek = nullptr;
2404
2405    // 6 - If the new playback position is later than the end of the media resource, then let it be the end
2406    // of the media resource instead.
2407    time = std::min(time, duration());
2408
2409    // 7 - If the new playback position is less than the earliest possible position, let it be that position instead.
2410    double earliestTime = m_player->startTime();
2411    time = std::max(time, earliestTime);
2412
2413    // Ask the media engine for the time value in the movie's time scale before comparing with current time. This
2414    // is necessary because if the seek time is not equal to currentTime but the delta is less than the movie's
2415    // time scale, we will ask the media engine to "seek" to the current movie time, which may be a noop and
2416    // not generate a timechanged callback. This means m_seeking will never be cleared and we will never
2417    // fire a 'seeked' event.
2418#if !LOG_DISABLED
2419    double mediaTime = m_player->mediaTimeForTimeValue(time);
2420    if (time != mediaTime)
2421        LOG(Media, "HTMLMediaElement::seekTimerFired(%f) - media timeline equivalent is %f", time, mediaTime);
2422#endif
2423    time = m_player->mediaTimeForTimeValue(time);
2424
2425    // 8 - If the (possibly now changed) new playback position is not in one of the ranges given in the
2426    // seekable attribute, then let it be the position in one of the ranges given in the seekable attribute
2427    // that is the nearest to the new playback position. ... If there are no ranges given in the seekable
2428    // attribute then set the seeking IDL attribute to false and abort these steps.
2429    RefPtr<TimeRanges> seekableRanges = seekable();
2430
2431    // Short circuit seeking to the current time by just firing the events if no seek is required.
2432    // Don't skip calling the media engine if we are in poster mode because a seek should always
2433    // cancel poster display.
2434    bool noSeekRequired = !seekableRanges->length() || (time == now && displayMode() != Poster);
2435
2436#if ENABLE(MEDIA_SOURCE)
2437    // Always notify the media engine of a seek if the source is not closed. This ensures that the source is
2438    // always in a flushed state when the 'seeking' event fires.
2439    if (m_mediaSource && m_mediaSource->isClosed())
2440        noSeekRequired = false;
2441#endif
2442
2443    if (noSeekRequired) {
2444        if (time == now) {
2445            scheduleEvent(eventNames().seekingEvent);
2446            scheduleTimeupdateEvent(false);
2447            scheduleEvent(eventNames().seekedEvent);
2448        }
2449        m_seeking = false;
2450        return;
2451    }
2452    time = seekableRanges->nearest(time);
2453
2454    m_sentEndEvent = false;
2455    m_lastSeekTime = time;
2456
2457    // 10 - Queue a task to fire a simple event named seeking at the element.
2458    scheduleEvent(eventNames().seekingEvent);
2459
2460    // 11 - Set the current playback position to the given new playback position
2461    m_player->seekWithTolerance(time, negativeTolerance, positiveTolerance);
2462
2463    // 12 - Wait until the user agent has established whether or not the media data for the new playback
2464    // position is available, and, if it is, until it has decoded enough data to play back that position.
2465    // 13 - Await a stable state. The synchronous section consists of all the remaining steps of this algorithm.
2466}
2467
2468void HTMLMediaElement::finishSeek()
2469{
2470    LOG(Media, "HTMLMediaElement::finishSeek");
2471
2472    // 4.8.10.9 Seeking
2473    // 14 - Set the seeking IDL attribute to false.
2474    m_seeking = false;
2475
2476    // 15 - Run the time maches on steps.
2477    // Handled by mediaPlayerTimeChanged().
2478
2479    // 16 - Queue a task to fire a simple event named timeupdate at the element.
2480    scheduleEvent(eventNames().timeupdateEvent);
2481
2482    // 17 - Queue a task to fire a simple event named seeked at the element.
2483    scheduleEvent(eventNames().seekedEvent);
2484
2485#if ENABLE(MEDIA_SOURCE)
2486    if (m_mediaSource)
2487        m_mediaSource->monitorSourceBuffers();
2488#endif
2489}
2490
2491HTMLMediaElement::ReadyState HTMLMediaElement::readyState() const
2492{
2493    return m_readyState;
2494}
2495
2496MediaPlayer::MovieLoadType HTMLMediaElement::movieLoadType() const
2497{
2498    return m_player ? m_player->movieLoadType() : MediaPlayer::Unknown;
2499}
2500
2501bool HTMLMediaElement::hasAudio() const
2502{
2503    return m_player ? m_player->hasAudio() : false;
2504}
2505
2506bool HTMLMediaElement::seeking() const
2507{
2508    return m_seeking;
2509}
2510
2511void HTMLMediaElement::refreshCachedTime() const
2512{
2513    m_cachedTime = m_player->currentTime();
2514    if (!m_cachedTime) {
2515        // Do not use m_cachedTime until the media engine returns a non-zero value because we can't
2516        // estimate current time until playback actually begins.
2517        invalidateCachedTime();
2518        return;
2519    }
2520
2521    m_clockTimeAtLastCachedTimeUpdate = monotonicallyIncreasingTime();
2522}
2523
2524void HTMLMediaElement::invalidateCachedTime() const
2525{
2526#if !LOG_DISABLED
2527    if (m_cachedTime != MediaPlayer::invalidTime())
2528        LOG(Media, "HTMLMediaElement::invalidateCachedTime");
2529#endif
2530
2531    // Don't try to cache movie time when playback first starts as the time reported by the engine
2532    // sometimes fluctuates for a short amount of time, so the cached time will be off if we take it
2533    // too early.
2534    static const double minimumTimePlayingBeforeCacheSnapshot = 0.5;
2535
2536    m_minimumClockTimeToUpdateCachedTime = monotonicallyIncreasingTime() + minimumTimePlayingBeforeCacheSnapshot;
2537    m_cachedTime = MediaPlayer::invalidTime();
2538}
2539
2540// playback state
2541double HTMLMediaElement::currentTime() const
2542{
2543#if LOG_CACHED_TIME_WARNINGS
2544    static const double minCachedDeltaForWarning = 0.01;
2545#endif
2546
2547    if (!m_player)
2548        return 0;
2549
2550    if (m_seeking) {
2551        LOG(Media, "HTMLMediaElement::currentTime - seeking, returning %f", m_lastSeekTime);
2552        return m_lastSeekTime;
2553    }
2554
2555    if (m_cachedTime != MediaPlayer::invalidTime() && m_paused) {
2556#if LOG_CACHED_TIME_WARNINGS
2557        double delta = m_cachedTime - m_player->currentTime();
2558        if (delta > minCachedDeltaForWarning)
2559            LOG(Media, "HTMLMediaElement::currentTime - WARNING, cached time is %f seconds off of media time when paused", delta);
2560#endif
2561        return m_cachedTime;
2562    }
2563
2564    // Is it too soon use a cached time?
2565    double now = monotonicallyIncreasingTime();
2566    double maximumDurationToCacheMediaTime = m_player->maximumDurationToCacheMediaTime();
2567
2568    if (maximumDurationToCacheMediaTime && m_cachedTime != MediaPlayer::invalidTime() && !m_paused && now > m_minimumClockTimeToUpdateCachedTime) {
2569        double clockDelta = now - m_clockTimeAtLastCachedTimeUpdate;
2570
2571        // Not too soon, use the cached time only if it hasn't expired.
2572        if (clockDelta < maximumDurationToCacheMediaTime) {
2573            double adjustedCacheTime = m_cachedTime + (effectivePlaybackRate() * clockDelta);
2574
2575#if LOG_CACHED_TIME_WARNINGS
2576            double delta = adjustedCacheTime - m_player->currentTime();
2577            if (delta > minCachedDeltaForWarning)
2578                LOG(Media, "HTMLMediaElement::currentTime - WARNING, cached time is %f seconds off of media time when playing", delta);
2579#endif
2580            return adjustedCacheTime;
2581        }
2582    }
2583
2584#if LOG_CACHED_TIME_WARNINGS
2585    if (maximumDurationToCacheMediaTime && now > m_minimumClockTimeToUpdateCachedTime && m_cachedTime != MediaPlayer::invalidTime()) {
2586        double clockDelta = now - m_clockTimeAtLastCachedTimeUpdate;
2587        double delta = m_cachedTime + (effectivePlaybackRate() * clockDelta) - m_player->currentTime();
2588        LOG(Media, "HTMLMediaElement::currentTime - cached time was %f seconds off of media time when it expired", delta);
2589    }
2590#endif
2591
2592    refreshCachedTime();
2593
2594    if (m_cachedTime == MediaPlayer::invalidTime())
2595        return 0;
2596
2597    return m_cachedTime;
2598}
2599
2600void HTMLMediaElement::setCurrentTime(double time)
2601{
2602    if (m_mediaController)
2603        return;
2604
2605    seekInternal(time);
2606}
2607
2608void HTMLMediaElement::setCurrentTime(double time, ExceptionCode& ec)
2609{
2610    // On setting, if the media element has a current media controller, then the user agent must
2611    // throw an InvalidStateError exception
2612    if (m_mediaController) {
2613        ec = INVALID_STATE_ERR;
2614        return;
2615    }
2616
2617    seek(time);
2618}
2619
2620double HTMLMediaElement::duration() const
2621{
2622    if (m_player && m_readyState >= HAVE_METADATA)
2623        return m_player->duration();
2624
2625    return std::numeric_limits<double>::quiet_NaN();
2626}
2627
2628bool HTMLMediaElement::paused() const
2629{
2630    // As of this writing, JavaScript garbage collection calls this function directly. In the past
2631    // we had problems where this was called on an object after a bad cast. The assertion below
2632    // made our regression test detect the problem, so we should keep it because of that. But note
2633    // that the value of the assertion relies on the compiler not being smart enough to know that
2634    // isHTMLUnknownElement is guaranteed to return false for an HTMLMediaElement.
2635    ASSERT(!isHTMLUnknownElement());
2636
2637    return m_paused;
2638}
2639
2640double HTMLMediaElement::defaultPlaybackRate() const
2641{
2642    return m_defaultPlaybackRate;
2643}
2644
2645void HTMLMediaElement::setDefaultPlaybackRate(double rate)
2646{
2647    if (m_defaultPlaybackRate != rate) {
2648        m_defaultPlaybackRate = rate;
2649        scheduleEvent(eventNames().ratechangeEvent);
2650    }
2651}
2652
2653double HTMLMediaElement::effectivePlaybackRate() const
2654{
2655    return m_mediaController ? m_mediaController->playbackRate() : m_playbackRate;
2656}
2657
2658double HTMLMediaElement::playbackRate() const
2659{
2660    return m_playbackRate;
2661}
2662
2663void HTMLMediaElement::setPlaybackRate(double rate)
2664{
2665    LOG(Media, "HTMLMediaElement::setPlaybackRate(%f)", rate);
2666
2667    if (m_player && potentiallyPlaying() && m_player->rate() != rate && !m_mediaController)
2668        m_player->setRate(rate);
2669
2670    if (m_playbackRate != rate) {
2671        m_playbackRate = rate;
2672        invalidateCachedTime();
2673        scheduleEvent(eventNames().ratechangeEvent);
2674    }
2675}
2676
2677void HTMLMediaElement::updatePlaybackRate()
2678{
2679    double effectiveRate = effectivePlaybackRate();
2680    if (m_player && potentiallyPlaying() && m_player->rate() != effectiveRate)
2681        m_player->setRate(effectiveRate);
2682}
2683
2684bool HTMLMediaElement::webkitPreservesPitch() const
2685{
2686    return m_webkitPreservesPitch;
2687}
2688
2689void HTMLMediaElement::setWebkitPreservesPitch(bool preservesPitch)
2690{
2691    LOG(Media, "HTMLMediaElement::setWebkitPreservesPitch(%s)", boolString(preservesPitch));
2692
2693    m_webkitPreservesPitch = preservesPitch;
2694
2695    if (!m_player)
2696        return;
2697
2698    m_player->setPreservesPitch(preservesPitch);
2699}
2700
2701bool HTMLMediaElement::ended() const
2702{
2703    // 4.8.10.8 Playing the media resource
2704    // The ended attribute must return true if the media element has ended
2705    // playback and the direction of playback is forwards, and false otherwise.
2706    return endedPlayback() && effectivePlaybackRate() > 0;
2707}
2708
2709bool HTMLMediaElement::autoplay() const
2710{
2711#if PLATFORM(IOS)
2712    // Unless the restriction on requiring user actions has been lifted, we do not
2713    // allow playback to start just because the page has "autoplay". They are either
2714    // lifted through Settings, or once the user explictly calls load() or play()
2715    // because they have OK'ed us loading data. This allows playback to continue if
2716    // the URL is changed while the movie is playing.
2717    if (!m_mediaSession->playbackPermitted(*this) || !m_mediaSession->dataLoadingPermitted(*this))
2718        return false;
2719#endif
2720
2721    return fastHasAttribute(autoplayAttr);
2722}
2723
2724String HTMLMediaElement::preload() const
2725{
2726    switch (m_preload) {
2727    case MediaPlayer::None:
2728        return ASCIILiteral("none");
2729    case MediaPlayer::MetaData:
2730        return ASCIILiteral("metadata");
2731    case MediaPlayer::Auto:
2732        return ASCIILiteral("auto");
2733    }
2734
2735    ASSERT_NOT_REACHED();
2736    return String();
2737}
2738
2739void HTMLMediaElement::setPreload(const String& preload)
2740{
2741    LOG(Media, "HTMLMediaElement::setPreload(%s)", preload.utf8().data());
2742    setAttribute(preloadAttr, preload);
2743}
2744
2745void HTMLMediaElement::play()
2746{
2747    LOG(Media, "HTMLMediaElement::play()");
2748
2749    if (!m_mediaSession->playbackPermitted(*this))
2750        return;
2751    if (ScriptController::processingUserGesture())
2752        removeBehaviorsRestrictionsAfterFirstUserGesture();
2753
2754    playInternal();
2755}
2756
2757void HTMLMediaElement::playInternal()
2758{
2759    LOG(Media, "HTMLMediaElement::playInternal");
2760
2761    if (!m_mediaSession->clientWillBeginPlayback()) {
2762        LOG(Media, "  returning because of interruption");
2763        return;
2764    }
2765
2766    // 4.8.10.9. Playing the media resource
2767    if (!m_player || m_networkState == NETWORK_EMPTY)
2768        scheduleDelayedAction(LoadMediaResource);
2769
2770    if (endedPlayback())
2771        seekInternal(0);
2772
2773    if (m_mediaController)
2774        m_mediaController->bringElementUpToSpeed(this);
2775
2776    if (m_paused) {
2777        m_paused = false;
2778        invalidateCachedTime();
2779        scheduleEvent(eventNames().playEvent);
2780
2781        if (m_readyState <= HAVE_CURRENT_DATA)
2782            scheduleEvent(eventNames().waitingEvent);
2783        else if (m_readyState >= HAVE_FUTURE_DATA)
2784            scheduleEvent(eventNames().playingEvent);
2785    }
2786    m_autoplaying = false;
2787#if PLATFORM(IOS)
2788    m_requestingPlay = true;
2789#endif
2790    updatePlayState();
2791    updateMediaController();
2792}
2793
2794void HTMLMediaElement::pause()
2795{
2796    LOG(Media, "HTMLMediaElement::pause()");
2797
2798    if (!m_mediaSession->playbackPermitted(*this))
2799        return;
2800
2801    pauseInternal();
2802}
2803
2804
2805void HTMLMediaElement::pauseInternal()
2806{
2807    LOG(Media, "HTMLMediaElement::pauseInternal");
2808
2809    if (!m_mediaSession->clientWillPausePlayback()) {
2810        LOG(Media, "  returning because of interruption");
2811        return;
2812    }
2813
2814    // 4.8.10.9. Playing the media resource
2815    if (!m_player || m_networkState == NETWORK_EMPTY) {
2816#if PLATFORM(IOS)
2817        // Unless the restriction on media requiring user action has been lifted
2818        // don't trigger loading if a script calls pause().
2819        if (!m_mediaSession->playbackPermitted(*this))
2820            return;
2821#endif
2822        scheduleDelayedAction(LoadMediaResource);
2823    }
2824
2825    m_autoplaying = false;
2826
2827    if (!m_paused) {
2828        m_paused = true;
2829        scheduleTimeupdateEvent(false);
2830        scheduleEvent(eventNames().pauseEvent);
2831    }
2832
2833    updatePlayState();
2834}
2835
2836#if ENABLE(MEDIA_SOURCE)
2837void HTMLMediaElement::closeMediaSource()
2838{
2839    if (!m_mediaSource)
2840        return;
2841
2842    m_mediaSource->close();
2843    m_mediaSource = 0;
2844}
2845#endif
2846
2847#if ENABLE(ENCRYPTED_MEDIA)
2848void HTMLMediaElement::webkitGenerateKeyRequest(const String& keySystem, PassRefPtr<Uint8Array> initData, ExceptionCode& ec)
2849{
2850#if ENABLE(ENCRYPTED_MEDIA_V2)
2851    static bool firstTime = true;
2852    if (firstTime && scriptExecutionContext()) {
2853        scriptExecutionContext()->addConsoleMessage(MessageSource::JS, MessageLevel::Warning, ASCIILiteral("'HTMLMediaElement.webkitGenerateKeyRequest()' is deprecated.  Use 'MediaKeys.createSession()' instead."));
2854        firstTime = false;
2855    }
2856#endif
2857
2858    if (keySystem.isEmpty()) {
2859        ec = SYNTAX_ERR;
2860        return;
2861    }
2862
2863    if (!m_player) {
2864        ec = INVALID_STATE_ERR;
2865        return;
2866    }
2867
2868    const unsigned char* initDataPointer = 0;
2869    unsigned initDataLength = 0;
2870    if (initData) {
2871        initDataPointer = initData->data();
2872        initDataLength = initData->length();
2873    }
2874
2875    MediaPlayer::MediaKeyException result = m_player->generateKeyRequest(keySystem, initDataPointer, initDataLength);
2876    ec = exceptionCodeForMediaKeyException(result);
2877}
2878
2879void HTMLMediaElement::webkitGenerateKeyRequest(const String& keySystem, ExceptionCode& ec)
2880{
2881    webkitGenerateKeyRequest(keySystem, Uint8Array::create(0), ec);
2882}
2883
2884void HTMLMediaElement::webkitAddKey(const String& keySystem, PassRefPtr<Uint8Array> key, PassRefPtr<Uint8Array> initData, const String& sessionId, ExceptionCode& ec)
2885{
2886#if ENABLE(ENCRYPTED_MEDIA_V2)
2887    static bool firstTime = true;
2888    if (firstTime && scriptExecutionContext()) {
2889        scriptExecutionContext()->addConsoleMessage(MessageSource::JS, MessageLevel::Warning, ASCIILiteral("'HTMLMediaElement.webkitAddKey()' is deprecated.  Use 'MediaKeySession.update()' instead."));
2890        firstTime = false;
2891    }
2892#endif
2893
2894    if (keySystem.isEmpty()) {
2895        ec = SYNTAX_ERR;
2896        return;
2897    }
2898
2899    if (!key) {
2900        ec = SYNTAX_ERR;
2901        return;
2902    }
2903
2904    if (!key->length()) {
2905        ec = TYPE_MISMATCH_ERR;
2906        return;
2907    }
2908
2909    if (!m_player) {
2910        ec = INVALID_STATE_ERR;
2911        return;
2912    }
2913
2914    const unsigned char* initDataPointer = 0;
2915    unsigned initDataLength = 0;
2916    if (initData) {
2917        initDataPointer = initData->data();
2918        initDataLength = initData->length();
2919    }
2920
2921    MediaPlayer::MediaKeyException result = m_player->addKey(keySystem, key->data(), key->length(), initDataPointer, initDataLength, sessionId);
2922    ec = exceptionCodeForMediaKeyException(result);
2923}
2924
2925void HTMLMediaElement::webkitAddKey(const String& keySystem, PassRefPtr<Uint8Array> key, ExceptionCode& ec)
2926{
2927    webkitAddKey(keySystem, key, Uint8Array::create(0), String(), ec);
2928}
2929
2930void HTMLMediaElement::webkitCancelKeyRequest(const String& keySystem, const String& sessionId, ExceptionCode& ec)
2931{
2932    if (keySystem.isEmpty()) {
2933        ec = SYNTAX_ERR;
2934        return;
2935    }
2936
2937    if (!m_player) {
2938        ec = INVALID_STATE_ERR;
2939        return;
2940    }
2941
2942    MediaPlayer::MediaKeyException result = m_player->cancelKeyRequest(keySystem, sessionId);
2943    ec = exceptionCodeForMediaKeyException(result);
2944}
2945
2946#endif
2947
2948bool HTMLMediaElement::loop() const
2949{
2950    return fastHasAttribute(loopAttr);
2951}
2952
2953void HTMLMediaElement::setLoop(bool b)
2954{
2955    LOG(Media, "HTMLMediaElement::setLoop(%s)", boolString(b));
2956    setBooleanAttribute(loopAttr, b);
2957}
2958
2959bool HTMLMediaElement::controls() const
2960{
2961    Frame* frame = document().frame();
2962
2963    // always show controls when scripting is disabled
2964    if (frame && !frame->script().canExecuteScripts(NotAboutToExecuteScript))
2965        return true;
2966
2967    // always show controls for video when fullscreen playback is required.
2968    if (isVideo() && m_mediaSession->requiresFullscreenForVideoPlayback(*this))
2969        return true;
2970
2971    // Always show controls when in full screen mode.
2972    if (isFullscreen())
2973        return true;
2974
2975    return fastHasAttribute(controlsAttr);
2976}
2977
2978void HTMLMediaElement::setControls(bool b)
2979{
2980    LOG(Media, "HTMLMediaElement::setControls(%s)", boolString(b));
2981    setBooleanAttribute(controlsAttr, b);
2982}
2983
2984double HTMLMediaElement::volume() const
2985{
2986    return m_volume;
2987}
2988
2989void HTMLMediaElement::setVolume(double vol, ExceptionCode& ec)
2990{
2991    LOG(Media, "HTMLMediaElement::setVolume(%f)", vol);
2992
2993    if (vol < 0.0f || vol > 1.0f) {
2994        ec = INDEX_SIZE_ERR;
2995        return;
2996    }
2997
2998#if !PLATFORM(IOS)
2999    if (m_volume != vol) {
3000        m_volume = vol;
3001        m_volumeInitialized = true;
3002        updateVolume();
3003        scheduleEvent(eventNames().volumechangeEvent);
3004    }
3005#endif
3006}
3007
3008bool HTMLMediaElement::muted() const
3009{
3010    return m_explicitlyMuted ? m_muted : fastHasAttribute(mutedAttr);
3011}
3012
3013void HTMLMediaElement::setMuted(bool muted)
3014{
3015    LOG(Media, "HTMLMediaElement::setMuted(%s)", boolString(muted));
3016
3017#if PLATFORM(IOS)
3018    UNUSED_PARAM(muted);
3019#else
3020    if (m_muted != muted || !m_explicitlyMuted) {
3021        m_muted = muted;
3022        m_explicitlyMuted = true;
3023        // Avoid recursion when the player reports volume changes.
3024        if (!processingMediaPlayerCallback()) {
3025            if (m_player) {
3026                m_player->setMuted(m_muted);
3027                if (hasMediaControls())
3028                    mediaControls()->changedMute();
3029            }
3030        }
3031        scheduleEvent(eventNames().volumechangeEvent);
3032    }
3033#endif
3034}
3035
3036void HTMLMediaElement::togglePlayState()
3037{
3038    LOG(Media, "HTMLMediaElement::togglePlayState - canPlay() is %s", boolString(canPlay()));
3039
3040    // We can safely call the internal play/pause methods, which don't check restrictions, because
3041    // this method is only called from the built-in media controller
3042    if (canPlay()) {
3043        updatePlaybackRate();
3044        playInternal();
3045    } else
3046        pauseInternal();
3047}
3048
3049void HTMLMediaElement::beginScrubbing()
3050{
3051    LOG(Media, "HTMLMediaElement::beginScrubbing - paused() is %s", boolString(paused()));
3052
3053    if (!paused()) {
3054        if (ended()) {
3055            // Because a media element stays in non-paused state when it reaches end, playback resumes
3056            // when the slider is dragged from the end to another position unless we pause first. Do
3057            // a "hard pause" so an event is generated, since we want to stay paused after scrubbing finishes.
3058            pause();
3059        } else {
3060            // Not at the end but we still want to pause playback so the media engine doesn't try to
3061            // continue playing during scrubbing. Pause without generating an event as we will
3062            // unpause after scrubbing finishes.
3063            setPausedInternal(true);
3064        }
3065    }
3066}
3067
3068void HTMLMediaElement::endScrubbing()
3069{
3070    LOG(Media, "HTMLMediaElement::endScrubbing - m_pausedInternal is %s", boolString(m_pausedInternal));
3071
3072    if (m_pausedInternal)
3073        setPausedInternal(false);
3074}
3075
3076void HTMLMediaElement::beginScanning(ScanDirection direction)
3077{
3078    m_scanType = supportsScanning() ? Scan : Seek;
3079    m_scanDirection = direction;
3080
3081    if (m_scanType == Seek) {
3082        // Scanning by seeking requires the video to be paused during scanning.
3083        m_actionAfterScan = paused() ? Nothing : Play;
3084        pause();
3085    } else {
3086        // Scanning by scanning requires the video to be playing during scanninging.
3087        m_actionAfterScan = paused() ? Pause : Nothing;
3088        play();
3089        setPlaybackRate(nextScanRate());
3090    }
3091
3092    m_scanTimer.start(0, m_scanType == Seek ? SeekRepeatDelay : ScanRepeatDelay);
3093}
3094
3095void HTMLMediaElement::endScanning()
3096{
3097    if (m_scanType == Scan)
3098        setPlaybackRate(defaultPlaybackRate());
3099
3100    if (m_actionAfterScan == Play)
3101        play();
3102    else if (m_actionAfterScan == Pause)
3103        pause();
3104
3105    if (m_scanTimer.isActive())
3106        m_scanTimer.stop();
3107}
3108
3109double HTMLMediaElement::nextScanRate()
3110{
3111    double rate = std::min(ScanMaximumRate, fabs(playbackRate() * 2));
3112    if (m_scanDirection == Backward)
3113        rate *= -1;
3114#if PLATFORM(IOS)
3115    rate = std::min(std::max(rate, minFastReverseRate()), maxFastForwardRate());
3116#endif
3117    return rate;
3118}
3119
3120void HTMLMediaElement::scanTimerFired(Timer<HTMLMediaElement>&)
3121{
3122    if (m_scanType == Seek) {
3123        double seekTime = m_scanDirection == Forward ? SeekTime : -SeekTime;
3124        setCurrentTime(currentTime() + seekTime);
3125    } else
3126        setPlaybackRate(nextScanRate());
3127}
3128
3129// The spec says to fire periodic timeupdate events (those sent while playing) every
3130// "15 to 250ms", we choose the slowest frequency
3131static const double maxTimeupdateEventFrequency = 0.25;
3132
3133void HTMLMediaElement::startPlaybackProgressTimer()
3134{
3135    if (m_playbackProgressTimer.isActive())
3136        return;
3137
3138    m_previousProgressTime = monotonicallyIncreasingTime();
3139    m_playbackProgressTimer.startRepeating(maxTimeupdateEventFrequency);
3140}
3141
3142void HTMLMediaElement::playbackProgressTimerFired(Timer<HTMLMediaElement>&)
3143{
3144    ASSERT(m_player);
3145
3146    if (m_fragmentEndTime != MediaPlayer::invalidTime() && currentTime() >= m_fragmentEndTime && effectivePlaybackRate() > 0) {
3147        m_fragmentEndTime = MediaPlayer::invalidTime();
3148        if (!m_mediaController && !m_paused) {
3149            // changes paused to true and fires a simple event named pause at the media element.
3150            pauseInternal();
3151        }
3152    }
3153
3154    scheduleTimeupdateEvent(true);
3155
3156    if (!effectivePlaybackRate())
3157        return;
3158
3159    if (!m_paused && hasMediaControls())
3160        mediaControls()->playbackProgressed();
3161
3162#if ENABLE(VIDEO_TRACK)
3163    if (RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled())
3164        updateActiveTextTrackCues(currentTime());
3165#endif
3166
3167#if ENABLE(MEDIA_SOURCE)
3168    if (m_mediaSource)
3169        m_mediaSource->monitorSourceBuffers();
3170#endif
3171}
3172
3173void HTMLMediaElement::scheduleTimeupdateEvent(bool periodicEvent)
3174{
3175    double now = monotonicallyIncreasingTime();
3176    double timedelta = now - m_clockTimeAtLastUpdateEvent;
3177
3178    // throttle the periodic events
3179    if (periodicEvent && timedelta < maxTimeupdateEventFrequency)
3180        return;
3181
3182    // Some media engines make multiple "time changed" callbacks at the same time, but we only want one
3183    // event at a given time so filter here
3184    double movieTime = currentTime();
3185    if (movieTime != m_lastTimeUpdateEventMovieTime) {
3186        scheduleEvent(eventNames().timeupdateEvent);
3187        m_clockTimeAtLastUpdateEvent = now;
3188        m_lastTimeUpdateEventMovieTime = movieTime;
3189    }
3190}
3191
3192bool HTMLMediaElement::canPlay() const
3193{
3194    return paused() || ended() || m_readyState < HAVE_METADATA;
3195}
3196
3197double HTMLMediaElement::percentLoaded() const
3198{
3199    if (!m_player)
3200        return 0;
3201    double duration = m_player->duration();
3202
3203    if (!duration || std::isinf(duration))
3204        return 0;
3205
3206    MediaTime buffered = MediaTime::zeroTime();
3207    bool ignored;
3208    std::unique_ptr<PlatformTimeRanges> timeRanges = m_player->buffered();
3209    for (unsigned i = 0; i < timeRanges->length(); ++i) {
3210        MediaTime start = timeRanges->start(i, ignored);
3211        MediaTime end = timeRanges->end(i, ignored);
3212        buffered += end - start;
3213    }
3214    return buffered.toDouble() / duration;
3215}
3216
3217#if ENABLE(VIDEO_TRACK)
3218
3219void HTMLMediaElement::mediaPlayerDidAddAudioTrack(PassRefPtr<AudioTrackPrivate> prpTrack)
3220{
3221    if (!RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled())
3222        return;
3223
3224    addAudioTrack(AudioTrack::create(this, prpTrack));
3225}
3226
3227void HTMLMediaElement::mediaPlayerDidAddTextTrack(PassRefPtr<InbandTextTrackPrivate> prpTrack)
3228{
3229    if (!RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled())
3230        return;
3231
3232    // 4.8.10.12.2 Sourcing in-band text tracks
3233    // 1. Associate the relevant data with a new text track and its corresponding new TextTrack object.
3234    RefPtr<InbandTextTrack> textTrack = InbandTextTrack::create(ActiveDOMObject::scriptExecutionContext(), this, prpTrack);
3235    textTrack->setMediaElement(this);
3236
3237    // 2. Set the new text track's kind, label, and language based on the semantics of the relevant data,
3238    // as defined by the relevant specification. If there is no label in that data, then the label must
3239    // be set to the empty string.
3240    // 3. Associate the text track list of cues with the rules for updating the text track rendering appropriate
3241    // for the format in question.
3242    // 4. If the new text track's kind is metadata, then set the text track in-band metadata track dispatch type
3243    // as follows, based on the type of the media resource:
3244    // 5. Populate the new text track's list of cues with the cues parsed so far, folllowing the guidelines for exposing
3245    // cues, and begin updating it dynamically as necessary.
3246    //   - Thess are all done by the media engine.
3247
3248    // 6. Set the new text track's readiness state to loaded.
3249    textTrack->setReadinessState(TextTrack::Loaded);
3250
3251    // 7. Set the new text track's mode to the mode consistent with the user's preferences and the requirements of
3252    // the relevant specification for the data.
3253    //  - This will happen in configureTextTracks()
3254    scheduleDelayedAction(ConfigureTextTracks);
3255
3256    // 8. Add the new text track to the media element's list of text tracks.
3257    // 9. Fire an event with the name addtrack, that does not bubble and is not cancelable, and that uses the TrackEvent
3258    // interface, with the track attribute initialized to the text track's TextTrack object, at the media element's
3259    // textTracks attribute's TextTrackList object.
3260    addTextTrack(textTrack.release());
3261}
3262
3263void HTMLMediaElement::mediaPlayerDidAddVideoTrack(PassRefPtr<VideoTrackPrivate> prpTrack)
3264{
3265    if (!RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled())
3266        return;
3267
3268    addVideoTrack(VideoTrack::create(this, prpTrack));
3269}
3270
3271void HTMLMediaElement::mediaPlayerDidRemoveAudioTrack(PassRefPtr<AudioTrackPrivate> prpTrack)
3272{
3273    prpTrack->willBeRemoved();
3274}
3275
3276void HTMLMediaElement::mediaPlayerDidRemoveTextTrack(PassRefPtr<InbandTextTrackPrivate> prpTrack)
3277{
3278    prpTrack->willBeRemoved();
3279}
3280
3281void HTMLMediaElement::mediaPlayerDidRemoveVideoTrack(PassRefPtr<VideoTrackPrivate> prpTrack)
3282{
3283    prpTrack->willBeRemoved();
3284}
3285
3286#if USE(PLATFORM_TEXT_TRACK_MENU)
3287void HTMLMediaElement::setSelectedTextTrack(PassRefPtr<PlatformTextTrack> platformTrack)
3288{
3289    if (!m_textTracks)
3290        return;
3291
3292    TrackDisplayUpdateScope scope(this);
3293
3294    if (!platformTrack) {
3295        setSelectedTextTrack(TextTrack::captionMenuOffItem());
3296        return;
3297    }
3298
3299    TextTrack* textTrack;
3300    if (platformTrack == PlatformTextTrack::captionMenuOffItem())
3301        textTrack = TextTrack::captionMenuOffItem();
3302    else if (platformTrack == PlatformTextTrack::captionMenuAutomaticItem())
3303        textTrack = TextTrack::captionMenuAutomaticItem();
3304    else {
3305        size_t i;
3306        for (i = 0; i < m_textTracks->length(); ++i) {
3307            textTrack = m_textTracks->item(i);
3308
3309            if (textTrack->platformTextTrack() == platformTrack)
3310                break;
3311        }
3312        if (i == m_textTracks->length())
3313            return;
3314    }
3315
3316    setSelectedTextTrack(textTrack);
3317}
3318
3319Vector<RefPtr<PlatformTextTrack>> HTMLMediaElement::platformTextTracks()
3320{
3321    if (!m_textTracks || !m_textTracks->length())
3322        return Vector<RefPtr<PlatformTextTrack>>();
3323
3324    Vector<RefPtr<PlatformTextTrack>> platformTracks;
3325    for (size_t i = 0; i < m_textTracks->length(); ++i)
3326        platformTracks.append(m_textTracks->item(i)->platformTextTrack());
3327
3328    return platformTracks;
3329}
3330
3331void HTMLMediaElement::notifyMediaPlayerOfTextTrackChanges()
3332{
3333    if (!m_textTracks || !m_textTracks->length() || !platformTextTrackMenu())
3334        return;
3335
3336    m_platformMenu->tracksDidChange();
3337}
3338
3339PlatformTextTrackMenuInterface* HTMLMediaElement::platformTextTrackMenu()
3340{
3341    if (m_platformMenu)
3342        return m_platformMenu.get();
3343
3344    if (!m_player || !m_player->implementsTextTrackControls())
3345        return 0;
3346
3347    m_platformMenu = m_player->textTrackMenu();
3348    if (!m_platformMenu)
3349        return 0;
3350
3351    m_platformMenu->setClient(this);
3352
3353    return m_platformMenu.get();
3354}
3355#endif // #if USE(PLATFORM_TEXT_TRACK_MENU)
3356
3357void HTMLMediaElement::closeCaptionTracksChanged()
3358{
3359    if (hasMediaControls())
3360        mediaControls()->closedCaptionTracksChanged();
3361
3362#if USE(PLATFORM_TEXT_TRACK_MENU)
3363    if (m_player && m_player->implementsTextTrackControls())
3364        scheduleDelayedAction(TextTrackChangesNotification);
3365#endif
3366}
3367
3368void HTMLMediaElement::addAudioTrack(PassRefPtr<AudioTrack> track)
3369{
3370    if (!RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled())
3371        return;
3372
3373    audioTracks()->append(track);
3374}
3375
3376void HTMLMediaElement::addTextTrack(PassRefPtr<TextTrack> track)
3377{
3378    if (!RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled())
3379        return;
3380
3381    textTracks()->append(track);
3382
3383    closeCaptionTracksChanged();
3384}
3385
3386void HTMLMediaElement::addVideoTrack(PassRefPtr<VideoTrack> track)
3387{
3388    if (!RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled())
3389        return;
3390
3391    videoTracks()->append(track);
3392}
3393
3394void HTMLMediaElement::removeAudioTrack(AudioTrack* track)
3395{
3396    if (!RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled())
3397        return;
3398
3399    m_audioTracks->remove(track);
3400}
3401
3402void HTMLMediaElement::removeTextTrack(TextTrack* track, bool scheduleEvent)
3403{
3404    if (!RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled())
3405        return;
3406
3407    TrackDisplayUpdateScope scope(this);
3408    TextTrackCueList* cues = track->cues();
3409    if (cues)
3410        textTrackRemoveCues(track, cues);
3411    track->clearClient();
3412    m_textTracks->remove(track, scheduleEvent);
3413
3414    closeCaptionTracksChanged();
3415}
3416
3417void HTMLMediaElement::removeVideoTrack(VideoTrack* track)
3418{
3419    if (!RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled())
3420        return;
3421
3422    m_videoTracks->remove(track);
3423}
3424
3425void HTMLMediaElement::forgetResourceSpecificTracks()
3426{
3427    while (m_audioTracks &&  m_audioTracks->length())
3428        removeAudioTrack(m_audioTracks->lastItem());
3429
3430    if (m_textTracks) {
3431        TrackDisplayUpdateScope scope(this);
3432        for (int i = m_textTracks->length() - 1; i >= 0; --i) {
3433            TextTrack* track = m_textTracks->item(i);
3434
3435            if (track->trackType() == TextTrack::InBand)
3436                removeTextTrack(track, false);
3437        }
3438    }
3439
3440    while (m_videoTracks &&  m_videoTracks->length())
3441        removeVideoTrack(m_videoTracks->lastItem());
3442}
3443
3444PassRefPtr<TextTrack> HTMLMediaElement::addTextTrack(const String& kind, const String& label, const String& language, ExceptionCode& ec)
3445{
3446    if (!RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled())
3447        return 0;
3448
3449    // 4.8.10.12.4 Text track API
3450    // The addTextTrack(kind, label, language) method of media elements, when invoked, must run the following steps:
3451
3452    // 1. If kind is not one of the following strings, then throw a SyntaxError exception and abort these steps
3453    if (!TextTrack::isValidKindKeyword(kind)) {
3454        ec = SYNTAX_ERR;
3455        return 0;
3456    }
3457
3458    // 2. If the label argument was omitted, let label be the empty string.
3459    // 3. If the language argument was omitted, let language be the empty string.
3460    // 4. Create a new TextTrack object.
3461
3462    // 5. Create a new text track corresponding to the new object, and set its text track kind to kind, its text
3463    // track label to label, its text track language to language...
3464    RefPtr<TextTrack> textTrack = TextTrack::create(ActiveDOMObject::scriptExecutionContext(), this, kind, emptyString(), label, language);
3465
3466    // Note, due to side effects when changing track parameters, we have to
3467    // first append the track to the text track list.
3468
3469    // 6. Add the new text track to the media element's list of text tracks.
3470    addTextTrack(textTrack);
3471
3472    // ... its text track readiness state to the text track loaded state ...
3473    textTrack->setReadinessState(TextTrack::Loaded);
3474
3475    // ... its text track mode to the text track hidden mode, and its text track list of cues to an empty list ...
3476    textTrack->setMode(TextTrack::hiddenKeyword());
3477
3478    return textTrack.release();
3479}
3480
3481AudioTrackList* HTMLMediaElement::audioTracks()
3482{
3483    if (!RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled())
3484        return 0;
3485
3486    if (!m_audioTracks)
3487        m_audioTracks = AudioTrackList::create(this, ActiveDOMObject::scriptExecutionContext());
3488
3489    return m_audioTracks.get();
3490}
3491
3492TextTrackList* HTMLMediaElement::textTracks()
3493{
3494    if (!RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled())
3495        return 0;
3496
3497    if (!m_textTracks)
3498        m_textTracks = TextTrackList::create(this, ActiveDOMObject::scriptExecutionContext());
3499
3500    return m_textTracks.get();
3501}
3502
3503VideoTrackList* HTMLMediaElement::videoTracks()
3504{
3505    if (!RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled())
3506        return 0;
3507
3508    if (!m_videoTracks)
3509        m_videoTracks = VideoTrackList::create(this, ActiveDOMObject::scriptExecutionContext());
3510
3511    return m_videoTracks.get();
3512}
3513
3514void HTMLMediaElement::didAddTextTrack(HTMLTrackElement* trackElement)
3515{
3516    ASSERT(trackElement->hasTagName(trackTag));
3517
3518    if (!RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled())
3519        return;
3520
3521    // 4.8.10.12.3 Sourcing out-of-band text tracks
3522    // When a track element's parent element changes and the new parent is a media element,
3523    // then the user agent must add the track element's corresponding text track to the
3524    // media element's list of text tracks ... [continues in TextTrackList::append]
3525    RefPtr<TextTrack> textTrack = trackElement->track();
3526    if (!textTrack)
3527        return;
3528
3529    addTextTrack(textTrack.release());
3530
3531    // Do not schedule the track loading until parsing finishes so we don't start before all tracks
3532    // in the markup have been added.
3533    if (!m_parsingInProgress)
3534        scheduleDelayedAction(ConfigureTextTracks);
3535
3536    if (hasMediaControls())
3537        mediaControls()->closedCaptionTracksChanged();
3538}
3539
3540void HTMLMediaElement::didRemoveTextTrack(HTMLTrackElement* trackElement)
3541{
3542    ASSERT(trackElement->hasTagName(trackTag));
3543
3544    if (!RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled())
3545        return;
3546
3547#if !LOG_DISABLED
3548    if (trackElement->hasTagName(trackTag)) {
3549        URL url = trackElement->getNonEmptyURLAttribute(srcAttr);
3550        LOG(Media, "HTMLMediaElement::didRemoveTrack - 'src' is %s", urlForLoggingMedia(url).utf8().data());
3551    }
3552#endif
3553
3554    RefPtr<TextTrack> textTrack = trackElement->track();
3555    if (!textTrack)
3556        return;
3557
3558    textTrack->setHasBeenConfigured(false);
3559
3560    if (!m_textTracks)
3561        return;
3562
3563    // 4.8.10.12.3 Sourcing out-of-band text tracks
3564    // When a track element's parent element changes and the old parent was a media element,
3565    // then the user agent must remove the track element's corresponding text track from the
3566    // media element's list of text tracks.
3567    removeTextTrack(textTrack.get());
3568
3569    size_t index = m_textTracksWhenResourceSelectionBegan.find(textTrack.get());
3570    if (index != notFound)
3571        m_textTracksWhenResourceSelectionBegan.remove(index);
3572}
3573
3574void HTMLMediaElement::configureTextTrackGroup(const TrackGroup& group)
3575{
3576    ASSERT(group.tracks.size());
3577
3578    LOG(Media, "HTMLMediaElement::configureTextTrackGroup");
3579
3580    Page* page = document().page();
3581    CaptionUserPreferences* captionPreferences = page? page->group().captionPreferences() : 0;
3582    CaptionUserPreferences::CaptionDisplayMode displayMode = captionPreferences ? captionPreferences->captionDisplayMode() : CaptionUserPreferences::Automatic;
3583
3584    // First, find the track in the group that should be enabled (if any).
3585    Vector<RefPtr<TextTrack>> currentlyEnabledTracks;
3586    RefPtr<TextTrack> trackToEnable;
3587    RefPtr<TextTrack> defaultTrack;
3588    RefPtr<TextTrack> fallbackTrack;
3589    RefPtr<TextTrack> forcedSubitleTrack;
3590    int highestTrackScore = 0;
3591    int highestForcedScore = 0;
3592
3593    // If there is a visible track, it has already been configured so it won't be considered in the loop below. We don't want to choose another
3594    // track if it is less suitable, and we do want to disable it if another track is more suitable.
3595    int alreadyVisibleTrackScore = 0;
3596    if (group.visibleTrack && captionPreferences) {
3597        alreadyVisibleTrackScore = captionPreferences->textTrackSelectionScore(group.visibleTrack.get(), this);
3598        currentlyEnabledTracks.append(group.visibleTrack);
3599    }
3600
3601    for (size_t i = 0; i < group.tracks.size(); ++i) {
3602        RefPtr<TextTrack> textTrack = group.tracks[i];
3603
3604        if (m_processingPreferenceChange && textTrack->mode() == TextTrack::showingKeyword())
3605            currentlyEnabledTracks.append(textTrack);
3606
3607        int trackScore = captionPreferences ? captionPreferences->textTrackSelectionScore(textTrack.get(), this) : 0;
3608        LOG(Media, "HTMLMediaElement::configureTextTrackGroup -  '%s' track with language '%s' has score %i", textTrack->kind().string().utf8().data(), textTrack->language().string().utf8().data(), trackScore);
3609
3610        if (trackScore) {
3611
3612            // * If the text track kind is { [subtitles or captions] [descriptions] } and the user has indicated an interest in having a
3613            // track with this text track kind, text track language, and text track label enabled, and there is no
3614            // other text track in the media element's list of text tracks with a text track kind of either subtitles
3615            // or captions whose text track mode is showing
3616            // ...
3617            // * If the text track kind is chapters and the text track language is one that the user agent has reason
3618            // to believe is appropriate for the user, and there is no other text track in the media element's list of
3619            // text tracks with a text track kind of chapters whose text track mode is showing
3620            //    Let the text track mode be showing.
3621            if (trackScore > highestTrackScore && trackScore > alreadyVisibleTrackScore) {
3622                highestTrackScore = trackScore;
3623                trackToEnable = textTrack;
3624            }
3625
3626            if (!defaultTrack && textTrack->isDefault())
3627                defaultTrack = textTrack;
3628            if (!defaultTrack && !fallbackTrack)
3629                fallbackTrack = textTrack;
3630            if (textTrack->containsOnlyForcedSubtitles() && trackScore > highestForcedScore) {
3631                forcedSubitleTrack = textTrack;
3632                highestForcedScore = trackScore;
3633            }
3634        } else if (!group.visibleTrack && !defaultTrack && textTrack->isDefault()) {
3635            // * If the track element has a default attribute specified, and there is no other text track in the media
3636            // element's list of text tracks whose text track mode is showing or showing by default
3637            //    Let the text track mode be showing by default.
3638            if (group.kind != TrackGroup::CaptionsAndSubtitles || displayMode != CaptionUserPreferences::ForcedOnly)
3639                defaultTrack = textTrack;
3640        }
3641    }
3642
3643    if (!trackToEnable && defaultTrack)
3644        trackToEnable = defaultTrack;
3645
3646    // If no track matches the user's preferred language, none was marked as 'default', and there is a forced subtitle track
3647    // in the same language as the language of the primary audio track, enable it.
3648    if (!trackToEnable && forcedSubitleTrack)
3649        trackToEnable = forcedSubitleTrack;
3650
3651    // If no track matches, don't disable an already visible track unless preferences say they all should be off.
3652    if (group.kind != TrackGroup::CaptionsAndSubtitles || displayMode != CaptionUserPreferences::ForcedOnly) {
3653        if (!trackToEnable && !defaultTrack && group.visibleTrack)
3654            trackToEnable = group.visibleTrack;
3655    }
3656
3657    // If no track matches the user's preferred language and non was marked 'default', enable the first track
3658    // because the user has explicitly stated a preference for this kind of track.
3659    if (!trackToEnable && fallbackTrack)
3660        trackToEnable = fallbackTrack;
3661
3662    if (trackToEnable)
3663        m_subtitleTrackLanguage = trackToEnable->language();
3664    else
3665        m_subtitleTrackLanguage = emptyString();
3666
3667    if (currentlyEnabledTracks.size()) {
3668        for (size_t i = 0; i < currentlyEnabledTracks.size(); ++i) {
3669            RefPtr<TextTrack> textTrack = currentlyEnabledTracks[i];
3670            if (textTrack != trackToEnable)
3671                textTrack->setMode(TextTrack::disabledKeyword());
3672        }
3673    }
3674
3675    if (trackToEnable) {
3676        trackToEnable->setMode(TextTrack::showingKeyword());
3677
3678        // If user preferences indicate we should always display captions, make sure we reflect the
3679        // proper status via the webkitClosedCaptionsVisible API call:
3680        if (!webkitClosedCaptionsVisible() && closedCaptionsVisible() && displayMode == CaptionUserPreferences::AlwaysOn)
3681            m_webkitLegacyClosedCaptionOverride = true;
3682    }
3683
3684    updateCaptionContainer();
3685
3686    m_processingPreferenceChange = false;
3687}
3688
3689static JSC::JSValue controllerJSValue(JSC::ExecState& exec, JSDOMGlobalObject& globalObject, HTMLMediaElement& media)
3690{
3691    auto mediaJSWrapper = toJS(&exec, &globalObject, &media);
3692
3693    // Retrieve the controller through the JS object graph
3694    JSC::JSObject* mediaJSWrapperObject = JSC::jsDynamicCast<JSC::JSObject*>(mediaJSWrapper);
3695    if (!mediaJSWrapperObject)
3696        return JSC::jsNull();
3697
3698    JSC::Identifier controlsHost(&exec.vm(), "controlsHost");
3699    JSC::JSValue controlsHostJSWrapper = mediaJSWrapperObject->get(&exec, controlsHost);
3700    if (exec.hadException())
3701        return JSC::jsNull();
3702
3703    JSC::JSObject* controlsHostJSWrapperObject = JSC::jsDynamicCast<JSC::JSObject*>(controlsHostJSWrapper);
3704    if (!controlsHostJSWrapperObject)
3705        return JSC::jsNull();
3706
3707    JSC::Identifier controllerID(&exec.vm(), "controller");
3708    JSC::JSValue controllerJSWrapper = controlsHostJSWrapperObject->get(&exec, controllerID);
3709    if (exec.hadException())
3710        return JSC::jsNull();
3711
3712    return controllerJSWrapper;
3713}
3714
3715void HTMLMediaElement::updateCaptionContainer()
3716{
3717    LOG(Media, "HTMLMediaElement::updateCaptionContainer");
3718#if ENABLE(MEDIA_CONTROLS_SCRIPT)
3719    Page* page = document().page();
3720    if (!page)
3721        return;
3722
3723    DOMWrapperWorld& world = ensureIsolatedWorld();
3724
3725    if (!ensureMediaControlsInjectedScript())
3726        return;
3727
3728    ensureUserAgentShadowRoot();
3729
3730    if (!m_mediaControlsHost)
3731        m_mediaControlsHost = MediaControlsHost::create(this);
3732
3733    ScriptController& scriptController = page->mainFrame().script();
3734    JSDOMGlobalObject* globalObject = JSC::jsCast<JSDOMGlobalObject*>(scriptController.globalObject(world));
3735    JSC::ExecState* exec = globalObject->globalExec();
3736    JSC::JSLockHolder lock(exec);
3737
3738    JSC::JSValue controllerValue = controllerJSValue(*exec, *globalObject, *this);
3739    JSC::JSObject* controllerObject = JSC::jsDynamicCast<JSC::JSObject*>(controllerValue);
3740    if (!controllerObject)
3741        return;
3742
3743    // The media controls script must provide a method on the Controller object with the following details.
3744    // Name: updateCaptionContainer
3745    // Parameters:
3746    //     None
3747    // Return value:
3748    //     None
3749    JSC::JSValue methodValue = controllerObject->get(exec, JSC::Identifier(exec, "updateCaptionContainer"));
3750    JSC::JSObject* methodObject = JSC::jsDynamicCast<JSC::JSObject*>(methodValue);
3751    if (!methodObject)
3752        return;
3753
3754    JSC::CallData callData;
3755    JSC::CallType callType = methodObject->methodTable()->getCallData(methodObject, callData);
3756    if (callType == JSC::CallTypeNone)
3757        return;
3758
3759    JSC::MarkedArgumentBuffer noArguments;
3760    JSC::call(exec, methodObject, callType, callData, controllerObject, noArguments);
3761
3762    if (exec->hadException())
3763        exec->clearException();
3764#endif
3765}
3766
3767void HTMLMediaElement::setSelectedTextTrack(TextTrack* trackToSelect)
3768{
3769    TextTrackList* trackList = textTracks();
3770    if (!trackList || !trackList->length())
3771        return;
3772
3773    if (trackToSelect != TextTrack::captionMenuOffItem() && trackToSelect != TextTrack::captionMenuAutomaticItem()) {
3774        if (!trackList->contains(trackToSelect))
3775            return;
3776
3777        for (int i = 0, length = trackList->length(); i < length; ++i) {
3778            TextTrack* track = trackList->item(i);
3779            if (!trackToSelect || track != trackToSelect)
3780                track->setMode(TextTrack::disabledKeyword());
3781            else
3782                track->setMode(TextTrack::showingKeyword());
3783        }
3784    } else if (trackToSelect == TextTrack::captionMenuOffItem()) {
3785        for (int i = 0, length = trackList->length(); i < length; ++i)
3786            trackList->item(i)->setMode(TextTrack::disabledKeyword());
3787    }
3788
3789    CaptionUserPreferences* captionPreferences = document().page() ? document().page()->group().captionPreferences() : 0;
3790    if (!captionPreferences)
3791        return;
3792
3793    CaptionUserPreferences::CaptionDisplayMode displayMode = captionPreferences->captionDisplayMode();
3794    if (trackToSelect == TextTrack::captionMenuOffItem())
3795        displayMode = CaptionUserPreferences::ForcedOnly;
3796    else if (trackToSelect == TextTrack::captionMenuAutomaticItem())
3797        displayMode = CaptionUserPreferences::Automatic;
3798    else {
3799        displayMode = CaptionUserPreferences::AlwaysOn;
3800        if (trackToSelect->language().length())
3801            captionPreferences->setPreferredLanguage(trackToSelect->language());
3802    }
3803
3804    captionPreferences->setCaptionDisplayMode(displayMode);
3805}
3806
3807void HTMLMediaElement::configureTextTracks()
3808{
3809    TrackGroup captionAndSubtitleTracks(TrackGroup::CaptionsAndSubtitles);
3810    TrackGroup descriptionTracks(TrackGroup::Description);
3811    TrackGroup chapterTracks(TrackGroup::Chapter);
3812    TrackGroup metadataTracks(TrackGroup::Metadata);
3813    TrackGroup otherTracks(TrackGroup::Other);
3814
3815    if (!m_textTracks)
3816        return;
3817
3818    for (size_t i = 0; i < m_textTracks->length(); ++i) {
3819        RefPtr<TextTrack> textTrack = m_textTracks->item(i);
3820        if (!textTrack)
3821            continue;
3822
3823        String kind = textTrack->kind();
3824        TrackGroup* currentGroup;
3825        if (kind == TextTrack::subtitlesKeyword() || kind == TextTrack::captionsKeyword() || kind == TextTrack::forcedKeyword())
3826            currentGroup = &captionAndSubtitleTracks;
3827        else if (kind == TextTrack::descriptionsKeyword())
3828            currentGroup = &descriptionTracks;
3829        else if (kind == TextTrack::chaptersKeyword())
3830            currentGroup = &chapterTracks;
3831        else if (kind == TextTrack::metadataKeyword())
3832            currentGroup = &metadataTracks;
3833        else
3834            currentGroup = &otherTracks;
3835
3836        if (!currentGroup->visibleTrack && textTrack->mode() == TextTrack::showingKeyword())
3837            currentGroup->visibleTrack = textTrack;
3838        if (!currentGroup->defaultTrack && textTrack->isDefault())
3839            currentGroup->defaultTrack = textTrack;
3840
3841        // Do not add this track to the group if it has already been automatically configured
3842        // as we only want to call configureTextTrack once per track so that adding another
3843        // track after the initial configuration doesn't reconfigure every track - only those
3844        // that should be changed by the new addition. For example all metadata tracks are
3845        // disabled by default, and we don't want a track that has been enabled by script
3846        // to be disabled automatically when a new metadata track is added later.
3847        if (textTrack->hasBeenConfigured())
3848            continue;
3849
3850        if (textTrack->language().length())
3851            currentGroup->hasSrcLang = true;
3852        currentGroup->tracks.append(textTrack);
3853    }
3854
3855    if (captionAndSubtitleTracks.tracks.size())
3856        configureTextTrackGroup(captionAndSubtitleTracks);
3857    if (descriptionTracks.tracks.size())
3858        configureTextTrackGroup(descriptionTracks);
3859    if (chapterTracks.tracks.size())
3860        configureTextTrackGroup(chapterTracks);
3861    if (metadataTracks.tracks.size())
3862        configureTextTrackGroup(metadataTracks);
3863    if (otherTracks.tracks.size())
3864        configureTextTrackGroup(otherTracks);
3865
3866    configureTextTrackDisplay();
3867    if (hasMediaControls())
3868        mediaControls()->closedCaptionTracksChanged();
3869}
3870#endif
3871
3872bool HTMLMediaElement::havePotentialSourceChild()
3873{
3874    // Stash the current <source> node and next nodes so we can restore them after checking
3875    // to see there is another potential.
3876    RefPtr<HTMLSourceElement> currentSourceNode = m_currentSourceNode;
3877    RefPtr<Node> nextNode = m_nextChildNodeToConsider;
3878
3879    URL nextURL = selectNextSourceChild(0, 0, DoNothing);
3880
3881    m_currentSourceNode = currentSourceNode;
3882    m_nextChildNodeToConsider = nextNode;
3883
3884    return nextURL.isValid();
3885}
3886
3887URL HTMLMediaElement::selectNextSourceChild(ContentType* contentType, String* keySystem, InvalidURLAction actionIfInvalid)
3888{
3889#if !LOG_DISABLED
3890    // Don't log if this was just called to find out if there are any valid <source> elements.
3891    bool shouldLog = actionIfInvalid != DoNothing;
3892    if (shouldLog)
3893        LOG(Media, "HTMLMediaElement::selectNextSourceChild");
3894#endif
3895
3896    if (!m_nextChildNodeToConsider) {
3897#if !LOG_DISABLED
3898        if (shouldLog)
3899            LOG(Media, "HTMLMediaElement::selectNextSourceChild -> 0x0000, \"\"");
3900#endif
3901        return URL();
3902    }
3903
3904    URL mediaURL;
3905    HTMLSourceElement* source = 0;
3906    String type;
3907    String system;
3908    bool lookingForStartNode = m_nextChildNodeToConsider;
3909    bool canUseSourceElement = false;
3910    bool okToLoadSourceURL;
3911
3912    NodeVector potentialSourceNodes;
3913    getChildNodes(*this, potentialSourceNodes);
3914
3915    for (unsigned i = 0; !canUseSourceElement && i < potentialSourceNodes.size(); ++i) {
3916        Node& node = potentialSourceNodes[i].get();
3917        if (lookingForStartNode && m_nextChildNodeToConsider != &node)
3918            continue;
3919        lookingForStartNode = false;
3920
3921        if (!node.hasTagName(sourceTag))
3922            continue;
3923        if (node.parentNode() != this)
3924            continue;
3925
3926        source = toHTMLSourceElement(&node);
3927
3928        // If candidate does not have a src attribute, or if its src attribute's value is the empty string ... jump down to the failed step below
3929        mediaURL = source->getNonEmptyURLAttribute(srcAttr);
3930#if !LOG_DISABLED
3931        if (shouldLog)
3932            LOG(Media, "HTMLMediaElement::selectNextSourceChild - 'src' is %s", urlForLoggingMedia(mediaURL).utf8().data());
3933#endif
3934        if (mediaURL.isEmpty())
3935            goto check_again;
3936
3937        if (source->fastHasAttribute(mediaAttr)) {
3938            MediaQueryEvaluator screenEval("screen", document().frame(), renderer() ? &renderer()->style() : nullptr);
3939            RefPtr<MediaQuerySet> media = MediaQuerySet::createAllowingDescriptionSyntax(source->media());
3940#if !LOG_DISABLED
3941            if (shouldLog)
3942                LOG(Media, "HTMLMediaElement::selectNextSourceChild - 'media' is %s", source->media().utf8().data());
3943#endif
3944            if (!screenEval.eval(media.get()))
3945                goto check_again;
3946        }
3947
3948        type = source->type();
3949        // FIXME(82965): Add support for keySystem in <source> and set system from source.
3950        if (type.isEmpty() && mediaURL.protocolIsData())
3951            type = mimeTypeFromDataURL(mediaURL);
3952        if (!type.isEmpty() || !system.isEmpty()) {
3953#if !LOG_DISABLED
3954            if (shouldLog)
3955                LOG(Media, "HTMLMediaElement::selectNextSourceChild - 'type' is '%s' - key system is '%s'", type.utf8().data(), system.utf8().data());
3956#endif
3957            MediaEngineSupportParameters parameters;
3958            ContentType contentType(type);
3959            parameters.type = contentType.type().lower();
3960            parameters.codecs = contentType.parameter(ASCIILiteral("codecs"));
3961            parameters.url = mediaURL;
3962#if ENABLE(ENCRYPTED_MEDIA)
3963            parameters.keySystem = system;
3964#endif
3965#if ENABLE(MEDIA_SOURCE)
3966            parameters.isMediaSource = mediaURL.protocolIs(mediaSourceBlobProtocol);
3967#endif
3968            if (!MediaPlayer::supportsType(parameters, this))
3969                goto check_again;
3970        }
3971
3972        // Is it safe to load this url?
3973        okToLoadSourceURL = isSafeToLoadURL(mediaURL, actionIfInvalid) && dispatchBeforeLoadEvent(mediaURL.string());
3974
3975        // A 'beforeload' event handler can mutate the DOM, so check to see if the source element is still a child node.
3976        if (node.parentNode() != this) {
3977            LOG(Media, "HTMLMediaElement::selectNextSourceChild : 'beforeload' removed current element");
3978            source = 0;
3979            goto check_again;
3980        }
3981
3982        if (!okToLoadSourceURL)
3983            goto check_again;
3984
3985        // Making it this far means the <source> looks reasonable.
3986        canUseSourceElement = true;
3987
3988check_again:
3989        if (!canUseSourceElement && actionIfInvalid == Complain && source)
3990            source->scheduleErrorEvent();
3991    }
3992
3993    if (canUseSourceElement) {
3994        if (contentType)
3995            *contentType = ContentType(type);
3996        if (keySystem)
3997            *keySystem = system;
3998        m_currentSourceNode = source;
3999        m_nextChildNodeToConsider = source->nextSibling();
4000    } else {
4001        m_currentSourceNode = 0;
4002        m_nextChildNodeToConsider = 0;
4003    }
4004
4005#if !LOG_DISABLED
4006    if (shouldLog)
4007        LOG(Media, "HTMLMediaElement::selectNextSourceChild -> %p, %s", m_currentSourceNode.get(), canUseSourceElement ? urlForLoggingMedia(mediaURL).utf8().data() : "");
4008#endif
4009    return canUseSourceElement ? mediaURL : URL();
4010}
4011
4012void HTMLMediaElement::sourceWasAdded(HTMLSourceElement* source)
4013{
4014    LOG(Media, "HTMLMediaElement::sourceWasAdded(%p)", source);
4015
4016#if !LOG_DISABLED
4017    if (source->hasTagName(sourceTag)) {
4018        URL url = source->getNonEmptyURLAttribute(srcAttr);
4019        LOG(Media, "HTMLMediaElement::sourceWasAdded - 'src' is %s", urlForLoggingMedia(url).utf8().data());
4020    }
4021#endif
4022
4023    // We should only consider a <source> element when there is not src attribute at all.
4024    if (fastHasAttribute(srcAttr))
4025        return;
4026
4027    // 4.8.8 - If a source element is inserted as a child of a media element that has no src
4028    // attribute and whose networkState has the value NETWORK_EMPTY, the user agent must invoke
4029    // the media element's resource selection algorithm.
4030    if (networkState() == HTMLMediaElement::NETWORK_EMPTY) {
4031        scheduleDelayedAction(LoadMediaResource);
4032        m_nextChildNodeToConsider = source;
4033        return;
4034    }
4035
4036    if (m_currentSourceNode && source == m_currentSourceNode->nextSibling()) {
4037        LOG(Media, "HTMLMediaElement::sourceWasAdded - <source> inserted immediately after current source");
4038        m_nextChildNodeToConsider = source;
4039        return;
4040    }
4041
4042    if (m_nextChildNodeToConsider)
4043        return;
4044
4045    // 4.8.9.5, resource selection algorithm, source elements section:
4046    // 21. Wait until the node after pointer is a node other than the end of the list. (This step might wait forever.)
4047    // 22. Asynchronously await a stable state...
4048    // 23. Set the element's delaying-the-load-event flag back to true (this delays the load event again, in case
4049    // it hasn't been fired yet).
4050    setShouldDelayLoadEvent(true);
4051
4052    // 24. Set the networkState back to NETWORK_LOADING.
4053    m_networkState = NETWORK_LOADING;
4054
4055    // 25. Jump back to the find next candidate step above.
4056    m_nextChildNodeToConsider = source;
4057    scheduleNextSourceChild();
4058}
4059
4060void HTMLMediaElement::sourceWasRemoved(HTMLSourceElement* source)
4061{
4062    LOG(Media, "HTMLMediaElement::sourceWasRemoved(%p)", source);
4063
4064#if !LOG_DISABLED
4065    if (source->hasTagName(sourceTag)) {
4066        URL url = source->getNonEmptyURLAttribute(srcAttr);
4067        LOG(Media, "HTMLMediaElement::sourceWasRemoved - 'src' is %s", urlForLoggingMedia(url).utf8().data());
4068    }
4069#endif
4070
4071    if (source != m_currentSourceNode && source != m_nextChildNodeToConsider)
4072        return;
4073
4074    if (source == m_nextChildNodeToConsider) {
4075        if (m_currentSourceNode)
4076            m_nextChildNodeToConsider = m_currentSourceNode->nextSibling();
4077        LOG(Media, "HTMLMediaElement::sourceRemoved - m_nextChildNodeToConsider set to %p", m_nextChildNodeToConsider.get());
4078    } else if (source == m_currentSourceNode) {
4079        // Clear the current source node pointer, but don't change the movie as the spec says:
4080        // 4.8.8 - Dynamically modifying a source element and its attribute when the element is already
4081        // inserted in a video or audio element will have no effect.
4082        m_currentSourceNode = 0;
4083        LOG(Media, "HTMLMediaElement::sourceRemoved - m_currentSourceNode set to 0");
4084    }
4085}
4086
4087void HTMLMediaElement::mediaPlayerTimeChanged(MediaPlayer*)
4088{
4089    LOG(Media, "HTMLMediaElement::mediaPlayerTimeChanged");
4090
4091#if ENABLE(VIDEO_TRACK)
4092    if (RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled())
4093        updateActiveTextTrackCues(currentTime());
4094#endif
4095
4096    beginProcessingMediaPlayerCallback();
4097
4098    invalidateCachedTime();
4099
4100    // 4.8.10.9 step 14 & 15.  Needed if no ReadyState change is associated with the seek.
4101    if (m_seeking && m_readyState >= HAVE_CURRENT_DATA && !m_player->seeking())
4102        finishSeek();
4103
4104    // Always call scheduleTimeupdateEvent when the media engine reports a time discontinuity,
4105    // it will only queue a 'timeupdate' event if we haven't already posted one at the current
4106    // movie time.
4107    else
4108        scheduleTimeupdateEvent(false);
4109
4110    double now = currentTime();
4111    double dur = duration();
4112    double playbackRate = effectivePlaybackRate();
4113
4114    // When the current playback position reaches the end of the media resource then the user agent must follow these steps:
4115    if (!std::isnan(dur) && dur) {
4116        // If the media element has a loop attribute specified and does not have a current media controller,
4117        if (loop() && !m_mediaController && playbackRate > 0) {
4118            m_sentEndEvent = false;
4119            // then seek to the earliest possible position of the media resource and abort these steps when the direction of
4120            // playback is forwards,
4121            if (now >= dur)
4122                seekInternal(0);
4123        } else if ((now <= 0 && playbackRate < 0) || (now >= dur && playbackRate > 0)) {
4124            // If the media element does not have a current media controller, and the media element
4125            // has still ended playback and paused is false,
4126            if (!m_mediaController && !m_paused) {
4127                // changes paused to true and fires a simple event named pause at the media element.
4128                m_paused = true;
4129                scheduleEvent(eventNames().pauseEvent);
4130                m_mediaSession->clientWillPausePlayback();
4131            }
4132            // Queue a task to fire a simple event named ended at the media element.
4133            if (!m_sentEndEvent) {
4134                m_sentEndEvent = true;
4135                scheduleEvent(eventNames().endedEvent);
4136            }
4137            // If the media element has a current media controller, then report the controller state
4138            // for the media element's current media controller.
4139            updateMediaController();
4140        } else
4141            m_sentEndEvent = false;
4142    } else {
4143#if PLATFORM(IOS)
4144        // The controller changes movie time directly instead of calling through here so we need
4145        // to post timeupdate events in response to time changes.
4146        scheduleTimeupdateEvent(false);
4147#endif
4148        m_sentEndEvent = false;
4149    }
4150
4151    updatePlayState();
4152    endProcessingMediaPlayerCallback();
4153}
4154
4155void HTMLMediaElement::mediaPlayerVolumeChanged(MediaPlayer*)
4156{
4157    LOG(Media, "HTMLMediaElement::mediaPlayerVolumeChanged");
4158
4159    beginProcessingMediaPlayerCallback();
4160    if (m_player) {
4161        double vol = m_player->volume();
4162        if (vol != m_volume) {
4163            m_volume = vol;
4164            updateVolume();
4165            scheduleEvent(eventNames().volumechangeEvent);
4166        }
4167    }
4168    endProcessingMediaPlayerCallback();
4169}
4170
4171void HTMLMediaElement::mediaPlayerMuteChanged(MediaPlayer*)
4172{
4173    LOG(Media, "HTMLMediaElement::mediaPlayerMuteChanged");
4174
4175    beginProcessingMediaPlayerCallback();
4176    if (m_player)
4177        setMuted(m_player->muted());
4178    endProcessingMediaPlayerCallback();
4179}
4180
4181void HTMLMediaElement::mediaPlayerDurationChanged(MediaPlayer* player)
4182{
4183    LOG(Media, "HTMLMediaElement::mediaPlayerDurationChanged");
4184
4185    beginProcessingMediaPlayerCallback();
4186
4187    scheduleEvent(eventNames().durationchangeEvent);
4188    mediaPlayerCharacteristicChanged(player);
4189
4190    double now = currentTime();
4191    double dur = duration();
4192    if (now > dur)
4193        seekInternal(dur);
4194
4195    endProcessingMediaPlayerCallback();
4196}
4197
4198void HTMLMediaElement::mediaPlayerRateChanged(MediaPlayer*)
4199{
4200    LOG(Media, "HTMLMediaElement::mediaPlayerRateChanged");
4201
4202    beginProcessingMediaPlayerCallback();
4203
4204    // Stash the rate in case the one we tried to set isn't what the engine is
4205    // using (eg. it can't handle the rate we set)
4206    m_playbackRate = m_player->rate();
4207    if (m_playing)
4208        invalidateCachedTime();
4209
4210    updateSleepDisabling();
4211
4212    endProcessingMediaPlayerCallback();
4213}
4214
4215void HTMLMediaElement::mediaPlayerPlaybackStateChanged(MediaPlayer*)
4216{
4217    LOG(Media, "HTMLMediaElement::mediaPlayerPlaybackStateChanged");
4218
4219    if (!m_player || m_pausedInternal)
4220        return;
4221
4222    beginProcessingMediaPlayerCallback();
4223    if (m_player->paused())
4224        pauseInternal();
4225    else
4226        playInternal();
4227    endProcessingMediaPlayerCallback();
4228}
4229
4230void HTMLMediaElement::mediaPlayerSawUnsupportedTracks(MediaPlayer*)
4231{
4232    LOG(Media, "HTMLMediaElement::mediaPlayerSawUnsupportedTracks");
4233
4234    // The MediaPlayer came across content it cannot completely handle.
4235    // This is normally acceptable except when we are in a standalone
4236    // MediaDocument. If so, tell the document what has happened.
4237    if (document().isMediaDocument())
4238        toMediaDocument(document()).mediaElementSawUnsupportedTracks();
4239}
4240
4241void HTMLMediaElement::mediaPlayerResourceNotSupported(MediaPlayer*)
4242{
4243    LOG(Media, "HTMLMediaElement::mediaPlayerResourceNotSupported");
4244
4245    // The MediaPlayer came across content which no installed engine supports.
4246    mediaLoadingFailed(MediaPlayer::FormatError);
4247}
4248
4249// MediaPlayerPresentation methods
4250void HTMLMediaElement::mediaPlayerRepaint(MediaPlayer*)
4251{
4252    beginProcessingMediaPlayerCallback();
4253    updateDisplayState();
4254    if (renderer())
4255        renderer()->repaint();
4256    endProcessingMediaPlayerCallback();
4257}
4258
4259void HTMLMediaElement::mediaPlayerSizeChanged(MediaPlayer*)
4260{
4261    LOG(Media, "HTMLMediaElement::mediaPlayerSizeChanged");
4262
4263    beginProcessingMediaPlayerCallback();
4264    if (renderer())
4265        renderer()->updateFromElement();
4266    endProcessingMediaPlayerCallback();
4267}
4268
4269bool HTMLMediaElement::mediaPlayerRenderingCanBeAccelerated(MediaPlayer*)
4270{
4271    if (renderer() && renderer()->isVideo())
4272        return renderer()->view().compositor().canAccelerateVideoRendering(toRenderVideo(*renderer()));
4273    return false;
4274}
4275
4276void HTMLMediaElement::mediaPlayerRenderingModeChanged(MediaPlayer*)
4277{
4278    LOG(Media, "HTMLMediaElement::mediaPlayerRenderingModeChanged");
4279
4280    // Kick off a fake recalcStyle that will update the compositing tree.
4281    setNeedsStyleRecalc(SyntheticStyleChange);
4282}
4283
4284#if PLATFORM(WIN) && USE(AVFOUNDATION)
4285GraphicsDeviceAdapter* HTMLMediaElement::mediaPlayerGraphicsDeviceAdapter(const MediaPlayer*) const
4286{
4287    if (!document().page())
4288        return 0;
4289
4290    return document().page()->chrome().client().graphicsDeviceAdapter();
4291}
4292#endif
4293
4294void HTMLMediaElement::mediaPlayerEngineUpdated(MediaPlayer*)
4295{
4296    LOG(Media, "HTMLMediaElement::mediaPlayerEngineUpdated");
4297    beginProcessingMediaPlayerCallback();
4298    if (renderer())
4299        renderer()->updateFromElement();
4300    endProcessingMediaPlayerCallback();
4301
4302#if ENABLE(MEDIA_SOURCE)
4303    m_droppedVideoFrames = 0;
4304#endif
4305
4306    m_havePreparedToPlay = false;
4307
4308#if PLATFORM(IOS)
4309    if (!m_player)
4310        return;
4311    m_player->setVideoFullscreenFrame(m_videoFullscreenFrame);
4312    m_player->setVideoFullscreenGravity(m_videoFullscreenGravity);
4313    m_player->setVideoFullscreenLayer(m_videoFullscreenLayer.get());
4314#endif
4315}
4316
4317void HTMLMediaElement::mediaPlayerFirstVideoFrameAvailable(MediaPlayer*)
4318{
4319    LOG(Media, "HTMLMediaElement::mediaPlayerFirstVideoFrameAvailable(%p) - current display mode = %i", this, (int)displayMode());
4320
4321    beginProcessingMediaPlayerCallback();
4322    if (displayMode() == PosterWaitingForVideo) {
4323        setDisplayMode(Video);
4324        mediaPlayerRenderingModeChanged(m_player.get());
4325    }
4326    endProcessingMediaPlayerCallback();
4327}
4328
4329void HTMLMediaElement::mediaPlayerCharacteristicChanged(MediaPlayer*)
4330{
4331    LOG(Media, "HTMLMediaElement::mediaPlayerCharacteristicChanged");
4332
4333    beginProcessingMediaPlayerCallback();
4334
4335#if ENABLE(VIDEO_TRACK)
4336    if (m_captionDisplayMode == CaptionUserPreferences::Automatic && m_subtitleTrackLanguage != m_player->languageOfPrimaryAudioTrack())
4337        markCaptionAndSubtitleTracksAsUnconfigured(AfterDelay);
4338#endif
4339
4340    if (potentiallyPlaying() && displayMode() == PosterWaitingForVideo) {
4341        setDisplayMode(Video);
4342        mediaPlayerRenderingModeChanged(m_player.get());
4343    }
4344
4345    if (hasMediaControls())
4346        mediaControls()->reset();
4347    if (renderer())
4348        renderer()->updateFromElement();
4349    endProcessingMediaPlayerCallback();
4350}
4351
4352PassRefPtr<TimeRanges> HTMLMediaElement::buffered() const
4353{
4354    if (!m_player)
4355        return TimeRanges::create();
4356
4357#if ENABLE(MEDIA_SOURCE)
4358    if (m_mediaSource)
4359        return TimeRanges::create(*m_mediaSource->buffered());
4360#endif
4361
4362    return TimeRanges::create(*m_player->buffered());
4363}
4364
4365PassRefPtr<TimeRanges> HTMLMediaElement::played()
4366{
4367    if (m_playing) {
4368        double time = currentTime();
4369        if (time > m_lastSeekTime)
4370            addPlayedRange(m_lastSeekTime, time);
4371    }
4372
4373    if (!m_playedTimeRanges)
4374        m_playedTimeRanges = TimeRanges::create();
4375
4376    return m_playedTimeRanges->copy();
4377}
4378
4379PassRefPtr<TimeRanges> HTMLMediaElement::seekable() const
4380{
4381    if (m_player)
4382        return TimeRanges::create(*m_player->seekable());
4383
4384    return TimeRanges::create();
4385}
4386
4387bool HTMLMediaElement::potentiallyPlaying() const
4388{
4389    if (isBlockedOnMediaController())
4390        return false;
4391
4392    if (!couldPlayIfEnoughData())
4393        return false;
4394
4395    if (m_readyState >= HAVE_FUTURE_DATA)
4396        return true;
4397
4398    return m_readyStateMaximum >= HAVE_FUTURE_DATA && m_readyState < HAVE_FUTURE_DATA;
4399}
4400
4401bool HTMLMediaElement::couldPlayIfEnoughData() const
4402{
4403    if (paused())
4404        return false;
4405
4406    if (endedPlayback())
4407        return false;
4408
4409    if (stoppedDueToErrors())
4410        return false;
4411
4412    if (pausedForUserInteraction())
4413        return false;
4414
4415    return true;
4416}
4417
4418bool HTMLMediaElement::endedPlayback() const
4419{
4420    double dur = duration();
4421    if (!m_player || std::isnan(dur))
4422        return false;
4423
4424    // 4.8.10.8 Playing the media resource
4425
4426    // A media element is said to have ended playback when the element's
4427    // readyState attribute is HAVE_METADATA or greater,
4428    if (m_readyState < HAVE_METADATA)
4429        return false;
4430
4431    // and the current playback position is the end of the media resource and the direction
4432    // of playback is forwards, Either the media element does not have a loop attribute specified,
4433    // or the media element has a current media controller.
4434    double now = currentTime();
4435    if (effectivePlaybackRate() > 0)
4436        return dur > 0 && now >= dur && (!loop() || m_mediaController);
4437
4438    // or the current playback position is the earliest possible position and the direction
4439    // of playback is backwards
4440    if (effectivePlaybackRate() < 0)
4441        return now <= 0;
4442
4443    return false;
4444}
4445
4446bool HTMLMediaElement::stoppedDueToErrors() const
4447{
4448    if (m_readyState >= HAVE_METADATA && m_error) {
4449        RefPtr<TimeRanges> seekableRanges = seekable();
4450        if (!seekableRanges->contain(currentTime()))
4451            return true;
4452    }
4453
4454    return false;
4455}
4456
4457bool HTMLMediaElement::pausedForUserInteraction() const
4458{
4459    if (m_mediaSession->state() == MediaSession::Interrupted)
4460        return true;
4461
4462    return false;
4463}
4464
4465double HTMLMediaElement::minTimeSeekable() const
4466{
4467    return m_player ? m_player->minTimeSeekable() : 0;
4468}
4469
4470double HTMLMediaElement::maxTimeSeekable() const
4471{
4472    return m_player ? m_player->maxTimeSeekable() : 0;
4473}
4474
4475void HTMLMediaElement::updateVolume()
4476{
4477#if PLATFORM(IOS)
4478    // Only the user can change audio volume so update the cached volume and post the changed event.
4479    float volume = m_player->volume();
4480    if (m_volume != volume) {
4481        m_volume = volume;
4482        scheduleEvent(eventNames().volumechangeEvent);
4483    }
4484#else
4485    if (!m_player)
4486        return;
4487
4488    // Avoid recursion when the player reports volume changes.
4489    if (!processingMediaPlayerCallback()) {
4490        Page* page = document().page();
4491        double volumeMultiplier = page ? page->mediaVolume() : 1;
4492        bool shouldMute = muted();
4493
4494        if (m_mediaController) {
4495            volumeMultiplier *= m_mediaController->volume();
4496            shouldMute = m_mediaController->muted();
4497        }
4498
4499        m_player->setMuted(shouldMute);
4500        if (m_volumeInitialized)
4501            m_player->setVolume(m_volume * volumeMultiplier);
4502    }
4503
4504    if (hasMediaControls())
4505        mediaControls()->changedVolume();
4506#endif
4507}
4508
4509void HTMLMediaElement::updatePlayState()
4510{
4511    if (!m_player)
4512        return;
4513
4514    if (m_pausedInternal) {
4515        if (!m_player->paused())
4516            m_player->pause();
4517        refreshCachedTime();
4518        m_playbackProgressTimer.stop();
4519        if (hasMediaControls())
4520            mediaControls()->playbackStopped();
4521        m_activityToken = nullptr;
4522        return;
4523    }
4524
4525    bool shouldBePlaying = potentiallyPlaying();
4526    bool playerPaused = m_player->paused();
4527
4528    LOG(Media, "HTMLMediaElement::updatePlayState - shouldBePlaying = %s, playerPaused = %s",
4529        boolString(shouldBePlaying), boolString(playerPaused));
4530
4531    if (shouldBePlaying) {
4532        setDisplayMode(Video);
4533        invalidateCachedTime();
4534
4535        if (playerPaused) {
4536            m_mediaSession->clientWillBeginPlayback();
4537
4538            if (m_mediaSession->requiresFullscreenForVideoPlayback(*this) && !isFullscreen())
4539                enterFullscreen();
4540
4541            // Set rate, muted before calling play in case they were set before the media engine was setup.
4542            // The media engine should just stash the rate and muted values since it isn't already playing.
4543            m_player->setRate(effectivePlaybackRate());
4544            m_player->setMuted(muted());
4545
4546            m_player->play();
4547        }
4548
4549        if (hasMediaControls())
4550            mediaControls()->playbackStarted();
4551        if (document().page() && document().page()->pageThrottler())
4552            m_activityToken = document().page()->pageThrottler()->mediaActivityToken();
4553
4554        startPlaybackProgressTimer();
4555        m_playing = true;
4556
4557    } else {
4558        if (!playerPaused)
4559            m_player->pause();
4560        refreshCachedTime();
4561
4562        m_playbackProgressTimer.stop();
4563        m_playing = false;
4564        double time = currentTime();
4565        if (time > m_lastSeekTime)
4566            addPlayedRange(m_lastSeekTime, time);
4567
4568        if (couldPlayIfEnoughData())
4569            prepareToPlay();
4570
4571        if (hasMediaControls())
4572            mediaControls()->playbackStopped();
4573        m_activityToken = nullptr;
4574    }
4575
4576#if PLATFORM(IOS)
4577    m_requestingPlay = false;
4578#endif
4579
4580    updateMediaController();
4581
4582    if (renderer())
4583        renderer()->updateFromElement();
4584}
4585
4586void HTMLMediaElement::setPausedInternal(bool b)
4587{
4588    m_pausedInternal = b;
4589    updatePlayState();
4590}
4591
4592void HTMLMediaElement::stopPeriodicTimers()
4593{
4594    m_progressEventTimer.stop();
4595    m_playbackProgressTimer.stop();
4596}
4597
4598void HTMLMediaElement::userCancelledLoad()
4599{
4600    LOG(Media, "HTMLMediaElement::userCancelledLoad");
4601
4602    // FIXME: We should look to reconcile the iOS and non-iOS code (below).
4603#if PLATFORM(IOS)
4604    if (m_networkState == NETWORK_EMPTY || m_readyState >= HAVE_METADATA)
4605        return;
4606#else
4607    if (m_networkState == NETWORK_EMPTY || m_completelyLoaded)
4608        return;
4609#endif
4610
4611    // If the media data fetching process is aborted by the user:
4612
4613    // 1 - The user agent should cancel the fetching process.
4614    clearMediaPlayer(-1);
4615
4616    // 2 - Set the error attribute to a new MediaError object whose code attribute is set to MEDIA_ERR_ABORTED.
4617    m_error = MediaError::create(MediaError::MEDIA_ERR_ABORTED);
4618
4619    // 3 - Queue a task to fire a simple event named error at the media element.
4620    scheduleEvent(eventNames().abortEvent);
4621
4622#if ENABLE(MEDIA_SOURCE)
4623    closeMediaSource();
4624#endif
4625
4626    // 4 - If the media element's readyState attribute has a value equal to HAVE_NOTHING, set the
4627    // element's networkState attribute to the NETWORK_EMPTY value and queue a task to fire a
4628    // simple event named emptied at the element. Otherwise, set the element's networkState
4629    // attribute to the NETWORK_IDLE value.
4630    if (m_readyState == HAVE_NOTHING) {
4631        m_networkState = NETWORK_EMPTY;
4632        scheduleEvent(eventNames().emptiedEvent);
4633    }
4634    else
4635        m_networkState = NETWORK_IDLE;
4636
4637    // 5 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
4638    setShouldDelayLoadEvent(false);
4639
4640    // 6 - Abort the overall resource selection algorithm.
4641    m_currentSourceNode = 0;
4642
4643    // Reset m_readyState since m_player is gone.
4644    m_readyState = HAVE_NOTHING;
4645    updateMediaController();
4646#if ENABLE(VIDEO_TRACK)
4647    if (RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled())
4648        updateActiveTextTrackCues(0);
4649#endif
4650}
4651
4652void HTMLMediaElement::clearMediaPlayer(int flags)
4653{
4654#if USE(PLATFORM_TEXT_TRACK_MENU)
4655    if (platformTextTrackMenu()) {
4656        m_platformMenu->setClient(0);
4657        m_platformMenu = 0;
4658    }
4659#endif
4660
4661#if ENABLE(VIDEO_TRACK)
4662    forgetResourceSpecificTracks();
4663#endif
4664
4665#if ENABLE(MEDIA_SOURCE)
4666    closeMediaSource();
4667#endif
4668
4669    m_player.clear();
4670
4671    stopPeriodicTimers();
4672    m_loadTimer.stop();
4673
4674    clearFlags(m_pendingActionFlags, flags);
4675    m_loadState = WaitingForSource;
4676
4677#if ENABLE(VIDEO_TRACK)
4678    if (m_textTracks)
4679        configureTextTrackDisplay();
4680#endif
4681
4682    updateSleepDisabling();
4683}
4684
4685bool HTMLMediaElement::canSuspend() const
4686{
4687    return true;
4688}
4689
4690void HTMLMediaElement::stop()
4691{
4692    LOG(Media, "HTMLMediaElement::stop");
4693    if (m_isFullscreen)
4694        exitFullscreen();
4695
4696    m_inActiveDocument = false;
4697
4698    // Stop the playback without generating events
4699    m_playing = false;
4700    setPausedInternal(true);
4701
4702    userCancelledLoad();
4703
4704    if (renderer())
4705        renderer()->updateFromElement();
4706
4707    stopPeriodicTimers();
4708    cancelPendingEventsAndCallbacks();
4709
4710    m_asyncEventQueue.close();
4711
4712    // Once an active DOM object has been stopped it can not be restarted, so we can deallocate
4713    // the media player now. Note that userCancelledLoad will already have cleared the player
4714    // if the media was not fully loaded. This handles all other cases.
4715    m_player.clear();
4716
4717    updateSleepDisabling();
4718}
4719
4720void HTMLMediaElement::suspend(ReasonForSuspension why)
4721{
4722    LOG(Media, "HTMLMediaElement::suspend");
4723
4724    switch (why)
4725    {
4726        case DocumentWillBecomeInactive:
4727            stop();
4728            m_mediaSession->addBehaviorRestriction(HTMLMediaSession::RequirePageConsentToResumeMedia);
4729            break;
4730        case DocumentWillBePaused:
4731        case JavaScriptDebuggerPaused:
4732        case PageWillBeSuspended:
4733        case WillDeferLoading:
4734            // Do nothing, we don't pause media playback in these cases.
4735            break;
4736    }
4737}
4738
4739void HTMLMediaElement::resume()
4740{
4741    LOG(Media, "HTMLMediaElement::resume");
4742
4743    m_inActiveDocument = true;
4744
4745    if (!m_mediaSession->pageAllowsPlaybackAfterResuming(*this))
4746        document().addMediaCanStartListener(this);
4747    else
4748        setPausedInternal(false);
4749
4750    m_mediaSession->removeBehaviorRestriction(HTMLMediaSession::RequirePageConsentToResumeMedia);
4751
4752    if (m_error && m_error->code() == MediaError::MEDIA_ERR_ABORTED) {
4753        // Restart the load if it was aborted in the middle by moving the document to the page cache.
4754        // m_error is only left at MEDIA_ERR_ABORTED when the document becomes inactive (it is set to
4755        //  MEDIA_ERR_ABORTED while the abortEvent is being sent, but cleared immediately afterwards).
4756        // This behavior is not specified but it seems like a sensible thing to do.
4757#if PLATFORM(IOS)
4758        // FIXME: <rdar://problem/9751303> Merge: Does r1033092 need to be refixed in ToT?
4759#endif
4760        // As it is not safe to immedately start loading now, let's schedule a load.
4761        scheduleDelayedAction(LoadMediaResource);
4762    }
4763
4764    if (renderer())
4765        renderer()->updateFromElement();
4766}
4767
4768bool HTMLMediaElement::hasPendingActivity() const
4769{
4770    return (hasAudio() && isPlaying()) || m_asyncEventQueue.hasPendingEvents();
4771}
4772
4773void HTMLMediaElement::mediaVolumeDidChange()
4774{
4775    LOG(Media, "HTMLMediaElement::mediaVolumeDidChange");
4776    updateVolume();
4777}
4778
4779void HTMLMediaElement::visibilityStateChanged()
4780{
4781    LOG(Media, "HTMLMediaElement::visibilityStateChanged");
4782    m_elementIsHidden = document().hidden();
4783    updateSleepDisabling();
4784    m_mediaSession->visibilityChanged();
4785}
4786
4787#if ENABLE(VIDEO_TRACK)
4788bool HTMLMediaElement::requiresTextTrackRepresentation() const
4789{
4790    return m_isFullscreen && m_player ? m_player->requiresTextTrackRepresentation() : false;
4791}
4792
4793void HTMLMediaElement::setTextTrackRepresentation(TextTrackRepresentation* representation)
4794{
4795    if (m_player)
4796        m_player->setTextTrackRepresentation(representation);
4797}
4798
4799void HTMLMediaElement::syncTextTrackBounds()
4800{
4801    if (m_player)
4802        m_player->syncTextTrackBounds();
4803}
4804#endif // ENABLE(VIDEO_TRACK)
4805
4806#if ENABLE(IOS_AIRPLAY)
4807void HTMLMediaElement::webkitShowPlaybackTargetPicker()
4808{
4809    m_mediaSession->showPlaybackTargetPicker(*this);
4810}
4811
4812bool HTMLMediaElement::webkitCurrentPlaybackTargetIsWireless() const
4813{
4814    return m_mediaSession->currentPlaybackTargetIsWireless(*this);
4815}
4816
4817void HTMLMediaElement::mediaPlayerCurrentPlaybackTargetIsWirelessChanged(MediaPlayer*)
4818{
4819    LOG(Media, "HTMLMediaElement::mediaPlayerCurrentPlaybackTargetIsWirelessChanged - webkitCurrentPlaybackTargetIsWireless = %s", boolString(webkitCurrentPlaybackTargetIsWireless()));
4820    scheduleEvent(eventNames().webkitcurrentplaybacktargetiswirelesschangedEvent);
4821}
4822
4823void HTMLMediaElement::mediaPlayerPlaybackTargetAvailabilityChanged(MediaPlayer*)
4824{
4825    enqueuePlaybackTargetAvailabilityChangedEvent();
4826}
4827
4828bool HTMLMediaElement::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> listener, bool useCapture)
4829{
4830    if (eventType != eventNames().webkitplaybacktargetavailabilitychangedEvent)
4831        return Node::addEventListener(eventType, listener, useCapture);
4832
4833    bool isFirstAvailabilityChangedListener = !hasEventListeners(eventNames().webkitplaybacktargetavailabilitychangedEvent);
4834    if (!Node::addEventListener(eventType, listener, useCapture))
4835        return false;
4836
4837    if (isFirstAvailabilityChangedListener)
4838        m_mediaSession->setHasPlaybackTargetAvailabilityListeners(*this, true);
4839
4840    LOG(Media, "HTMLMediaElement::addEventListener('webkitplaybacktargetavailabilitychanged')");
4841
4842    enqueuePlaybackTargetAvailabilityChangedEvent(); // Ensure the event listener gets at least one event.
4843    return true;
4844}
4845
4846bool HTMLMediaElement::removeEventListener(const AtomicString& eventType, EventListener* listener, bool useCapture)
4847{
4848    if (eventType != eventNames().webkitplaybacktargetavailabilitychangedEvent)
4849        return Node::removeEventListener(eventType, listener, useCapture);
4850
4851    if (!Node::removeEventListener(eventType, listener, useCapture))
4852        return false;
4853
4854    bool didRemoveLastAvailabilityChangedListener = !hasEventListeners(eventNames().webkitplaybacktargetavailabilitychangedEvent);
4855    if (didRemoveLastAvailabilityChangedListener)
4856        m_mediaSession->setHasPlaybackTargetAvailabilityListeners(*this, false);
4857
4858    return true;
4859}
4860
4861void HTMLMediaElement::enqueuePlaybackTargetAvailabilityChangedEvent()
4862{
4863    LOG(Media, "HTMLMediaElement::enqueuePlaybackTargetAvailabilityChangedEvent");
4864    RefPtr<Event> event = WebKitPlaybackTargetAvailabilityEvent::create(eventNames().webkitplaybacktargetavailabilitychangedEvent, m_mediaSession->hasWirelessPlaybackTargets(*this));
4865    event->setTarget(this);
4866    m_asyncEventQueue.enqueueEvent(event.release());
4867}
4868#endif
4869
4870double HTMLMediaElement::minFastReverseRate() const
4871{
4872    return m_player ? m_player->minFastReverseRate() : 0;
4873}
4874
4875double HTMLMediaElement::maxFastForwardRate() const
4876{
4877    return m_player ? m_player->maxFastForwardRate() : 0;
4878}
4879
4880bool HTMLMediaElement::isFullscreen() const
4881{
4882    if (m_isFullscreen)
4883        return true;
4884
4885#if ENABLE(FULLSCREEN_API)
4886    if (document().webkitIsFullScreen() && document().webkitCurrentFullScreenElement() == this)
4887        return true;
4888#endif
4889
4890    return false;
4891}
4892
4893void HTMLMediaElement::toggleFullscreenState()
4894{
4895    LOG(Media, "HTMLMediaElement::toggleFullscreenState - isFullscreen() is %s", boolString(isFullscreen()));
4896
4897    if (isFullscreen())
4898        exitFullscreen();
4899    else
4900        enterFullscreen();
4901}
4902
4903void HTMLMediaElement::enterFullscreen()
4904{
4905    LOG(Media, "HTMLMediaElement::enterFullscreen");
4906    if (m_isFullscreen)
4907        return;
4908
4909#if ENABLE(FULLSCREEN_API)
4910    if (document().settings() && document().settings()->fullScreenEnabled()) {
4911        document().requestFullScreenForElement(this, 0, Document::ExemptIFrameAllowFullScreenRequirement);
4912        return;
4913    }
4914#endif
4915
4916    m_isFullscreen = true;
4917    if (hasMediaControls())
4918        mediaControls()->enteredFullscreen();
4919    if (document().page()) {
4920        if (document().page()->chrome().client().supportsFullscreenForNode(this)) {
4921            document().page()->chrome().client().enterFullscreenForNode(this);
4922            scheduleEvent(eventNames().webkitbeginfullscreenEvent);
4923        }
4924    }
4925}
4926
4927void HTMLMediaElement::exitFullscreen()
4928{
4929    LOG(Media, "HTMLMediaElement::exitFullscreen");
4930
4931#if ENABLE(FULLSCREEN_API)
4932    if (document().settings() && document().settings()->fullScreenEnabled()) {
4933        if (document().webkitIsFullScreen() && document().webkitCurrentFullScreenElement() == this)
4934            document().webkitCancelFullScreen();
4935        return;
4936    }
4937#endif
4938    ASSERT(m_isFullscreen);
4939    m_isFullscreen = false;
4940    if (hasMediaControls())
4941        mediaControls()->exitedFullscreen();
4942    if (document().page()) {
4943        if (m_mediaSession->requiresFullscreenForVideoPlayback(*this))
4944            pauseInternal();
4945
4946        if (document().page()->chrome().client().supportsFullscreenForNode(this)) {
4947            document().page()->chrome().client().exitFullscreenForNode(this);
4948            scheduleEvent(eventNames().webkitendfullscreenEvent);
4949        }
4950    }
4951}
4952
4953void HTMLMediaElement::didBecomeFullscreenElement()
4954{
4955    if (hasMediaControls())
4956        mediaControls()->enteredFullscreen();
4957}
4958
4959void HTMLMediaElement::willStopBeingFullscreenElement()
4960{
4961    if (hasMediaControls())
4962        mediaControls()->exitedFullscreen();
4963}
4964
4965PlatformMedia HTMLMediaElement::platformMedia() const
4966{
4967    return m_player ? m_player->platformMedia() : NoPlatformMedia;
4968}
4969
4970PlatformLayer* HTMLMediaElement::platformLayer() const
4971{
4972    return m_player ? m_player->platformLayer() : nullptr;
4973}
4974
4975#if PLATFORM(IOS)
4976void HTMLMediaElement::setVideoFullscreenLayer(PlatformLayer* platformLayer)
4977{
4978    m_videoFullscreenLayer = platformLayer;
4979    if (!m_player)
4980        return;
4981
4982    m_player->setVideoFullscreenLayer(platformLayer);
4983    setNeedsStyleRecalc(SyntheticStyleChange);
4984#if ENABLE(VIDEO_TRACK)
4985    if (RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled())
4986        updateTextTrackDisplay();
4987#endif
4988}
4989
4990void HTMLMediaElement::setVideoFullscreenFrame(FloatRect frame)
4991{
4992    m_videoFullscreenFrame = frame;
4993    if (m_player)
4994        m_player->setVideoFullscreenFrame(frame);
4995}
4996
4997void HTMLMediaElement::setVideoFullscreenGravity(MediaPlayer::VideoGravity gravity)
4998{
4999    m_videoFullscreenGravity = gravity;
5000    if (m_player)
5001        m_player->setVideoFullscreenGravity(gravity);
5002}
5003#endif
5004
5005bool HTMLMediaElement::hasClosedCaptions() const
5006{
5007    if (m_player && m_player->hasClosedCaptions())
5008        return true;
5009
5010#if ENABLE(VIDEO_TRACK)
5011    if (!RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled() || !m_textTracks)
5012        return false;
5013
5014    for (unsigned i = 0; i < m_textTracks->length(); ++i) {
5015        if (m_textTracks->item(i)->readinessState() == TextTrack::FailedToLoad)
5016            continue;
5017
5018        if (m_textTracks->item(i)->kind() == TextTrack::captionsKeyword()
5019            || m_textTracks->item(i)->kind() == TextTrack::subtitlesKeyword())
5020            return true;
5021    }
5022#endif
5023
5024    return false;
5025}
5026
5027bool HTMLMediaElement::closedCaptionsVisible() const
5028{
5029    return m_closedCaptionsVisible;
5030}
5031
5032#if ENABLE(VIDEO_TRACK)
5033void HTMLMediaElement::updateTextTrackDisplay()
5034{
5035#if ENABLE(MEDIA_CONTROLS_SCRIPT)
5036    ensureUserAgentShadowRoot();
5037    ASSERT(m_mediaControlsHost);
5038    m_mediaControlsHost->updateTextTrackContainer();
5039    return;
5040#endif
5041    if (!hasMediaControls() && !createMediaControls())
5042        return;
5043
5044    mediaControls()->updateTextTrackDisplay();
5045}
5046#endif
5047
5048void HTMLMediaElement::setClosedCaptionsVisible(bool closedCaptionVisible)
5049{
5050    LOG(Media, "HTMLMediaElement::setClosedCaptionsVisible(%s)", boolString(closedCaptionVisible));
5051
5052    m_closedCaptionsVisible = false;
5053
5054    if (!m_player || !hasClosedCaptions())
5055        return;
5056
5057    m_closedCaptionsVisible = closedCaptionVisible;
5058    m_player->setClosedCaptionsVisible(closedCaptionVisible);
5059
5060#if ENABLE(VIDEO_TRACK)
5061    if (RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled()) {
5062        markCaptionAndSubtitleTracksAsUnconfigured(Immediately);
5063        updateTextTrackDisplay();
5064    }
5065#else
5066    if (hasMediaControls())
5067        mediaControls()->changedClosedCaptionsVisibility();
5068#endif
5069}
5070
5071void HTMLMediaElement::setWebkitClosedCaptionsVisible(bool visible)
5072{
5073    m_webkitLegacyClosedCaptionOverride = visible;
5074    setClosedCaptionsVisible(visible);
5075}
5076
5077bool HTMLMediaElement::webkitClosedCaptionsVisible() const
5078{
5079    return m_webkitLegacyClosedCaptionOverride && m_closedCaptionsVisible;
5080}
5081
5082
5083bool HTMLMediaElement::webkitHasClosedCaptions() const
5084{
5085    return hasClosedCaptions();
5086}
5087
5088#if ENABLE(MEDIA_STATISTICS)
5089unsigned HTMLMediaElement::webkitAudioDecodedByteCount() const
5090{
5091    if (!m_player)
5092        return 0;
5093    return m_player->audioDecodedByteCount();
5094}
5095
5096unsigned HTMLMediaElement::webkitVideoDecodedByteCount() const
5097{
5098    if (!m_player)
5099        return 0;
5100    return m_player->videoDecodedByteCount();
5101}
5102#endif
5103
5104void HTMLMediaElement::mediaCanStart()
5105{
5106    LOG(Media, "HTMLMediaElement::mediaCanStart");
5107
5108    ASSERT(m_isWaitingUntilMediaCanStart || m_pausedInternal);
5109    if (m_isWaitingUntilMediaCanStart) {
5110        m_isWaitingUntilMediaCanStart = false;
5111        loadInternal();
5112    }
5113    if (m_pausedInternal)
5114        setPausedInternal(false);
5115}
5116
5117bool HTMLMediaElement::isURLAttribute(const Attribute& attribute) const
5118{
5119    return attribute.name() == srcAttr || HTMLElement::isURLAttribute(attribute);
5120}
5121
5122void HTMLMediaElement::setShouldDelayLoadEvent(bool shouldDelay)
5123{
5124    if (m_shouldDelayLoadEvent == shouldDelay)
5125        return;
5126
5127    LOG(Media, "HTMLMediaElement::setShouldDelayLoadEvent(%s)", boolString(shouldDelay));
5128
5129    m_shouldDelayLoadEvent = shouldDelay;
5130    if (shouldDelay)
5131        document().incrementLoadEventDelayCount();
5132    else
5133        document().decrementLoadEventDelayCount();
5134}
5135
5136
5137void HTMLMediaElement::getSitesInMediaCache(Vector<String>& sites)
5138{
5139    MediaPlayer::getSitesInMediaCache(sites);
5140}
5141
5142void HTMLMediaElement::clearMediaCache()
5143{
5144    MediaPlayer::clearMediaCache();
5145}
5146
5147void HTMLMediaElement::clearMediaCacheForSite(const String& site)
5148{
5149    MediaPlayer::clearMediaCacheForSite(site);
5150}
5151
5152void HTMLMediaElement::resetMediaEngines()
5153{
5154    MediaPlayer::resetMediaEngines();
5155}
5156
5157void HTMLMediaElement::privateBrowsingStateDidChange()
5158{
5159    if (!m_player)
5160        return;
5161
5162    bool privateMode = document().page() && document().page()->usesEphemeralSession();
5163    LOG(Media, "HTMLMediaElement::privateBrowsingStateDidChange(%s)", boolString(privateMode));
5164    m_player->setPrivateBrowsingMode(privateMode);
5165}
5166
5167MediaControls* HTMLMediaElement::mediaControls() const
5168{
5169#if ENABLE(MEDIA_CONTROLS_SCRIPT)
5170    return 0;
5171#else
5172    return toMediaControls(userAgentShadowRoot()->firstChild());
5173#endif
5174}
5175
5176bool HTMLMediaElement::hasMediaControls() const
5177{
5178#if ENABLE(MEDIA_CONTROLS_SCRIPT)
5179    return false;
5180#else
5181
5182    if (ShadowRoot* userAgent = userAgentShadowRoot()) {
5183        Node* node = userAgent->firstChild();
5184        ASSERT_WITH_SECURITY_IMPLICATION(!node || node->isMediaControls());
5185        return node;
5186    }
5187
5188    return false;
5189#endif
5190}
5191
5192bool HTMLMediaElement::createMediaControls()
5193{
5194#if ENABLE(MEDIA_CONTROLS_SCRIPT)
5195    ensureUserAgentShadowRoot();
5196    return false;
5197#else
5198    if (hasMediaControls())
5199        return true;
5200
5201    RefPtr<MediaControls> mediaControls = MediaControls::create(document());
5202    if (!mediaControls)
5203        return false;
5204
5205    mediaControls->setMediaController(m_mediaController ? m_mediaController.get() : static_cast<MediaControllerInterface*>(this));
5206    mediaControls->reset();
5207    if (isFullscreen())
5208        mediaControls->enteredFullscreen();
5209
5210    ensureUserAgentShadowRoot().appendChild(mediaControls, ASSERT_NO_EXCEPTION);
5211
5212    if (!controls() || !inDocument())
5213        mediaControls->hide();
5214
5215    return true;
5216#endif
5217}
5218
5219void HTMLMediaElement::configureMediaControls()
5220{
5221#if ENABLE(MEDIA_CONTROLS_SCRIPT)
5222    if (!controls() || !inDocument())
5223        return;
5224
5225    ensureUserAgentShadowRoot();
5226    return;
5227#endif
5228
5229    if (!controls() || !inDocument()) {
5230        if (hasMediaControls())
5231            mediaControls()->hide();
5232        return;
5233    }
5234
5235    if (!hasMediaControls() && !createMediaControls())
5236        return;
5237
5238    mediaControls()->show();
5239}
5240
5241#if ENABLE(VIDEO_TRACK)
5242void HTMLMediaElement::configureTextTrackDisplay(TextTrackVisibilityCheckType checkType)
5243{
5244    ASSERT(m_textTracks);
5245
5246    if (m_processingPreferenceChange)
5247        return;
5248
5249    LOG(Media, "HTMLMediaElement::configureTextTrackDisplay");
5250
5251    bool haveVisibleTextTrack = false;
5252    for (unsigned i = 0; i < m_textTracks->length(); ++i) {
5253        if (m_textTracks->item(i)->mode() == TextTrack::showingKeyword()) {
5254            haveVisibleTextTrack = true;
5255            break;
5256        }
5257    }
5258
5259    if (checkType == CheckTextTrackVisibility && m_haveVisibleTextTrack == haveVisibleTextTrack) {
5260        updateActiveTextTrackCues(currentTime());
5261        return;
5262    }
5263
5264    m_haveVisibleTextTrack = haveVisibleTextTrack;
5265    m_closedCaptionsVisible = m_haveVisibleTextTrack;
5266
5267#if ENABLE(MEDIA_CONTROLS_SCRIPT)
5268    if (!m_haveVisibleTextTrack)
5269        return;
5270
5271    ensureUserAgentShadowRoot();
5272    return;
5273#endif
5274
5275    if (!m_haveVisibleTextTrack && !hasMediaControls())
5276        return;
5277    if (!hasMediaControls() && !createMediaControls())
5278        return;
5279
5280    mediaControls()->changedClosedCaptionsVisibility();
5281
5282    if (RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled()) {
5283        updateTextTrackDisplay();
5284        updateActiveTextTrackCues(currentTime());
5285    }
5286}
5287
5288void HTMLMediaElement::captionPreferencesChanged()
5289{
5290    if (!isVideo())
5291        return;
5292
5293    if (hasMediaControls())
5294        mediaControls()->textTrackPreferencesChanged();
5295
5296#if ENABLE(MEDIA_CONTROLS_SCRIPT)
5297    if (m_mediaControlsHost)
5298        m_mediaControlsHost->updateCaptionDisplaySizes();
5299#endif
5300
5301    if (!document().page())
5302        return;
5303
5304    CaptionUserPreferences::CaptionDisplayMode displayMode = document().page()->group().captionPreferences()->captionDisplayMode();
5305    if (m_captionDisplayMode == displayMode)
5306        return;
5307
5308    m_captionDisplayMode = displayMode;
5309    setWebkitClosedCaptionsVisible(m_captionDisplayMode == CaptionUserPreferences::AlwaysOn);
5310}
5311
5312void HTMLMediaElement::markCaptionAndSubtitleTracksAsUnconfigured(ReconfigureMode mode)
5313{
5314    if (!m_textTracks)
5315        return;
5316
5317    LOG(Media, "HTMLMediaElement::markCaptionAndSubtitleTracksAsUnconfigured");
5318
5319    // Mark all tracks as not "configured" so that configureTextTracks()
5320    // will reconsider which tracks to display in light of new user preferences
5321    // (e.g. default tracks should not be displayed if the user has turned off
5322    // captions and non-default tracks should be displayed based on language
5323    // preferences if the user has turned captions on).
5324    for (unsigned i = 0; i < m_textTracks->length(); ++i) {
5325
5326        RefPtr<TextTrack> textTrack = m_textTracks->item(i);
5327        String kind = textTrack->kind();
5328
5329        if (kind == TextTrack::subtitlesKeyword() || kind == TextTrack::captionsKeyword())
5330            textTrack->setHasBeenConfigured(false);
5331    }
5332
5333    m_processingPreferenceChange = true;
5334    clearFlags(m_pendingActionFlags, ConfigureTextTracks);
5335    if (mode == Immediately)
5336        configureTextTracks();
5337    else
5338        scheduleDelayedAction(ConfigureTextTracks);
5339}
5340
5341#endif
5342
5343void HTMLMediaElement::createMediaPlayer()
5344{
5345#if ENABLE(WEB_AUDIO)
5346    if (m_audioSourceNode)
5347        m_audioSourceNode->lock();
5348#endif
5349
5350#if ENABLE(MEDIA_SOURCE)
5351    if (m_mediaSource)
5352        m_mediaSource->close();
5353#endif
5354
5355#if ENABLE(VIDEO_TRACK)
5356    forgetResourceSpecificTracks();
5357#endif
5358    m_player = MediaPlayer::create(this);
5359
5360#if ENABLE(WEB_AUDIO)
5361    if (m_audioSourceNode) {
5362        // When creating the player, make sure its AudioSourceProvider knows about the MediaElementAudioSourceNode.
5363        if (audioSourceProvider())
5364            audioSourceProvider()->setClient(m_audioSourceNode);
5365
5366        m_audioSourceNode->unlock();
5367    }
5368#endif
5369
5370#if ENABLE(IOS_AIRPLAY)
5371    if (hasEventListeners(eventNames().webkitplaybacktargetavailabilitychangedEvent)) {
5372        m_mediaSession->setHasPlaybackTargetAvailabilityListeners(*this, true);
5373        enqueuePlaybackTargetAvailabilityChangedEvent(); // Ensure the event listener gets at least one event.
5374    }
5375#endif
5376}
5377
5378#if ENABLE(WEB_AUDIO)
5379void HTMLMediaElement::setAudioSourceNode(MediaElementAudioSourceNode* sourceNode)
5380{
5381    m_audioSourceNode = sourceNode;
5382
5383    if (audioSourceProvider())
5384        audioSourceProvider()->setClient(m_audioSourceNode);
5385}
5386
5387AudioSourceProvider* HTMLMediaElement::audioSourceProvider()
5388{
5389    if (m_player)
5390        return m_player->audioSourceProvider();
5391
5392    return 0;
5393}
5394#endif
5395
5396const String& HTMLMediaElement::mediaGroup() const
5397{
5398    return m_mediaGroup;
5399}
5400
5401void HTMLMediaElement::setMediaGroup(const String& group)
5402{
5403    if (m_mediaGroup == group)
5404        return;
5405    m_mediaGroup = group;
5406
5407    // When a media element is created with a mediagroup attribute, and when a media element's mediagroup
5408    // attribute is set, changed, or removed, the user agent must run the following steps:
5409    // 1. Let m [this] be the media element in question.
5410    // 2. Let m have no current media controller, if it currently has one.
5411    setController(0);
5412
5413    // 3. If m's mediagroup attribute is being removed, then abort these steps.
5414    if (group.isNull() || group.isEmpty())
5415        return;
5416
5417    // 4. If there is another media element whose Document is the same as m's Document (even if one or both
5418    // of these elements are not actually in the Document),
5419    HashSet<HTMLMediaElement*> elements = documentToElementSetMap().get(&document());
5420    for (HashSet<HTMLMediaElement*>::iterator i = elements.begin(); i != elements.end(); ++i) {
5421        if (*i == this)
5422            continue;
5423
5424        // and which also has a mediagroup attribute, and whose mediagroup attribute has the same value as
5425        // the new value of m's mediagroup attribute,
5426        if ((*i)->mediaGroup() == group) {
5427            //  then let controller be that media element's current media controller.
5428            setController((*i)->controller());
5429            return;
5430        }
5431    }
5432
5433    // Otherwise, let controller be a newly created MediaController.
5434    setController(MediaController::create(document()));
5435}
5436
5437MediaController* HTMLMediaElement::controller() const
5438{
5439    return m_mediaController.get();
5440}
5441
5442void HTMLMediaElement::setController(PassRefPtr<MediaController> controller)
5443{
5444    if (m_mediaController)
5445        m_mediaController->removeMediaElement(this);
5446
5447    m_mediaController = controller;
5448
5449    if (m_mediaController)
5450        m_mediaController->addMediaElement(this);
5451
5452    if (hasMediaControls())
5453        mediaControls()->setMediaController(m_mediaController ? m_mediaController.get() : static_cast<MediaControllerInterface*>(this));
5454}
5455
5456void HTMLMediaElement::updateMediaController()
5457{
5458    if (m_mediaController)
5459        m_mediaController->reportControllerState();
5460}
5461
5462bool HTMLMediaElement::isBlocked() const
5463{
5464    // A media element is a blocked media element if its readyState attribute is in the
5465    // HAVE_NOTHING state, the HAVE_METADATA state, or the HAVE_CURRENT_DATA state,
5466    if (m_readyState <= HAVE_CURRENT_DATA)
5467        return true;
5468
5469    // or if the element has paused for user interaction.
5470    return pausedForUserInteraction();
5471}
5472
5473bool HTMLMediaElement::isBlockedOnMediaController() const
5474{
5475    if (!m_mediaController)
5476        return false;
5477
5478    // A media element is blocked on its media controller if the MediaController is a blocked
5479    // media controller,
5480    if (m_mediaController->isBlocked())
5481        return true;
5482
5483    // or if its media controller position is either before the media resource's earliest possible
5484    // position relative to the MediaController's timeline or after the end of the media resource
5485    // relative to the MediaController's timeline.
5486    double mediaControllerPosition = m_mediaController->currentTime();
5487    if (mediaControllerPosition < 0 || mediaControllerPosition > duration())
5488        return true;
5489
5490    return false;
5491}
5492
5493void HTMLMediaElement::prepareMediaFragmentURI()
5494{
5495    MediaFragmentURIParser fragmentParser(m_currentSrc);
5496    double dur = duration();
5497
5498    double start = fragmentParser.startTime();
5499    if (start != MediaFragmentURIParser::invalidTimeValue() && start > 0) {
5500        m_fragmentStartTime = start;
5501        if (m_fragmentStartTime > dur)
5502            m_fragmentStartTime = dur;
5503    } else
5504        m_fragmentStartTime = MediaPlayer::invalidTime();
5505
5506    double end = fragmentParser.endTime();
5507    if (end != MediaFragmentURIParser::invalidTimeValue() && end > 0 && end > m_fragmentStartTime) {
5508        m_fragmentEndTime = end;
5509        if (m_fragmentEndTime > dur)
5510            m_fragmentEndTime = dur;
5511    } else
5512        m_fragmentEndTime = MediaPlayer::invalidTime();
5513
5514    if (m_fragmentStartTime != MediaPlayer::invalidTime() && m_readyState < HAVE_FUTURE_DATA)
5515        prepareToPlay();
5516}
5517
5518void HTMLMediaElement::applyMediaFragmentURI()
5519{
5520    if (m_fragmentStartTime != MediaPlayer::invalidTime()) {
5521        m_sentEndEvent = false;
5522        seek(m_fragmentStartTime);
5523    }
5524}
5525
5526void HTMLMediaElement::updateSleepDisabling()
5527{
5528    if (!shouldDisableSleep() && m_sleepDisabler)
5529        m_sleepDisabler = nullptr;
5530    else if (shouldDisableSleep() && !m_sleepDisabler)
5531        m_sleepDisabler = DisplaySleepDisabler::create("com.apple.WebCore: HTMLMediaElement playback");
5532}
5533
5534bool HTMLMediaElement::shouldDisableSleep() const
5535{
5536#if !PLATFORM(COCOA)
5537    return false;
5538#endif
5539
5540    if (m_elementIsHidden)
5541        return false;
5542
5543    return m_player && !m_player->paused() && hasVideo() && hasAudio() && !loop();
5544}
5545
5546String HTMLMediaElement::mediaPlayerReferrer() const
5547{
5548    Frame* frame = document().frame();
5549    if (!frame)
5550        return String();
5551
5552    return SecurityPolicy::generateReferrerHeader(document().referrerPolicy(), m_currentSrc, frame->loader().outgoingReferrer());
5553}
5554
5555String HTMLMediaElement::mediaPlayerUserAgent() const
5556{
5557    Frame* frame = document().frame();
5558    if (!frame)
5559        return String();
5560
5561    return frame->loader().userAgent(m_currentSrc);
5562
5563}
5564
5565#if ENABLE(AVF_CAPTIONS)
5566Vector<RefPtr<PlatformTextTrack>> HTMLMediaElement::outOfBandTrackSources()
5567{
5568    Vector<RefPtr<PlatformTextTrack>> outOfBandTrackSources;
5569    for (auto& trackElement : childrenOfType<HTMLTrackElement>(*this)) {
5570
5571        if (!trackElement.fastHasAttribute(srcAttr))
5572            continue;
5573
5574        URL url = trackElement.getNonEmptyURLAttribute(srcAttr);
5575        if (url.isEmpty())
5576            continue;
5577
5578        if (!document().contentSecurityPolicy()->allowMediaFromSource(url))
5579            continue;
5580
5581        PlatformTextTrack::TrackKind platformKind = PlatformTextTrack::Caption;
5582        if (trackElement.kind() == TextTrack::captionsKeyword())
5583            platformKind = PlatformTextTrack::Caption;
5584        else if (trackElement.kind() == TextTrack::subtitlesKeyword())
5585            platformKind = PlatformTextTrack::Subtitle;
5586        else if (trackElement.kind() == TextTrack::descriptionsKeyword())
5587            platformKind = PlatformTextTrack::Description;
5588        else if (trackElement.kind() == TextTrack::forcedKeyword())
5589            platformKind = PlatformTextTrack::Forced;
5590        else
5591            continue;
5592
5593        const AtomicString& mode = trackElement.track()->mode();
5594
5595        PlatformTextTrack::TrackMode platformMode = PlatformTextTrack::Disabled;
5596        if (TextTrack::hiddenKeyword() == mode)
5597            platformMode = PlatformTextTrack::Hidden;
5598        else if (TextTrack::disabledKeyword() == mode)
5599            platformMode = PlatformTextTrack::Disabled;
5600        else if (TextTrack::showingKeyword() == mode)
5601            platformMode = PlatformTextTrack::Showing;
5602
5603        outOfBandTrackSources.append(PlatformTextTrack::createOutOfBand(trackElement.label(), trackElement.srclang(), url.string(), platformMode, platformKind, trackElement.track()->uniqueId(), trackElement.isDefault()));
5604    }
5605
5606    return outOfBandTrackSources;
5607}
5608#endif
5609
5610MediaPlayerClient::CORSMode HTMLMediaElement::mediaPlayerCORSMode() const
5611{
5612    if (!fastHasAttribute(HTMLNames::crossoriginAttr))
5613        return Unspecified;
5614    if (equalIgnoringCase(fastGetAttribute(HTMLNames::crossoriginAttr), "use-credentials"))
5615        return UseCredentials;
5616    return Anonymous;
5617}
5618
5619bool HTMLMediaElement::mediaPlayerNeedsSiteSpecificHacks() const
5620{
5621    Settings* settings = document().settings();
5622    return settings && settings->needsSiteSpecificQuirks();
5623}
5624
5625String HTMLMediaElement::mediaPlayerDocumentHost() const
5626{
5627    return document().url().host();
5628}
5629
5630void HTMLMediaElement::mediaPlayerEnterFullscreen()
5631{
5632    enterFullscreen();
5633}
5634
5635void HTMLMediaElement::mediaPlayerExitFullscreen()
5636{
5637    exitFullscreen();
5638}
5639
5640bool HTMLMediaElement::mediaPlayerIsFullscreen() const
5641{
5642    return isFullscreen();
5643}
5644
5645bool HTMLMediaElement::mediaPlayerIsFullscreenPermitted() const
5646{
5647    return m_mediaSession->fullscreenPermitted(*this);
5648}
5649
5650bool HTMLMediaElement::mediaPlayerIsVideo() const
5651{
5652    return isVideo();
5653}
5654
5655LayoutRect HTMLMediaElement::mediaPlayerContentBoxRect() const
5656{
5657    if (renderer())
5658        return renderer()->enclosingBox().contentBoxRect();
5659    return LayoutRect();
5660}
5661
5662void HTMLMediaElement::mediaPlayerSetSize(const IntSize& size)
5663{
5664    setIntegralAttribute(widthAttr, size.width());
5665    setIntegralAttribute(heightAttr, size.height());
5666}
5667
5668void HTMLMediaElement::mediaPlayerPause()
5669{
5670    pause();
5671}
5672
5673void HTMLMediaElement::mediaPlayerPlay()
5674{
5675    play();
5676}
5677
5678bool HTMLMediaElement::mediaPlayerPlatformVolumeConfigurationRequired() const
5679{
5680    return !m_volumeInitialized;
5681}
5682
5683bool HTMLMediaElement::mediaPlayerIsPaused() const
5684{
5685    return paused();
5686}
5687
5688bool HTMLMediaElement::mediaPlayerIsLooping() const
5689{
5690    return loop();
5691}
5692
5693HostWindow* HTMLMediaElement::mediaPlayerHostWindow()
5694{
5695    return mediaPlayerOwningDocument()->view()->hostWindow();
5696}
5697
5698IntRect HTMLMediaElement::mediaPlayerWindowClipRect()
5699{
5700    return mediaPlayerOwningDocument()->view()->windowClipRect();
5701}
5702
5703CachedResourceLoader* HTMLMediaElement::mediaPlayerCachedResourceLoader()
5704{
5705    return mediaPlayerOwningDocument()->cachedResourceLoader();
5706}
5707
5708bool HTMLMediaElement::mediaPlayerShouldWaitForResponseToAuthenticationChallenge(const AuthenticationChallenge& challenge)
5709{
5710    Frame* frame = document().frame();
5711    if (!frame)
5712        return false;
5713
5714    Page* page = frame->page();
5715    if (!page)
5716        return false;
5717
5718    ResourceRequest request(m_currentSrc);
5719    ResourceLoadNotifier& notifier = frame->loader().notifier();
5720    DocumentLoader* documentLoader = document().loader();
5721    unsigned long identifier = page->progress().createUniqueIdentifier();
5722
5723    notifier.assignIdentifierToInitialRequest(identifier, documentLoader, request);
5724    notifier.didReceiveAuthenticationChallenge(identifier, documentLoader, challenge);
5725
5726    return true;
5727}
5728
5729String HTMLMediaElement::mediaPlayerSourceApplicationIdentifier() const
5730{
5731    if (Frame* frame = document().frame()) {
5732        if (NetworkingContext* networkingContext = frame->loader().networkingContext())
5733            return networkingContext->sourceApplicationIdentifier();
5734    }
5735    return emptyString();
5736}
5737
5738#if PLATFORM(IOS)
5739String HTMLMediaElement::mediaPlayerNetworkInterfaceName() const
5740{
5741    Settings* settings = document().settings();
5742    if (!settings)
5743        return emptyString();
5744
5745    return settings->networkInterfaceName();
5746}
5747
5748bool HTMLMediaElement::mediaPlayerGetRawCookies(const URL& url, Vector<Cookie>& cookies) const
5749{
5750    return getRawCookies(&document(), url, cookies);
5751}
5752#endif
5753
5754void HTMLMediaElement::removeBehaviorsRestrictionsAfterFirstUserGesture()
5755{
5756    m_mediaSession->removeBehaviorRestriction(HTMLMediaSession::RequireUserGestureForLoad);
5757    m_mediaSession->removeBehaviorRestriction(HTMLMediaSession::RequireUserGestureForRateChange);
5758    m_mediaSession->removeBehaviorRestriction(HTMLMediaSession::RequireUserGestureForFullscreen);
5759#if ENABLE(IOS_AIRPLAY)
5760    m_mediaSession->removeBehaviorRestriction(HTMLMediaSession::RequireUserGestureToShowPlaybackTargetPicker);
5761#endif
5762}
5763
5764#if ENABLE(MEDIA_SOURCE)
5765RefPtr<VideoPlaybackQuality> HTMLMediaElement::getVideoPlaybackQuality()
5766{
5767#if ENABLE(WEB_TIMING)
5768    DOMWindow* domWindow = document().domWindow();
5769    Performance* performance = domWindow ? domWindow->performance() : nullptr;
5770    double now = performance ? performance->now() : 0;
5771#else
5772    DocumentLoader* loader = document().loader();
5773    double now = loader ? 1000.0 * loader->timing()->monotonicTimeToZeroBasedDocumentTime(monotonicallyIncreasingTime()) : 0;
5774#endif
5775
5776    if (!m_player)
5777        return VideoPlaybackQuality::create(now, 0, 0, 0, 0);
5778
5779    return VideoPlaybackQuality::create(now,
5780        m_droppedVideoFrames + m_player->totalVideoFrames(),
5781        m_droppedVideoFrames + m_player->droppedVideoFrames(),
5782        m_player->corruptedVideoFrames(),
5783        m_player->totalFrameDelay());
5784}
5785#endif
5786
5787#if ENABLE(MEDIA_CONTROLS_SCRIPT)
5788DOMWrapperWorld& HTMLMediaElement::ensureIsolatedWorld()
5789{
5790    if (!m_isolatedWorld)
5791        m_isolatedWorld = DOMWrapperWorld::create(JSDOMWindow::commonVM());
5792    return *m_isolatedWorld;
5793}
5794
5795bool HTMLMediaElement::ensureMediaControlsInjectedScript()
5796{
5797    LOG(Media, "HTMLMediaElement::ensureMediaControlsInjectedScript");
5798    Page* page = document().page();
5799    if (!page)
5800        return false;
5801
5802    String mediaControlsScript = RenderTheme::themeForPage(page)->mediaControlsScript();
5803    if (!mediaControlsScript.length())
5804        return false;
5805
5806    DOMWrapperWorld& world = ensureIsolatedWorld();
5807    ScriptController& scriptController = page->mainFrame().script();
5808    JSDOMGlobalObject* globalObject = JSC::jsCast<JSDOMGlobalObject*>(scriptController.globalObject(world));
5809    JSC::ExecState* exec = globalObject->globalExec();
5810    JSC::JSLockHolder lock(exec);
5811
5812    JSC::JSValue functionValue = globalObject->get(exec, JSC::Identifier(exec, "createControls"));
5813    if (functionValue.isFunction())
5814        return true;
5815
5816#ifndef NDEBUG
5817    // Setting a scriptURL allows the source to be debuggable in the inspector.
5818    URL scriptURL = URL(ParsedURLString, ASCIILiteral("mediaControlsScript"));
5819#else
5820    URL scriptURL;
5821#endif
5822    scriptController.evaluateInWorld(ScriptSourceCode(mediaControlsScript, scriptURL), world);
5823    if (exec->hadException()) {
5824        exec->clearException();
5825        return false;
5826    }
5827
5828    return true;
5829}
5830
5831static void setPageScaleFactorProperty(JSC::ExecState* exec, JSC::JSValue controllerValue, float pageScaleFactor)
5832{
5833    JSC::PutPropertySlot propertySlot(controllerValue);
5834    JSC::JSObject* controllerObject = controllerValue.toObject(exec);
5835    controllerObject->methodTable()->put(controllerObject, exec, JSC::Identifier(exec, "pageScaleFactor"), JSC::jsNumber(pageScaleFactor), propertySlot);
5836}
5837
5838void HTMLMediaElement::didAddUserAgentShadowRoot(ShadowRoot* root)
5839{
5840    LOG(Media, "HTMLMediaElement::didAddUserAgentShadowRoot");
5841    Page* page = document().page();
5842    if (!page)
5843        return;
5844
5845    DOMWrapperWorld& world = ensureIsolatedWorld();
5846
5847    if (!ensureMediaControlsInjectedScript())
5848        return;
5849
5850    ScriptController& scriptController = page->mainFrame().script();
5851    JSDOMGlobalObject* globalObject = JSC::jsCast<JSDOMGlobalObject*>(scriptController.globalObject(world));
5852    JSC::ExecState* exec = globalObject->globalExec();
5853    JSC::JSLockHolder lock(exec);
5854
5855    // The media controls script must provide a method with the following details.
5856    // Name: createControls
5857    // Parameters:
5858    //     1. The ShadowRoot element that will hold the controls.
5859    //     2. This object (and HTMLMediaElement).
5860    //     3. The MediaControlsHost object.
5861    // Return value:
5862    //     A reference to the created media controller instance.
5863
5864    JSC::JSValue functionValue = globalObject->get(exec, JSC::Identifier(exec, "createControls"));
5865    if (functionValue.isUndefinedOrNull())
5866        return;
5867
5868    if (!m_mediaControlsHost)
5869        m_mediaControlsHost = MediaControlsHost::create(this);
5870
5871    auto mediaJSWrapper = toJS(exec, globalObject, this);
5872    auto mediaControlsHostJSWrapper = toJS(exec, globalObject, m_mediaControlsHost.get());
5873
5874    JSC::MarkedArgumentBuffer argList;
5875    argList.append(toJS(exec, globalObject, root));
5876    argList.append(mediaJSWrapper);
5877    argList.append(mediaControlsHostJSWrapper);
5878
5879    JSC::JSObject* function = functionValue.toObject(exec);
5880    JSC::CallData callData;
5881    JSC::CallType callType = function->methodTable()->getCallData(function, callData);
5882    if (callType == JSC::CallTypeNone)
5883        return;
5884
5885    JSC::JSValue controllerValue = JSC::call(exec, function, callType, callData, globalObject, argList);
5886    JSC::JSObject* controllerObject = JSC::jsDynamicCast<JSC::JSObject*>(controllerValue);
5887    if (!controllerObject)
5888        return;
5889
5890    // Connect the Media, MediaControllerHost, and Controller so the GC knows about their relationship
5891    JSC::JSObject* mediaJSWrapperObject = mediaJSWrapper.toObject(exec);
5892    JSC::Identifier controlsHost(&exec->vm(), "controlsHost");
5893
5894    ASSERT(!mediaJSWrapperObject->hasProperty(exec, controlsHost));
5895
5896    mediaJSWrapperObject->putDirect(exec->vm(), controlsHost, mediaControlsHostJSWrapper, JSC::DontDelete | JSC::DontEnum | JSC::ReadOnly);
5897
5898    JSC::JSObject* mediaControlsHostJSWrapperObject = JSC::jsDynamicCast<JSC::JSObject*>(mediaControlsHostJSWrapper);
5899    if (!mediaControlsHostJSWrapperObject)
5900        return;
5901
5902    JSC::Identifier controller(&exec->vm(), "controller");
5903
5904    ASSERT(!controllerObject->hasProperty(exec, controller));
5905
5906    mediaControlsHostJSWrapperObject->putDirect(exec->vm(), controller, controllerValue, JSC::DontDelete | JSC::DontEnum | JSC::ReadOnly);
5907
5908    setPageScaleFactorProperty(exec, controllerValue, page->pageScaleFactor());
5909
5910    if (exec->hadException())
5911        exec->clearException();
5912}
5913
5914void HTMLMediaElement::setMediaControlsDependOnPageScaleFactor(bool dependsOnPageScale)
5915{
5916    LOG(Media, "MediaElement::setMediaControlsDependPageScaleFactor = %s", boolString(dependsOnPageScale));
5917    if (m_mediaControlsDependOnPageScaleFactor == dependsOnPageScale)
5918        return;
5919
5920    m_mediaControlsDependOnPageScaleFactor = dependsOnPageScale;
5921
5922    if (m_mediaControlsDependOnPageScaleFactor)
5923        document().registerForPageScaleFactorChangedCallbacks(this);
5924    else
5925        document().unregisterForPageScaleFactorChangedCallbacks(this);
5926}
5927
5928void HTMLMediaElement::pageScaleFactorChanged()
5929{
5930    Page* page = document().page();
5931    if (!page)
5932        return;
5933
5934    LOG(Media, "HTMLMediaElement::pageScaleFactorChanged = %f", page->pageScaleFactor());
5935    DOMWrapperWorld& world = ensureIsolatedWorld();
5936    ScriptController& scriptController = page->mainFrame().script();
5937    JSDOMGlobalObject* globalObject = JSC::jsCast<JSDOMGlobalObject*>(scriptController.globalObject(world));
5938    JSC::ExecState* exec = globalObject->globalExec();
5939    JSC::JSLockHolder lock(exec);
5940
5941    JSC::JSValue controllerValue = controllerJSValue(*exec, *globalObject, *this);
5942
5943    setPageScaleFactorProperty(exec, controllerValue, page->pageScaleFactor());
5944}
5945#endif // ENABLE(MEDIA_CONTROLS_SCRIPT)
5946
5947unsigned long long HTMLMediaElement::fileSize() const
5948{
5949    if (m_player)
5950        return m_player->fileSize();
5951
5952    return 0;
5953}
5954
5955MediaSession::MediaType HTMLMediaElement::mediaType() const
5956{
5957    if (m_player && m_readyState >= HAVE_METADATA)
5958        return hasVideo() ? MediaSession::Video : MediaSession::Audio;
5959
5960    if (hasTagName(HTMLNames::videoTag))
5961        return MediaSession::Video;
5962
5963    return MediaSession::Audio;
5964}
5965
5966MediaSession::MediaType HTMLMediaElement::presentationType() const
5967{
5968    if (hasTagName(HTMLNames::videoTag))
5969        return MediaSession::Video;
5970
5971    return MediaSession::Audio;
5972}
5973
5974#if ENABLE(MEDIA_SOURCE)
5975size_t HTMLMediaElement::maximumSourceBufferSize(const SourceBuffer& buffer) const
5976{
5977    return m_mediaSession->maximumMediaSourceBufferSize(buffer);
5978}
5979#endif
5980
5981void HTMLMediaElement::pausePlayback()
5982{
5983    LOG(Media, "HTMLMediaElement::pausePlayback - paused = %s", boolString(paused()));
5984    if (!paused())
5985        pause();
5986}
5987
5988void HTMLMediaElement::resumePlayback()
5989{
5990    LOG(Media, "HTMLMediaElement::resumePlayback - paused = %s", boolString(paused()));
5991    if (paused())
5992        play();
5993}
5994
5995String HTMLMediaElement::mediaSessionTitle() const
5996{
5997    if (fastHasAttribute(titleAttr))
5998        return fastGetAttribute(titleAttr);
5999
6000    return m_currentSrc;
6001}
6002
6003void HTMLMediaElement::didReceiveRemoteControlCommand(MediaSession::RemoteControlCommandType command)
6004{
6005    LOG(Media, "HTMLMediaElement::didReceiveRemoteControlCommand(%i)", static_cast<int>(command));
6006
6007    switch (command) {
6008    case MediaSession::PlayCommand:
6009        play();
6010        break;
6011    case MediaSession::PauseCommand:
6012        pause();
6013        break;
6014    case MediaSession::TogglePlayPauseCommand:
6015        canPlay() ? play() : pause();
6016        break;
6017    case MediaSession::BeginSeekingBackwardCommand:
6018        beginScanning(Backward);
6019        break;
6020    case MediaSession::BeginSeekingForwardCommand:
6021        beginScanning(Forward);
6022        break;
6023    case MediaSession::EndSeekingBackwardCommand:
6024    case MediaSession::EndSeekingForwardCommand:
6025        endScanning();
6026        break;
6027    default:
6028        { } // Do nothing
6029    }
6030}
6031
6032bool HTMLMediaElement::overrideBackgroundPlaybackRestriction() const
6033{
6034#if ENABLE(IOS_AIRPLAY)
6035    if (m_player && m_player->isCurrentPlaybackTargetWireless())
6036        return true;
6037#endif
6038    return false;
6039}
6040
6041bool HTMLMediaElement::doesHaveAttribute(const AtomicString& attribute, AtomicString* value) const
6042{
6043    QualifiedName attributeName(nullAtom, attribute, nullAtom);
6044
6045    AtomicString elementValue = fastGetAttribute(attributeName);
6046    if (elementValue.isNull())
6047        return false;
6048
6049    if (Settings* settings = document().settings()) {
6050        if (attributeName == HTMLNames::x_itunes_inherit_uri_query_componentAttr && !settings->enableInheritURIQueryComponent())
6051            return false;
6052    }
6053
6054    if (value)
6055        *value = elementValue;
6056
6057    return true;
6058}
6059
6060void HTMLMediaElement::setShouldBufferData(bool shouldBuffer)
6061{
6062    if (m_player)
6063        return m_player->setShouldBufferData(shouldBuffer);
6064}
6065
6066}
6067
6068#endif
6069