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