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