cd20d327dc6e6dbccd9e9ff22a4811081ffdb05e
[WebKit-https.git] / Source / WebCore / html / HTMLMediaElement.cpp
1 /*
2  * Copyright (C) 2007-2017 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 #include "HTMLMediaElement.h"
28
29 #if ENABLE(VIDEO)
30
31 #include "ApplicationCacheHost.h"
32 #include "ApplicationCacheResource.h"
33 #include "Attribute.h"
34 #include "Blob.h"
35 #include "CSSPropertyNames.h"
36 #include "CSSValueKeywords.h"
37 #include "ChromeClient.h"
38 #include "CommonVM.h"
39 #include "ContentSecurityPolicy.h"
40 #include "ContentType.h"
41 #include "CookieJar.h"
42 #include "DiagnosticLoggingClient.h"
43 #include "DiagnosticLoggingKeys.h"
44 #include "Document.h"
45 #include "DocumentLoader.h"
46 #include "ElementChildIterator.h"
47 #include "EventNames.h"
48 #include "FrameLoader.h"
49 #include "FrameLoaderClient.h"
50 #include "FrameView.h"
51 #include "HTMLParserIdioms.h"
52 #include "HTMLSourceElement.h"
53 #include "HTMLVideoElement.h"
54 #include "JSDOMError.h"
55 #include "JSDOMPromiseDeferred.h"
56 #include "JSHTMLMediaElement.h"
57 #include "Language.h"
58 #include "Logging.h"
59 #include "MIMETypeRegistry.h"
60 #include "MainFrame.h"
61 #include "MediaController.h"
62 #include "MediaControls.h"
63 #include "MediaDocument.h"
64 #include "MediaError.h"
65 #include "MediaFragmentURIParser.h"
66 #include "MediaList.h"
67 #include "MediaPlayer.h"
68 #include "MediaQueryEvaluator.h"
69 #include "MediaResourceLoader.h"
70 #include "NetworkingContext.h"
71 #include "NoEventDispatchAssertion.h"
72 #include "Page.h"
73 #include "PageGroup.h"
74 #include "PlatformMediaSessionManager.h"
75 #include "ProgressTracker.h"
76 #include "RenderLayerCompositor.h"
77 #include "RenderTheme.h"
78 #include "RenderVideo.h"
79 #include "RenderView.h"
80 #include "ResourceLoadInfo.h"
81 #include "ScriptController.h"
82 #include "ScriptSourceCode.h"
83 #include "SecurityOriginData.h"
84 #include "SecurityPolicy.h"
85 #include "Settings.h"
86 #include "ShadowRoot.h"
87 #include "TimeRanges.h"
88 #include "UserContentController.h"
89 #include "UserGestureIndicator.h"
90 #include <limits>
91 #include <pal/SessionID.h>
92 #include <pal/system/SleepDisabler.h>
93 #include <runtime/Uint8Array.h>
94 #include <wtf/Algorithms.h>
95 #include <wtf/CurrentTime.h>
96 #include <wtf/MathExtras.h>
97 #include <wtf/MemoryPressureHandler.h>
98 #include <wtf/Ref.h>
99 #include <wtf/text/CString.h>
100
101 #if ENABLE(VIDEO_TRACK)
102 #include "AudioTrackList.h"
103 #include "HTMLTrackElement.h"
104 #include "InbandGenericTextTrack.h"
105 #include "InbandTextTrackPrivate.h"
106 #include "InbandWebVTTTextTrack.h"
107 #include "RuntimeEnabledFeatures.h"
108 #include "TextTrackCueList.h"
109 #include "TextTrackList.h"
110 #include "VideoTrackList.h"
111 #endif
112
113 #if ENABLE(WEB_AUDIO)
114 #include "AudioSourceProvider.h"
115 #include "MediaElementAudioSourceNode.h"
116 #endif
117
118 #if PLATFORM(IOS)
119 #include "RuntimeApplicationChecks.h"
120 #include "VideoFullscreenInterfaceAVKit.h"
121 #endif
122
123 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
124 #include "WebKitPlaybackTargetAvailabilityEvent.h"
125 #endif
126
127 #if ENABLE(MEDIA_SESSION)
128 #include "MediaSession.h"
129 #endif
130
131 #if ENABLE(MEDIA_SOURCE)
132 #include "DOMWindow.h"
133 #include "MediaSource.h"
134 #include "VideoPlaybackQuality.h"
135 #endif
136
137 #if ENABLE(MEDIA_STREAM)
138 #include "DOMURL.h"
139 #include "MediaStream.h"
140 #include "MediaStreamRegistry.h"
141 #endif
142
143 #if ENABLE(LEGACY_ENCRYPTED_MEDIA)
144 #include "WebKitMediaKeyNeededEvent.h"
145 #include "WebKitMediaKeys.h"
146 #endif
147
148 #if ENABLE(ENCRYPTED_MEDIA)
149 #include "MediaEncryptedEvent.h"
150 #include "MediaKeys.h"
151 #endif
152
153 #if ENABLE(MEDIA_CONTROLS_SCRIPT)
154 #include "JSMediaControlsHost.h"
155 #include "MediaControlsHost.h"
156 #include <bindings/ScriptObject.h>
157 #endif
158
159 #if ENABLE(ENCRYPTED_MEDIA)
160 #include "NotImplemented.h"
161 #endif
162
163 #if PLATFORM(IOS) || (PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE))
164 #include "VideoFullscreenModel.h"
165 #endif
166
167 #define RELEASE_LOG_IF_ALLOWED(fmt, ...) RELEASE_LOG_IF(document().page() && document().page()->isAlwaysOnLoggingAllowed(), Media, "%p - HTMLMediaElement::" fmt, this, ##__VA_ARGS__)
168
169 namespace WebCore {
170
171 static const Seconds SeekRepeatDelay { 100_ms };
172 static const double SeekTime = 0.2;
173 static const Seconds ScanRepeatDelay { 1.5_s };
174 static const double ScanMaximumRate = 8;
175 static const double AutoplayInterferenceTimeThreshold = 10;
176
177 static const Seconds hideMediaControlsAfterEndedDelay { 6_s };
178
179 static void setFlags(unsigned& value, unsigned flags)
180 {
181     value |= flags;
182 }
183
184 static void clearFlags(unsigned& value, unsigned flags)
185 {
186     value &= ~flags;
187 }
188
189 #if !LOG_DISABLED
190 static String urlForLoggingMedia(const URL& url)
191 {
192     static const unsigned maximumURLLengthForLogging = 128;
193
194     if (url.string().length() < maximumURLLengthForLogging)
195         return url.string();
196     return url.string().substring(0, maximumURLLengthForLogging) + "...";
197 }
198
199 static const char* boolString(bool val)
200 {
201     return val ? "true" : "false";
202 }
203
204 static String actionName(HTMLMediaElementEnums::DelayedActionType action)
205 {
206     StringBuilder actionBuilder;
207
208 #define ACTION(_actionType) \
209     if (action & (HTMLMediaElementEnums::_actionType)) { \
210         if (!actionBuilder.isEmpty()) \
211         actionBuilder.appendLiteral(", "); \
212         actionBuilder.append(#_actionType); \
213     } \
214
215     ACTION(ConfigureTextTracks);
216     ACTION(TextTrackChangesNotification);
217     ACTION(ConfigureTextTrackDisplay);
218     ACTION(CheckPlaybackTargetCompatablity);
219     ACTION(CheckMediaState);
220     ACTION(MediaEngineUpdated);
221
222     return actionBuilder.toString();
223
224 #undef ACTION
225 }
226
227 #endif
228
229 #ifndef LOG_MEDIA_EVENTS
230 // Default to not logging events because so many are generated they can overwhelm the rest of
231 // the logging.
232 #define LOG_MEDIA_EVENTS 0
233 #endif
234
235 #ifndef LOG_CACHED_TIME_WARNINGS
236 // Default to not logging warnings about excessive drift in the cached media time because it adds a
237 // fair amount of overhead and logging.
238 #define LOG_CACHED_TIME_WARNINGS 0
239 #endif
240
241 #if ENABLE(MEDIA_SOURCE)
242 // URL protocol used to signal that the media source API is being used.
243 static const char* mediaSourceBlobProtocol = "blob";
244 #endif
245
246 #if ENABLE(MEDIA_STREAM)
247 // URL protocol used to signal that the media stream API is being used.
248 static const char* mediaStreamBlobProtocol = "blob";
249 #endif
250
251 using namespace HTMLNames;
252
253 typedef HashMap<Document*, HashSet<HTMLMediaElement*>> DocumentElementSetMap;
254 static DocumentElementSetMap& documentToElementSetMap()
255 {
256     static NeverDestroyed<DocumentElementSetMap> map;
257     return map;
258 }
259
260 static void addElementToDocumentMap(HTMLMediaElement& element, Document& document)
261 {
262     DocumentElementSetMap& map = documentToElementSetMap();
263     HashSet<HTMLMediaElement*> set = map.take(&document);
264     set.add(&element);
265     map.add(&document, set);
266 }
267
268 static void removeElementFromDocumentMap(HTMLMediaElement& element, Document& document)
269 {
270     DocumentElementSetMap& map = documentToElementSetMap();
271     HashSet<HTMLMediaElement*> set = map.take(&document);
272     set.remove(&element);
273     if (!set.isEmpty())
274         map.add(&document, set);
275 }
276
277 #if ENABLE(VIDEO_TRACK)
278
279 class TrackDisplayUpdateScope {
280 public:
281     TrackDisplayUpdateScope(HTMLMediaElement& element)
282         : m_element(element)
283     {
284         m_element.beginIgnoringTrackDisplayUpdateRequests();
285     }
286     ~TrackDisplayUpdateScope()
287     {
288         m_element.endIgnoringTrackDisplayUpdateRequests();
289     }
290
291 private:
292     HTMLMediaElement& m_element;
293 };
294
295 #endif
296
297 struct HTMLMediaElement::TrackGroup {
298     enum GroupKind { CaptionsAndSubtitles, Description, Chapter, Metadata, Other };
299
300     TrackGroup(GroupKind kind)
301         : kind(kind)
302     {
303     }
304
305     Vector<RefPtr<TextTrack>> tracks;
306     RefPtr<TextTrack> visibleTrack;
307     RefPtr<TextTrack> defaultTrack;
308     GroupKind kind;
309     bool hasSrcLang { false };
310 };
311
312 HashSet<HTMLMediaElement*>& HTMLMediaElement::allMediaElements()
313 {
314     static NeverDestroyed<HashSet<HTMLMediaElement*>> elements;
315     return elements;
316 }
317
318 #if ENABLE(MEDIA_SESSION)
319 typedef HashMap<uint64_t, HTMLMediaElement*> IDToElementMap;
320
321 static IDToElementMap& elementIDsToElements()
322 {
323     static NeverDestroyed<IDToElementMap> map;
324     return map;
325 }
326
327 HTMLMediaElement* HTMLMediaElement::elementWithID(uint64_t id)
328 {
329     if (id == HTMLMediaElementInvalidID)
330         return nullptr;
331
332     return elementIDsToElements().get(id);
333 }
334
335 static uint64_t nextElementID()
336 {
337     static uint64_t elementID = 0;
338     return ++elementID;
339 }
340 #endif
341
342 struct MediaElementSessionInfo {
343     const MediaElementSession* session;
344     MediaElementSession::PlaybackControlsPurpose purpose;
345
346     double timeOfLastUserInteraction;
347     bool canShowControlsManager : 1;
348     bool isVisibleInViewportOrFullscreen : 1;
349     bool isLargeEnoughForMainContent : 1;
350     bool isPlayingAudio : 1;
351 };
352
353 static MediaElementSessionInfo mediaElementSessionInfoForSession(const MediaElementSession& session, MediaElementSession::PlaybackControlsPurpose purpose)
354 {
355     const HTMLMediaElement& element = session.element();
356     return {
357         &session,
358         purpose,
359         session.mostRecentUserInteractionTime(),
360         session.canShowControlsManager(purpose),
361         element.isFullscreen() || element.isVisibleInViewport(),
362         session.isLargeEnoughForMainContent(MediaSessionMainContentPurpose::MediaControls),
363         element.isPlaying() && element.hasAudio() && !element.muted()
364     };
365 }
366
367 static bool preferMediaControlsForCandidateSessionOverOtherCandidateSession(const MediaElementSessionInfo& session, const MediaElementSessionInfo& otherSession)
368 {
369     MediaElementSession::PlaybackControlsPurpose purpose = session.purpose;
370     ASSERT(purpose == otherSession.purpose);
371
372     // For the controls manager, prioritize visible media over offscreen media.
373     if (purpose == MediaElementSession::PlaybackControlsPurpose::ControlsManager && session.isVisibleInViewportOrFullscreen != otherSession.isVisibleInViewportOrFullscreen)
374         return session.isVisibleInViewportOrFullscreen;
375
376     // For Now Playing, prioritize elements that would normally satisfy main content.
377     if (purpose == MediaElementSession::PlaybackControlsPurpose::NowPlaying && session.isLargeEnoughForMainContent != otherSession.isLargeEnoughForMainContent)
378         return session.isLargeEnoughForMainContent;
379
380     // As a tiebreaker, prioritize elements that the user recently interacted with.
381     return session.timeOfLastUserInteraction > otherSession.timeOfLastUserInteraction;
382 }
383
384 static bool mediaSessionMayBeConfusedWithMainContent(const MediaElementSessionInfo& session, MediaElementSession::PlaybackControlsPurpose purpose)
385 {
386     if (purpose == MediaElementSession::PlaybackControlsPurpose::NowPlaying)
387         return session.isPlayingAudio;
388
389     if (!session.isVisibleInViewportOrFullscreen)
390         return false;
391
392     if (!session.isLargeEnoughForMainContent)
393         return false;
394
395     // Even if this video is not a candidate, if it is visible to the user and large enough
396     // to be main content, it poses a risk for being confused with main content.
397     return true;
398 }
399
400 HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document& document, bool createdByParser)
401     : HTMLElement(tagName, document)
402     , ActiveDOMObject(&document)
403     , m_weakFactory(this)
404     , m_pendingActionTimer(*this, &HTMLMediaElement::pendingActionTimerFired)
405     , m_progressEventTimer(*this, &HTMLMediaElement::progressEventTimerFired)
406     , m_playbackProgressTimer(*this, &HTMLMediaElement::playbackProgressTimerFired)
407     , m_scanTimer(*this, &HTMLMediaElement::scanTimerFired)
408     , m_playbackControlsManagerBehaviorRestrictionsTimer(*this, &HTMLMediaElement::playbackControlsManagerBehaviorRestrictionsTimerFired)
409     , m_seekToPlaybackPositionEndedTimer(*this, &HTMLMediaElement::seekToPlaybackPositionEndedTimerFired)
410     , m_asyncEventQueue(*this)
411     , m_lastTimeUpdateEventMovieTime(MediaTime::positiveInfiniteTime())
412     , m_firstTimePlaying(true)
413     , m_playing(false)
414     , m_isWaitingUntilMediaCanStart(false)
415     , m_shouldDelayLoadEvent(false)
416     , m_haveFiredLoadedData(false)
417     , m_inActiveDocument(true)
418     , m_autoplaying(true)
419     , m_muted(false)
420     , m_explicitlyMuted(false)
421     , m_initiallyMuted(false)
422     , m_paused(true)
423     , m_seeking(false)
424     , m_seekRequested(false)
425     , m_sentStalledEvent(false)
426     , m_sentEndEvent(false)
427     , m_pausedInternal(false)
428     , m_closedCaptionsVisible(false)
429     , m_webkitLegacyClosedCaptionOverride(false)
430     , m_completelyLoaded(false)
431     , m_havePreparedToPlay(false)
432     , m_parsingInProgress(createdByParser)
433     , m_elementIsHidden(document.hidden())
434     , m_creatingControls(false)
435     , m_receivedLayoutSizeChanged(false)
436     , m_hasEverNotifiedAboutPlaying(false)
437     , m_hasEverHadAudio(false)
438     , m_hasEverHadVideo(false)
439 #if ENABLE(MEDIA_CONTROLS_SCRIPT)
440     , m_mediaControlsDependOnPageScaleFactor(false)
441     , m_haveSetUpCaptionContainer(false)
442 #endif
443     , m_isScrubbingRemotely(false)
444 #if ENABLE(VIDEO_TRACK)
445     , m_tracksAreReady(true)
446     , m_haveVisibleTextTrack(false)
447     , m_processingPreferenceChange(false)
448 #endif
449     , m_mediaSession(std::make_unique<MediaElementSession>(*this))
450 {
451     allMediaElements().add(this);
452
453     LOG(Media, "HTMLMediaElement::HTMLMediaElement(%p)", this);
454     setHasCustomStyleResolveCallbacks();
455
456     m_mediaSession->addBehaviorRestriction(MediaElementSession::RequireUserGestureForFullscreen);
457     m_mediaSession->addBehaviorRestriction(MediaElementSession::RequirePageConsentToLoadMedia);
458 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
459     m_mediaSession->addBehaviorRestriction(MediaElementSession::RequireUserGestureToAutoplayToExternalDevice);
460 #endif
461     m_mediaSession->addBehaviorRestriction(MediaElementSession::RequireUserGestureToControlControlsManager);
462     m_mediaSession->addBehaviorRestriction(MediaElementSession::RequirePlaybackToControlControlsManager);
463
464     auto* page = document.page();
465
466     if (document.settings().invisibleAutoplayNotPermitted())
467         m_mediaSession->addBehaviorRestriction(MediaElementSession::InvisibleAutoplayNotPermitted);
468
469     if (document.ownerElement() || !document.isMediaDocument()) {
470         const auto& topDocument = document.topDocument();
471         const bool isProcessingUserGesture = processingUserGestureForMedia();
472         const bool shouldAudioPlaybackRequireUserGesture = topDocument.audioPlaybackRequiresUserGesture() && !isProcessingUserGesture;
473         const bool shouldVideoPlaybackRequireUserGesture = topDocument.videoPlaybackRequiresUserGesture() && !isProcessingUserGesture;
474
475         if (shouldVideoPlaybackRequireUserGesture) {
476             m_mediaSession->addBehaviorRestriction(MediaElementSession::RequireUserGestureForVideoRateChange);
477             if (document.settings().requiresUserGestureToLoadVideo())
478                 m_mediaSession->addBehaviorRestriction(MediaElementSession::RequireUserGestureForLoad);
479         }
480
481         if (page && page->isLowPowerModeEnabled())
482             m_mediaSession->addBehaviorRestriction(MediaElementSession::RequireUserGestureForVideoDueToLowPowerMode);
483
484         if (shouldAudioPlaybackRequireUserGesture)
485             m_mediaSession->addBehaviorRestriction(MediaElementSession::RequireUserGestureForAudioRateChange);
486
487 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
488         if (shouldVideoPlaybackRequireUserGesture || shouldAudioPlaybackRequireUserGesture)
489             m_mediaSession->addBehaviorRestriction(MediaElementSession::RequireUserGestureToShowPlaybackTargetPicker);
490 #endif
491
492         if (!document.settings().mediaDataLoadsAutomatically())
493             m_mediaSession->addBehaviorRestriction(MediaElementSession::AutoPreloadingNotPermitted);
494
495         if (document.settings().mainContentUserGestureOverrideEnabled())
496             m_mediaSession->addBehaviorRestriction(MediaElementSession::OverrideUserGestureRequirementForMainContent);
497     }
498
499 #if PLATFORM(IOS)
500     if (!document.settings().videoPlaybackRequiresUserGesture() && !document.settings().audioPlaybackRequiresUserGesture()) {
501         // Relax RequireUserGestureForFullscreen when videoPlaybackRequiresUserGesture and audioPlaybackRequiresUserGesture is not set:
502         m_mediaSession->removeBehaviorRestriction(MediaElementSession::RequireUserGestureForFullscreen);
503     }
504 #endif
505
506 #if ENABLE(VIDEO_TRACK)
507     if (page)
508         m_captionDisplayMode = page->group().captionPreferences().captionDisplayMode();
509 #endif
510
511 #if ENABLE(MEDIA_SESSION)
512     m_elementID = nextElementID();
513     elementIDsToElements().add(m_elementID, this);
514
515     setSessionInternal(document.defaultMediaSession());
516 #endif
517
518     registerWithDocument(document);
519
520 #if USE(AUDIO_SESSION) && PLATFORM(MAC)
521     AudioSession::sharedSession().addMutedStateObserver(this);
522 #endif
523
524     mediaSession().clientWillBeginAutoplaying();
525 }
526
527 HTMLMediaElement::~HTMLMediaElement()
528 {
529     LOG(Media, "HTMLMediaElement::~HTMLMediaElement(%p)", this);
530
531     beginIgnoringTrackDisplayUpdateRequests();
532     allMediaElements().remove(this);
533
534     m_asyncEventQueue.close();
535
536     setShouldDelayLoadEvent(false);
537     unregisterWithDocument(document());
538
539 #if USE(AUDIO_SESSION) && PLATFORM(MAC)
540     AudioSession::sharedSession().removeMutedStateObserver(this);
541 #endif
542
543 #if ENABLE(VIDEO_TRACK)
544     if (m_audioTracks)
545         m_audioTracks->clearElement();
546     if (m_textTracks)
547         m_textTracks->clearElement();
548     if (m_videoTracks)
549         m_videoTracks->clearElement();
550 #endif
551
552 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
553     if (hasEventListeners(eventNames().webkitplaybacktargetavailabilitychangedEvent)) {
554         m_hasPlaybackTargetAvailabilityListeners = false;
555         m_mediaSession->setHasPlaybackTargetAvailabilityListeners(*this, false);
556         updateMediaState();
557     }
558 #endif
559
560     if (m_mediaController) {
561         m_mediaController->removeMediaElement(*this);
562         m_mediaController = nullptr;
563     }
564
565 #if ENABLE(MEDIA_SOURCE)
566     detachMediaSource();
567 #endif
568
569 #if ENABLE(LEGACY_ENCRYPTED_MEDIA)
570     webkitSetMediaKeys(nullptr);
571 #endif
572
573 #if ENABLE(ENCRYPTED_MEDIA)
574     if (m_mediaKeys) {
575         m_mediaKeys->detachCDMClient(*this);
576         if (m_player)
577             m_player->cdmInstanceDetached(m_mediaKeys->cdmInstance());
578     }
579 #endif
580
581 #if ENABLE(MEDIA_CONTROLS_SCRIPT)
582     if (m_isolatedWorld)
583         m_isolatedWorld->clearWrappers();
584 #endif
585
586 #if ENABLE(MEDIA_SESSION)
587     if (m_session) {
588         m_session->removeMediaElement(*this);
589         m_session = nullptr;
590     }
591
592     elementIDsToElements().remove(m_elementID);
593 #endif
594
595     m_seekTaskQueue.close();
596     m_promiseTaskQueue.close();
597     m_pauseAfterDetachedTaskQueue.close();
598     m_updatePlaybackControlsManagerQueue.close();
599     m_playbackControlsManagerBehaviorRestrictionsQueue.close();
600     m_resourceSelectionTaskQueue.close();
601     m_visibilityChangeTaskQueue.close();
602 #if ENABLE(ENCRYPTED_MEDIA)
603     m_encryptedMediaQueue.close();
604 #endif
605
606     m_completelyLoaded = true;
607
608     if (m_player) {
609         m_player->invalidate();
610         m_player = nullptr;
611     }
612
613     m_mediaSession = nullptr;
614     updatePlaybackControlsManager();
615 }
616
617 static bool needsAutoplayPlayPauseEventsQuirk(const Document& document)
618 {
619     auto* page = document.page();
620     if (!page || !page->settings().needsSiteSpecificQuirks())
621         return false;
622
623     auto* loader = document.loader();
624     return loader && loader->allowedAutoplayQuirks().contains(AutoplayQuirk::SynthesizedPauseEvents);
625 }
626
627 HTMLMediaElement* HTMLMediaElement::bestMediaElementForShowingPlaybackControlsManager(MediaElementSession::PlaybackControlsPurpose purpose)
628 {
629     auto allSessions = PlatformMediaSessionManager::sharedManager().currentSessionsMatching([] (const PlatformMediaSession& session) {
630         return is<MediaElementSession>(session);
631     });
632
633     Vector<MediaElementSessionInfo> candidateSessions;
634     bool atLeastOneNonCandidateMayBeConfusedForMainContent = false;
635     for (auto& session : allSessions) {
636         auto mediaElementSessionInfo = mediaElementSessionInfoForSession(downcast<MediaElementSession>(*session), purpose);
637         if (mediaElementSessionInfo.canShowControlsManager)
638             candidateSessions.append(mediaElementSessionInfo);
639         else if (mediaSessionMayBeConfusedWithMainContent(mediaElementSessionInfo, purpose))
640             atLeastOneNonCandidateMayBeConfusedForMainContent = true;
641     }
642
643     if (!candidateSessions.size())
644         return nullptr;
645
646     std::sort(candidateSessions.begin(), candidateSessions.end(), preferMediaControlsForCandidateSessionOverOtherCandidateSession);
647     auto strongestSessionCandidate = candidateSessions.first();
648     if (!strongestSessionCandidate.isVisibleInViewportOrFullscreen && !strongestSessionCandidate.isPlayingAudio && atLeastOneNonCandidateMayBeConfusedForMainContent)
649         return nullptr;
650
651     return &strongestSessionCandidate.session->element();
652 }
653
654 void HTMLMediaElement::registerWithDocument(Document& document)
655 {
656     m_mediaSession->registerWithDocument(document);
657
658     if (m_isWaitingUntilMediaCanStart)
659         document.addMediaCanStartListener(this);
660
661 #if !PLATFORM(IOS)
662     document.registerForMediaVolumeCallbacks(this);
663     document.registerForPrivateBrowsingStateChangedCallbacks(this);
664 #endif
665
666     document.registerForVisibilityStateChangedCallbacks(this);
667
668 #if ENABLE(VIDEO_TRACK)
669     if (m_requireCaptionPreferencesChangedCallbacks)
670         document.registerForCaptionPreferencesChangedCallbacks(this);
671 #endif
672
673 #if ENABLE(MEDIA_CONTROLS_SCRIPT)
674     if (m_mediaControlsDependOnPageScaleFactor)
675         document.registerForPageScaleFactorChangedCallbacks(this);
676     document.registerForUserInterfaceLayoutDirectionChangedCallbacks(*this);
677 #endif
678
679 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
680     document.registerForDocumentSuspensionCallbacks(this);
681 #endif
682
683     document.registerForAllowsMediaDocumentInlinePlaybackChangedCallbacks(*this);
684
685     document.addAudioProducer(this);
686     addElementToDocumentMap(*this, document);
687
688 #if ENABLE(MEDIA_STREAM)
689     document.registerForMediaStreamStateChangeCallbacks(*this);
690 #endif
691 }
692
693 void HTMLMediaElement::unregisterWithDocument(Document& document)
694 {
695     m_mediaSession->unregisterWithDocument(document);
696
697     if (m_isWaitingUntilMediaCanStart)
698         document.removeMediaCanStartListener(this);
699
700 #if !PLATFORM(IOS)
701     document.unregisterForMediaVolumeCallbacks(this);
702     document.unregisterForPrivateBrowsingStateChangedCallbacks(this);
703 #endif
704
705     document.unregisterForVisibilityStateChangedCallbacks(this);
706
707 #if ENABLE(VIDEO_TRACK)
708     if (m_requireCaptionPreferencesChangedCallbacks)
709         document.unregisterForCaptionPreferencesChangedCallbacks(this);
710 #endif
711
712 #if ENABLE(MEDIA_CONTROLS_SCRIPT)
713     if (m_mediaControlsDependOnPageScaleFactor)
714         document.unregisterForPageScaleFactorChangedCallbacks(this);
715     document.unregisterForUserInterfaceLayoutDirectionChangedCallbacks(*this);
716 #endif
717
718 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
719     document.unregisterForDocumentSuspensionCallbacks(this);
720 #endif
721
722     document.unregisterForAllowsMediaDocumentInlinePlaybackChangedCallbacks(*this);
723
724     document.removeAudioProducer(this);
725     removeElementFromDocumentMap(*this, document);
726
727 #if ENABLE(MEDIA_STREAM)
728     document.unregisterForMediaStreamStateChangeCallbacks(*this);
729 #endif
730
731 }
732
733 void HTMLMediaElement::didMoveToNewDocument(Document& oldDocument, Document& newDocument)
734 {
735     ASSERT_WITH_SECURITY_IMPLICATION(&document() == &newDocument);
736     if (m_shouldDelayLoadEvent) {
737         oldDocument.decrementLoadEventDelayCount();
738         newDocument.incrementLoadEventDelayCount();
739     }
740
741     unregisterWithDocument(oldDocument);
742     registerWithDocument(newDocument);
743
744     HTMLElement::didMoveToNewDocument(oldDocument, newDocument);
745     updateShouldAutoplay();
746 }
747
748 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
749 void HTMLMediaElement::prepareForDocumentSuspension()
750 {
751     m_mediaSession->unregisterWithDocument(document());
752 }
753
754 void HTMLMediaElement::resumeFromDocumentSuspension()
755 {
756     m_mediaSession->registerWithDocument(document());
757     updateShouldAutoplay();
758 }
759 #endif
760
761 bool HTMLMediaElement::supportsFocus() const
762 {
763     if (document().isMediaDocument())
764         return false;
765
766     // If no controls specified, we should still be able to focus the element if it has tabIndex.
767     return controls() ||  HTMLElement::supportsFocus();
768 }
769
770 bool HTMLMediaElement::isMouseFocusable() const
771 {
772     return false;
773 }
774
775 void HTMLMediaElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
776 {
777     if (name == srcAttr) {
778         // https://html.spec.whatwg.org/multipage/embedded-content.html#location-of-the-media-resource
779         // Location of the Media Resource
780         // 12 February 2017
781
782         // If a src attribute of a media element is set or changed, the user
783         // agent must invoke the media element's media element load algorithm.
784 #if PLATFORM(IOS)
785         // Note, unless the restriction on requiring user action has been removed,
786         // do not begin downloading data on iOS.
787         if (!value.isNull() && m_mediaSession->dataLoadingPermitted(*this))
788 #else
789         if (!value.isNull())
790 #endif
791             prepareForLoad();
792     } else if (name == controlsAttr)
793         configureMediaControls();
794     else if (name == loopAttr)
795         updateSleepDisabling();
796     else if (name == preloadAttr) {
797         if (equalLettersIgnoringASCIICase(value, "none"))
798             m_preload = MediaPlayer::None;
799         else if (equalLettersIgnoringASCIICase(value, "metadata"))
800             m_preload = MediaPlayer::MetaData;
801         else {
802             // The spec does not define an "invalid value default" but "auto" is suggested as the
803             // "missing value default", so use it for everything except "none" and "metadata"
804             m_preload = MediaPlayer::Auto;
805         }
806
807         // The attribute must be ignored if the autoplay attribute is present
808         if (!autoplay() && !m_havePreparedToPlay && m_player)
809             m_player->setPreload(m_mediaSession->effectivePreloadForElement(*this));
810
811     } else if (name == mediagroupAttr)
812         setMediaGroup(value);
813     else if (name == autoplayAttr) {
814         if (processingUserGestureForMedia())
815             removeBehaviorsRestrictionsAfterFirstUserGesture();
816     }
817     else
818         HTMLElement::parseAttribute(name, value);
819 }
820
821 void HTMLMediaElement::finishParsingChildren()
822 {
823     HTMLElement::finishParsingChildren();
824     m_parsingInProgress = false;
825
826 #if ENABLE(VIDEO_TRACK)
827     if (descendantsOfType<HTMLTrackElement>(*this).first())
828         scheduleDelayedAction(ConfigureTextTracks);
829 #endif
830 }
831
832 bool HTMLMediaElement::rendererIsNeeded(const RenderStyle& style)
833 {
834     return controls() && HTMLElement::rendererIsNeeded(style);
835 }
836
837 RenderPtr<RenderElement> HTMLMediaElement::createElementRenderer(RenderStyle&& style, const RenderTreePosition&)
838 {
839     return createRenderer<RenderMedia>(*this, WTFMove(style));
840 }
841
842 bool HTMLMediaElement::childShouldCreateRenderer(const Node& child) const
843 {
844 #if ENABLE(MEDIA_CONTROLS_SCRIPT)
845     return hasShadowRootParent(child) && HTMLElement::childShouldCreateRenderer(child);
846 #else
847     if (!hasMediaControls())
848         return false;
849     // <media> doesn't allow its content, including shadow subtree, to
850     // be rendered. So this should return false for most of the children.
851     // One exception is a shadow tree built for rendering controls which should be visible.
852     // So we let them go here by comparing its subtree root with one of the controls.
853     return &mediaControls()->treeScope() == &child.treeScope()
854         && hasShadowRootParent(child)
855         && HTMLElement::childShouldCreateRenderer(child);
856 #endif
857 }
858
859 Node::InsertionNotificationRequest HTMLMediaElement::insertedInto(ContainerNode& insertionPoint)
860 {
861     LOG(Media, "HTMLMediaElement::insertedInto(%p)", this);
862
863     HTMLElement::insertedInto(insertionPoint);
864     if (insertionPoint.isConnected()) {
865         m_inActiveDocument = true;
866
867 #if PLATFORM(IOS)
868         if (m_networkState == NETWORK_EMPTY && !attributeWithoutSynchronization(srcAttr).isEmpty() && m_mediaSession->dataLoadingPermitted(*this))
869 #else
870         if (m_networkState == NETWORK_EMPTY && !attributeWithoutSynchronization(srcAttr).isEmpty())
871 #endif
872             prepareForLoad();
873     }
874
875     if (!m_explicitlyMuted) {
876         m_explicitlyMuted = true;
877         m_muted = hasAttributeWithoutSynchronization(mutedAttr);
878         m_mediaSession->canProduceAudioChanged();
879     }
880
881     return InsertionShouldCallFinishedInsertingSubtree;
882 }
883
884 void HTMLMediaElement::finishedInsertingSubtree()
885 {
886     configureMediaControls();
887 }
888
889 void HTMLMediaElement::pauseAfterDetachedTask()
890 {
891     // If we were re-inserted into an active document, no need to pause.
892     if (m_inActiveDocument)
893         return;
894
895     if (hasMediaControls())
896         mediaControls()->hide();
897     if (m_networkState > NETWORK_EMPTY)
898         pause();
899     if (m_videoFullscreenMode != VideoFullscreenModeNone)
900         exitFullscreen();
901
902     if (!m_player)
903         return;
904
905     size_t extraMemoryCost = m_player->extraMemoryCost();
906     if (extraMemoryCost > m_reportedExtraMemoryCost) {
907         JSC::VM& vm = commonVM();
908         JSC::JSLockHolder lock(vm);
909
910         size_t extraMemoryCostDelta = extraMemoryCost - m_reportedExtraMemoryCost;
911         m_reportedExtraMemoryCost = extraMemoryCost;
912         // FIXME: Adopt reportExtraMemoryVisited, and switch to reportExtraMemoryAllocated.
913         // https://bugs.webkit.org/show_bug.cgi?id=142595
914         vm.heap.deprecatedReportExtraMemory(extraMemoryCostDelta);
915     }
916 }
917
918 void HTMLMediaElement::removedFrom(ContainerNode& insertionPoint)
919 {
920     LOG(Media, "HTMLMediaElement::removedFrom(%p)", this);
921
922     m_inActiveDocument = false;
923     if (insertionPoint.isConnected()) {
924         // Pause asynchronously to let the operation that removed us finish, in case we get inserted back into a document.
925         m_pauseAfterDetachedTaskQueue.enqueueTask(std::bind(&HTMLMediaElement::pauseAfterDetachedTask, this));
926     }
927
928     HTMLElement::removedFrom(insertionPoint);
929 }
930
931 void HTMLMediaElement::willAttachRenderers()
932 {
933     ASSERT(!renderer());
934 }
935
936 inline void HTMLMediaElement::updateRenderer()
937 {
938     if (auto* renderer = this->renderer())
939         renderer->updateFromElement();
940 }
941
942 void HTMLMediaElement::didAttachRenderers()
943 {
944     if (auto* renderer = this->renderer()) {
945         renderer->updateFromElement();
946         if (m_mediaSession && m_mediaSession->wantsToObserveViewportVisibilityForAutoplay())
947             renderer->registerForVisibleInViewportCallback();
948     }
949     updateShouldAutoplay();
950 }
951
952 void HTMLMediaElement::willDetachRenderers()
953 {
954     if (auto* renderer = this->renderer())
955         renderer->unregisterForVisibleInViewportCallback();
956 }
957
958 void HTMLMediaElement::didDetachRenderers()
959 {
960     updateShouldAutoplay();
961 }
962
963 void HTMLMediaElement::didRecalcStyle(Style::Change)
964 {
965     updateRenderer();
966 }
967
968 void HTMLMediaElement::scheduleDelayedAction(DelayedActionType actionType)
969 {
970     LOG(Media, "HTMLMediaElement::scheduleDelayedAction(%p) - setting %s flag", this, actionName(actionType).utf8().data());
971
972 #if ENABLE(VIDEO_TRACK)
973     if (actionType & ConfigureTextTracks)
974         setFlags(m_pendingActionFlags, ConfigureTextTracks);
975 #endif
976
977 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
978     if (actionType & CheckPlaybackTargetCompatablity)
979         setFlags(m_pendingActionFlags, CheckPlaybackTargetCompatablity);
980 #endif
981
982     if (actionType & CheckMediaState)
983         setFlags(m_pendingActionFlags, CheckMediaState);
984
985     if (actionType & MediaEngineUpdated)
986         setFlags(m_pendingActionFlags, MediaEngineUpdated);
987
988     if (actionType & UpdatePlayState)
989         setFlags(m_pendingActionFlags, UpdatePlayState);
990
991     m_pendingActionTimer.startOneShot(0_s);
992 }
993
994 void HTMLMediaElement::scheduleNextSourceChild()
995 {
996     // Schedule the timer to try the next <source> element WITHOUT resetting state ala prepareForLoad.
997     m_resourceSelectionTaskQueue.enqueueTask([this] {
998         loadNextSourceChild();
999     });
1000 }
1001
1002 void HTMLMediaElement::mediaPlayerActiveSourceBuffersChanged(const MediaPlayer*)
1003 {
1004     m_hasEverHadAudio |= hasAudio();
1005     m_hasEverHadVideo |= hasVideo();
1006 }
1007
1008 void HTMLMediaElement::scheduleEvent(const AtomicString& eventName)
1009 {
1010 #if LOG_MEDIA_EVENTS
1011     LOG(Media, "HTMLMediaElement::scheduleEvent(%p) - scheduling '%s'", this, eventName.string().ascii().data());
1012 #endif
1013     RefPtr<Event> event = Event::create(eventName, false, true);
1014
1015     // Don't set the event target, the event queue will set it in GenericEventQueue::timerFired and setting it here
1016     // will trigger an ASSERT if this element has been marked for deletion.
1017
1018     m_asyncEventQueue.enqueueEvent(WTFMove(event));
1019 }
1020
1021 void HTMLMediaElement::scheduleResolvePendingPlayPromises()
1022 {
1023     m_promiseTaskQueue.enqueueTask(std::bind(&HTMLMediaElement::resolvePendingPlayPromises, this));
1024 }
1025
1026 void HTMLMediaElement::rejectPendingPlayPromises(DOMError& error)
1027 {
1028     Vector<DOMPromiseDeferred<void>> pendingPlayPromises = WTFMove(m_pendingPlayPromises);
1029
1030     for (auto& promise : pendingPlayPromises)
1031         promise.rejectType<IDLInterface<DOMError>>(error);
1032 }
1033
1034 void HTMLMediaElement::resolvePendingPlayPromises()
1035 {
1036     Vector<DOMPromiseDeferred<void>> pendingPlayPromises = WTFMove(m_pendingPlayPromises);
1037
1038     for (auto& promise : pendingPlayPromises)
1039         promise.resolve();
1040 }
1041
1042 void HTMLMediaElement::scheduleNotifyAboutPlaying()
1043 {
1044     m_promiseTaskQueue.enqueueTask(std::bind(&HTMLMediaElement::notifyAboutPlaying, this));
1045 }
1046
1047 void HTMLMediaElement::notifyAboutPlaying()
1048 {
1049     Ref<HTMLMediaElement> protectedThis(*this); // The 'playing' event can make arbitrary DOM mutations.
1050     m_playbackStartedTime = currentMediaTime().toDouble();
1051     dispatchEvent(Event::create(eventNames().playingEvent, false, true));
1052     resolvePendingPlayPromises();
1053
1054     m_hasEverNotifiedAboutPlaying = true;
1055     scheduleUpdatePlaybackControlsManager();
1056 }
1057
1058 bool HTMLMediaElement::hasEverNotifiedAboutPlaying() const
1059 {
1060     return m_hasEverNotifiedAboutPlaying;
1061 }
1062
1063 void HTMLMediaElement::pendingActionTimerFired()
1064 {
1065     Ref<HTMLMediaElement> protectedThis(*this); // loadNextSourceChild may fire 'beforeload', which can make arbitrary DOM mutations.
1066     PendingActionFlags pendingActions = m_pendingActionFlags;
1067     m_pendingActionFlags = 0;
1068
1069 #if ENABLE(VIDEO_TRACK)
1070     if (pendingActions & ConfigureTextTracks)
1071         configureTextTracks();
1072 #endif
1073
1074 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
1075     if (pendingActions & CheckPlaybackTargetCompatablity && m_isPlayingToWirelessTarget && !m_player->canPlayToWirelessPlaybackTarget()) {
1076         LOG(Media, "HTMLMediaElement::pendingActionTimerFired(%p) - calling setShouldPlayToPlaybackTarget(false)", this);
1077         m_failedToPlayToWirelessTarget = true;
1078         m_player->setShouldPlayToPlaybackTarget(false);
1079     }
1080
1081     if (pendingActions & CheckMediaState)
1082         updateMediaState();
1083 #endif
1084
1085     if (pendingActions & MediaEngineUpdated)
1086         mediaEngineWasUpdated();
1087
1088     if (pendingActions & UpdatePlayState)
1089         updatePlayState();
1090 }
1091
1092 MediaError* HTMLMediaElement::error() const
1093 {
1094     return m_error.get();
1095 }
1096
1097 void HTMLMediaElement::setSrcObject(MediaProvider&& mediaProvider)
1098 {
1099     // FIXME: Setting the srcObject attribute may cause other changes to the media element's internal state:
1100     // Specifically, if srcObject is specified, the UA must use it as the source of media, even if the src
1101     // attribute is also set or children are present. If the value of srcObject is replaced or set to null
1102     // the UA must re-run the media element load algorithm.
1103     //
1104     // https://bugs.webkit.org/show_bug.cgi?id=124896
1105
1106
1107     // https://www.w3.org/TR/html51/semantics-embedded-content.html#dom-htmlmediaelement-srcobject
1108     // 4.7.14.2. Location of the media resource
1109     // srcObject: On setting, it must set the element’s assigned media provider object to the new
1110     // value, and then invoke the element’s media element load algorithm.
1111     m_mediaProvider = WTFMove(mediaProvider);
1112     prepareForLoad();
1113 }
1114
1115 void HTMLMediaElement::setCrossOrigin(const AtomicString& value)
1116 {
1117     setAttributeWithoutSynchronization(crossoriginAttr, value);
1118 }
1119
1120 String HTMLMediaElement::crossOrigin() const
1121 {
1122     return parseCORSSettingsAttribute(attributeWithoutSynchronization(crossoriginAttr));
1123 }
1124
1125 HTMLMediaElement::NetworkState HTMLMediaElement::networkState() const
1126 {
1127     return m_networkState;
1128 }
1129
1130 String HTMLMediaElement::canPlayType(const String& mimeType) const
1131 {
1132     MediaEngineSupportParameters parameters;
1133     ContentType contentType(mimeType);
1134     parameters.type = contentType;
1135     parameters.contentTypesRequiringHardwareSupport = mediaContentTypesRequiringHardwareSupport();
1136     MediaPlayer::SupportsType support = MediaPlayer::supportsType(parameters, this);
1137     String canPlay;
1138
1139     // 4.8.10.3
1140     switch (support)
1141     {
1142         case MediaPlayer::IsNotSupported:
1143             canPlay = emptyString();
1144             break;
1145         case MediaPlayer::MayBeSupported:
1146             canPlay = ASCIILiteral("maybe");
1147             break;
1148         case MediaPlayer::IsSupported:
1149             canPlay = ASCIILiteral("probably");
1150             break;
1151     }
1152
1153     LOG(Media, "HTMLMediaElement::canPlayType(%p) - [%s] -> %s", this, mimeType.utf8().data(), canPlay.utf8().data());
1154
1155     return canPlay;
1156 }
1157
1158 double HTMLMediaElement::getStartDate() const
1159 {
1160     if (!m_player)
1161         return std::numeric_limits<double>::quiet_NaN();
1162     return m_player->getStartDate().toDouble();
1163 }
1164
1165 void HTMLMediaElement::load()
1166 {
1167     Ref<HTMLMediaElement> protectedThis(*this); // prepareForLoad may result in a 'beforeload' event, which can make arbitrary DOM mutations.
1168
1169     LOG(Media, "HTMLMediaElement::load(%p)", this);
1170
1171     if (!m_mediaSession->dataLoadingPermitted(*this))
1172         return;
1173     if (processingUserGestureForMedia())
1174         removeBehaviorsRestrictionsAfterFirstUserGesture();
1175
1176     prepareForLoad();
1177     m_resourceSelectionTaskQueue.enqueueTask([this] {
1178         prepareToPlay();
1179     });
1180 }
1181
1182 void HTMLMediaElement::prepareForLoad()
1183 {
1184     // https://html.spec.whatwg.org/multipage/embedded-content.html#media-element-load-algorithm
1185     // The Media Element Load Algorithm
1186     // 12 February 2017
1187
1188     LOG(Media, "HTMLMediaElement::prepareForLoad(%p)", this);
1189
1190     // 1 - Abort any already-running instance of the resource selection algorithm for this element.
1191     // Perform the cleanup required for the resource load algorithm to run.
1192     stopPeriodicTimers();
1193     m_pendingActionTimer.stop();
1194     m_resourceSelectionTaskQueue.cancelAllTasks();
1195     // FIXME: Figure out appropriate place to reset LoadTextTrackResource if necessary and set m_pendingActionFlags to 0 here.
1196     m_sentEndEvent = false;
1197     m_sentStalledEvent = false;
1198     m_haveFiredLoadedData = false;
1199     m_completelyLoaded = false;
1200     m_havePreparedToPlay = false;
1201     m_displayMode = Unknown;
1202     m_currentSrc = URL();
1203
1204 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
1205     m_failedToPlayToWirelessTarget = false;
1206 #endif
1207
1208     m_loadState = WaitingForSource;
1209     m_currentSourceNode = nullptr;
1210
1211     createMediaPlayer();
1212
1213     // 2 - Let pending tasks be a list of all tasks from the media element's media element event task source in one of the task queues.
1214     // 3 - For each task in pending tasks that would resolve pending play promises or reject pending play promises, immediately resolve or reject those promises in the order the corresponding tasks were queued.
1215     // 4 - Remove each task in pending tasks from its task queue
1216     cancelPendingEventsAndCallbacks();
1217
1218     // 5 - If the media element's networkState is set to NETWORK_LOADING or NETWORK_IDLE, queue
1219     // a task to fire a simple event named abort at the media element.
1220     if (m_networkState == NETWORK_LOADING || m_networkState == NETWORK_IDLE)
1221         scheduleEvent(eventNames().abortEvent);
1222
1223     // 6 - If the media element's networkState is not set to NETWORK_EMPTY, then run these substeps
1224     if (m_networkState != NETWORK_EMPTY) {
1225         // 6.1 - Queue a task to fire a simple event named emptied at the media element.
1226         scheduleEvent(eventNames().emptiedEvent);
1227
1228         // 6.2 - If a fetching process is in progress for the media element, the user agent should stop it.
1229         m_networkState = NETWORK_EMPTY;
1230
1231         // 6.3 - If the media element’s assigned media provider object is a MediaSource object, then detach it.
1232 #if ENABLE(MEDIA_SOURCE)
1233         detachMediaSource();
1234 #endif
1235
1236         // 6.4 - Forget the media element's media-resource-specific tracks.
1237         forgetResourceSpecificTracks();
1238
1239         // 6.5 - If readyState is not set to HAVE_NOTHING, then set it to that state.
1240         m_readyState = HAVE_NOTHING;
1241         m_readyStateMaximum = HAVE_NOTHING;
1242
1243         // 6.6 - If the paused attribute is false, then set it to true.
1244         m_paused = true;
1245
1246         // 6.7 - If seeking is true, set it to false.
1247         clearSeeking();
1248
1249         // 6.8 - Set the current playback position to 0.
1250         //       Set the official playback position to 0.
1251         //       If this changed the official playback position, then queue a task to fire a simple event named timeupdate at the media element.
1252         m_lastSeekTime = MediaTime::zeroTime();
1253         m_playedTimeRanges = TimeRanges::create();
1254         // FIXME: Add support for firing this event. e.g., scheduleEvent(eventNames().timeUpdateEvent);
1255
1256         // 4.9 - Set the initial playback position to 0.
1257         // FIXME: Make this less subtle. The position only becomes 0 because of the createMediaPlayer() call
1258         // above.
1259         refreshCachedTime();
1260
1261         invalidateCachedTime();
1262
1263         // 4.10 - Set the timeline offset to Not-a-Number (NaN).
1264         // 4.11 - Update the duration attribute to Not-a-Number (NaN).
1265
1266         updateMediaController();
1267 #if ENABLE(VIDEO_TRACK)
1268         updateActiveTextTrackCues(MediaTime::zeroTime());
1269 #endif
1270     }
1271
1272     // 7 - Set the playbackRate attribute to the value of the defaultPlaybackRate attribute.
1273     setPlaybackRate(defaultPlaybackRate());
1274
1275     // 8 - Set the error attribute to null and the autoplaying flag to true.
1276     m_error = nullptr;
1277     m_autoplaying = true;
1278     mediaSession().clientWillBeginAutoplaying();
1279
1280     // 9 - Invoke the media element's resource selection algorithm.
1281     selectMediaResource();
1282
1283     // 10 - Note: Playback of any previously playing media resource for this element stops.
1284
1285     configureMediaControls();
1286 }
1287
1288 void HTMLMediaElement::selectMediaResource()
1289 {
1290     // https://www.w3.org/TR/2016/REC-html51-20161101/semantics-embedded-content.html#resource-selection-algorithm
1291     // The Resource Selection Algorithm
1292
1293     // 1. Set the element’s networkState attribute to the NETWORK_NO_SOURCE value.
1294     m_networkState = NETWORK_NO_SOURCE;
1295
1296     // 2. Set the element’s show poster flag to true.
1297     setDisplayMode(Poster);
1298
1299     // 3. Set the media element’s delaying-the-load-event flag to true (this delays the load event).
1300     setShouldDelayLoadEvent(true);
1301
1302     // 4. in parallel await a stable state, allowing the task that invoked this algorithm to continue.
1303     if (m_resourceSelectionTaskQueue.hasPendingTasks())
1304         return;
1305
1306     if (!m_mediaSession->pageAllowsDataLoading(*this)) {
1307         LOG(Media, "HTMLMediaElement::selectMediaResource(%p) - not allowed to load in background, waiting", this);
1308         setShouldDelayLoadEvent(false);
1309         if (m_isWaitingUntilMediaCanStart)
1310             return;
1311         m_isWaitingUntilMediaCanStart = true;
1312         document().addMediaCanStartListener(this);
1313         return;
1314     }
1315
1316     // Once the page has allowed an element to load media, it is free to load at will. This allows a
1317     // playlist that starts in a foreground tab to continue automatically if the tab is subsequently
1318     // put into the background.
1319     m_mediaSession->removeBehaviorRestriction(MediaElementSession::RequirePageConsentToLoadMedia);
1320
1321
1322     m_resourceSelectionTaskQueue.enqueueTask([this]  {
1323         // 5. If the media element’s blocked-on-parser flag is false, then populate the list of pending text tracks.
1324 #if ENABLE(VIDEO_TRACK)
1325         if (hasMediaControls())
1326             mediaControls()->changedClosedCaptionsVisibility();
1327
1328         // HTMLMediaElement::textTracksAreReady will need "... the text tracks whose mode was not in the
1329         // disabled state when the element's resource selection algorithm last started".
1330         // FIXME: Update this to match "populate the list of pending text tracks" step.
1331         m_textTracksWhenResourceSelectionBegan.clear();
1332         if (m_textTracks) {
1333             for (unsigned i = 0; i < m_textTracks->length(); ++i) {
1334                 TextTrack* track = m_textTracks->item(i);
1335                 if (track->mode() != TextTrack::Mode::Disabled)
1336                     m_textTracksWhenResourceSelectionBegan.append(track);
1337             }
1338         }
1339 #endif
1340
1341         enum Mode { None, Object, Attribute, Children };
1342         Mode mode = None;
1343
1344         if (m_mediaProvider) {
1345             // 6. If the media element has an assigned media provider object, then let mode be object.
1346             mode = Object;
1347         } else if (hasAttributeWithoutSynchronization(srcAttr)) {
1348             //    Otherwise, if the media element has no assigned media provider object but has a src attribute, then let mode be attribute.
1349             mode = Attribute;
1350             ASSERT(m_player);
1351             if (!m_player) {
1352                 RELEASE_LOG_ERROR(Media, "HTMLMediaElement::selectMediaResource(%p) - has srcAttr but m_player is not created", this);
1353                 return;
1354             }
1355         } else if (auto firstSource = childrenOfType<HTMLSourceElement>(*this).first()) {
1356             //    Otherwise, if the media element does not have an assigned media provider object and does not have a src attribute,
1357             //    but does have a source element child, then let mode be children and let candidate be the first such source element
1358             //    child in tree order.
1359             mode = Children;
1360             m_nextChildNodeToConsider = firstSource;
1361             m_currentSourceNode = nullptr;
1362         } else {
1363             //  Otherwise the media element has no assigned media provider object and has neither a src attribute nor a source
1364             //  element child: set the networkState to NETWORK_EMPTY, and abort these steps; the synchronous section ends.
1365             m_loadState = WaitingForSource;
1366             setShouldDelayLoadEvent(false);
1367             m_networkState = NETWORK_EMPTY;
1368
1369             LOG(Media, "HTMLMediaElement::selectMediaResource(%p) - nothing to load", this);
1370             return;
1371         }
1372
1373         // 7. Set the media element’s networkState to NETWORK_LOADING.
1374         m_networkState = NETWORK_LOADING;
1375
1376         // 8. Queue a task to fire a simple event named loadstart at the media element.
1377         scheduleEvent(eventNames().loadstartEvent);
1378
1379         // 9. Run the appropriate steps from the following list:
1380         // ↳ If mode is object
1381         if (mode == Object) {
1382             // 1. Set the currentSrc attribute to the empty string.
1383             m_currentSrc = URL();
1384
1385             // 2. End the synchronous section, continuing the remaining steps in parallel.
1386             // 3. Run the resource fetch algorithm with the assigned media provider object.
1387             WTF::visit(WTF::makeVisitor(
1388 #if ENABLE(MEDIA_STREAM)
1389                 [this](RefPtr<MediaStream> stream) { m_mediaStreamSrcObject = stream; },
1390 #endif
1391 #if ENABLE(MEDIA_SOURCE)
1392                 [this](RefPtr<MediaSource> source) { m_mediaSource = source; },
1393 #endif
1394                 [this](RefPtr<Blob> blob) { m_blob = blob; }
1395             ), m_mediaProvider.value());
1396
1397             ContentType contentType;
1398             loadResource(URL(), contentType, String());
1399             LOG(Media, "HTMLMediaElement::selectMediaResource(%p) - using 'srcObject' property", this);
1400
1401             //    If that algorithm returns without aborting this one, then the load failed.
1402             // 4. Failed with media provider: Reaching this step indicates that the media resource
1403             //    failed to load. Queue a task to run the dedicated media source failure steps.
1404             // 5. Wait for the task queued by the previous step to have executed.
1405             // 6. Abort these steps. The element won’t attempt to load another resource until this
1406             //    algorithm is triggered again.
1407             return;
1408         }
1409
1410         // ↳ If mode is attribute
1411         if (mode == Attribute) {
1412             m_loadState = LoadingFromSrcAttr;
1413
1414             // 1. If the src attribute’s value is the empty string, then end the synchronous section,
1415             //    and jump down to the failed with attribute step below.
1416             // 2. Let absolute URL be the absolute URL that would have resulted from parsing the URL
1417             //    specified by the src attribute’s value relative to the media element when the src
1418             //    attribute was last changed.
1419             URL absoluteURL = getNonEmptyURLAttribute(srcAttr);
1420             if (absoluteURL.isEmpty()) {
1421                 mediaLoadingFailed(MediaPlayer::FormatError);
1422                 LOG(Media, "HTMLMediaElement::selectMediaResource(%p) -  empty 'src'", this);
1423                 return;
1424             }
1425
1426             if (!isSafeToLoadURL(absoluteURL, Complain) || !dispatchBeforeLoadEvent(absoluteURL.string())) {
1427                 mediaLoadingFailed(MediaPlayer::FormatError);
1428                 return;
1429             }
1430
1431             // 3. If absolute URL was obtained successfully, set the currentSrc attribute to absolute URL.
1432             m_currentSrc = absoluteURL;
1433
1434             // 4. End the synchronous section, continuing the remaining steps in parallel.
1435             // 5. If absolute URL was obtained successfully, run the resource fetch algorithm with absolute
1436             //    URL. If that algorithm returns without aborting this one, then the load failed.
1437
1438             // No type or key system information is available when the url comes
1439             // from the 'src' attribute so MediaPlayer
1440             // will have to pick a media engine based on the file extension.
1441             ContentType contentType;
1442             loadResource(absoluteURL, contentType, String());
1443             LOG(Media, "HTMLMediaElement::selectMediaResource(%p) - using 'src' attribute url", this);
1444
1445             // 6. Failed with attribute: Reaching this step indicates that the media resource failed to load
1446             //    or that the given URL could not be resolved. Queue a task to run the dedicated media source failure steps.
1447             // 7. Wait for the task queued by the previous step to have executed.
1448             // 8. Abort these steps. The element won’t attempt to load another resource until this algorithm is triggered again.
1449             return;
1450         }
1451
1452         // ↳ Otherwise (mode is children)
1453         // (Ctd. in loadNextSourceChild())
1454         loadNextSourceChild();
1455     });
1456 }
1457
1458 void HTMLMediaElement::loadNextSourceChild()
1459 {
1460     ContentType contentType;
1461     String keySystem;
1462     URL mediaURL = selectNextSourceChild(&contentType, &keySystem, Complain);
1463     if (!mediaURL.isValid()) {
1464         waitForSourceChange();
1465         return;
1466     }
1467
1468     // Recreate the media player for the new url
1469     createMediaPlayer();
1470
1471     m_loadState = LoadingFromSourceElement;
1472     loadResource(mediaURL, contentType, keySystem);
1473 }
1474
1475 void HTMLMediaElement::loadResource(const URL& initialURL, ContentType& contentType, const String& keySystem)
1476 {
1477     ASSERT(initialURL.isEmpty() || isSafeToLoadURL(initialURL, Complain));
1478
1479     LOG(Media, "HTMLMediaElement::loadResource(%p) - %s, %s, %s", this, urlForLoggingMedia(initialURL).utf8().data(), contentType.raw().utf8().data(), keySystem.utf8().data());
1480
1481     Frame* frame = document().frame();
1482     if (!frame) {
1483         mediaLoadingFailed(MediaPlayer::FormatError);
1484         return;
1485     }
1486
1487     Page* page = frame->page();
1488     if (!page) {
1489         mediaLoadingFailed(MediaPlayer::FormatError);
1490         return;
1491     }
1492
1493     URL url = initialURL;
1494     if (!url.isEmpty() && !frame->loader().willLoadMediaElementURL(url)) {
1495         mediaLoadingFailed(MediaPlayer::FormatError);
1496         return;
1497     }
1498
1499 #if ENABLE(CONTENT_EXTENSIONS)
1500     if (auto* documentLoader = frame->loader().documentLoader()) {
1501         if (page->userContentProvider().processContentExtensionRulesForLoad(url, ResourceType::Media, *documentLoader).blockedLoad) {
1502             mediaLoadingFailed(MediaPlayer::FormatError);
1503             return;
1504         }
1505     }
1506 #endif
1507
1508     // The resource fetch algorithm
1509     m_networkState = NETWORK_LOADING;
1510
1511     // If the URL should be loaded from the application cache, pass the URL of the cached file to the media engine.
1512     ApplicationCacheResource* resource = nullptr;
1513     if (!url.isEmpty() && frame->loader().documentLoader()->applicationCacheHost().shouldLoadResourceFromApplicationCache(ResourceRequest(url), resource)) {
1514         // Resources that are not present in the manifest will always fail to load (at least, after the
1515         // cache has been primed the first time), making the testing of offline applications simpler.
1516         if (!resource || resource->path().isEmpty()) {
1517             mediaLoadingFailed(MediaPlayer::NetworkError);
1518             return;
1519         }
1520     }
1521
1522     // Log that we started loading a media element.
1523     page->diagnosticLoggingClient().logDiagnosticMessage(isVideo() ? DiagnosticLoggingKeys::videoKey() : DiagnosticLoggingKeys::audioKey(), DiagnosticLoggingKeys::loadingKey(), ShouldSample::No);
1524
1525     m_firstTimePlaying = true;
1526
1527     // Set m_currentSrc *before* changing to the cache URL, the fact that we are loading from the app
1528     // cache is an internal detail not exposed through the media element API.
1529     m_currentSrc = url;
1530
1531     if (resource) {
1532         url = ApplicationCacheHost::createFileURL(resource->path());
1533         LOG(Media, "HTMLMediaElement::loadResource(%p) - will load from app cache -> %s", this, urlForLoggingMedia(url).utf8().data());
1534     }
1535
1536     LOG(Media, "HTMLMediaElement::loadResource(%p) - m_currentSrc -> %s", this, urlForLoggingMedia(m_currentSrc).utf8().data());
1537
1538     startProgressEventTimer();
1539
1540     bool privateMode = document().page() && document().page()->usesEphemeralSession();
1541     m_player->setPrivateBrowsingMode(privateMode);
1542
1543     // Reset display mode to force a recalculation of what to show because we are resetting the player.
1544     setDisplayMode(Unknown);
1545
1546     if (!autoplay() && !m_havePreparedToPlay)
1547         m_player->setPreload(m_mediaSession->effectivePreloadForElement(*this));
1548     m_player->setPreservesPitch(m_webkitPreservesPitch);
1549
1550     if (!m_explicitlyMuted) {
1551         m_explicitlyMuted = true;
1552         m_muted = hasAttributeWithoutSynchronization(mutedAttr);
1553         m_mediaSession->canProduceAudioChanged();
1554     }
1555
1556     updateVolume();
1557
1558     bool loadAttempted = false;
1559 #if ENABLE(MEDIA_SOURCE)
1560     if (!m_mediaSource && url.protocolIs(mediaSourceBlobProtocol))
1561         m_mediaSource = MediaSource::lookup(url.string());
1562
1563     if (m_mediaSource) {
1564         loadAttempted = true;
1565         if (!m_mediaSource->attachToElement(*this) || !m_player->load(url, contentType, m_mediaSource.get())) {
1566             // Forget our reference to the MediaSource, so we leave it alone
1567             // while processing remainder of load failure.
1568             m_mediaSource = nullptr;
1569             mediaLoadingFailed(MediaPlayer::FormatError);
1570         }
1571     }
1572 #endif
1573
1574 #if ENABLE(MEDIA_STREAM)
1575     if (!loadAttempted) {
1576         if (!m_mediaStreamSrcObject && url.protocolIs(mediaStreamBlobProtocol))
1577             m_mediaStreamSrcObject = MediaStreamRegistry::shared().lookUp(url);
1578
1579         if (m_mediaStreamSrcObject) {
1580             loadAttempted = true;
1581             if (!m_player->load(m_mediaStreamSrcObject->privateStream()))
1582                 mediaLoadingFailed(MediaPlayer::FormatError);
1583         }
1584     }
1585 #endif
1586
1587     if (!loadAttempted && m_blob) {
1588         loadAttempted = true;
1589         if (!m_player->load(m_blob->url(), contentType, keySystem))
1590             mediaLoadingFailed(MediaPlayer::FormatError);
1591     }
1592
1593     if (!loadAttempted && !m_player->load(url, contentType, keySystem))
1594         mediaLoadingFailed(MediaPlayer::FormatError);
1595
1596     // If there is no poster to display, allow the media engine to render video frames as soon as
1597     // they are available.
1598     updateDisplayState();
1599
1600     updateRenderer();
1601 }
1602
1603 #if ENABLE(VIDEO_TRACK)
1604
1605 static bool trackIndexCompare(TextTrack* a, TextTrack* b)
1606 {
1607     return a->trackIndex() - b->trackIndex() < 0;
1608 }
1609
1610 static bool eventTimeCueCompare(const std::pair<MediaTime, TextTrackCue*>& a, const std::pair<MediaTime, TextTrackCue*>& b)
1611 {
1612     // 12 - Sort the tasks in events in ascending time order (tasks with earlier
1613     // times first).
1614     if (a.first != b.first)
1615         return a.first - b.first < MediaTime::zeroTime();
1616
1617     // If the cues belong to different text tracks, it doesn't make sense to
1618     // compare the two tracks by the relative cue order, so return the relative
1619     // track order.
1620     if (a.second->track() != b.second->track())
1621         return trackIndexCompare(a.second->track(), b.second->track());
1622
1623     // 12 - Further sort tasks in events that have the same time by the
1624     // relative text track cue order of the text track cues associated
1625     // with these tasks.
1626     return a.second->isOrderedBefore(b.second);
1627 }
1628
1629 static bool compareCueInterval(const CueInterval& one, const CueInterval& two)
1630 {
1631     return one.data()->isOrderedBefore(two.data());
1632 }
1633
1634 void HTMLMediaElement::updateActiveTextTrackCues(const MediaTime& movieTime)
1635 {
1636     // 4.8.10.8 Playing the media resource
1637
1638     //  If the current playback position changes while the steps are running,
1639     //  then the user agent must wait for the steps to complete, and then must
1640     //  immediately rerun the steps.
1641     if (ignoreTrackDisplayUpdateRequests())
1642         return;
1643
1644     LOG(Media, "HTMLMediaElement::updateActiveTextTrackCues(%p)", this);
1645
1646     // 1 - Let current cues be a list of cues, initialized to contain all the
1647     // cues of all the hidden, showing, or showing by default text tracks of the
1648     // media element (not the disabled ones) whose start times are less than or
1649     // equal to the current playback position and whose end times are greater
1650     // than the current playback position.
1651     CueList currentCues;
1652
1653     // The user agent must synchronously unset [the text track cue active] flag
1654     // whenever ... the media element's readyState is changed back to HAVE_NOTHING.
1655     if (m_readyState != HAVE_NOTHING && m_player) {
1656         currentCues = m_cueTree.allOverlaps(m_cueTree.createInterval(movieTime, movieTime));
1657         if (currentCues.size() > 1)
1658             std::sort(currentCues.begin(), currentCues.end(), &compareCueInterval);
1659     }
1660
1661     CueList previousCues;
1662     CueList missedCues;
1663
1664     // 2 - Let other cues be a list of cues, initialized to contain all the cues
1665     // of hidden, showing, and showing by default text tracks of the media
1666     // element that are not present in current cues.
1667     previousCues = m_currentlyActiveCues;
1668
1669     // 3 - Let last time be the current playback position at the time this
1670     // algorithm was last run for this media element, if this is not the first
1671     // time it has run.
1672     MediaTime lastTime = m_lastTextTrackUpdateTime;
1673
1674     // 4 - If the current playback position has, since the last time this
1675     // algorithm was run, only changed through its usual monotonic increase
1676     // during normal playback, then let missed cues be the list of cues in other
1677     // cues whose start times are greater than or equal to last time and whose
1678     // end times are less than or equal to the current playback position.
1679     // Otherwise, let missed cues be an empty list.
1680     if (lastTime >= MediaTime::zeroTime() && m_lastSeekTime < movieTime) {
1681         for (auto& cue : m_cueTree.allOverlaps(m_cueTree.createInterval(lastTime, movieTime))) {
1682             // Consider cues that may have been missed since the last seek time.
1683             if (cue.low() > std::max(m_lastSeekTime, lastTime) && cue.high() < movieTime)
1684                 missedCues.append(cue);
1685         }
1686     }
1687
1688     m_lastTextTrackUpdateTime = movieTime;
1689
1690     // 5 - If the time was reached through the usual monotonic increase of the
1691     // current playback position during normal playback, and if the user agent
1692     // has not fired a timeupdate event at the element in the past 15 to 250ms
1693     // and is not still running event handlers for such an event, then the user
1694     // agent must queue a task to fire a simple event named timeupdate at the
1695     // element. (In the other cases, such as explicit seeks, relevant events get
1696     // fired as part of the overall process of changing the current playback
1697     // position.)
1698     if (!m_paused && m_lastSeekTime <= lastTime)
1699         scheduleTimeupdateEvent(false);
1700
1701     // Explicitly cache vector sizes, as their content is constant from here.
1702     size_t currentCuesSize = currentCues.size();
1703     size_t missedCuesSize = missedCues.size();
1704     size_t previousCuesSize = previousCues.size();
1705
1706     // 6 - If all of the cues in current cues have their text track cue active
1707     // flag set, none of the cues in other cues have their text track cue active
1708     // flag set, and missed cues is empty, then abort these steps.
1709     bool activeSetChanged = missedCuesSize;
1710
1711     for (size_t i = 0; !activeSetChanged && i < previousCuesSize; ++i)
1712         if (!currentCues.contains(previousCues[i]) && previousCues[i].data()->isActive())
1713             activeSetChanged = true;
1714
1715     for (size_t i = 0; i < currentCuesSize; ++i) {
1716         TextTrackCue* cue = currentCues[i].data();
1717
1718         if (cue->isRenderable())
1719             toVTTCue(cue)->updateDisplayTree(movieTime);
1720
1721         if (!cue->isActive())
1722             activeSetChanged = true;
1723     }
1724
1725     if (!activeSetChanged)
1726         return;
1727
1728     // 7 - If the time was reached through the usual monotonic increase of the
1729     // current playback position during normal playback, and there are cues in
1730     // other cues that have their text track cue pause-on-exi flag set and that
1731     // either have their text track cue active flag set or are also in missed
1732     // cues, then immediately pause the media element.
1733     for (size_t i = 0; !m_paused && i < previousCuesSize; ++i) {
1734         if (previousCues[i].data()->pauseOnExit()
1735             && previousCues[i].data()->isActive()
1736             && !currentCues.contains(previousCues[i]))
1737             pause();
1738     }
1739
1740     for (size_t i = 0; !m_paused && i < missedCuesSize; ++i) {
1741         if (missedCues[i].data()->pauseOnExit())
1742             pause();
1743     }
1744
1745     // 8 - Let events be a list of tasks, initially empty. Each task in this
1746     // list will be associated with a text track, a text track cue, and a time,
1747     // which are used to sort the list before the tasks are queued.
1748     Vector<std::pair<MediaTime, TextTrackCue*>> eventTasks;
1749
1750     // 8 - Let affected tracks be a list of text tracks, initially empty.
1751     Vector<TextTrack*> affectedTracks;
1752
1753     for (size_t i = 0; i < missedCuesSize; ++i) {
1754         // 9 - For each text track cue in missed cues, prepare an event named enter
1755         // for the TextTrackCue object with the text track cue start time.
1756         eventTasks.append({ missedCues[i].data()->startMediaTime(), missedCues[i].data() });
1757
1758         // 10 - For each text track [...] in missed cues, prepare an event
1759         // named exit for the TextTrackCue object with the  with the later of
1760         // the text track cue end time and the text track cue start time.
1761
1762         // Note: An explicit task is added only if the cue is NOT a zero or
1763         // negative length cue. Otherwise, the need for an exit event is
1764         // checked when these tasks are actually queued below. This doesn't
1765         // affect sorting events before dispatch either, because the exit
1766         // event has the same time as the enter event.
1767         if (missedCues[i].data()->startMediaTime() < missedCues[i].data()->endMediaTime())
1768             eventTasks.append({ missedCues[i].data()->endMediaTime(), missedCues[i].data() });
1769     }
1770
1771     for (size_t i = 0; i < previousCuesSize; ++i) {
1772         // 10 - For each text track cue in other cues that has its text
1773         // track cue active flag set prepare an event named exit for the
1774         // TextTrackCue object with the text track cue end time.
1775         if (!currentCues.contains(previousCues[i]))
1776             eventTasks.append({ previousCues[i].data()->endMediaTime(), previousCues[i].data() });
1777     }
1778
1779     for (size_t i = 0; i < currentCuesSize; ++i) {
1780         // 11 - For each text track cue in current cues that does not have its
1781         // text track cue active flag set, prepare an event named enter for the
1782         // TextTrackCue object with the text track cue start time.
1783         if (!previousCues.contains(currentCues[i]))
1784             eventTasks.append({ currentCues[i].data()->startMediaTime(), currentCues[i].data() });
1785     }
1786
1787     // 12 - Sort the tasks in events in ascending time order (tasks with earlier
1788     // times first).
1789     std::sort(eventTasks.begin(), eventTasks.end(), eventTimeCueCompare);
1790
1791     for (auto& eventTask : eventTasks) {
1792         if (!affectedTracks.contains(eventTask.second->track()))
1793             affectedTracks.append(eventTask.second->track());
1794
1795         // 13 - Queue each task in events, in list order.
1796
1797         // Each event in eventTasks may be either an enterEvent or an exitEvent,
1798         // depending on the time that is associated with the event. This
1799         // correctly identifies the type of the event, if the startTime is
1800         // less than the endTime in the cue.
1801         if (eventTask.second->startTime() >= eventTask.second->endTime()) {
1802             auto enterEvent = Event::create(eventNames().enterEvent, false, false);
1803             enterEvent->setTarget(eventTask.second);
1804             m_asyncEventQueue.enqueueEvent(WTFMove(enterEvent));
1805
1806             auto exitEvent = Event::create(eventNames().exitEvent, false, false);
1807             exitEvent->setTarget(eventTask.second);
1808             m_asyncEventQueue.enqueueEvent(WTFMove(exitEvent));
1809         } else {
1810             RefPtr<Event> event;
1811             if (eventTask.first == eventTask.second->startMediaTime())
1812                 event = Event::create(eventNames().enterEvent, false, false);
1813             else
1814                 event = Event::create(eventNames().exitEvent, false, false);
1815             event->setTarget(eventTask.second);
1816             m_asyncEventQueue.enqueueEvent(WTFMove(event));
1817         }
1818     }
1819
1820     // 14 - Sort affected tracks in the same order as the text tracks appear in
1821     // the media element's list of text tracks, and remove duplicates.
1822     std::sort(affectedTracks.begin(), affectedTracks.end(), trackIndexCompare);
1823
1824     // 15 - For each text track in affected tracks, in the list order, queue a
1825     // task to fire a simple event named cuechange at the TextTrack object, and, ...
1826     for (auto& affectedTrack : affectedTracks) {
1827         auto event = Event::create(eventNames().cuechangeEvent, false, false);
1828         event->setTarget(affectedTrack);
1829         m_asyncEventQueue.enqueueEvent(WTFMove(event));
1830
1831         // ... if the text track has a corresponding track element, to then fire a
1832         // simple event named cuechange at the track element as well.
1833         if (is<LoadableTextTrack>(*affectedTrack)) {
1834             auto event = Event::create(eventNames().cuechangeEvent, false, false);
1835             auto* trackElement = downcast<LoadableTextTrack>(*affectedTrack).trackElement();
1836             ASSERT(trackElement);
1837             event->setTarget(trackElement);
1838             m_asyncEventQueue.enqueueEvent(WTFMove(event));
1839         }
1840     }
1841
1842     // 16 - Set the text track cue active flag of all the cues in the current
1843     // cues, and unset the text track cue active flag of all the cues in the
1844     // other cues.
1845     for (size_t i = 0; i < currentCuesSize; ++i)
1846         currentCues[i].data()->setIsActive(true);
1847
1848     for (size_t i = 0; i < previousCuesSize; ++i)
1849         if (!currentCues.contains(previousCues[i]))
1850             previousCues[i].data()->setIsActive(false);
1851
1852     // Update the current active cues.
1853     m_currentlyActiveCues = currentCues;
1854
1855     if (activeSetChanged)
1856         updateTextTrackDisplay();
1857 }
1858
1859 bool HTMLMediaElement::textTracksAreReady() const
1860 {
1861     // 4.8.10.12.1 Text track model
1862     // ...
1863     // The text tracks of a media element are ready if all the text tracks whose mode was not
1864     // in the disabled state when the element's resource selection algorithm last started now
1865     // have a text track readiness state of loaded or failed to load.
1866     for (unsigned i = 0; i < m_textTracksWhenResourceSelectionBegan.size(); ++i) {
1867         if (m_textTracksWhenResourceSelectionBegan[i]->readinessState() == TextTrack::Loading
1868             || m_textTracksWhenResourceSelectionBegan[i]->readinessState() == TextTrack::NotLoaded)
1869             return false;
1870     }
1871
1872     return true;
1873 }
1874
1875 void HTMLMediaElement::textTrackReadyStateChanged(TextTrack* track)
1876 {
1877     if (track->readinessState() != TextTrack::Loading
1878         && track->mode() != TextTrack::Mode::Disabled) {
1879         // The display trees exist as long as the track is active, in this case,
1880         // and if the same track is loaded again (for example if the src attribute was changed),
1881         // cues can be accumulated with the old ones, that's why they needs to be flushed
1882         if (hasMediaControls())
1883             mediaControls()->clearTextDisplayContainer();
1884         updateTextTrackDisplay();
1885     }
1886     if (m_player && m_textTracksWhenResourceSelectionBegan.contains(track)) {
1887         if (track->readinessState() != TextTrack::Loading)
1888             setReadyState(m_player->readyState());
1889     } else {
1890         // The track readiness state might have changed as a result of the user
1891         // clicking the captions button. In this case, a check whether all the
1892         // resources have failed loading should be done in order to hide the CC button.
1893         if (hasMediaControls() && track->readinessState() == TextTrack::FailedToLoad)
1894             mediaControls()->refreshClosedCaptionsButtonVisibility();
1895     }
1896 }
1897
1898 void HTMLMediaElement::audioTrackEnabledChanged(AudioTrack& track)
1899 {
1900     if (m_audioTracks && m_audioTracks->contains(track))
1901         m_audioTracks->scheduleChangeEvent();
1902     if (processingUserGestureForMedia())
1903         removeBehaviorsRestrictionsAfterFirstUserGesture(MediaElementSession::AllRestrictions & ~MediaElementSession::RequireUserGestureToControlControlsManager);
1904 }
1905
1906 void HTMLMediaElement::textTrackModeChanged(TextTrack& track)
1907 {
1908     bool trackIsLoaded = true;
1909     if (track.trackType() == TextTrack::TrackElement) {
1910         trackIsLoaded = false;
1911         for (auto& trackElement : childrenOfType<HTMLTrackElement>(*this)) {
1912             if (&trackElement.track() == &track) {
1913                 if (trackElement.readyState() == HTMLTrackElement::LOADING || trackElement.readyState() == HTMLTrackElement::LOADED)
1914                     trackIsLoaded = true;
1915                 break;
1916             }
1917         }
1918     }
1919
1920     // If this is the first added track, create the list of text tracks.
1921     if (!m_textTracks)
1922         m_textTracks = TextTrackList::create(this, ActiveDOMObject::scriptExecutionContext());
1923
1924     // Mark this track as "configured" so configureTextTracks won't change the mode again.
1925     track.setHasBeenConfigured(true);
1926
1927     if (track.mode() != TextTrack::Mode::Disabled && trackIsLoaded)
1928         textTrackAddCues(track, *track.cues());
1929
1930     configureTextTrackDisplay(AssumeTextTrackVisibilityChanged);
1931
1932     if (m_textTracks && m_textTracks->contains(track))
1933         m_textTracks->scheduleChangeEvent();
1934
1935 #if ENABLE(AVF_CAPTIONS)
1936     if (track.trackType() == TextTrack::TrackElement && m_player)
1937         m_player->notifyTrackModeChanged();
1938 #endif
1939 }
1940
1941 void HTMLMediaElement::videoTrackSelectedChanged(VideoTrack& track)
1942 {
1943     if (m_videoTracks && m_videoTracks->contains(track))
1944         m_videoTracks->scheduleChangeEvent();
1945 }
1946
1947 void HTMLMediaElement::textTrackKindChanged(TextTrack& track)
1948 {
1949     if (track.kind() != TextTrack::Kind::Captions && track.kind() != TextTrack::Kind::Subtitles && track.mode() == TextTrack::Mode::Showing)
1950         track.setMode(TextTrack::Mode::Hidden);
1951 }
1952
1953 void HTMLMediaElement::beginIgnoringTrackDisplayUpdateRequests()
1954 {
1955     ++m_ignoreTrackDisplayUpdate;
1956 }
1957
1958 void HTMLMediaElement::endIgnoringTrackDisplayUpdateRequests()
1959 {
1960     ASSERT(m_ignoreTrackDisplayUpdate);
1961     --m_ignoreTrackDisplayUpdate;
1962     if (!m_ignoreTrackDisplayUpdate && m_inActiveDocument)
1963         updateActiveTextTrackCues(currentMediaTime());
1964 }
1965
1966 void HTMLMediaElement::textTrackAddCues(TextTrack& track, const TextTrackCueList& cues)
1967 {
1968     if (track.mode() == TextTrack::Mode::Disabled)
1969         return;
1970
1971     TrackDisplayUpdateScope scope { *this };
1972     for (unsigned i = 0; i < cues.length(); ++i)
1973         textTrackAddCue(track, *cues.item(i));
1974 }
1975
1976 void HTMLMediaElement::textTrackRemoveCues(TextTrack&, const TextTrackCueList& cues)
1977 {
1978     TrackDisplayUpdateScope scope { *this };
1979     for (unsigned i = 0; i < cues.length(); ++i) {
1980         auto& cue = *cues.item(i);
1981         textTrackRemoveCue(*cue.track(), cue);
1982     }
1983 }
1984
1985 void HTMLMediaElement::textTrackAddCue(TextTrack& track, TextTrackCue& cue)
1986 {
1987     if (track.mode() == TextTrack::Mode::Disabled)
1988         return;
1989
1990     // Negative duration cues need be treated in the interval tree as
1991     // zero-length cues.
1992     MediaTime endTime = std::max(cue.startMediaTime(), cue.endMediaTime());
1993
1994     CueInterval interval = m_cueTree.createInterval(cue.startMediaTime(), endTime, &cue);
1995     if (!m_cueTree.contains(interval))
1996         m_cueTree.add(interval);
1997     updateActiveTextTrackCues(currentMediaTime());
1998 }
1999
2000 void HTMLMediaElement::textTrackRemoveCue(TextTrack&, TextTrackCue& cue)
2001 {
2002     // Negative duration cues need to be treated in the interval tree as
2003     // zero-length cues.
2004     MediaTime endTime = std::max(cue.startMediaTime(), cue.endMediaTime());
2005
2006     CueInterval interval = m_cueTree.createInterval(cue.startMediaTime(), endTime, &cue);
2007     m_cueTree.remove(interval);
2008
2009     // Since the cue will be removed from the media element and likely the
2010     // TextTrack might also be destructed, notifying the region of the cue
2011     // removal shouldn't be done.
2012     if (cue.isRenderable())
2013         toVTTCue(&cue)->notifyRegionWhenRemovingDisplayTree(false);
2014
2015     size_t index = m_currentlyActiveCues.find(interval);
2016     if (index != notFound) {
2017         cue.setIsActive(false);
2018         m_currentlyActiveCues.remove(index);
2019     }
2020
2021     if (cue.isRenderable())
2022         toVTTCue(&cue)->removeDisplayTree();
2023     updateActiveTextTrackCues(currentMediaTime());
2024
2025     if (cue.isRenderable())
2026         toVTTCue(&cue)->notifyRegionWhenRemovingDisplayTree(true);
2027 }
2028
2029 #endif
2030
2031 static inline bool isAllowedToLoadMediaURL(HTMLMediaElement& element, const URL& url, bool isInUserAgentShadowTree)
2032 {
2033     // Elements in user agent show tree should load whatever the embedding document policy is.
2034     if (isInUserAgentShadowTree)
2035         return true;
2036
2037     ASSERT(element.document().contentSecurityPolicy());
2038     return element.document().contentSecurityPolicy()->allowMediaFromSource(url);
2039 }
2040
2041 bool HTMLMediaElement::isSafeToLoadURL(const URL& url, InvalidURLAction actionIfInvalid)
2042 {
2043     if (!url.isValid()) {
2044         LOG(Media, "HTMLMediaElement::isSafeToLoadURL(%p) - %s -> FALSE because url is invalid", this, urlForLoggingMedia(url).utf8().data());
2045         return false;
2046     }
2047
2048     Frame* frame = document().frame();
2049     if (!frame || !document().securityOrigin().canDisplay(url)) {
2050         if (actionIfInvalid == Complain)
2051             FrameLoader::reportLocalLoadFailed(frame, url.stringCenterEllipsizedToLength());
2052         LOG(Media, "HTMLMediaElement::isSafeToLoadURL(%p) - %s -> FALSE rejected by SecurityOrigin", this, urlForLoggingMedia(url).utf8().data());
2053         return false;
2054     }
2055
2056     if (!isAllowedToLoadMediaURL(*this, url, isInUserAgentShadowTree())) {
2057         LOG(Media, "HTMLMediaElement::isSafeToLoadURL(%p) - %s -> rejected by Content Security Policy", this, urlForLoggingMedia(url).utf8().data());
2058         return false;
2059     }
2060
2061     return true;
2062 }
2063
2064 void HTMLMediaElement::startProgressEventTimer()
2065 {
2066     if (m_progressEventTimer.isActive())
2067         return;
2068
2069     m_previousProgressTime = monotonicallyIncreasingTime();
2070     // 350ms is not magic, it is in the spec!
2071     m_progressEventTimer.startRepeating(350_ms);
2072 }
2073
2074 void HTMLMediaElement::waitForSourceChange()
2075 {
2076     LOG(Media, "HTMLMediaElement::waitForSourceChange(%p)", this);
2077
2078     stopPeriodicTimers();
2079     m_loadState = WaitingForSource;
2080
2081     // 6.17 - Waiting: Set the element's networkState attribute to the NETWORK_NO_SOURCE value
2082     m_networkState = NETWORK_NO_SOURCE;
2083
2084     // 6.18 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
2085     setShouldDelayLoadEvent(false);
2086
2087     updateDisplayState();
2088     updateRenderer();
2089 }
2090
2091 void HTMLMediaElement::noneSupported()
2092 {
2093     LOG(Media, "HTMLMediaElement::noneSupported(%p)", this);
2094
2095     stopPeriodicTimers();
2096     m_loadState = WaitingForSource;
2097     m_currentSourceNode = nullptr;
2098
2099     // 4.8.10.5
2100     // 6 - Reaching this step indicates that the media resource failed to load or that the given
2101     // URL could not be resolved. In one atomic operation, run the following steps:
2102
2103     // 6.1 - Set the error attribute to a new MediaError object whose code attribute is set to
2104     // MEDIA_ERR_SRC_NOT_SUPPORTED.
2105     m_error = MediaError::create(MediaError::MEDIA_ERR_SRC_NOT_SUPPORTED);
2106
2107     // 6.2 - Forget the media element's media-resource-specific text tracks.
2108     forgetResourceSpecificTracks();
2109
2110     // 6.3 - Set the element's networkState attribute to the NETWORK_NO_SOURCE value.
2111     m_networkState = NETWORK_NO_SOURCE;
2112
2113     // 7 - Queue a task to fire a simple event named error at the media element.
2114     scheduleEvent(eventNames().errorEvent);
2115
2116     rejectPendingPlayPromises(DOMError::create("NotSupportedError", "The operation is not supported."));
2117
2118 #if ENABLE(MEDIA_SOURCE)
2119     detachMediaSource();
2120 #endif
2121
2122     // 8 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
2123     setShouldDelayLoadEvent(false);
2124
2125     // 9 - Abort these steps. Until the load() method is invoked or the src attribute is changed,
2126     // the element won't attempt to load another resource.
2127
2128     updateDisplayState();
2129     updateRenderer();
2130 }
2131
2132 void HTMLMediaElement::mediaLoadingFailedFatally(MediaPlayer::NetworkState error)
2133 {
2134     LOG(Media, "HTMLMediaElement::mediaLoadingFailedFatally(%p) - error = %d", this, static_cast<int>(error));
2135
2136     // 1 - The user agent should cancel the fetching process.
2137     stopPeriodicTimers();
2138     m_loadState = WaitingForSource;
2139
2140     // 2 - Set the error attribute to a new MediaError object whose code attribute is
2141     // set to MEDIA_ERR_NETWORK/MEDIA_ERR_DECODE.
2142     if (error == MediaPlayer::NetworkError)
2143         m_error = MediaError::create(MediaError::MEDIA_ERR_NETWORK);
2144     else if (error == MediaPlayer::DecodeError)
2145         m_error = MediaError::create(MediaError::MEDIA_ERR_DECODE);
2146     else
2147         ASSERT_NOT_REACHED();
2148
2149     // 3 - Queue a task to fire a simple event named error at the media element.
2150     scheduleEvent(eventNames().errorEvent);
2151
2152 #if ENABLE(MEDIA_SOURCE)
2153     detachMediaSource();
2154 #endif
2155
2156     // 4 - Set the element's networkState attribute to the NETWORK_EMPTY value and queue a
2157     // task to fire a simple event called emptied at the element.
2158     m_networkState = NETWORK_EMPTY;
2159     scheduleEvent(eventNames().emptiedEvent);
2160
2161     // 5 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
2162     setShouldDelayLoadEvent(false);
2163
2164     // 6 - Abort the overall resource selection algorithm.
2165     m_currentSourceNode = nullptr;
2166
2167 #if PLATFORM(COCOA)
2168     if (is<MediaDocument>(document()))
2169         downcast<MediaDocument>(document()).mediaElementSawUnsupportedTracks();
2170 #endif
2171 }
2172
2173 void HTMLMediaElement::cancelPendingEventsAndCallbacks()
2174 {
2175     LOG(Media, "HTMLMediaElement::cancelPendingEventsAndCallbacks(%p)", this);
2176     m_asyncEventQueue.cancelAllEvents();
2177
2178     for (auto& source : childrenOfType<HTMLSourceElement>(*this))
2179         source.cancelPendingErrorEvent();
2180
2181     rejectPendingPlayPromises(DOMError::create("AbortError", "The operation was aborted."));
2182 }
2183
2184 void HTMLMediaElement::mediaPlayerNetworkStateChanged(MediaPlayer*)
2185 {
2186     beginProcessingMediaPlayerCallback();
2187     setNetworkState(m_player->networkState());
2188     endProcessingMediaPlayerCallback();
2189 }
2190
2191 static void logMediaLoadRequest(Page* page, const String& mediaEngine, const String& errorMessage, bool succeeded)
2192 {
2193     if (!page)
2194         return;
2195
2196     DiagnosticLoggingClient& diagnosticLoggingClient = page->diagnosticLoggingClient();
2197     if (!succeeded) {
2198         diagnosticLoggingClient.logDiagnosticMessageWithResult(DiagnosticLoggingKeys::mediaLoadingFailedKey(), errorMessage, DiagnosticLoggingResultFail, ShouldSample::No);
2199         return;
2200     }
2201
2202     diagnosticLoggingClient.logDiagnosticMessage(DiagnosticLoggingKeys::mediaLoadedKey(), mediaEngine, ShouldSample::No);
2203
2204     if (!page->hasSeenAnyMediaEngine())
2205         diagnosticLoggingClient.logDiagnosticMessage(DiagnosticLoggingKeys::pageContainsAtLeastOneMediaEngineKey(), emptyString(), ShouldSample::No);
2206
2207     if (!page->hasSeenMediaEngine(mediaEngine))
2208         diagnosticLoggingClient.logDiagnosticMessage(DiagnosticLoggingKeys::pageContainsMediaEngineKey(), mediaEngine, ShouldSample::No);
2209
2210     page->sawMediaEngine(mediaEngine);
2211 }
2212
2213 static String stringForNetworkState(MediaPlayer::NetworkState state)
2214 {
2215     switch (state) {
2216     case MediaPlayer::Empty: return ASCIILiteral("Empty");
2217     case MediaPlayer::Idle: return ASCIILiteral("Idle");
2218     case MediaPlayer::Loading: return ASCIILiteral("Loading");
2219     case MediaPlayer::Loaded: return ASCIILiteral("Loaded");
2220     case MediaPlayer::FormatError: return ASCIILiteral("FormatError");
2221     case MediaPlayer::NetworkError: return ASCIILiteral("NetworkError");
2222     case MediaPlayer::DecodeError: return ASCIILiteral("DecodeError");
2223     default: return emptyString();
2224     }
2225 }
2226
2227 void HTMLMediaElement::mediaLoadingFailed(MediaPlayer::NetworkState error)
2228 {
2229     stopPeriodicTimers();
2230
2231     // If we failed while trying to load a <source> element, the movie was never parsed, and there are more
2232     // <source> children, schedule the next one
2233     if (m_readyState < HAVE_METADATA && m_loadState == LoadingFromSourceElement) {
2234
2235         // resource selection algorithm
2236         // 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.
2237         if (m_currentSourceNode)
2238             m_currentSourceNode->scheduleErrorEvent();
2239         else
2240             LOG(Media, "HTMLMediaElement::setNetworkState(%p) - error event not sent, <source> was removed", this);
2241
2242         // 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.
2243
2244         // 9.Otherwise.11 - Forget the media element's media-resource-specific tracks.
2245         forgetResourceSpecificTracks();
2246
2247         if (havePotentialSourceChild()) {
2248             LOG(Media, "HTMLMediaElement::setNetworkState(%p) - scheduling next <source>", this);
2249             scheduleNextSourceChild();
2250         } else {
2251             LOG(Media, "HTMLMediaElement::setNetworkState(%p) - no more <source> elements, waiting", this);
2252             waitForSourceChange();
2253         }
2254
2255         return;
2256     }
2257
2258     if ((error == MediaPlayer::NetworkError && m_readyState >= HAVE_METADATA) || error == MediaPlayer::DecodeError)
2259         mediaLoadingFailedFatally(error);
2260     else if ((error == MediaPlayer::FormatError || error == MediaPlayer::NetworkError) && m_loadState == LoadingFromSrcAttr)
2261         noneSupported();
2262
2263     updateDisplayState();
2264     if (hasMediaControls()) {
2265         mediaControls()->reset();
2266         mediaControls()->reportedError();
2267     }
2268
2269     logMediaLoadRequest(document().page(), String(), stringForNetworkState(error), false);
2270
2271     m_mediaSession->clientCharacteristicsChanged();
2272 }
2273
2274 void HTMLMediaElement::setNetworkState(MediaPlayer::NetworkState state)
2275 {
2276     LOG(Media, "HTMLMediaElement::setNetworkState(%p) - new state = %d, current state = %d", this, static_cast<int>(state), static_cast<int>(m_networkState));
2277
2278     if (state == MediaPlayer::Empty) {
2279         // Just update the cached state and leave, we can't do anything.
2280         m_networkState = NETWORK_EMPTY;
2281         return;
2282     }
2283
2284     if (state == MediaPlayer::FormatError || state == MediaPlayer::NetworkError || state == MediaPlayer::DecodeError) {
2285         mediaLoadingFailed(state);
2286         return;
2287     }
2288
2289     if (state == MediaPlayer::Idle) {
2290         if (m_networkState > NETWORK_IDLE) {
2291             changeNetworkStateFromLoadingToIdle();
2292             setShouldDelayLoadEvent(false);
2293         } else {
2294             m_networkState = NETWORK_IDLE;
2295         }
2296     }
2297
2298     if (state == MediaPlayer::Loading) {
2299         if (m_networkState < NETWORK_LOADING || m_networkState == NETWORK_NO_SOURCE)
2300             startProgressEventTimer();
2301         m_networkState = NETWORK_LOADING;
2302     }
2303
2304     if (state == MediaPlayer::Loaded) {
2305         if (m_networkState != NETWORK_IDLE)
2306             changeNetworkStateFromLoadingToIdle();
2307         m_completelyLoaded = true;
2308     }
2309
2310     if (hasMediaControls())
2311         mediaControls()->updateStatusDisplay();
2312 }
2313
2314 void HTMLMediaElement::changeNetworkStateFromLoadingToIdle()
2315 {
2316     m_progressEventTimer.stop();
2317     if (hasMediaControls() && m_player->didLoadingProgress())
2318         mediaControls()->bufferingProgressed();
2319
2320     // Schedule one last progress event so we guarantee that at least one is fired
2321     // for files that load very quickly.
2322     scheduleEvent(eventNames().progressEvent);
2323     scheduleEvent(eventNames().suspendEvent);
2324     m_networkState = NETWORK_IDLE;
2325 }
2326
2327 void HTMLMediaElement::mediaPlayerReadyStateChanged(MediaPlayer*)
2328 {
2329     beginProcessingMediaPlayerCallback();
2330
2331     setReadyState(m_player->readyState());
2332
2333     endProcessingMediaPlayerCallback();
2334 }
2335
2336 SuccessOr<MediaPlaybackDenialReason> HTMLMediaElement::canTransitionFromAutoplayToPlay() const
2337 {
2338     if (isAutoplaying()
2339         && mediaSession().autoplayPermitted()
2340         && paused()
2341         && autoplay()
2342         && !pausedForUserInteraction()
2343         && !document().isSandboxed(SandboxAutomaticFeatures)
2344         && m_readyState == HAVE_ENOUGH_DATA)
2345         return mediaSession().playbackPermitted(*this);
2346
2347     RELEASE_LOG(Media, "HTMLMediaElement::canTransitionFromAutoplayToPlay - page consent required");
2348     return MediaPlaybackDenialReason::PageConsentRequired;
2349 }
2350
2351 void HTMLMediaElement::dispatchPlayPauseEventsIfNeedsQuirks()
2352 {
2353     auto& document = this->document();
2354     if (!needsAutoplayPlayPauseEventsQuirk(document) && !needsAutoplayPlayPauseEventsQuirk(document.topDocument()))
2355         return;
2356
2357     scheduleEvent(eventNames().playingEvent);
2358     scheduleEvent(eventNames().pauseEvent);
2359 }
2360
2361 void HTMLMediaElement::setReadyState(MediaPlayer::ReadyState state)
2362 {
2363     LOG(Media, "HTMLMediaElement::setReadyState(%p) - new state = %d, current state = %d,", this, static_cast<int>(state), static_cast<int>(m_readyState));
2364
2365     // Set "wasPotentiallyPlaying" BEFORE updating m_readyState, potentiallyPlaying() uses it
2366     bool wasPotentiallyPlaying = potentiallyPlaying();
2367
2368     ReadyState oldState = m_readyState;
2369     ReadyState newState = static_cast<ReadyState>(state);
2370
2371 #if ENABLE(VIDEO_TRACK)
2372     bool tracksAreReady = textTracksAreReady();
2373
2374     if (newState == oldState && m_tracksAreReady == tracksAreReady)
2375         return;
2376
2377     m_tracksAreReady = tracksAreReady;
2378 #else
2379     if (newState == oldState)
2380         return;
2381     bool tracksAreReady = true;
2382 #endif
2383
2384     if (tracksAreReady)
2385         m_readyState = newState;
2386     else {
2387         // If a media file has text tracks the readyState may not progress beyond HAVE_FUTURE_DATA until
2388         // the text tracks are ready, regardless of the state of the media file.
2389         if (newState <= HAVE_METADATA)
2390             m_readyState = newState;
2391         else
2392             m_readyState = HAVE_CURRENT_DATA;
2393     }
2394
2395     if (oldState > m_readyStateMaximum)
2396         m_readyStateMaximum = oldState;
2397
2398     if (m_networkState == NETWORK_EMPTY)
2399         return;
2400
2401     if (m_seeking) {
2402         // 4.8.10.9, step 11
2403         if (wasPotentiallyPlaying && m_readyState < HAVE_FUTURE_DATA)
2404             scheduleEvent(eventNames().waitingEvent);
2405
2406         // 4.8.10.10 step 14 & 15.
2407         if (m_seekRequested && !m_player->seeking() && m_readyState >= HAVE_CURRENT_DATA)
2408             finishSeek();
2409     } else {
2410         if (wasPotentiallyPlaying && m_readyState < HAVE_FUTURE_DATA) {
2411             // 4.8.10.8
2412             invalidateCachedTime();
2413             scheduleTimeupdateEvent(false);
2414             scheduleEvent(eventNames().waitingEvent);
2415         }
2416     }
2417
2418     if (m_readyState >= HAVE_METADATA && oldState < HAVE_METADATA) {
2419         prepareMediaFragmentURI();
2420         scheduleEvent(eventNames().durationchangeEvent);
2421         scheduleResizeEvent();
2422         scheduleEvent(eventNames().loadedmetadataEvent);
2423 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
2424         if (hasEventListeners(eventNames().webkitplaybacktargetavailabilitychangedEvent))
2425             enqueuePlaybackTargetAvailabilityChangedEvent();
2426 #endif
2427         m_initiallyMuted = m_volume < 0.05 || muted();
2428
2429         if (hasMediaControls())
2430             mediaControls()->loadedMetadata();
2431         updateRenderer();
2432
2433         if (is<MediaDocument>(document()))
2434             downcast<MediaDocument>(document()).mediaElementNaturalSizeChanged(expandedIntSize(m_player->naturalSize()));
2435
2436         logMediaLoadRequest(document().page(), m_player->engineDescription(), String(), true);
2437
2438 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
2439         updateMediaState(UpdateState::Asynchronously);
2440 #endif
2441
2442         m_mediaSession->clientCharacteristicsChanged();
2443     }
2444
2445     bool shouldUpdateDisplayState = false;
2446
2447     if (m_readyState >= HAVE_CURRENT_DATA && oldState < HAVE_CURRENT_DATA) {
2448         if (!m_haveFiredLoadedData) {
2449             m_haveFiredLoadedData = true;
2450             scheduleEvent(eventNames().loadeddataEvent);
2451             // FIXME: It's not clear that it's correct to skip these two operations just
2452             // because m_haveFiredLoadedData is already true. At one time we were skipping
2453             // the call to setShouldDelayLoadEvent, which was definitely incorrect.
2454             shouldUpdateDisplayState = true;
2455             applyMediaFragmentURI();
2456         }
2457         setShouldDelayLoadEvent(false);
2458     }
2459
2460     bool isPotentiallyPlaying = potentiallyPlaying();
2461     if (m_readyState == HAVE_FUTURE_DATA && oldState <= HAVE_CURRENT_DATA && tracksAreReady) {
2462         scheduleEvent(eventNames().canplayEvent);
2463         if (isPotentiallyPlaying)
2464             scheduleNotifyAboutPlaying();
2465         shouldUpdateDisplayState = true;
2466     }
2467
2468     if (m_readyState == HAVE_ENOUGH_DATA && oldState < HAVE_ENOUGH_DATA && tracksAreReady) {
2469         if (oldState <= HAVE_CURRENT_DATA)
2470             scheduleEvent(eventNames().canplayEvent);
2471
2472         scheduleEvent(eventNames().canplaythroughEvent);
2473
2474         if (isPotentiallyPlaying && oldState <= HAVE_CURRENT_DATA)
2475             scheduleNotifyAboutPlaying();
2476
2477         auto success = canTransitionFromAutoplayToPlay();
2478         if (success) {
2479             m_paused = false;
2480             invalidateCachedTime();
2481             setPlaybackWithoutUserGesture(PlaybackWithoutUserGesture::Started);
2482             m_playbackStartedTime = currentMediaTime().toDouble();
2483             scheduleEvent(eventNames().playEvent);
2484             scheduleNotifyAboutPlaying();
2485         } else if (success.value() == MediaPlaybackDenialReason::UserGestureRequired)
2486             setPlaybackWithoutUserGesture(PlaybackWithoutUserGesture::Prevented);
2487
2488         shouldUpdateDisplayState = true;
2489     }
2490
2491     // If we transition to the Future Data state and we're about to begin playing, ensure playback is actually permitted first,
2492     // honoring any playback denial reasons such as the requirement of a user gesture.
2493     if (m_readyState == HAVE_FUTURE_DATA && oldState < HAVE_FUTURE_DATA && potentiallyPlaying() && !m_mediaSession->playbackPermitted(*this)) {
2494         pauseInternal();
2495         setPlaybackWithoutUserGesture(PlaybackWithoutUserGesture::Prevented);
2496     }
2497
2498     if (shouldUpdateDisplayState) {
2499         updateDisplayState();
2500         if (hasMediaControls()) {
2501             mediaControls()->refreshClosedCaptionsButtonVisibility();
2502             mediaControls()->updateStatusDisplay();
2503         }
2504     }
2505
2506     updatePlayState();
2507     updateMediaController();
2508 #if ENABLE(VIDEO_TRACK)
2509     updateActiveTextTrackCues(currentMediaTime());
2510 #endif
2511 }
2512
2513 #if ENABLE(LEGACY_ENCRYPTED_MEDIA)
2514 RefPtr<ArrayBuffer> HTMLMediaElement::mediaPlayerCachedKeyForKeyId(const String& keyId) const
2515 {
2516     return m_webKitMediaKeys ? m_webKitMediaKeys->cachedKeyForKeyId(keyId) : nullptr;
2517 }
2518
2519 bool HTMLMediaElement::mediaPlayerKeyNeeded(MediaPlayer*, Uint8Array* initData)
2520 {
2521     if (!RuntimeEnabledFeatures::sharedFeatures().legacyEncryptedMediaAPIEnabled())
2522         return false;
2523
2524     if (!hasEventListeners("webkitneedkey")) {
2525         m_error = MediaError::create(MediaError::MEDIA_ERR_ENCRYPTED);
2526         scheduleEvent(eventNames().errorEvent);
2527         return false;
2528     }
2529
2530     auto event = WebKitMediaKeyNeededEvent::create(eventNames().webkitneedkeyEvent, initData);
2531     event->setTarget(this);
2532     m_asyncEventQueue.enqueueEvent(WTFMove(event));
2533
2534     return true;
2535 }
2536
2537 String HTMLMediaElement::mediaPlayerMediaKeysStorageDirectory() const
2538 {
2539     auto* page = document().page();
2540     if (!page || page->usesEphemeralSession())
2541         return emptyString();
2542
2543     String storageDirectory = document().settings().mediaKeysStorageDirectory();
2544     if (storageDirectory.isEmpty())
2545         return emptyString();
2546
2547     return pathByAppendingComponent(storageDirectory, SecurityOriginData::fromSecurityOrigin(document().securityOrigin()).databaseIdentifier());
2548 }
2549
2550 void HTMLMediaElement::webkitSetMediaKeys(WebKitMediaKeys* mediaKeys)
2551 {
2552     if (!RuntimeEnabledFeatures::sharedFeatures().legacyEncryptedMediaAPIEnabled())
2553         return;
2554
2555     if (m_webKitMediaKeys == mediaKeys)
2556         return;
2557
2558     if (m_webKitMediaKeys)
2559         m_webKitMediaKeys->setMediaElement(nullptr);
2560     m_webKitMediaKeys = mediaKeys;
2561     if (m_webKitMediaKeys)
2562         m_webKitMediaKeys->setMediaElement(this);
2563 }
2564
2565 void HTMLMediaElement::keyAdded()
2566 {
2567     if (!RuntimeEnabledFeatures::sharedFeatures().legacyEncryptedMediaAPIEnabled())
2568         return;
2569
2570     if (m_player)
2571         m_player->keyAdded();
2572 }
2573
2574 #endif
2575
2576 #if ENABLE(ENCRYPTED_MEDIA)
2577
2578 MediaKeys* HTMLMediaElement::mediaKeys() const
2579 {
2580     return m_mediaKeys.get();
2581 }
2582
2583 void HTMLMediaElement::setMediaKeys(MediaKeys* mediaKeys, Ref<DeferredPromise>&& promise)
2584 {
2585     // https://w3c.github.io/encrypted-media/#dom-htmlmediaelement-setmediakeys
2586     // W3C Editor's Draft 23 June 2017
2587
2588     // 1. If this object's attaching media keys value is true, return a promise rejected with an InvalidStateError.
2589     if (m_attachingMediaKeys) {
2590         promise->reject(InvalidStateError);
2591         return;
2592     }
2593
2594     // 2. If mediaKeys and the mediaKeys attribute are the same object, return a resolved promise.
2595     if (mediaKeys == m_mediaKeys) {
2596         promise->resolve();
2597         return;
2598     }
2599
2600     // 3. Let this object's attaching media keys value be true.
2601     m_attachingMediaKeys = true;
2602
2603     // 4. Let promise be a new promise.
2604     // 5. Run the following steps in parallel:
2605     m_encryptedMediaQueue.enqueueTask([this, mediaKeys = RefPtr<MediaKeys>(mediaKeys), promise = WTFMove(promise)]() mutable {
2606         // 5.1. If all the following conditions hold:
2607         //      - mediaKeys is not null,
2608         //      - the CDM instance represented by mediaKeys is already in use by another media element
2609         //      - the user agent is unable to use it with this element
2610         //      then let this object's attaching media keys value be false and reject promise with a QuotaExceededError.
2611         // FIXME: ^
2612
2613         // 5.2. If the mediaKeys attribute is not null, run the following steps:
2614         if (m_mediaKeys) {
2615             // 5.2.1. If the user agent or CDM do not support removing the association, let this object's attaching media keys value be false and reject promise with a NotSupportedError.
2616             // 5.2.2. If the association cannot currently be removed, let this object's attaching media keys value be false and reject promise with an InvalidStateError.
2617             // 5.2.3. Stop using the CDM instance represented by the mediaKeys attribute to decrypt media data and remove the association with the media element.
2618             // 5.2.4. If the preceding step failed, let this object's attaching media keys value be false and reject promise with the appropriate error name.
2619             // FIXME: ^
2620
2621             m_mediaKeys->detachCDMClient(*this);
2622             if (m_player)
2623                 m_player->cdmInstanceDetached(m_mediaKeys->cdmInstance());
2624         }
2625
2626         // 5.3. If mediaKeys is not null, run the following steps:
2627         if (mediaKeys) {
2628             // 5.3.1. Associate the CDM instance represented by mediaKeys with the media element for decrypting media data.
2629             mediaKeys->attachCDMClient(*this);
2630             if (m_player)
2631                 m_player->cdmInstanceAttached(m_mediaKeys->cdmInstance());
2632
2633             // 5.3.2. If the preceding step failed, run the following steps:
2634             //   5.3.2.1. Set the mediaKeys attribute to null.
2635             //   5.3.2.2. Let this object's attaching media keys value be false.
2636             //   5.3.2.3. Reject promise with a new DOMException whose name is the appropriate error name.
2637             // FIXME: ^
2638
2639             // 5.3.3. Queue a task to run the Attempt to Resume Playback If Necessary algorithm on the media element.
2640             m_encryptedMediaQueue.enqueueTask([this] {
2641                 attemptToResumePlaybackIfNecessary();
2642             });
2643         }
2644
2645         // 5.4. Set the mediaKeys attribute to mediaKeys.
2646         // 5.5. Let this object's attaching media keys value be false.
2647         // 5.6. Resolve promise.
2648         m_mediaKeys = WTFMove(mediaKeys);
2649         m_attachingMediaKeys = false;
2650         promise->resolve();
2651     });
2652
2653     // 6. Return promise.
2654 }
2655
2656 void HTMLMediaElement::mediaPlayerInitializationDataEncountered(const String& initDataType, RefPtr<ArrayBuffer>&& initData)
2657 {
2658     // https://w3c.github.io/encrypted-media/#initdata-encountered
2659     // W3C Editor's Draft 23 June 2017
2660
2661     // 1. Let the media element be the specified HTMLMediaElement object.
2662     // 2. Let initDataType be the empty string.
2663     // 3. Let initData be null.
2664     // 4. If the media data is CORS-same-origin and not mixed content, run the following steps:
2665     //   4.1. Let initDataType be the string representing the Initialization Data Type of the Initialization Data.
2666     //   4.2. Let initData be the Initialization Data.
2667     // FIXME: ^
2668
2669     // 5. Queue a task to create an event named encrypted that does not bubble and is not cancellable using the
2670     //    MediaEncryptedEvent interface with its type attribute set to encrypted and its isTrusted attribute
2671     //    initialized to true, and dispatch it at the media element.
2672     //    The event interface MediaEncryptedEvent has:
2673     //      initDataType = initDataType
2674     //      initData = initData
2675     MediaEncryptedEventInit initializer { initDataType, WTFMove(initData) };
2676     m_asyncEventQueue.enqueueEvent(MediaEncryptedEvent::create(eventNames().encryptedEvent, initializer, Event::IsTrusted::Yes));
2677 }
2678
2679 void HTMLMediaElement::attemptToDecrypt()
2680 {
2681     // https://w3c.github.io/encrypted-media/#attempt-to-decrypt
2682     // W3C Editor's Draft 23 June 2017
2683
2684     // 1. Let the media element be the specified HTMLMediaElement object.
2685     // 2. If the media element's encrypted block queue is empty, abort these steps.
2686     // FIXME: ^
2687
2688     // 3. If the media element's mediaKeys attribute is not null, run the following steps:
2689     if (m_mediaKeys) {
2690         // 3.1. Let media keys be the MediaKeys object referenced by that attribute.
2691         // 3.2. Let cdm be the CDM instance represented by media keys's cdm instance value.
2692         auto& cdmInstance = m_mediaKeys->cdmInstance();
2693
2694         // 3.3. If cdm is no longer usable for any reason, run the following steps:
2695         //   3.3.1. Run the media data is corrupted steps of the resource fetch algorithm.
2696         //   3.3.2. Run the CDM Unavailable algorithm on media keys.
2697         //   3.3.3. Abort these steps.
2698         // FIXME: ^
2699
2700         // 3.4. If there is at least one MediaKeySession created by the media keys that is not closed, run the following steps:
2701         if (m_mediaKeys->hasOpenSessions()) {
2702             // Continued in MediaPlayer::attemptToDecryptWithInstance().
2703             if (m_player)
2704                 m_player->attemptToDecryptWithInstance(cdmInstance);
2705         }
2706     }
2707
2708     // 4. Set the media element's decryption blocked waiting for key value to true.
2709     // FIXME: ^
2710 }
2711
2712 void HTMLMediaElement::attemptToResumePlaybackIfNecessary()
2713 {
2714     // https://w3c.github.io/encrypted-media/#resume-playback
2715     // W3C Editor's Draft 23 June 2017
2716
2717     // 1. Let the media element be the specified HTMLMediaElement object.
2718     // 2. If the media element's playback blocked waiting for key is false, abort these steps.
2719     // FIXME: ^
2720
2721     // 3. Run the Attempt to Decrypt algorithm on the media element.
2722     attemptToDecrypt();
2723
2724     // 4. If the user agent can advance the current playback position in the direction of playback:
2725     //   4.1. Set the media element's decryption blocked waiting for key value to false.
2726     //   4.2. Set the media element's playback blocked waiting for key value to false.
2727     //   4.3. Set the media element's readyState value to HAVE_CURRENT_DATA, HAVE_FUTURE_DATA or HAVE_ENOUGH_DATA as appropriate.
2728     // FIXME: ^
2729 }
2730
2731 void HTMLMediaElement::cdmClientAttemptToResumePlaybackIfNecessary()
2732 {
2733     attemptToResumePlaybackIfNecessary();
2734 }
2735
2736 #endif // ENABLE(ENCRYPTED_MEDIA)
2737
2738 void HTMLMediaElement::progressEventTimerFired()
2739 {
2740     ASSERT(m_player);
2741     if (m_networkState != NETWORK_LOADING)
2742         return;
2743
2744     double time = monotonicallyIncreasingTime();
2745     double timedelta = time - m_previousProgressTime;
2746
2747     if (m_player->didLoadingProgress()) {
2748         scheduleEvent(eventNames().progressEvent);
2749         m_previousProgressTime = time;
2750         m_sentStalledEvent = false;
2751         updateRenderer();
2752         if (hasMediaControls())
2753             mediaControls()->bufferingProgressed();
2754     } else if (timedelta > 3.0 && !m_sentStalledEvent) {
2755         scheduleEvent(eventNames().stalledEvent);
2756         m_sentStalledEvent = true;
2757         setShouldDelayLoadEvent(false);
2758     }
2759 }
2760
2761 void HTMLMediaElement::rewind(double timeDelta)
2762 {
2763     LOG(Media, "HTMLMediaElement::rewind(%p) - %f", this, timeDelta);
2764     setCurrentTime(std::max(currentMediaTime() - MediaTime::createWithDouble(timeDelta), minTimeSeekable()));
2765 }
2766
2767 void HTMLMediaElement::returnToRealtime()
2768 {
2769     LOG(Media, "HTMLMediaElement::returnToRealtime(%p)", this);
2770     setCurrentTime(maxTimeSeekable());
2771 }
2772
2773 void HTMLMediaElement::addPlayedRange(const MediaTime& start, const MediaTime& end)
2774 {
2775     LOG(Media, "HTMLMediaElement::addPlayedRange(%p) - [%s, %s]", this, toString(start).utf8().data(), toString(end).utf8().data());
2776     if (!m_playedTimeRanges)
2777         m_playedTimeRanges = TimeRanges::create();
2778     m_playedTimeRanges->ranges().add(start, end);
2779 }
2780
2781 bool HTMLMediaElement::supportsScanning() const
2782 {
2783     return m_player ? m_player->supportsScanning() : false;
2784 }
2785
2786 void HTMLMediaElement::prepareToPlay()
2787 {
2788     LOG(Media, "HTMLMediaElement::prepareToPlay(%p)", this);
2789     if (m_havePreparedToPlay)
2790         return;
2791     m_havePreparedToPlay = true;
2792     if (m_player)
2793         m_player->prepareToPlay();
2794 }
2795
2796 void HTMLMediaElement::fastSeek(double time)
2797 {
2798     fastSeek(MediaTime::createWithDouble(time));
2799 }
2800
2801 void HTMLMediaElement::fastSeek(const MediaTime& time)
2802 {
2803     LOG(Media, "HTMLMediaElement::fastSeek(%p) - %s", this, toString(time).utf8().data());
2804     // 4.7.10.9 Seeking
2805     // 9. If the approximate-for-speed flag is set, adjust the new playback position to a value that will
2806     // allow for playback to resume promptly. If new playback position before this step is before current
2807     // playback position, then the adjusted new playback position must also be before the current playback
2808     // position. Similarly, if the new playback position before this step is after current playback position,
2809     // then the adjusted new playback position must also be after the current playback position.
2810     refreshCachedTime();
2811
2812     MediaTime delta = time - currentMediaTime();
2813     MediaTime negativeTolerance = delta < MediaTime::zeroTime() ? MediaTime::positiveInfiniteTime() : delta;
2814     seekWithTolerance(time, negativeTolerance, MediaTime::zeroTime(), true);
2815 }
2816
2817 void HTMLMediaElement::seek(const MediaTime& time)
2818 {
2819     LOG(Media, "HTMLMediaElement::seek(%p) - %s", this, toString(time).utf8().data());
2820     seekWithTolerance(time, MediaTime::zeroTime(), MediaTime::zeroTime(), true);
2821 }
2822
2823 void HTMLMediaElement::seekInternal(const MediaTime& time)
2824 {
2825     LOG(Media, "HTMLMediaElement::seekInternal(%p) - %s", this, toString(time).utf8().data());
2826     seekWithTolerance(time, MediaTime::zeroTime(), MediaTime::zeroTime(), false);
2827 }
2828
2829 void HTMLMediaElement::seekWithTolerance(const MediaTime& inTime, const MediaTime& negativeTolerance, const MediaTime& positiveTolerance, bool fromDOM)
2830 {
2831     // 4.8.10.9 Seeking
2832     MediaTime time = inTime;
2833
2834     // 1 - Set the media element's show poster flag to false.
2835     setDisplayMode(Video);
2836
2837     // 2 - If the media element's readyState is HAVE_NOTHING, abort these steps.
2838     if (m_readyState == HAVE_NOTHING || !m_player)
2839         return;
2840
2841     // If the media engine has been told to postpone loading data, let it go ahead now.
2842     if (m_preload < MediaPlayer::Auto && m_readyState < HAVE_FUTURE_DATA)
2843         prepareToPlay();
2844
2845     // Get the current time before setting m_seeking, m_lastSeekTime is returned once it is set.
2846     refreshCachedTime();
2847     MediaTime now = currentMediaTime();
2848
2849     // 3 - If the element's seeking IDL attribute is true, then another instance of this algorithm is
2850     // already running. Abort that other instance of the algorithm without waiting for the step that
2851     // it is running to complete.
2852     if (m_seekTaskQueue.hasPendingTasks()) {
2853         LOG(Media, "HTMLMediaElement::seekWithTolerance(%p) - cancelling pending seeks", this);
2854         m_seekTaskQueue.cancelAllTasks();
2855         if (m_pendingSeek) {
2856             now = m_pendingSeek->now;
2857             m_pendingSeek = nullptr;
2858         }
2859         m_pendingSeekType = NoSeek;
2860     }
2861
2862     // 4 - Set the seeking IDL attribute to true.
2863     // The flag will be cleared when the engine tells us the time has actually changed.
2864     m_seeking = true;
2865     if (m_playing) {
2866         if (m_lastSeekTime < now)
2867             addPlayedRange(m_lastSeekTime, now);
2868     }
2869     m_lastSeekTime = time;
2870
2871     // 5 - If the seek was in response to a DOM method call or setting of an IDL attribute, then continue
2872     // the script. The remainder of these steps must be run asynchronously.
2873     m_pendingSeek = std::make_unique<PendingSeek>(now, time, negativeTolerance, positiveTolerance);
2874     if (fromDOM) {
2875         LOG(Media, "HTMLMediaElement::seekWithTolerance(%p) - enqueuing seek from %s to %s", this, toString(now).utf8().data(), toString(time).utf8().data());
2876         m_seekTaskQueue.enqueueTask(std::bind(&HTMLMediaElement::seekTask, this));
2877     } else
2878         seekTask();
2879
2880     if (processingUserGestureForMedia())
2881         m_mediaSession->removeBehaviorRestriction(MediaElementSession::RequireUserGestureToControlControlsManager);
2882 }
2883
2884 void HTMLMediaElement::seekTask()
2885 {
2886     LOG(Media, "HTMLMediaElement::seekTask(%p)", this);
2887
2888     if (!m_player) {
2889         clearSeeking();
2890         return;
2891     }
2892
2893     ASSERT(m_pendingSeek);
2894     MediaTime now = m_pendingSeek->now;
2895     MediaTime time = m_pendingSeek->targetTime;
2896     MediaTime negativeTolerance = m_pendingSeek->negativeTolerance;
2897     MediaTime positiveTolerance = m_pendingSeek->positiveTolerance;
2898     m_pendingSeek = nullptr;
2899
2900     ASSERT(negativeTolerance >= MediaTime::zeroTime());
2901
2902     // 6 - If the new playback position is later than the end of the media resource, then let it be the end
2903     // of the media resource instead.
2904     time = std::min(time, durationMediaTime());
2905
2906     // 7 - If the new playback position is less than the earliest possible position, let it be that position instead.
2907     MediaTime earliestTime = m_player->startTime();
2908     time = std::max(time, earliestTime);
2909
2910     // Ask the media engine for the time value in the movie's time scale before comparing with current time. This
2911     // is necessary because if the seek time is not equal to currentTime but the delta is less than the movie's
2912     // time scale, we will ask the media engine to "seek" to the current movie time, which may be a noop and
2913     // not generate a timechanged callback. This means m_seeking will never be cleared and we will never
2914     // fire a 'seeked' event.
2915 #if !LOG_DISABLED
2916     MediaTime mediaTime = m_player->mediaTimeForTimeValue(time);
2917     if (time != mediaTime)
2918         LOG(Media, "HTMLMediaElement::seekTask(%p) - %s - media timeline equivalent is %s", this, toString(time).utf8().data(), toString(mediaTime).utf8().data());
2919 #endif
2920     time = m_player->mediaTimeForTimeValue(time);
2921
2922     // 8 - If the (possibly now changed) new playback position is not in one of the ranges given in the
2923     // seekable attribute, then let it be the position in one of the ranges given in the seekable attribute
2924     // that is the nearest to the new playback position. ... If there are no ranges given in the seekable
2925     // attribute then set the seeking IDL attribute to false and abort these steps.
2926     RefPtr<TimeRanges> seekableRanges = seekable();
2927     bool noSeekRequired = !seekableRanges->length();
2928
2929     // Short circuit seeking to the current time by just firing the events if no seek is required.
2930     // Don't skip calling the media engine if 1) we are in poster mode (because a seek should always cancel
2931     // poster display), or 2) if there is a pending fast seek, or 3) if this seek is not an exact seek
2932     SeekType thisSeekType = (negativeTolerance == MediaTime::zeroTime() && positiveTolerance == MediaTime::zeroTime()) ? Precise : Fast;
2933     if (!noSeekRequired && time == now && thisSeekType == Precise && m_pendingSeekType != Fast && displayMode() != Poster)
2934         noSeekRequired = true;
2935
2936 #if ENABLE(MEDIA_SOURCE)
2937     // Always notify the media engine of a seek if the source is not closed. This ensures that the source is
2938     // always in a flushed state when the 'seeking' event fires.
2939     if (m_mediaSource && !m_mediaSource->isClosed())
2940         noSeekRequired = false;
2941 #endif
2942
2943     if (noSeekRequired) {
2944         LOG(Media, "HTMLMediaElement::seekTask(%p) - seek to %s ignored", this, toString(time).utf8().data());
2945         if (time == now) {
2946             scheduleEvent(eventNames().seekingEvent);
2947             scheduleTimeupdateEvent(false);
2948             scheduleEvent(eventNames().seekedEvent);
2949         }
2950         clearSeeking();
2951         return;
2952     }
2953     time = seekableRanges->ranges().nearest(time);
2954
2955     m_sentEndEvent = false;
2956     m_lastSeekTime = time;
2957     m_pendingSeekType = thisSeekType;
2958     m_seeking = true;
2959
2960     // 10 - Queue a task to fire a simple event named seeking at the element.
2961     scheduleEvent(eventNames().seekingEvent);
2962
2963     // 11 - Set the current playback position to the given new playback position
2964     m_seekRequested = true;
2965     m_player->seekWithTolerance(time, negativeTolerance, positiveTolerance);
2966
2967     // 12 - Wait until the user agent has established whether or not the media data for the new playback
2968     // position is available, and, if it is, until it has decoded enough data to play back that position.
2969     // 13 - Await a stable state. The synchronous section consists of all the remaining steps of this algorithm.
2970 }
2971
2972 void HTMLMediaElement::clearSeeking()
2973 {
2974     m_seeking = false;
2975     m_seekRequested = false;
2976     m_pendingSeekType = NoSeek;
2977     invalidateCachedTime();
2978 }
2979
2980 void HTMLMediaElement::finishSeek()
2981 {
2982     // 4.8.10.9 Seeking
2983     // 14 - Set the seeking IDL attribute to false.
2984     clearSeeking();
2985
2986     LOG(Media, "HTMLMediaElement::finishSeek(%p) - current time = %s", this, toString(currentMediaTime()).utf8().data());
2987
2988     // 15 - Run the time maches on steps.
2989     // Handled by mediaPlayerTimeChanged().
2990
2991     // 16 - Queue a task to fire a simple event named timeupdate at the element.
2992     scheduleEvent(eventNames().timeupdateEvent);
2993
2994     // 17 - Queue a task to fire a simple event named seeked at the element.
2995     scheduleEvent(eventNames().seekedEvent);
2996
2997 #if ENABLE(MEDIA_SOURCE)
2998     if (m_mediaSource)
2999         m_mediaSource->monitorSourceBuffers();
3000 #endif
3001 }
3002
3003 HTMLMediaElement::ReadyState HTMLMediaElement::readyState() const
3004 {
3005     return m_readyState;
3006 }
3007
3008 MediaPlayer::MovieLoadType HTMLMediaElement::movieLoadType() const
3009 {
3010     return m_player ? m_player->movieLoadType() : MediaPlayer::Unknown;
3011 }
3012
3013 bool HTMLMediaElement::hasAudio() const
3014 {
3015     return m_player ? m_player->hasAudio() : false;
3016 }
3017
3018 bool HTMLMediaElement::seeking() const
3019 {
3020     return m_seeking;
3021 }
3022
3023 void HTMLMediaElement::refreshCachedTime() const
3024 {
3025     if (!m_player)
3026         return;
3027
3028     m_cachedTime = m_player->currentTime();
3029     if (!m_cachedTime) {
3030         // Do not use m_cachedTime until the media engine returns a non-zero value because we can't
3031         // estimate current time until playback actually begins.
3032         invalidateCachedTime();
3033         return;
3034     }
3035
3036     m_clockTimeAtLastCachedTimeUpdate = monotonicallyIncreasingTime();
3037 }
3038
3039 void HTMLMediaElement::invalidateCachedTime() const
3040 {
3041     m_cachedTime = MediaTime::invalidTime();
3042     if (!m_player || !m_player->maximumDurationToCacheMediaTime())
3043         return;
3044
3045 #if !LOG_DISABLED
3046     if (m_cachedTime.isValid())
3047         LOG(Media, "HTMLMediaElement::invalidateCachedTime(%p)", this);
3048 #endif
3049
3050     // Don't try to cache movie time when playback first starts as the time reported by the engine
3051     // sometimes fluctuates for a short amount of time, so the cached time will be off if we take it
3052     // too early.
3053     static const double minimumTimePlayingBeforeCacheSnapshot = 0.5;
3054
3055     m_minimumClockTimeToUpdateCachedTime = monotonicallyIncreasingTime() + minimumTimePlayingBeforeCacheSnapshot;
3056 }
3057
3058 // playback state
3059 double HTMLMediaElement::currentTime() const
3060 {
3061     return currentMediaTime().toDouble();
3062 }
3063
3064 MediaTime HTMLMediaElement::currentMediaTime() const
3065 {
3066 #if LOG_CACHED_TIME_WARNINGS
3067     static const MediaTime minCachedDeltaForWarning = MediaTime::create(1, 100);
3068 #endif
3069
3070     if (!m_player)
3071         return MediaTime::zeroTime();
3072
3073     if (m_seeking) {
3074         LOG(Media, "HTMLMediaElement::currentTime(%p) - seeking, returning %s", this, toString(m_lastSeekTime).utf8().data());
3075         return m_lastSeekTime;
3076     }
3077
3078     if (m_cachedTime.isValid() && m_paused) {
3079 #if LOG_CACHED_TIME_WARNINGS
3080         MediaTime delta = m_cachedTime - m_player->currentTime();
3081         if (delta > minCachedDeltaForWarning)
3082             LOG(Media, "HTMLMediaElement::currentTime(%p) - WARNING, cached time is %s seconds off of media time when paused", this, toString(delta).utf8().data());
3083 #endif
3084         return m_cachedTime;
3085     }
3086
3087     // Is it too soon use a cached time?
3088     double now = monotonicallyIncreasingTime();
3089     double maximumDurationToCacheMediaTime = m_player->maximumDurationToCacheMediaTime();
3090
3091     if (maximumDurationToCacheMediaTime && m_cachedTime.isValid() && !m_paused && now > m_minimumClockTimeToUpdateCachedTime) {
3092         double clockDelta = now - m_clockTimeAtLastCachedTimeUpdate;
3093
3094         // Not too soon, use the cached time only if it hasn't expired.
3095         if (clockDelta < maximumDurationToCacheMediaTime) {
3096             MediaTime adjustedCacheTime = m_cachedTime + MediaTime::createWithDouble(effectivePlaybackRate() * clockDelta);
3097
3098 #if LOG_CACHED_TIME_WARNINGS
3099             MediaTime delta = adjustedCacheTime - m_player->currentTime();
3100             if (delta > minCachedDeltaForWarning)
3101                 LOG(Media, "HTMLMediaElement::currentTime(%p) - WARNING, cached time is %f seconds off of media time when playing", this, delta);
3102 #endif
3103             return adjustedCacheTime;
3104         }
3105     }
3106
3107 #if LOG_CACHED_TIME_WARNINGS
3108     if (maximumDurationToCacheMediaTime && now > m_minimumClockTimeToUpdateCachedTime && m_cachedTime != MediaPlayer::invalidTime()) {
3109         double clockDelta = now - m_clockTimeAtLastCachedTimeUpdate;
3110         MediaTime delta = m_cachedTime + MediaTime::createWithDouble(effectivePlaybackRate() * clockDelta) - m_player->currentTime();
3111         LOG(Media, "HTMLMediaElement::currentTime(%p) - cached time was %s seconds off of media time when it expired", this, toString(delta).utf8().data());
3112     }
3113 #endif
3114
3115     refreshCachedTime();
3116
3117     if (m_cachedTime.isInvalid())
3118         return MediaTime::zeroTime();
3119
3120     return m_cachedTime;
3121 }
3122
3123 void HTMLMediaElement::setCurrentTime(double time)
3124 {
3125     setCurrentTime(MediaTime::createWithDouble(time));
3126 }
3127
3128 void HTMLMediaElement::setCurrentTime(const MediaTime& time)
3129 {
3130     if (m_mediaController)
3131         return;
3132
3133     seekInternal(time);
3134 }
3135
3136 ExceptionOr<void> HTMLMediaElement::setCurrentTimeForBindings(double time)
3137 {
3138     if (m_mediaController)
3139         return Exception { InvalidStateError };
3140     seek(MediaTime::createWithDouble(time));
3141     return { };
3142 }
3143
3144 double HTMLMediaElement::duration() const
3145 {
3146     return durationMediaTime().toDouble();
3147 }
3148
3149 MediaTime HTMLMediaElement::durationMediaTime() const
3150 {
3151     if (m_player && m_readyState >= HAVE_METADATA)
3152         return m_player->duration();
3153
3154     return MediaTime::invalidTime();
3155 }
3156
3157 bool HTMLMediaElement::paused() const
3158 {
3159     // As of this writing, JavaScript garbage collection calls this function directly. In the past
3160     // we had problems where this was called on an object after a bad cast. The assertion below
3161     // made our regression test detect the problem, so we should keep it because of that. But note
3162     // that the value of the assertion relies on the compiler not being smart enough to know that
3163     // isHTMLUnknownElement is guaranteed to return false for an HTMLMediaElement.
3164     ASSERT(!isHTMLUnknownElement());
3165
3166     return m_paused;
3167 }
3168
3169 double HTMLMediaElement::defaultPlaybackRate() const
3170 {
3171 #if ENABLE(MEDIA_STREAM)
3172     // http://w3c.github.io/mediacapture-main/#mediastreams-in-media-elements
3173     // "defaultPlaybackRate" - On setting: ignored. On getting: return 1.0
3174     // A MediaStream is not seekable. Therefore, this attribute must always have the
3175     // value 1.0 and any attempt to alter it must be ignored. Note that this also means
3176     // that the ratechange event will not fire.
3177     if (m_mediaStreamSrcObject)
3178         return 1;
3179 #endif
3180
3181     return m_defaultPlaybackRate;
3182 }
3183
3184 void HTMLMediaElement::setDefaultPlaybackRate(double rate)
3185 {
3186 #if ENABLE(MEDIA_STREAM)
3187     // http://w3c.github.io/mediacapture-main/#mediastreams-in-media-elements
3188     // "defaultPlaybackRate" - On setting: ignored. On getting: return 1.0
3189     // A MediaStream is not seekable. Therefore, this attribute must always have the
3190     // value 1.0 and any attempt to alter it must be ignored. Note that this also means
3191     // that the ratechange event will not fire.
3192     if (m_mediaStreamSrcObject)
3193         return;
3194 #endif
3195
3196     if (m_defaultPlaybackRate != rate) {
3197         LOG(Media, "HTMLMediaElement::setDefaultPlaybackRate(%p) - %f", this, rate);
3198         m_defaultPlaybackRate = rate;
3199         scheduleEvent(eventNames().ratechangeEvent);
3200     }
3201 }
3202
3203 double HTMLMediaElement::effectivePlaybackRate() const
3204 {
3205     return m_mediaController ? m_mediaController->playbackRate() : m_reportedPlaybackRate;
3206 }
3207
3208 double HTMLMediaElement::requestedPlaybackRate() const
3209 {
3210     return m_mediaController ? m_mediaController->playbackRate() : m_requestedPlaybackRate;
3211 }
3212
3213 double HTMLMediaElement::playbackRate() const
3214 {
3215 #if ENABLE(MEDIA_STREAM)
3216     // http://w3c.github.io/mediacapture-main/#mediastreams-in-media-elements
3217     // "playbackRate" - A MediaStream is not seekable. Therefore, this attribute must always
3218     // have the value 1.0 and any attempt to alter it must be ignored. Note that this also
3219     // means that the ratechange event will not fire.
3220     if (m_mediaStreamSrcObject)
3221         return 1;
3222 #endif
3223
3224     return m_requestedPlaybackRate;
3225 }
3226
3227 void HTMLMediaElement::setPlaybackRate(double rate)
3228 {
3229     LOG(Media, "HTMLMediaElement::setPlaybackRate(%p) - %f", this, rate);
3230
3231 #if ENABLE(MEDIA_STREAM)
3232     // http://w3c.github.io/mediacapture-main/#mediastreams-in-media-elements
3233     // "playbackRate" - A MediaStream is not seekable. Therefore, this attribute must always
3234     // have the value 1.0 and any attempt to alter it must be ignored. Note that this also
3235     // means that the ratechange event will not fire.
3236     if (m_mediaStreamSrcObject)
3237         return;
3238 #endif
3239
3240     if (m_player && potentiallyPlaying() && m_player->rate() != rate && !m_mediaController)
3241         m_player->setRate(rate);
3242
3243     if (m_requestedPlaybackRate != rate) {
3244         m_reportedPlaybackRate = m_requestedPlaybackRate = rate;
3245         invalidateCachedTime();
3246         scheduleEvent(eventNames().ratechangeEvent);
3247     }
3248 }
3249
3250 void HTMLMediaElement::updatePlaybackRate()
3251 {
3252     double requestedRate = requestedPlaybackRate();
3253     if (m_player && potentiallyPlaying() && m_player->rate() != requestedRate)
3254         m_player->setRate(requestedRate);
3255 }
3256
3257 bool HTMLMediaElement::webkitPreservesPitch() const
3258 {
3259     return m_webkitPreservesPitch;
3260 }
3261
3262 void HTMLMediaElement::setWebkitPreservesPitch(bool preservesPitch)
3263 {
3264     LOG(Media, "HTMLMediaElement::setWebkitPreservesPitch(%p) - %s", this, boolString(preservesPitch));
3265
3266     m_webkitPreservesPitch = preservesPitch;
3267
3268     if (!m_player)
3269         return;
3270
3271     m_player->setPreservesPitch(preservesPitch);
3272 }
3273
3274 bool HTMLMediaElement::ended() const
3275 {
3276 #if ENABLE(MEDIA_STREAM)
3277     // http://w3c.github.io/mediacapture-main/#mediastreams-in-media-elements
3278     // When the MediaStream state moves from the active to the inactive state, the User Agent
3279     // must raise an ended event on the HTMLMediaElement and set its ended attribute to true.
3280     if (m_mediaStreamSrcObject && m_player && m_player->ended())
3281         return true;
3282 #endif
3283
3284     // 4.8.10.8 Playing the media resource
3285     // The ended attribute must return true if the media element has ended
3286     // playback and the direction of playback is forwards, and false otherwise.
3287     return endedPlayback() && requestedPlaybackRate() > 0;
3288 }
3289
3290 bool HTMLMediaElement::autoplay() const
3291 {
3292     return hasAttributeWithoutSynchronization(autoplayAttr);
3293 }
3294
3295 String HTMLMediaElement::preload() const
3296 {
3297 #if ENABLE(MEDIA_STREAM)
3298     // http://w3c.github.io/mediacapture-main/#mediastreams-in-media-elements
3299     // "preload" - On getting: none. On setting: ignored.
3300     if (m_mediaStreamSrcObject)
3301         return ASCIILiteral("none");
3302 #endif
3303
3304     switch (m_preload) {
3305     case MediaPlayer::None:
3306         return ASCIILiteral("none");
3307     case MediaPlayer::MetaData:
3308         return ASCIILiteral("metadata");
3309     case MediaPlayer::Auto:
3310         return ASCIILiteral("auto");
3311     }
3312
3313     ASSERT_NOT_REACHED();
3314     return String();
3315 }
3316
3317 void HTMLMediaElement::setPreload(const String& preload)
3318 {
3319     LOG(Media, "HTMLMediaElement::setPreload(%p) - %s", this, preload.utf8().data());
3320 #if ENABLE(MEDIA_STREAM)
3321     // http://w3c.github.io/mediacapture-main/#mediastreams-in-media-elements
3322     // "preload" - On getting: none. On setting: ignored.
3323     if (m_mediaStreamSrcObject)
3324         return;
3325 #endif
3326
3327     setAttributeWithoutSynchronization(preloadAttr, preload);
3328 }
3329
3330 void HTMLMediaElement::play(DOMPromiseDeferred<void>&& promise)
3331 {
3332     LOG(Media, "HTMLMediaElement::play(%p)", this);
3333
3334     auto success = m_mediaSession->playbackPermitted(*this);
3335     if (!success) {
3336         if (success.value() == MediaPlaybackDenialReason::UserGestureRequired)
3337             setPlaybackWithoutUserGesture(PlaybackWithoutUserGesture::Prevented);
3338         promise.reject(NotAllowedError);
3339         return;
3340     }
3341
3342     if (m_error && m_error->code() == MediaError::MEDIA_ERR_SRC_NOT_SUPPORTED) {
3343         promise.reject(NotSupportedError, "The operation is not supported.");
3344         return;
3345     }
3346
3347     if (processingUserGestureForMedia())
3348         removeBehaviorsRestrictionsAfterFirstUserGesture();
3349
3350     if (!playInternal()) {
3351         promise.reject(NotAllowedError);
3352         return;
3353     }
3354
3355     m_pendingPlayPromises.append(WTFMove(promise));
3356 }
3357
3358 void HTMLMediaElement::play()
3359 {
3360     LOG(Media, "HTMLMediaElement::play(%p)", this);
3361
3362     auto success = m_mediaSession->playbackPermitted(*this);
3363     if (!success) {
3364         if (success.value() == MediaPlaybackDenialReason::UserGestureRequired)
3365             setPlaybackWithoutUserGesture(PlaybackWithoutUserGesture::Prevented);
3366         return;
3367     }
3368     if (processingUserGestureForMedia())
3369         removeBehaviorsRestrictionsAfterFirstUserGesture();
3370
3371     playInternal();
3372 }
3373
3374 bool HTMLMediaElement::playInternal()
3375 {
3376     LOG(Media, "HTMLMediaElement::playInternal(%p)", this);
3377
3378     if (!m_mediaSession->clientWillBeginPlayback()) {
3379         LOG(Media, "  returning because of interruption");
3380         return true; // Treat as success because we will begin playback on cessation of the interruption.
3381     }
3382
3383     // 4.8.10.9. Playing the media resource
3384     if (!m_player || m_networkState == NETWORK_EMPTY)
3385         prepareForLoad();
3386
3387     if (endedPlayback())
3388         seekInternal(MediaTime::zeroTime());
3389
3390     if (m_mediaController)
3391         m_mediaController->bringElementUpToSpeed(*this);
3392
3393     if (m_paused) {
3394         m_paused = false;
3395         invalidateCachedTime();
3396         m_playbackStartedTime = currentMediaTime().toDouble();
3397         scheduleEvent(eventNames().playEvent);
3398
3399         if (m_readyState <= HAVE_CURRENT_DATA)
3400             scheduleEvent(eventNames().waitingEvent);
3401         else if (m_readyState >= HAVE_FUTURE_DATA)
3402             scheduleNotifyAboutPlaying();
3403
3404 #if ENABLE(MEDIA_SESSION)
3405         // 6.3 Activating a media session from a media element
3406         // When the play() method is invoked, the paused attribute is true, and the readyState attribute has the value
3407         // HAVE_FUTURE_DATA or HAVE_ENOUGH_DATA, then
3408         // 1. Let media session be the value of the current media session.
3409         // 2. If we are not currently in media session's list of active participating media elements then append
3410         //    ourselves to this list.
3411         // 3. Let activated be the result of running the media session invocation algorithm for media session.
3412         // 4. If activated is failure, pause ourselves.
3413         if (m_readyState == HAVE_ENOUGH_DATA || m_readyState == HAVE_FUTURE_DATA) {
3414             if (m_session) {
3415                 m_session->addActiveMediaElement(*this);
3416
3417                 if (m_session->kind() == MediaSessionKind::Content) {
3418                     if (Page* page = document().page())
3419                         page->chrome().client().focusedContentMediaElementDidChange(m_elementID);
3420                 }
3421
3422                 if (!m_session->invoke()) {
3423                     pause();
3424                     return false;
3425                 }
3426             }
3427         }
3428 #endif
3429     } else if (m_readyState >= HAVE_FUTURE_DATA)
3430         scheduleResolvePendingPlayPromises();
3431
3432     if (processingUserGestureForMedia()) {
3433         if (m_playbackWithoutUserGesture == PlaybackWithoutUserGesture::Prevented) {
3434             handleAutoplayEvent(AutoplayEvent::DidPlayMediaPreventedFromPlaying);
3435             setPlaybackWithoutUserGesture(PlaybackWithoutUserGesture::None);
3436         }
3437     } else
3438         setPlaybackWithoutUserGesture(PlaybackWithoutUserGesture::Started);
3439
3440     m_autoplaying = false;
3441     updatePlayState();
3442
3443     return true;
3444 }
3445
3446 void HTMLMediaElement::pause()
3447 {
3448     LOG(Media, "HTMLMediaElement::pause(%p)", this);
3449
3450     m_temporarilyAllowingInlinePlaybackAfterFullscreen = false;
3451
3452     if (!m_mediaSession->playbackPermitted(*this))
3453         return;
3454
3455     if (processingUserGestureForMedia())
3456         removeBehaviorsRestrictionsAfterFirstUserGesture(MediaElementSession::RequireUserGestureToControlControlsManager);
3457
3458     pauseInternal();
3459 }
3460
3461
3462 void HTMLMediaElement::pauseInternal()
3463 {
3464     LOG(Media, "HTMLMediaElement::pauseInternal(%p)", this);
3465
3466     if (!m_mediaSession->clientWillPausePlayback()) {
3467         LOG(Media, "  returning because of interruption");
3468         return;
3469     }
3470
3471     // 4.8.10.9. Playing the media resource
3472     if (!m_player || m_networkState == NETWORK_EMPTY) {
3473         // Unless the restriction on media requiring user action has been lifted
3474         // don't trigger loading if a script calls pause().
3475         if (!m_mediaSession->playbackPermitted(*this))
3476             return;
3477         prepareForLoad();
3478     }
3479
3480     m_autoplaying = false;
3481
3482     if (processingUserGestureForMedia())
3483         userDidInterfereWithAutoplay();
3484
3485     setPlaybackWithoutUserGesture(PlaybackWithoutUserGesture::None);
3486
3487     if (!m_paused) {
3488         m_paused = true;
3489         scheduleTimeupdateEvent(false);
3490         scheduleEvent(eventNames().pauseEvent);
3491         m_promiseTaskQueue.enqueueTask([this]() {
3492             rejectPendingPlayPromises(DOMError::create("AbortError", "The operation was aborted."));
3493         });
3494         if (MemoryPressureHandler::singleton().isUnderMemoryPressure())
3495             purgeBufferedDataIfPossible();
3496     }
3497
3498     updatePlayState();
3499 }
3500
3501 #if ENABLE(MEDIA_SOURCE)
3502
3503 void HTMLMediaElement::detachMediaSource()
3504 {
3505     if (!m_mediaSource)
3506         return;
3507
3508     m_mediaSource->detachFromElement(*this);
3509     m_mediaSource = nullptr;
3510 }
3511
3512 #endif
3513
3514 bool HTMLMediaElement::loop() const
3515 {
3516     return hasAttributeWithoutSynchronization(loopAttr);
3517 }
3518
3519 void HTMLMediaElement::setLoop(bool b)
3520 {
3521     LOG(Media, "HTMLMediaElement::setLoop(%p) - %s", this, boolString(b));
3522     setBooleanAttribute(loopAttr, b);
3523 }
3524
3525 bool HTMLMediaElement::controls() const
3526 {
3527     Frame* frame = document().frame();
3528
3529     // always show controls when scripting is disabled
3530     if (frame && !frame->script().canExecuteScripts(NotAboutToExecuteScript))
3531         return true;
3532
3533     return hasAttributeWithoutSynchronization(controlsAttr);
3534 }
3535
3536 void HTMLMediaElement::setControls(bool b)
3537 {
3538     LOG(Media, "HTMLMediaElement::setControls(%p) - %s", this, boolString(b));
3539     setBooleanAttribute(controlsAttr, b);
3540 }
3541
3542 double HTMLMediaElement::volume() const
3543 {
3544     return m_volume;
3545 }
3546
3547 ExceptionOr<void> HTMLMediaElement::setVolume(double volume)
3548 {
3549     LOG(Media, "HTMLMediaElement::setVolume(%p) - %f", this, volume);
3550
3551     if (!(volume >= 0 && volume <= 1))
3552         return Exception { IndexSizeError };
3553
3554 #if !PLATFORM(IOS)
3555     if (m_volume == volume)
3556         return { };
3557
3558     m_volume = volume;
3559     m_volumeInitialized = true;
3560     updateVolume();
3561     scheduleEvent(eventNames().volumechangeEvent);
3562 #endif
3563     return { };
3564 }
3565
3566 bool HTMLMediaElement::muted() const
3567 {
3568     return m_explicitlyMuted ? m_muted : hasAttributeWithoutSynchronization(mutedAttr);
3569 }
3570
3571 void HTMLMediaElement::setMuted(bool muted)
3572 {
3573     LOG(Media, "HTMLMediaElement::setMuted(%p) - %s", this, boolString(muted));
3574
3575     bool mutedStateChanged = m_muted != muted;
3576     if (mutedStateChanged || !m_explicitlyMuted) {
3577         if (processingUserGestureForMedia()) {
3578             removeBehaviorsRestrictionsAfterFirstUserGesture(MediaElementSession::AllRestrictions & ~MediaElementSession::RequireUserGestureToControlControlsManager);
3579
3580             if (hasAudio() && muted)
3581                 userDidInterfereWithAutoplay();
3582         }
3583
3584         m_muted = muted;
3585         m_explicitlyMuted = true;
3586
3587         // Avoid recursion when the player reports volume changes.
3588         if (!processingMediaPlayerCallback()) {
3589             if (m_player) {
3590                 m_player->setMuted(effectiveMuted());
3591                 if (hasMediaControls())
3592                     mediaControls()->changedMute();
3593             }
3594         }
3595
3596         if (mutedStateChanged)
3597             scheduleEvent(eventNames().volumechangeEvent);
3598
3599         updateShouldPlay();
3600
3601 #if ENABLE(MEDIA_SESSION)
3602         document().updateIsPlayingMedia(m_elementID);
3603 #else
3604         document().updateIsPlayingMedia();
3605 #endif
3606
3607 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
3608         updateMediaState(UpdateState::Asynchronously);
3609 #endif
3610         m_mediaSession->canProduceAudioChanged();
3611     }
3612
3613     scheduleUpdatePlaybackControlsManager();
3614 }
3615
3616 #if USE(AUDIO_SESSION) && PLATFORM(MAC)
3617 void HTMLMediaElement::hardwareMutedStateDidChange(AudioSession* session)
3618 {
3619     if (!session->isMuted())
3620         return;
3621
3622     if (!hasAudio())
3623         return;
3624
3625     if (effectiveMuted() || !volume())
3626         return;
3627
3628     userDidInterfereWithAutoplay();
3629 }
3630 #endif
3631
3632 void HTMLMediaElement::togglePlayState()
3633 {
3634     LOG(Media, "HTMLMediaElement::togglePlayState(%p) - canPlay() is %s", this, boolString(canPlay()));
3635
3636     // We can safely call the internal play/pause methods, which don't check restrictions, because
3637     // this method is only called from the built-in media controller
3638     if (canPlay()) {
3639         updatePlaybackRate();
3640         playInternal();
3641     } else
3642         pauseInternal();
3643 }
3644
3645 void HTMLMediaElement::beginScrubbing()
3646 {
3647     LOG(Media, "HTMLMediaElement::beginScrubbing(%p) - paused() is %s", this, boolString(paused()));
3648
3649     if (!paused()) {
3650         if (ended()) {
3651             // Because a media element stays in non-paused state when it reaches end, playback resumes
3652             // when the slider is dragged from the end to another position unless we pause first. Do
3653             // a "hard pause" so an event is generated, since we want to stay paused after scrubbing finishes.
3654             pause();
3655         } else {
3656             // Not at the end but we still want to pause playback so the media engine doesn't try to
3657             // continue playing during scrubbing. Pause without generating an event as we will
3658             // unpause after scrubbing finishes.
3659             setPausedInternal(true);
3660         }
3661     }
3662
3663     m_mediaSession->removeBehaviorRestriction(MediaElementSession::RequireUserGestureToControlControlsManager);
3664 }
3665
3666 void HTMLMediaElement::endScrubbing()
3667 {
3668     LOG(Media, "HTMLMediaElement::endScrubbing(%p) - m_pausedInternal is %s", this, boolString(m_pausedInternal));
3669
3670     if (m_pausedInternal)
3671         setPausedInternal(false);
3672 }
3673
3674 void HTMLMediaElement::beginScanning(ScanDirection direction)
3675 {
3676     m_scanType = supportsScanning() ? Scan : Seek;
3677     m_scanDirection = direction;
3678
3679     if (m_scanType == Seek) {
3680         // Scanning by seeking requires the video to be paused during scanning.
3681         m_actionAfterScan = paused() ? Nothing : Play;
3682         pause();
3683     } else {
3684         // Scanning by scanning requires the video to be playing during scanninging.
3685         m_actionAfterScan = paused() ? Pause : Nothing;
3686         play();
3687         setPlaybackRate(nextScanRate());
3688     }
3689
3690     m_scanTimer.start(0_s, m_scanType == Seek ? SeekRepeatDelay : ScanRepeatDelay);
3691 }
3692
3693 void HTMLMediaElement::endScanning()
3694 {
3695     if (m_scanType == Scan)
3696         setPlaybackRate(defaultPlaybackRate());
3697
3698     if (m_actionAfterScan == Play)
3699         play();
3700     else if (m_actionAfterScan == Pause)
3701         pause();
3702
3703     if (m_scanTimer.isActive())
3704         m_scanTimer.stop();
3705 }
3706
3707 double HTMLMediaElement::nextScanRate()
3708 {
3709     double rate = std::min(ScanMaximumRate, fabs(playbackRate() * 2));
3710     if (m_scanDirection == Backward)
3711         rate *= -1;
3712 #if PLATFORM(IOS)
3713     rate = std::min(std::max(rate, minFastReverseRate()), maxFastForwardRate());
3714 #endif
3715     return rate;
3716 }
3717
3718 void HTMLMediaElement::scanTimerFired()
3719 {
3720     if (m_scanType == Seek) {
3721         double seekTime = m_scanDirection == Forward ? SeekTime : -SeekTime;
3722         setCurrentTime(currentTime() + seekTime);
3723     } else
3724         setPlaybackRate(nextScanRate());
3725 }
3726
3727 // The spec says to fire periodic timeupdate events (those sent while playing) every
3728 // "15 to 250ms", we choose the slowest frequency
3729 static const Seconds maxTimeupdateEventFrequency { 250_ms };
3730
3731 void HTMLMediaElement::startPlaybackProgressTimer()
3732 {
3733     if (m_playbackProgressTimer.isActive())
3734         return;
3735
3736     m_previousProgressTime = monotonicallyIncreasingTime();
3737     m_playbackProgressTimer.startRepeating(maxTimeupdateEventFrequency);
3738 }
3739
3740 void HTMLMediaElement::playbackProgressTimerFired()
3741 {
3742     ASSERT(m_player);
3743
3744     if (m_fragmentEndTime.isValid() && currentMediaTime() >= m_fragmentEndTime && requestedPlaybackRate() > 0) {
3745         m_fragmentEndTime = MediaTime::invalidTime();
3746         if (!m_mediaController && !m_paused) {
3747             // changes paused to true and fires a simple event named pause at the media element.
3748             pauseInternal();
3749         }
3750     }
3751
3752     scheduleTimeupdateEvent(true);
3753
3754     if (!requestedPlaybackRate())
3755         return;
3756
3757     if (!m_paused && hasMediaControls())
3758         mediaControls()->playbackProgressed();
3759
3760 #if ENABLE(VIDEO_TRACK)
3761     updateActiveTextTrackCues(currentMediaTime());
3762 #endif
3763
3764 #if ENABLE(MEDIA_SOURCE)
3765     if (m_mediaSource)
3766         m_mediaSource->monitorSourceBuffers();
3767 #endif
3768
3769     if (!seeking() && m_playbackWithoutUserGesture == PlaybackWithoutUserGesture::Started && currentTime() - m_playbackWithoutUserGestureStartedTime->toDouble() > AutoplayInterferenceTimeThreshold) {
3770         handleAutoplayEvent(AutoplayEvent::DidAutoplayMediaPastThresholdWithoutUserInterference);
3771         setPlaybackWithoutUserGesture(PlaybackWithoutUserGesture::None);
3772     }
3773 }
3774
3775 void HTMLMediaElement::scheduleTimeupdateEvent(bool periodicEvent)
3776 {
3777     MonotonicTime now = MonotonicTime::now();
3778     Seconds timedelta = now - m_clockTimeAtLastUpdateEvent;
3779
3780     // throttle the periodic events
3781     if (periodicEvent && timedelta < maxTimeupdateEventFrequency)
3782         return;
3783
3784     // Some media engines make multiple "time changed" callbacks at the same time, but we only want one
3785     // event at a given time so filter here
3786     MediaTime movieTime = currentMediaTime();
3787     if (movieTime != m_lastTimeUpdateEventMovieTime) {
3788         scheduleEvent(eventNames().timeupdateEvent);
3789         m_clockTimeAtLastUpdateEvent = now;
3790         m_lastTimeUpdateEventMovieTime = movieTime;
3791     }
3792 }
3793
3794 bool HTMLMediaElement::canPlay() const
3795 {
3796     return paused() || ended() || m_readyState < HAVE_METADATA;
3797 }
3798
3799 double HTMLMediaElement::percentLoaded() const
3800 {
3801     if (!m_player)
3802         return 0;
3803     MediaTime duration = m_player->duration();
3804
3805     if (!duration || duration.isPositiveInfinite() || duration.isNegativeInfinite())
3806         return 0;
3807
3808     MediaTime buffered = MediaTime::zeroTime();
3809     bool ignored;
3810     std::unique_ptr<PlatformTimeRanges> timeRanges = m_player->buffered();
3811     for (unsigned i = 0; i < timeRanges->length(); ++i) {
3812         MediaTime start = timeRanges->start(i, ignored);
3813         MediaTime end = timeRanges->end(i, ignored);
3814         buffered += end - start;
3815     }
3816     return buffered.toDouble() / duration.toDouble();
3817 }
3818
3819 #if ENABLE(VIDEO_TRACK)
3820
3821 void HTMLMediaElement::mediaPlayerDidAddAudioTrack(AudioTrackPrivate& track)
3822 {
3823     if (isPlaying() && !m_mediaSession->playbackPermitted(*this)) {
3824         pauseInternal();
3825         setPlaybackWithoutUserGesture(PlaybackWithoutUserGesture::Prevented);
3826     }
3827
3828     addAudioTrack(AudioTrack::create(*this, track));
3829 }
3830
3831 void HTMLMediaElement::mediaPlayerDidAddTextTrack(InbandTextTrackPrivate& track)
3832 {
3833     // 4.8.10.12.2 Sourcing in-band text tracks
3834     // 1. Associate the relevant data with a new text track and its corresponding new TextTrack object.
3835     auto textTrack = InbandTextTrack::create(*ActiveDOMObject::scriptExecutionContext(), *this, track);
3836     textTrack->setMediaElement(this);
3837
3838     // 2. Set the new text track's kind, label, and language based on the semantics of the relevant data,
3839     // as defined by the relevant specification. If there is no label in that data, then the label must
3840     // be set to the empty string.
3841     // 3. Associate the text track list of cues with the rules for updating the text track rendering appropriate
3842     // for the format in question.
3843     // 4. If the new text track's kind is metadata, then set the text track in-band metadata track dispatch type
3844     // as follows, based on the type of the media resource:
3845     // 5. Populate the new text track's list of cues with the cues parsed so far, folllowing the guidelines for exposing
3846     // cues, and begin updating it dynamically as necessary.
3847     //   - Thess are all done by the media engine.
3848
3849     // 6. Set the new text track's readiness state to loaded.
3850     textTrack->setReadinessState(TextTrack::Loaded);
3851
3852     // 7. Set the new text track's mode to the mode consistent with the user's preferences and the requirements of
3853     // the relevant specification for the data.
3854     //  - This will happen in configureTextTracks()
3855     scheduleDelayedAction(ConfigureTextTracks);
3856
3857     // 8. Add the new text track to the media element's list of text tracks.
3858     // 9. Fire an event with the name addtrack, that does not bubble and is not cancelable, and that uses the TrackEvent
3859     // interface, with the track attribute initialized to the text track's TextTrack object, at the media element's
3860     // textTracks attribute's TextTrackList object.
3861     addTextTrack(WTFMove(textTrack));
3862 }
3863
3864 void HTMLMediaElement::mediaPlayerDidAddVideoTrack(VideoTrackPrivate& track)
3865 {
3866     addVideoTrack(VideoTrack::create(*this, track));
3867 }
3868
3869 void HTMLMediaElement::mediaPlayerDidRemoveAudioTrack(AudioTrackPrivate& track)
3870 {
3871     track.willBeRemoved();
3872 }
3873
3874 void HTMLMediaElement::mediaPlayerDidRemoveTextTrack(InbandTextTrackPrivate& track)
3875 {
3876     track.willBeRemoved();
3877 }
3878
3879 void HTMLMediaElement::mediaPlayerDidRemoveVideoTrack(VideoTrackPrivate& track)
3880 {
3881     track.willBeRemoved();
3882 }
3883
3884 void HTMLMediaElement::closeCaptionTracksChanged()
3885 {
3886     if (hasMediaControls())
3887         mediaControls()->closedCaptionTracksChanged();
3888 }
3889
3890 void HTMLMediaElement::addAudioTrack(Ref<AudioTrack>&& track)
3891 {
3892     audioTracks().append(WTFMove(track));
3893 }
3894
3895 void HTMLMediaElement::addTextTrack(Ref<TextTrack>&& track)
3896 {
3897     if (!m_requireCaptionPreferencesChangedCallbacks) {
3898         m_requireCaptionPreferencesChangedCallbacks = true;
3899         Document& document = this->document();
3900         document.registerForCaptionPreferencesChangedCallbacks(this);
3901         if (Page* page = document.page())
3902             m_captionDisplayMode = page->group().captionPreferences().captionDisplayMode();
3903     }
3904
3905     textTracks().append(WTFMove(track));
3906
3907     closeCaptionTracksChanged();
3908 }
3909
3910 void HTMLMediaElement::addVideoTrack(Ref<VideoTrack>&& track)
3911 {
3912     videoTracks().append(WTFMove(track));
3913 }
3914
3915 void HTMLMediaElement::removeAudioTrack(AudioTrack& track)
3916 {
3917     m_audioTracks->remove(track);
3918     track.clearClient();
3919 }
3920
3921 void HTMLMediaElement::removeTextTrack(TextTrack& track, bool scheduleEvent)
3922 {
3923     TrackDisplayUpdateScope scope { *this };
3924     if (auto* cues = track.cues())
3925         textTrackRemoveCues(track, *cues);
3926     track.clearClient();
3927     if (m_textTracks)
3928         m_textTracks->remove(track, scheduleEvent);
3929
3930     closeCaptionTracksChanged();
3931 }
3932
3933 void HTMLMediaElement::removeVideoTrack(VideoTrack& track)
3934 {
3935     m_videoTracks->remove(track);
3936     track.clearClient();
3937 }
3938
3939 void HTMLMediaElement::forgetResourceSpecificTracks()
3940 {
3941     while (m_audioTracks &&  m_audioTracks->length())
3942         removeAudioTrack(*m_audioTracks->lastItem());
3943
3944     if (m_textTracks) {
3945         TrackDisplayUpdateScope scope { *this };
3946         for (int i = m_textTracks->length() - 1; i >= 0; --i) {
3947             auto& track = *m_textTracks->item(i);
3948             if (track.trackType() == TextTrack::InBand)
3949                 removeTextTrack(track, false);
3950         }
3951     }
3952
3953     while (m_videoTracks &&  m_videoTracks->length())
3954         removeVideoTrack(*m_videoTracks->lastItem());
3955 }
3956
3957 ExceptionOr<TextTrack&> HTMLMediaElement::addTextTrack(const String& kind, const String& label, const String& language)
3958 {
3959     // 4.8.10.12.4 Text track API
3960     // The addTextTrack(kind, label, language) method of media elements, when invoked, must run the following steps:
3961
3962     // 1. If kind is not one of the following strings, then throw a SyntaxError exception and abort these steps
3963     if (!TextTrack::isValidKindKeyword(kind))
3964         return Exception { TypeError };
3965
3966     // 2. If the label argument was omitted, let label be the empty string.
3967     // 3. If the language argument was omitted, let language be the empty string.
3968     // 4. Create a new TextTrack object.
3969
3970     // 5. Create a new text track corresponding to the new object, and set its text track kind to kind, its text
3971     // track label to label, its text track language to language...
3972     auto track = TextTrack::create(ActiveDOMObject::scriptExecutionContext(), this, kind, emptyString(), label, language);
3973     auto& trackReference = track.get();
3974
3975     // Note, due to side effects when changing track parameters, we have to
3976     // first append the track to the text track list.
3977
3978     // 6. Add the new text track to the media element's list of text tracks.
3979     addTextTrack(WTFMove(track));
3980
3981     // ... its text track readiness state to the text track loaded state ...
3982     trackReference.setReadinessState(TextTrack::Loaded);
3983
3984     // ... its text track mode to the text track hidden mode, and its text track list of cues to an empty list ...
3985     trackReference.setMode(TextTrack::Mode::Hidden);
3986
3987     return trackReference;
3988 }
3989
3990 AudioTrackList& HTMLMediaElement::audioTracks()
3991 {
3992     if (!m_audioTracks)
3993         m_audioTracks = AudioTrackList::create(this, ActiveDOMObject::scriptExecutionContext());
3994
3995     return *m_audioTracks;
3996 }
3997
3998 TextTrackList& HTMLMediaElement::textTracks()
3999 {
4000     if (!m_textTracks)
4001         m_textTracks = TextTrackList::create(this, ActiveDOMObject::scriptExecutionContext());
4002
4003     return *m_textTracks;
4004 }
4005
4006 VideoTrackList& HTMLMediaElement::videoTracks()
4007 {
4008     if (!m_videoTracks)
4009         m_videoTracks = VideoTrackList::create(this, ActiveDOMObject::scriptExecutionContext());
4010
4011     return *m_videoTracks;
4012 }
4013
4014 void HTMLMediaElement::didAddTextTrack(HTMLTrackElement& trackElement)
4015 {
4016     ASSERT(trackElement.hasTagName(trackTag));
4017
4018     // 4.8.10.12.3 Sourcing out-of-band text tracks
4019     // When a track element's parent element changes and the new parent is a media element,
4020     // then the user agent must add the track element's corresponding text track to the
4021     // media element's list of text tracks ... [continues in TextTrackList::append]
4022     addTextTrack(trackElement.track());
4023
4024     // Do not schedule the track loading until parsing finishes so we don't start before all tracks
4025     // in the markup have been added.
4026     if (!m_parsingInProgress)
4027         scheduleDelayedAction(ConfigureTextTracks);
4028
4029     if (hasMediaControls())
4030         mediaControls()->closedCaptionTracksChanged();
4031 }
4032
4033 void HTMLMediaElement::didRemoveTextTrack(HTMLTrackElement& trackElement)
4034 {
4035     ASSERT(trackElement.hasTagName(trackTag));
4036
4037 #if !LOG_DISABLED
4038     if (trackElement.hasTagName(trackTag)) {
4039         URL url = trackElement.getNonEmptyURLAttribute(srcAttr);
4040         LOG(Media, "HTMLMediaElement::didRemoveTrack(%p) - 'src' is %s", this, urlForLoggingMedia(url).utf8().data());
4041     }
4042 #endif
4043
4044     auto& textTrack = trackElement.track();
4045
4046     textTrack.setHasBeenConfigured(false);
4047
4048     if (!m_textTracks)
4049         return;
4050
4051     // 4.8.10.12.3 Sourcing out-of-band text tracks
4052     // When a track element's parent element changes and the old parent was a media element,
4053     // then the user agent must remove the track element's corresponding text track from the
4054     // media element's list of text tracks.
4055     removeTextTrack(textTrack);
4056
4057     m_textTracksWhenResourceSelectionBegan.removeFirst(&textTrack);
4058 }
4059
4060 void HTMLMediaElement::configureTextTrackGroup(const TrackGroup& group)
4061 {
4062     ASSERT(group.tracks.size());
4063
4064     LOG(Media, "HTMLMediaElement::configureTextTrackGroup(%p)", this);
4065
4066     Page* page = document().page();
4067     CaptionUserPreferences* captionPreferences = page ? &page->group().captionPreferences() : 0;
4068     CaptionUserPreferences::CaptionDisplayMode displayMode = captionPreferences ? captionPreferences->captionDisplayMode() : CaptionUserPreferences::Automatic;
4069
4070     // First, find the track in the group that should be enabled (if any).
4071     Vector<RefPtr<TextTrack>> currentlyEnabledTracks;
4072     RefPtr<TextTrack> trackToEnable;
4073     RefPtr<TextTrack> defaultTrack;
4074     RefPtr<TextTrack> fallbackTrack;
4075     RefPtr<TextTrack> forcedSubitleTrack;
4076     int highestTrackScore = 0;
4077     int highestForcedScore = 0;
4078
4079     // 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
4080     // track if it is less suitable, and we do want to disable it if another track is more suitable.
4081     int alreadyVisibleTrackScore = 0;