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