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