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