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