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