08f1e00c47c3838b9b6f8ba491e87d63c5e32531
[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     if (!needsAutoplayPlayPauseEventsQuirk(document().topDocument()))
2324         return;
2325
2326     scheduleEvent(eventNames().playingEvent);
2327     scheduleEvent(eventNames().pauseEvent);
2328 }
2329
2330 void HTMLMediaElement::setReadyState(MediaPlayer::ReadyState state)
2331 {
2332     LOG(Media, "HTMLMediaElement::setReadyState(%p) - new state = %d, current state = %d,", this, static_cast<int>(state), static_cast<int>(m_readyState));
2333
2334     // Set "wasPotentiallyPlaying" BEFORE updating m_readyState, potentiallyPlaying() uses it
2335     bool wasPotentiallyPlaying = potentiallyPlaying();
2336
2337     ReadyState oldState = m_readyState;
2338     ReadyState newState = static_cast<ReadyState>(state);
2339
2340 #if ENABLE(VIDEO_TRACK)
2341     bool tracksAreReady = textTracksAreReady();
2342
2343     if (newState == oldState && m_tracksAreReady == tracksAreReady)
2344         return;
2345
2346     m_tracksAreReady = tracksAreReady;
2347 #else
2348     if (newState == oldState)
2349         return;
2350     bool tracksAreReady = true;
2351 #endif
2352     
2353     if (tracksAreReady)
2354         m_readyState = newState;
2355     else {
2356         // If a media file has text tracks the readyState may not progress beyond HAVE_FUTURE_DATA until
2357         // the text tracks are ready, regardless of the state of the media file.
2358         if (newState <= HAVE_METADATA)
2359             m_readyState = newState;
2360         else
2361             m_readyState = HAVE_CURRENT_DATA;
2362     }
2363     
2364     if (oldState > m_readyStateMaximum)
2365         m_readyStateMaximum = oldState;
2366
2367     if (m_networkState == NETWORK_EMPTY)
2368         return;
2369
2370     if (m_seeking) {
2371         // 4.8.10.9, step 11
2372         if (wasPotentiallyPlaying && m_readyState < HAVE_FUTURE_DATA)
2373             scheduleEvent(eventNames().waitingEvent);
2374
2375         // 4.8.10.10 step 14 & 15.
2376         if (!m_player->seeking() && m_readyState >= HAVE_CURRENT_DATA)
2377             finishSeek();
2378     } else {
2379         if (wasPotentiallyPlaying && m_readyState < HAVE_FUTURE_DATA) {
2380             // 4.8.10.8
2381             invalidateCachedTime();
2382             scheduleTimeupdateEvent(false);
2383             scheduleEvent(eventNames().waitingEvent);
2384         }
2385     }
2386
2387     if (m_readyState >= HAVE_METADATA && oldState < HAVE_METADATA) {
2388         prepareMediaFragmentURI();
2389         scheduleEvent(eventNames().durationchangeEvent);
2390         scheduleResizeEvent();
2391         scheduleEvent(eventNames().loadedmetadataEvent);
2392 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
2393         if (hasEventListeners(eventNames().webkitplaybacktargetavailabilitychangedEvent))
2394             enqueuePlaybackTargetAvailabilityChangedEvent();
2395 #endif
2396         m_initiallyMuted = m_volume < 0.05 || muted();
2397
2398         if (hasMediaControls())
2399             mediaControls()->loadedMetadata();
2400         updateRenderer();
2401
2402         if (is<MediaDocument>(document()))
2403             downcast<MediaDocument>(document()).mediaElementNaturalSizeChanged(expandedIntSize(m_player->naturalSize()));
2404
2405         logMediaLoadRequest(document().page(), m_player->engineDescription(), String(), true);
2406
2407 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
2408         updateMediaState(UpdateState::Asynchronously);
2409 #endif
2410
2411         m_mediaSession->clientCharacteristicsChanged();
2412     }
2413
2414     bool shouldUpdateDisplayState = false;
2415
2416     if (m_readyState >= HAVE_CURRENT_DATA && oldState < HAVE_CURRENT_DATA && !m_haveFiredLoadedData) {
2417         m_haveFiredLoadedData = true;
2418         shouldUpdateDisplayState = true;
2419         scheduleEvent(eventNames().loadeddataEvent);
2420         setShouldDelayLoadEvent(false);
2421         applyMediaFragmentURI();
2422     }
2423
2424     bool isPotentiallyPlaying = potentiallyPlaying();
2425     if (m_readyState == HAVE_FUTURE_DATA && oldState <= HAVE_CURRENT_DATA && tracksAreReady) {
2426         scheduleEvent(eventNames().canplayEvent);
2427         if (isPotentiallyPlaying)
2428             scheduleNotifyAboutPlaying();
2429         shouldUpdateDisplayState = true;
2430     }
2431
2432     if (m_readyState == HAVE_ENOUGH_DATA && oldState < HAVE_ENOUGH_DATA && tracksAreReady) {
2433         if (oldState <= HAVE_CURRENT_DATA)
2434             scheduleEvent(eventNames().canplayEvent);
2435
2436         scheduleEvent(eventNames().canplaythroughEvent);
2437
2438         if (isPotentiallyPlaying && oldState <= HAVE_CURRENT_DATA)
2439             scheduleNotifyAboutPlaying();
2440
2441         auto success = canTransitionFromAutoplayToPlay();
2442         if (success) {
2443             m_paused = false;
2444             invalidateCachedTime();
2445             setPlaybackWithoutUserGesture(PlaybackWithoutUserGesture::Started);
2446             m_playbackStartedTime = currentMediaTime().toDouble();
2447             scheduleEvent(eventNames().playEvent);
2448             scheduleNotifyAboutPlaying();
2449         } else if (success.value() == MediaPlaybackDenialReason::UserGestureRequired)
2450             setPlaybackWithoutUserGesture(PlaybackWithoutUserGesture::Prevented);
2451
2452         shouldUpdateDisplayState = true;
2453     }
2454
2455     // If we transition to the Future Data state and we're about to begin playing, ensure playback is actually permitted first,
2456     // honoring any playback denial reasons such as the requirement of a user gesture.
2457     if (m_readyState == HAVE_FUTURE_DATA && oldState < HAVE_FUTURE_DATA && potentiallyPlaying() && !m_mediaSession->playbackPermitted(*this)) {
2458         pauseInternal();
2459         setPlaybackWithoutUserGesture(PlaybackWithoutUserGesture::Prevented);
2460     }
2461
2462     if (shouldUpdateDisplayState) {
2463         updateDisplayState();
2464         if (hasMediaControls()) {
2465             mediaControls()->refreshClosedCaptionsButtonVisibility();
2466             mediaControls()->updateStatusDisplay();
2467         }
2468     }
2469
2470     updatePlayState();
2471     updateMediaController();
2472 #if ENABLE(VIDEO_TRACK)
2473     updateActiveTextTrackCues(currentMediaTime());
2474 #endif
2475 }
2476
2477 #if ENABLE(LEGACY_ENCRYPTED_MEDIA)
2478 RefPtr<ArrayBuffer> HTMLMediaElement::mediaPlayerCachedKeyForKeyId(const String& keyId) const
2479 {
2480     return m_webKitMediaKeys ? m_webKitMediaKeys->cachedKeyForKeyId(keyId) : nullptr;
2481 }
2482
2483 bool HTMLMediaElement::mediaPlayerKeyNeeded(MediaPlayer*, Uint8Array* initData)
2484 {
2485     if (!hasEventListeners("webkitneedkey")) {
2486         m_error = MediaError::create(MediaError::MEDIA_ERR_ENCRYPTED);
2487         scheduleEvent(eventNames().errorEvent);
2488         return false;
2489     }
2490
2491     auto event = WebKitMediaKeyNeededEvent::create(eventNames().webkitneedkeyEvent, initData);
2492     event->setTarget(this);
2493     m_asyncEventQueue.enqueueEvent(WTFMove(event));
2494
2495     return true;
2496 }
2497
2498 String HTMLMediaElement::mediaPlayerMediaKeysStorageDirectory() const
2499 {
2500     String storageDirectory = document().settings().mediaKeysStorageDirectory();
2501     if (storageDirectory.isEmpty())
2502         return emptyString();
2503
2504     return pathByAppendingComponent(storageDirectory, SecurityOriginData::fromSecurityOrigin(document().securityOrigin()).databaseIdentifier());
2505 }
2506
2507 void HTMLMediaElement::webkitSetMediaKeys(WebKitMediaKeys* mediaKeys)
2508 {
2509     if (m_webKitMediaKeys == mediaKeys)
2510         return;
2511
2512     if (m_webKitMediaKeys)
2513         m_webKitMediaKeys->setMediaElement(nullptr);
2514     m_webKitMediaKeys = mediaKeys;
2515     if (m_webKitMediaKeys)
2516         m_webKitMediaKeys->setMediaElement(this);
2517 }
2518
2519 void HTMLMediaElement::keyAdded()
2520 {
2521     if (m_player)
2522         m_player->keyAdded();
2523 }
2524
2525 #endif
2526
2527 #if ENABLE(ENCRYPTED_MEDIA)
2528
2529 MediaKeys* HTMLMediaElement::mediaKeys() const
2530 {
2531     return nullptr;
2532 }
2533
2534 void HTMLMediaElement::setMediaKeys(MediaKeys*, Ref<DeferredPromise>&&)
2535 {
2536     notImplemented();
2537 }
2538
2539 #endif // ENABLE(ENCRYPTED_MEDIA)
2540
2541 void HTMLMediaElement::progressEventTimerFired()
2542 {
2543     ASSERT(m_player);
2544     if (m_networkState != NETWORK_LOADING)
2545         return;
2546
2547     double time = monotonicallyIncreasingTime();
2548     double timedelta = time - m_previousProgressTime;
2549
2550     if (m_player->didLoadingProgress()) {
2551         scheduleEvent(eventNames().progressEvent);
2552         m_previousProgressTime = time;
2553         m_sentStalledEvent = false;
2554         updateRenderer();
2555         if (hasMediaControls())
2556             mediaControls()->bufferingProgressed();
2557     } else if (timedelta > 3.0 && !m_sentStalledEvent) {
2558         scheduleEvent(eventNames().stalledEvent);
2559         m_sentStalledEvent = true;
2560         setShouldDelayLoadEvent(false);
2561     }
2562 }
2563
2564 void HTMLMediaElement::rewind(double timeDelta)
2565 {
2566     LOG(Media, "HTMLMediaElement::rewind(%p) - %f", this, timeDelta);
2567     setCurrentTime(std::max(currentMediaTime() - MediaTime::createWithDouble(timeDelta), minTimeSeekable()));
2568 }
2569
2570 void HTMLMediaElement::returnToRealtime()
2571 {
2572     LOG(Media, "HTMLMediaElement::returnToRealtime(%p)", this);
2573     setCurrentTime(maxTimeSeekable());
2574 }
2575
2576 void HTMLMediaElement::addPlayedRange(const MediaTime& start, const MediaTime& end)
2577 {
2578     LOG(Media, "HTMLMediaElement::addPlayedRange(%p) - [%s, %s]", this, toString(start).utf8().data(), toString(end).utf8().data());
2579     if (!m_playedTimeRanges)
2580         m_playedTimeRanges = TimeRanges::create();
2581     m_playedTimeRanges->ranges().add(start, end);
2582 }  
2583
2584 bool HTMLMediaElement::supportsScanning() const
2585 {
2586     return m_player ? m_player->supportsScanning() : false;
2587 }
2588
2589 void HTMLMediaElement::prepareToPlay()
2590 {
2591     LOG(Media, "HTMLMediaElement::prepareToPlay(%p)", this);
2592     if (m_havePreparedToPlay)
2593         return;
2594     m_havePreparedToPlay = true;
2595     if (m_player)
2596         m_player->prepareToPlay();
2597 }
2598
2599 void HTMLMediaElement::fastSeek(double time)
2600 {
2601     fastSeek(MediaTime::createWithDouble(time));
2602 }
2603
2604 void HTMLMediaElement::fastSeek(const MediaTime& time)
2605 {
2606     LOG(Media, "HTMLMediaElement::fastSeek(%p) - %s", this, toString(time).utf8().data());
2607     // 4.7.10.9 Seeking
2608     // 9. If the approximate-for-speed flag is set, adjust the new playback position to a value that will
2609     // allow for playback to resume promptly. If new playback position before this step is before current
2610     // playback position, then the adjusted new playback position must also be before the current playback
2611     // position. Similarly, if the new playback position before this step is after current playback position,
2612     // then the adjusted new playback position must also be after the current playback position.
2613     refreshCachedTime();
2614     MediaTime delta = time - currentMediaTime();
2615     MediaTime negativeTolerance = delta >= MediaTime::zeroTime() ? delta : MediaTime::positiveInfiniteTime();
2616     MediaTime positiveTolerance = delta < MediaTime::zeroTime() ? -delta : MediaTime::positiveInfiniteTime();
2617
2618     seekWithTolerance(time, negativeTolerance, positiveTolerance, true);
2619 }
2620
2621 void HTMLMediaElement::seek(const MediaTime& time)
2622 {
2623     LOG(Media, "HTMLMediaElement::seek(%p) - %s", this, toString(time).utf8().data());
2624     seekWithTolerance(time, MediaTime::zeroTime(), MediaTime::zeroTime(), true);
2625 }
2626
2627 void HTMLMediaElement::seekInternal(const MediaTime& time)
2628 {
2629     LOG(Media, "HTMLMediaElement::seekInternal(%p) - %s", this, toString(time).utf8().data());
2630     seekWithTolerance(time, MediaTime::zeroTime(), MediaTime::zeroTime(), false);
2631 }
2632
2633 void HTMLMediaElement::seekWithTolerance(const MediaTime& inTime, const MediaTime& negativeTolerance, const MediaTime& positiveTolerance, bool fromDOM)
2634 {
2635     // 4.8.10.9 Seeking
2636     MediaTime time = inTime;
2637
2638     // 1 - Set the media element's show poster flag to false.
2639     setDisplayMode(Video);
2640
2641     // 2 - If the media element's readyState is HAVE_NOTHING, abort these steps.
2642     if (m_readyState == HAVE_NOTHING || !m_player)
2643         return;
2644
2645     // If the media engine has been told to postpone loading data, let it go ahead now.
2646     if (m_preload < MediaPlayer::Auto && m_readyState < HAVE_FUTURE_DATA)
2647         prepareToPlay();
2648
2649     // Get the current time before setting m_seeking, m_lastSeekTime is returned once it is set.
2650     refreshCachedTime();
2651     MediaTime now = currentMediaTime();
2652
2653     // 3 - If the element's seeking IDL attribute is true, then another instance of this algorithm is
2654     // already running. Abort that other instance of the algorithm without waiting for the step that
2655     // it is running to complete.
2656     if (m_seekTaskQueue.hasPendingTasks()) {
2657         LOG(Media, "HTMLMediaElement::seekWithTolerance(%p) - cancelling pending seeks", this);
2658         m_seekTaskQueue.cancelAllTasks();
2659         if (m_pendingSeek) {
2660             now = m_pendingSeek->now;
2661             m_pendingSeek = nullptr;
2662         }
2663         m_pendingSeekType = NoSeek;
2664     }
2665
2666     // 4 - Set the seeking IDL attribute to true.
2667     // The flag will be cleared when the engine tells us the time has actually changed.
2668     m_seeking = true;
2669     if (m_playing) {
2670         if (m_lastSeekTime < now)
2671             addPlayedRange(m_lastSeekTime, now);
2672     }
2673     m_lastSeekTime = time;
2674
2675     // 5 - If the seek was in response to a DOM method call or setting of an IDL attribute, then continue
2676     // the script. The remainder of these steps must be run asynchronously.
2677     m_pendingSeek = std::make_unique<PendingSeek>(now, time, negativeTolerance, positiveTolerance);
2678     if (fromDOM) {
2679         LOG(Media, "HTMLMediaElement::seekWithTolerance(%p) - enqueuing seek from %s to %s", this, toString(now).utf8().data(), toString(time).utf8().data());
2680         m_seekTaskQueue.enqueueTask(std::bind(&HTMLMediaElement::seekTask, this));
2681     } else
2682         seekTask();
2683
2684     if (ScriptController::processingUserGestureForMedia())
2685         m_mediaSession->removeBehaviorRestriction(MediaElementSession::RequireUserGestureToControlControlsManager);
2686 }
2687
2688 void HTMLMediaElement::seekTask()
2689 {
2690     LOG(Media, "HTMLMediaElement::seekTask(%p)", this);
2691
2692     if (!m_player) {
2693         clearSeeking();
2694         return;
2695     }
2696
2697     ASSERT(m_pendingSeek);
2698     MediaTime now = m_pendingSeek->now;
2699     MediaTime time = m_pendingSeek->targetTime;
2700     MediaTime negativeTolerance = m_pendingSeek->negativeTolerance;
2701     MediaTime positiveTolerance = m_pendingSeek->positiveTolerance;
2702     m_pendingSeek = nullptr;
2703
2704     // 6 - If the new playback position is later than the end of the media resource, then let it be the end 
2705     // of the media resource instead.
2706     time = std::min(time, durationMediaTime());
2707
2708     // 7 - If the new playback position is less than the earliest possible position, let it be that position instead.
2709     MediaTime earliestTime = m_player->startTime();
2710     time = std::max(time, earliestTime);
2711
2712     // Ask the media engine for the time value in the movie's time scale before comparing with current time. This
2713     // is necessary because if the seek time is not equal to currentTime but the delta is less than the movie's
2714     // time scale, we will ask the media engine to "seek" to the current movie time, which may be a noop and
2715     // not generate a timechanged callback. This means m_seeking will never be cleared and we will never 
2716     // fire a 'seeked' event.
2717 #if !LOG_DISABLED
2718     MediaTime mediaTime = m_player->mediaTimeForTimeValue(time);
2719     if (time != mediaTime)
2720         LOG(Media, "HTMLMediaElement::seekTask(%p) - %s - media timeline equivalent is %s", this, toString(time).utf8().data(), toString(mediaTime).utf8().data());
2721 #endif
2722     time = m_player->mediaTimeForTimeValue(time);
2723
2724     // 8 - If the (possibly now changed) new playback position is not in one of the ranges given in the
2725     // seekable attribute, then let it be the position in one of the ranges given in the seekable attribute 
2726     // that is the nearest to the new playback position. ... If there are no ranges given in the seekable
2727     // attribute then set the seeking IDL attribute to false and abort these steps.
2728     RefPtr<TimeRanges> seekableRanges = seekable();
2729     bool noSeekRequired = !seekableRanges->length();
2730
2731     // Short circuit seeking to the current time by just firing the events if no seek is required.
2732     // Don't skip calling the media engine if 1) we are in poster mode (because a seek should always cancel
2733     // poster display), or 2) if there is a pending fast seek, or 3) if this seek is not an exact seek
2734     SeekType thisSeekType = (negativeTolerance == MediaTime::zeroTime() && positiveTolerance == MediaTime::zeroTime()) ? Precise : Fast;
2735     if (!noSeekRequired && time == now && thisSeekType == Precise && m_pendingSeekType != Fast && displayMode() != Poster)
2736         noSeekRequired = true;
2737
2738 #if ENABLE(MEDIA_SOURCE)
2739     // Always notify the media engine of a seek if the source is not closed. This ensures that the source is
2740     // always in a flushed state when the 'seeking' event fires.
2741     if (m_mediaSource && !m_mediaSource->isClosed())
2742         noSeekRequired = false;
2743 #endif
2744
2745     if (noSeekRequired) {
2746         LOG(Media, "HTMLMediaElement::seekTask(%p) - seek to %s ignored", this, toString(time).utf8().data());
2747         if (time == now) {
2748             scheduleEvent(eventNames().seekingEvent);
2749             scheduleTimeupdateEvent(false);
2750             scheduleEvent(eventNames().seekedEvent);
2751         }
2752         clearSeeking();
2753         return;
2754     }
2755     time = seekableRanges->ranges().nearest(time);
2756
2757     m_sentEndEvent = false;
2758     m_lastSeekTime = time;
2759     m_pendingSeekType = thisSeekType;
2760     m_seeking = true;
2761
2762     // 10 - Queue a task to fire a simple event named seeking at the element.
2763     scheduleEvent(eventNames().seekingEvent);
2764
2765     // 11 - Set the current playback position to the given new playback position
2766     m_player->seekWithTolerance(time, negativeTolerance, positiveTolerance);
2767
2768     // 12 - Wait until the user agent has established whether or not the media data for the new playback
2769     // position is available, and, if it is, until it has decoded enough data to play back that position.
2770     // 13 - Await a stable state. The synchronous section consists of all the remaining steps of this algorithm.
2771 }
2772
2773 void HTMLMediaElement::clearSeeking()
2774 {
2775     m_seeking = false;
2776     m_pendingSeekType = NoSeek;
2777     invalidateCachedTime();
2778 }
2779
2780 void HTMLMediaElement::finishSeek()
2781 {
2782     // 4.8.10.9 Seeking
2783     // 14 - Set the seeking IDL attribute to false.
2784     clearSeeking();
2785
2786     LOG(Media, "HTMLMediaElement::finishSeek(%p) - current time = %s", this, toString(currentMediaTime()).utf8().data());
2787
2788     // 15 - Run the time maches on steps.
2789     // Handled by mediaPlayerTimeChanged().
2790
2791     // 16 - Queue a task to fire a simple event named timeupdate at the element.
2792     scheduleEvent(eventNames().timeupdateEvent);
2793
2794     // 17 - Queue a task to fire a simple event named seeked at the element.
2795     scheduleEvent(eventNames().seekedEvent);
2796
2797 #if ENABLE(MEDIA_SOURCE)
2798     if (m_mediaSource)
2799         m_mediaSource->monitorSourceBuffers();
2800 #endif
2801 }
2802
2803 HTMLMediaElement::ReadyState HTMLMediaElement::readyState() const
2804 {
2805     return m_readyState;
2806 }
2807
2808 MediaPlayer::MovieLoadType HTMLMediaElement::movieLoadType() const
2809 {
2810     return m_player ? m_player->movieLoadType() : MediaPlayer::Unknown;
2811 }
2812
2813 bool HTMLMediaElement::hasAudio() const
2814 {
2815     return m_player ? m_player->hasAudio() : false;
2816 }
2817
2818 bool HTMLMediaElement::seeking() const
2819 {
2820     return m_seeking;
2821 }
2822
2823 void HTMLMediaElement::refreshCachedTime() const
2824 {
2825     if (!m_player)
2826         return;
2827
2828     m_cachedTime = m_player->currentTime();
2829     if (!m_cachedTime) {
2830         // Do not use m_cachedTime until the media engine returns a non-zero value because we can't 
2831         // estimate current time until playback actually begins. 
2832         invalidateCachedTime(); 
2833         return; 
2834     } 
2835
2836     m_clockTimeAtLastCachedTimeUpdate = monotonicallyIncreasingTime();
2837 }
2838
2839 void HTMLMediaElement::invalidateCachedTime() const
2840 {
2841     m_cachedTime = MediaTime::invalidTime();
2842     if (!m_player || !m_player->maximumDurationToCacheMediaTime())
2843         return;
2844
2845 #if !LOG_DISABLED
2846     if (m_cachedTime.isValid())
2847         LOG(Media, "HTMLMediaElement::invalidateCachedTime(%p)", this);
2848 #endif
2849
2850     // Don't try to cache movie time when playback first starts as the time reported by the engine
2851     // sometimes fluctuates for a short amount of time, so the cached time will be off if we take it
2852     // too early.
2853     static const double minimumTimePlayingBeforeCacheSnapshot = 0.5;
2854
2855     m_minimumClockTimeToUpdateCachedTime = monotonicallyIncreasingTime() + minimumTimePlayingBeforeCacheSnapshot;
2856 }
2857
2858 // playback state
2859 double HTMLMediaElement::currentTime() const
2860 {
2861     return currentMediaTime().toDouble();
2862 }
2863
2864 MediaTime HTMLMediaElement::currentMediaTime() const
2865 {
2866 #if LOG_CACHED_TIME_WARNINGS
2867     static const MediaTime minCachedDeltaForWarning = MediaTime::create(1, 100);
2868 #endif
2869
2870     if (!m_player)
2871         return MediaTime::zeroTime();
2872
2873     if (m_seeking) {
2874         LOG(Media, "HTMLMediaElement::currentTime(%p) - seeking, returning %s", this, toString(m_lastSeekTime).utf8().data());
2875         return m_lastSeekTime;
2876     }
2877
2878     if (m_cachedTime.isValid() && m_paused) {
2879 #if LOG_CACHED_TIME_WARNINGS
2880         MediaTime delta = m_cachedTime - m_player->currentTime();
2881         if (delta > minCachedDeltaForWarning)
2882             LOG(Media, "HTMLMediaElement::currentTime(%p) - WARNING, cached time is %s seconds off of media time when paused", this, toString(delta).utf8().data());
2883 #endif
2884         return m_cachedTime;
2885     }
2886
2887     // Is it too soon use a cached time?
2888     double now = monotonicallyIncreasingTime();
2889     double maximumDurationToCacheMediaTime = m_player->maximumDurationToCacheMediaTime();
2890
2891     if (maximumDurationToCacheMediaTime && m_cachedTime.isValid() && !m_paused && now > m_minimumClockTimeToUpdateCachedTime) {
2892         double clockDelta = now - m_clockTimeAtLastCachedTimeUpdate;
2893
2894         // Not too soon, use the cached time only if it hasn't expired.
2895         if (clockDelta < maximumDurationToCacheMediaTime) {
2896             MediaTime adjustedCacheTime = m_cachedTime + MediaTime::createWithDouble(effectivePlaybackRate() * clockDelta);
2897
2898 #if LOG_CACHED_TIME_WARNINGS
2899             MediaTime delta = adjustedCacheTime - m_player->currentTime();
2900             if (delta > minCachedDeltaForWarning)
2901                 LOG(Media, "HTMLMediaElement::currentTime(%p) - WARNING, cached time is %f seconds off of media time when playing", this, delta);
2902 #endif
2903             return adjustedCacheTime;
2904         }
2905     }
2906
2907 #if LOG_CACHED_TIME_WARNINGS
2908     if (maximumDurationToCacheMediaTime && now > m_minimumClockTimeToUpdateCachedTime && m_cachedTime != MediaPlayer::invalidTime()) {
2909         double clockDelta = now - m_clockTimeAtLastCachedTimeUpdate;
2910         MediaTime delta = m_cachedTime + MediaTime::createWithDouble(effectivePlaybackRate() * clockDelta) - m_player->currentTime();
2911         LOG(Media, "HTMLMediaElement::currentTime(%p) - cached time was %s seconds off of media time when it expired", this, toString(delta).utf8().data());
2912     }
2913 #endif
2914
2915     refreshCachedTime();
2916
2917     if (m_cachedTime.isInvalid())
2918         return MediaTime::zeroTime();
2919     
2920     return m_cachedTime;
2921 }
2922
2923 void HTMLMediaElement::setCurrentTime(double time)
2924 {
2925     setCurrentTime(MediaTime::createWithDouble(time));
2926 }
2927
2928 void HTMLMediaElement::setCurrentTime(const MediaTime& time)
2929 {
2930     if (m_mediaController)
2931         return;
2932
2933     seekInternal(time);
2934 }
2935
2936 ExceptionOr<void> HTMLMediaElement::setCurrentTimeForBindings(double time)
2937 {
2938     if (m_mediaController)
2939         return Exception { INVALID_STATE_ERR };
2940     seek(MediaTime::createWithDouble(time));
2941     return { };
2942 }
2943
2944 double HTMLMediaElement::duration() const
2945 {
2946     return durationMediaTime().toDouble();
2947 }
2948
2949 MediaTime HTMLMediaElement::durationMediaTime() const
2950 {
2951     if (m_player && m_readyState >= HAVE_METADATA)
2952         return m_player->duration();
2953
2954     return MediaTime::invalidTime();
2955 }
2956
2957 bool HTMLMediaElement::paused() const
2958 {
2959     // As of this writing, JavaScript garbage collection calls this function directly. In the past
2960     // we had problems where this was called on an object after a bad cast. The assertion below
2961     // made our regression test detect the problem, so we should keep it because of that. But note
2962     // that the value of the assertion relies on the compiler not being smart enough to know that
2963     // isHTMLUnknownElement is guaranteed to return false for an HTMLMediaElement.
2964     ASSERT(!isHTMLUnknownElement());
2965
2966     return m_paused;
2967 }
2968
2969 double HTMLMediaElement::defaultPlaybackRate() const
2970 {
2971 #if ENABLE(MEDIA_STREAM)
2972     // http://w3c.github.io/mediacapture-main/#mediastreams-in-media-elements
2973     // "defaultPlaybackRate" - On setting: ignored. On getting: return 1.0
2974     // A MediaStream is not seekable. Therefore, this attribute must always have the
2975     // value 1.0 and any attempt to alter it must be ignored. Note that this also means
2976     // that the ratechange event will not fire.
2977     if (m_mediaStreamSrcObject)
2978         return 1;
2979 #endif
2980
2981     return m_defaultPlaybackRate;
2982 }
2983
2984 void HTMLMediaElement::setDefaultPlaybackRate(double rate)
2985 {
2986 #if ENABLE(MEDIA_STREAM)
2987     // http://w3c.github.io/mediacapture-main/#mediastreams-in-media-elements
2988     // "defaultPlaybackRate" - On setting: ignored. On getting: return 1.0
2989     // A MediaStream is not seekable. Therefore, this attribute must always have the
2990     // value 1.0 and any attempt to alter it must be ignored. Note that this also means
2991     // that the ratechange event will not fire.
2992     if (m_mediaStreamSrcObject)
2993         return;
2994 #endif
2995
2996     if (m_defaultPlaybackRate != rate) {
2997         LOG(Media, "HTMLMediaElement::setDefaultPlaybackRate(%p) - %f", this, rate);
2998         m_defaultPlaybackRate = rate;
2999         scheduleEvent(eventNames().ratechangeEvent);
3000     }
3001 }
3002
3003 double HTMLMediaElement::effectivePlaybackRate() const
3004 {
3005     return m_mediaController ? m_mediaController->playbackRate() : m_reportedPlaybackRate;
3006 }
3007
3008 double HTMLMediaElement::requestedPlaybackRate() const
3009 {
3010     return m_mediaController ? m_mediaController->playbackRate() : m_requestedPlaybackRate;
3011 }
3012
3013 double HTMLMediaElement::playbackRate() const
3014 {
3015 #if ENABLE(MEDIA_STREAM)
3016     // http://w3c.github.io/mediacapture-main/#mediastreams-in-media-elements
3017     // "playbackRate" - A MediaStream is not seekable. Therefore, this attribute must always
3018     // have the value 1.0 and any attempt to alter it must be ignored. Note that this also
3019     // means that the ratechange event will not fire.
3020     if (m_mediaStreamSrcObject)
3021         return 1;
3022 #endif
3023
3024     return m_requestedPlaybackRate;
3025 }
3026
3027 void HTMLMediaElement::setPlaybackRate(double rate)
3028 {
3029     LOG(Media, "HTMLMediaElement::setPlaybackRate(%p) - %f", this, rate);
3030
3031 #if ENABLE(MEDIA_STREAM)
3032     // http://w3c.github.io/mediacapture-main/#mediastreams-in-media-elements
3033     // "playbackRate" - A MediaStream is not seekable. Therefore, this attribute must always
3034     // have the value 1.0 and any attempt to alter it must be ignored. Note that this also
3035     // means that the ratechange event will not fire.
3036     if (m_mediaStreamSrcObject)
3037         return;
3038 #endif
3039
3040     if (m_player && potentiallyPlaying() && m_player->rate() != rate && !m_mediaController)
3041         m_player->setRate(rate);
3042
3043     if (m_requestedPlaybackRate != rate) {
3044         m_reportedPlaybackRate = m_requestedPlaybackRate = rate;
3045         invalidateCachedTime();
3046         scheduleEvent(eventNames().ratechangeEvent);
3047     }
3048 }
3049
3050 void HTMLMediaElement::updatePlaybackRate()
3051 {
3052     double requestedRate = requestedPlaybackRate();
3053     if (m_player && potentiallyPlaying() && m_player->rate() != requestedRate)
3054         m_player->setRate(requestedRate);
3055 }
3056
3057 bool HTMLMediaElement::webkitPreservesPitch() const
3058 {
3059     return m_webkitPreservesPitch;
3060 }
3061
3062 void HTMLMediaElement::setWebkitPreservesPitch(bool preservesPitch)
3063 {
3064     LOG(Media, "HTMLMediaElement::setWebkitPreservesPitch(%p) - %s", this, boolString(preservesPitch));
3065
3066     m_webkitPreservesPitch = preservesPitch;
3067     
3068     if (!m_player)
3069         return;
3070
3071     m_player->setPreservesPitch(preservesPitch);
3072 }
3073
3074 bool HTMLMediaElement::ended() const
3075 {
3076 #if ENABLE(MEDIA_STREAM)
3077     // http://w3c.github.io/mediacapture-main/#mediastreams-in-media-elements
3078     // When the MediaStream state moves from the active to the inactive state, the User Agent
3079     // must raise an ended event on the HTMLMediaElement and set its ended attribute to true.
3080     if (m_mediaStreamSrcObject && m_player && m_player->ended())
3081         return true;
3082 #endif
3083
3084     // 4.8.10.8 Playing the media resource
3085     // The ended attribute must return true if the media element has ended 
3086     // playback and the direction of playback is forwards, and false otherwise.
3087     return endedPlayback() && requestedPlaybackRate() > 0;
3088 }
3089
3090 bool HTMLMediaElement::autoplay() const
3091 {
3092     return hasAttributeWithoutSynchronization(autoplayAttr);
3093 }
3094
3095 String HTMLMediaElement::preload() const
3096 {
3097 #if ENABLE(MEDIA_STREAM)
3098     // http://w3c.github.io/mediacapture-main/#mediastreams-in-media-elements
3099     // "preload" - On getting: none. On setting: ignored.
3100     if (m_mediaStreamSrcObject)
3101         return ASCIILiteral("none");
3102 #endif
3103
3104     switch (m_preload) {
3105     case MediaPlayer::None:
3106         return ASCIILiteral("none");
3107     case MediaPlayer::MetaData:
3108         return ASCIILiteral("metadata");
3109     case MediaPlayer::Auto:
3110         return ASCIILiteral("auto");
3111     }
3112
3113     ASSERT_NOT_REACHED();
3114     return String();
3115 }
3116
3117 void HTMLMediaElement::setPreload(const String& preload)
3118 {
3119     LOG(Media, "HTMLMediaElement::setPreload(%p) - %s", this, preload.utf8().data());
3120 #if ENABLE(MEDIA_STREAM)
3121     // http://w3c.github.io/mediacapture-main/#mediastreams-in-media-elements
3122     // "preload" - On getting: none. On setting: ignored.
3123     if (m_mediaStreamSrcObject)
3124         return;
3125 #endif
3126
3127     setAttributeWithoutSynchronization(preloadAttr, preload);
3128 }
3129
3130 void HTMLMediaElement::play(DOMPromise<void>&& promise)
3131 {
3132     LOG(Media, "HTMLMediaElement::play(%p)", this);
3133
3134     auto success = m_mediaSession->playbackPermitted(*this);
3135     if (!success) {
3136         if (success.value() == MediaPlaybackDenialReason::UserGestureRequired)
3137             setPlaybackWithoutUserGesture(PlaybackWithoutUserGesture::Prevented);
3138         promise.reject(NotAllowedError);
3139         return;
3140     }
3141
3142     if (m_error && m_error->code() == MediaError::MEDIA_ERR_SRC_NOT_SUPPORTED) {
3143         promise.reject(NOT_SUPPORTED_ERR, "The operation is not supported.");
3144         return;
3145     }
3146
3147     if (ScriptController::processingUserGestureForMedia())
3148         removeBehaviorsRestrictionsAfterFirstUserGesture();
3149
3150     if (!playInternal()) {
3151         promise.reject(NotAllowedError);
3152         return;
3153     }
3154
3155     m_pendingPlayPromises.append(WTFMove(promise));
3156 }
3157
3158 void HTMLMediaElement::play()
3159 {
3160     LOG(Media, "HTMLMediaElement::play(%p)", this);
3161
3162     auto success = m_mediaSession->playbackPermitted(*this);
3163     if (!success) {
3164         if (success.value() == MediaPlaybackDenialReason::UserGestureRequired)
3165             setPlaybackWithoutUserGesture(PlaybackWithoutUserGesture::Prevented);
3166         return;
3167     }
3168     if (ScriptController::processingUserGestureForMedia())
3169         removeBehaviorsRestrictionsAfterFirstUserGesture();
3170
3171     playInternal();
3172 }
3173
3174 bool HTMLMediaElement::playInternal()
3175 {
3176     LOG(Media, "HTMLMediaElement::playInternal(%p)", this);
3177     
3178     if (!m_mediaSession->clientWillBeginPlayback()) {
3179         LOG(Media, "  returning because of interruption");
3180         return true; // Treat as success because we will begin playback on cessation of the interruption.
3181     }
3182
3183     // 4.8.10.9. Playing the media resource
3184     if (!m_player || m_networkState == NETWORK_EMPTY)
3185         prepareForLoad();
3186
3187     if (endedPlayback())
3188         seekInternal(MediaTime::zeroTime());
3189
3190     if (m_mediaController)
3191         m_mediaController->bringElementUpToSpeed(*this);
3192
3193     if (m_paused) {
3194         m_paused = false;
3195         invalidateCachedTime();
3196         m_playbackStartedTime = currentMediaTime().toDouble();
3197         scheduleEvent(eventNames().playEvent);
3198
3199         if (m_readyState <= HAVE_CURRENT_DATA)
3200             scheduleEvent(eventNames().waitingEvent);
3201         else if (m_readyState >= HAVE_FUTURE_DATA)
3202             scheduleNotifyAboutPlaying();
3203
3204 #if ENABLE(MEDIA_SESSION)
3205         // 6.3 Activating a media session from a media element
3206         // When the play() method is invoked, the paused attribute is true, and the readyState attribute has the value
3207         // HAVE_FUTURE_DATA or HAVE_ENOUGH_DATA, then
3208         // 1. Let media session be the value of the current media session.
3209         // 2. If we are not currently in media session's list of active participating media elements then append
3210         //    ourselves to this list.
3211         // 3. Let activated be the result of running the media session invocation algorithm for media session.
3212         // 4. If activated is failure, pause ourselves.
3213         if (m_readyState == HAVE_ENOUGH_DATA || m_readyState == HAVE_FUTURE_DATA) {
3214             if (m_session) {
3215                 m_session->addActiveMediaElement(*this);
3216
3217                 if (m_session->kind() == MediaSessionKind::Content) {
3218                     if (Page* page = document().page())
3219                         page->chrome().client().focusedContentMediaElementDidChange(m_elementID);
3220                 }
3221
3222                 if (!m_session->invoke()) {
3223                     pause();
3224                     return false;
3225                 }
3226             }
3227         }
3228 #endif
3229     } else if (m_readyState >= HAVE_FUTURE_DATA)
3230         scheduleResolvePendingPlayPromises();
3231
3232     if (ScriptController::processingUserGestureForMedia()) {
3233         if (m_playbackWithoutUserGesture == PlaybackWithoutUserGesture::Prevented) {
3234             if (Page* page = document().page())
3235                 page->chrome().client().handleAutoplayEvent(AutoplayEvent::DidPlayMediaPreventedFromPlaying);
3236             setPlaybackWithoutUserGesture(PlaybackWithoutUserGesture::None);
3237         }
3238     } else
3239         setPlaybackWithoutUserGesture(PlaybackWithoutUserGesture::Started);
3240
3241     m_autoplaying = false;
3242     updatePlayState();
3243
3244     return true;
3245 }
3246
3247 void HTMLMediaElement::pause()
3248 {
3249     LOG(Media, "HTMLMediaElement::pause(%p)", this);
3250
3251     if (!m_mediaSession->playbackPermitted(*this))
3252         return;
3253
3254     if (ScriptController::processingUserGestureForMedia())
3255         removeBehaviorsRestrictionsAfterFirstUserGesture(MediaElementSession::RequireUserGestureToControlControlsManager);
3256
3257     pauseInternal();
3258 }
3259
3260
3261 void HTMLMediaElement::pauseInternal()
3262 {
3263     LOG(Media, "HTMLMediaElement::pauseInternal(%p)", this);
3264
3265     if (!m_mediaSession->clientWillPausePlayback()) {
3266         LOG(Media, "  returning because of interruption");
3267         return;
3268     }
3269
3270     // 4.8.10.9. Playing the media resource
3271     if (!m_player || m_networkState == NETWORK_EMPTY) {
3272         // Unless the restriction on media requiring user action has been lifted
3273         // don't trigger loading if a script calls pause().
3274         if (!m_mediaSession->playbackPermitted(*this))
3275             return;
3276         prepareForLoad();
3277     }
3278
3279     m_autoplaying = false;
3280
3281     if (ScriptController::processingUserGestureForMedia())
3282         userDidInterfereWithAutoplay();
3283
3284     setPlaybackWithoutUserGesture(PlaybackWithoutUserGesture::None);
3285
3286     if (!m_paused) {
3287         m_paused = true;
3288         scheduleTimeupdateEvent(false);
3289         scheduleEvent(eventNames().pauseEvent);
3290         rejectPendingPlayPromises(DOMError::create("AbortError", "The operation was aborted."));
3291
3292         if (MemoryPressureHandler::singleton().isUnderMemoryPressure())
3293             purgeBufferedDataIfPossible();
3294     }
3295
3296     updatePlayState();
3297 }
3298
3299 #if ENABLE(MEDIA_SOURCE)
3300
3301 void HTMLMediaElement::detachMediaSource()
3302 {
3303     if (!m_mediaSource)
3304         return;
3305
3306     m_mediaSource->detachFromElement(*this);
3307     m_mediaSource = nullptr;
3308 }
3309
3310 #endif
3311
3312 bool HTMLMediaElement::loop() const
3313 {
3314     return hasAttributeWithoutSynchronization(loopAttr);
3315 }
3316
3317 void HTMLMediaElement::setLoop(bool b)
3318 {
3319     LOG(Media, "HTMLMediaElement::setLoop(%p) - %s", this, boolString(b));
3320     setBooleanAttribute(loopAttr, b);
3321 }
3322
3323 bool HTMLMediaElement::controls() const
3324 {
3325     Frame* frame = document().frame();
3326
3327     // always show controls when scripting is disabled
3328     if (frame && !frame->script().canExecuteScripts(NotAboutToExecuteScript))
3329         return true;
3330
3331     return hasAttributeWithoutSynchronization(controlsAttr);
3332 }
3333
3334 void HTMLMediaElement::setControls(bool b)
3335 {
3336     LOG(Media, "HTMLMediaElement::setControls(%p) - %s", this, boolString(b));
3337     setBooleanAttribute(controlsAttr, b);
3338 }
3339
3340 double HTMLMediaElement::volume() const
3341 {
3342     return m_volume;
3343 }
3344
3345 ExceptionOr<void> HTMLMediaElement::setVolume(double volume)
3346 {
3347     LOG(Media, "HTMLMediaElement::setVolume(%p) - %f", this, volume);
3348
3349     if (!(volume >= 0 && volume <= 1))
3350         return Exception { INDEX_SIZE_ERR };
3351
3352 #if !PLATFORM(IOS)
3353     if (m_volume == volume)
3354         return { };
3355
3356     m_volume = volume;
3357     m_volumeInitialized = true;
3358     updateVolume();
3359     scheduleEvent(eventNames().volumechangeEvent);
3360 #endif
3361     return { };
3362 }
3363
3364 bool HTMLMediaElement::muted() const
3365 {
3366     return m_explicitlyMuted ? m_muted : hasAttributeWithoutSynchronization(mutedAttr);
3367 }
3368
3369 void HTMLMediaElement::setMuted(bool muted)
3370 {
3371     LOG(Media, "HTMLMediaElement::setMuted(%p) - %s", this, boolString(muted));
3372
3373     bool mutedStateChanged = m_muted != muted;
3374     if (mutedStateChanged || !m_explicitlyMuted) {
3375         m_muted = muted;
3376         m_explicitlyMuted = true;
3377
3378         if (ScriptController::processingUserGestureForMedia()) {
3379             removeBehaviorsRestrictionsAfterFirstUserGesture(MediaElementSession::AllRestrictions & ~MediaElementSession::RequireUserGestureToControlControlsManager);
3380
3381             if (hasAudio() && m_muted)
3382                 userDidInterfereWithAutoplay();
3383         }
3384
3385         // Avoid recursion when the player reports volume changes.
3386         if (!processingMediaPlayerCallback()) {
3387             if (m_player) {
3388                 m_player->setMuted(effectiveMuted());
3389                 if (hasMediaControls())
3390                     mediaControls()->changedMute();
3391             }
3392         }
3393
3394         if (mutedStateChanged)
3395             scheduleEvent(eventNames().volumechangeEvent);
3396
3397         updateShouldPlay();
3398
3399 #if ENABLE(MEDIA_SESSION)
3400         document().updateIsPlayingMedia(m_elementID);
3401 #else
3402         document().updateIsPlayingMedia();
3403 #endif
3404
3405 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
3406         updateMediaState(UpdateState::Asynchronously);
3407 #endif
3408         m_mediaSession->canProduceAudioChanged();
3409     }
3410
3411     scheduleUpdatePlaybackControlsManager();
3412 }
3413
3414 void HTMLMediaElement::togglePlayState()
3415 {
3416     LOG(Media, "HTMLMediaElement::togglePlayState(%p) - canPlay() is %s", this, boolString(canPlay()));
3417
3418     // We can safely call the internal play/pause methods, which don't check restrictions, because
3419     // this method is only called from the built-in media controller
3420     if (canPlay()) {
3421         updatePlaybackRate();
3422         playInternal();
3423     } else 
3424         pauseInternal();
3425 }
3426
3427 void HTMLMediaElement::beginScrubbing()
3428 {
3429     LOG(Media, "HTMLMediaElement::beginScrubbing(%p) - paused() is %s", this, boolString(paused()));
3430
3431     if (!paused()) {
3432         if (ended()) {
3433             // Because a media element stays in non-paused state when it reaches end, playback resumes 
3434             // when the slider is dragged from the end to another position unless we pause first. Do 
3435             // a "hard pause" so an event is generated, since we want to stay paused after scrubbing finishes.
3436             pause();
3437         } else {
3438             // Not at the end but we still want to pause playback so the media engine doesn't try to
3439             // continue playing during scrubbing. Pause without generating an event as we will 
3440             // unpause after scrubbing finishes.
3441             setPausedInternal(true);
3442         }
3443     }
3444
3445     m_mediaSession->removeBehaviorRestriction(MediaElementSession::RequireUserGestureToControlControlsManager);
3446 }
3447
3448 void HTMLMediaElement::endScrubbing()
3449 {
3450     LOG(Media, "HTMLMediaElement::endScrubbing(%p) - m_pausedInternal is %s", this, boolString(m_pausedInternal));
3451
3452     if (m_pausedInternal)
3453         setPausedInternal(false);
3454 }
3455
3456 void HTMLMediaElement::beginScanning(ScanDirection direction)
3457 {
3458     m_scanType = supportsScanning() ? Scan : Seek;
3459     m_scanDirection = direction;
3460
3461     if (m_scanType == Seek) {
3462         // Scanning by seeking requires the video to be paused during scanning.
3463         m_actionAfterScan = paused() ? Nothing : Play;
3464         pause();
3465     } else {
3466         // Scanning by scanning requires the video to be playing during scanninging.
3467         m_actionAfterScan = paused() ? Pause : Nothing;
3468         play();
3469         setPlaybackRate(nextScanRate());
3470     }
3471
3472     m_scanTimer.start(0_s, m_scanType == Seek ? SeekRepeatDelay : ScanRepeatDelay);
3473 }
3474
3475 void HTMLMediaElement::endScanning()
3476 {
3477     if (m_scanType == Scan)
3478         setPlaybackRate(defaultPlaybackRate());
3479
3480     if (m_actionAfterScan == Play)
3481         play();
3482     else if (m_actionAfterScan == Pause)
3483         pause();
3484
3485     if (m_scanTimer.isActive())
3486         m_scanTimer.stop();
3487 }
3488
3489 double HTMLMediaElement::nextScanRate()
3490 {
3491     double rate = std::min(ScanMaximumRate, fabs(playbackRate() * 2));
3492     if (m_scanDirection == Backward)
3493         rate *= -1;
3494 #if PLATFORM(IOS)