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