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