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