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