REGRESSION(r233926): media/modern-media-controls/media-controller/media-controller...
[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 "CommonVM.h"
39 #include "ContentSecurityPolicy.h"
40 #include "ContentType.h"
41 #include "CookieJar.h"
42 #include "DeprecatedGlobalSettings.h"
43 #include "DiagnosticLoggingClient.h"
44 #include "DiagnosticLoggingKeys.h"
45 #include "Document.h"
46 #include "DocumentLoader.h"
47 #include "ElementChildIterator.h"
48 #include "EventNames.h"
49 #include "Frame.h"
50 #include "FrameLoader.h"
51 #include "FrameLoaderClient.h"
52 #include "FrameView.h"
53 #include "HTMLParserIdioms.h"
54 #include "HTMLSourceElement.h"
55 #include "HTMLVideoElement.h"
56 #include "JSDOMException.h"
57 #include "JSDOMPromiseDeferred.h"
58 #include "JSHTMLMediaElement.h"
59 #include "Logging.h"
60 #include "MIMETypeRegistry.h"
61 #include "MediaController.h"
62 #include "MediaControls.h"
63 #include "MediaDocument.h"
64 #include "MediaError.h"
65 #include "MediaFragmentURIParser.h"
66 #include "MediaList.h"
67 #include "MediaPlayer.h"
68 #include "MediaQueryEvaluator.h"
69 #include "MediaResourceLoader.h"
70 #include "NetworkingContext.h"
71 #include "Page.h"
72 #include "PageGroup.h"
73 #include "PlatformMediaSessionManager.h"
74 #include "ProgressTracker.h"
75 #include "PublicSuffix.h"
76 #include "RenderLayerCompositor.h"
77 #include "RenderTheme.h"
78 #include "RenderVideo.h"
79 #include "RenderView.h"
80 #include "ResourceLoadInfo.h"
81 #include "ScriptController.h"
82 #include "ScriptDisallowedScope.h"
83 #include "ScriptSourceCode.h"
84 #include "SecurityOriginData.h"
85 #include "SecurityPolicy.h"
86 #include "Settings.h"
87 #include "ShadowRoot.h"
88 #include "TimeRanges.h"
89 #include "UserContentController.h"
90 #include "UserGestureIndicator.h"
91 #include <JavaScriptCore/Uint8Array.h>
92 #include <limits>
93 #include <pal/SessionID.h>
94 #include <pal/system/SleepDisabler.h>
95 #include <wtf/Algorithms.h>
96 #include <wtf/IsoMallocInlines.h>
97 #include <wtf/Language.h>
98 #include <wtf/MathExtras.h>
99 #include <wtf/MemoryPressureHandler.h>
100 #include <wtf/Ref.h>
101 #include <wtf/text/CString.h>
102
103 #if ENABLE(VIDEO_TRACK)
104 #include "AudioTrackList.h"
105 #include "HTMLTrackElement.h"
106 #include "InbandGenericTextTrack.h"
107 #include "InbandTextTrackPrivate.h"
108 #include "InbandWebVTTTextTrack.h"
109 #include "RuntimeEnabledFeatures.h"
110 #include "TextTrackCueList.h"
111 #include "TextTrackList.h"
112 #include "VideoTrackList.h"
113 #endif
114
115 #if ENABLE(WEB_AUDIO)
116 #include "AudioSourceProvider.h"
117 #include "MediaElementAudioSourceNode.h"
118 #endif
119
120 #if PLATFORM(IOS)
121 #include "RuntimeApplicationChecks.h"
122 #include "VideoFullscreenInterfaceAVKit.h"
123 #endif
124
125 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
126 #include "WebKitPlaybackTargetAvailabilityEvent.h"
127 #endif
128
129 #if ENABLE(MEDIA_SESSION)
130 #include "MediaSession.h"
131 #endif
132
133 #if ENABLE(MEDIA_SOURCE)
134 #include "DOMWindow.h"
135 #include "MediaSource.h"
136 #include "VideoPlaybackQuality.h"
137 #endif
138
139 #if ENABLE(MEDIA_STREAM)
140 #include "DOMURL.h"
141 #include "MediaStream.h"
142 #include "MediaStreamRegistry.h"
143 #endif
144
145 #if ENABLE(LEGACY_ENCRYPTED_MEDIA)
146 #include "WebKitMediaKeyNeededEvent.h"
147 #include "WebKitMediaKeys.h"
148 #endif
149
150 #if ENABLE(ENCRYPTED_MEDIA)
151 #include "MediaEncryptedEvent.h"
152 #include "MediaKeys.h"
153 #endif
154
155 #if ENABLE(MEDIA_CONTROLS_SCRIPT)
156 #include "JSMediaControlsHost.h"
157 #include "MediaControlsHost.h"
158 #include <JavaScriptCore/ScriptObject.h>
159 #endif
160
161 #if ENABLE(ENCRYPTED_MEDIA)
162 #include "NotImplemented.h"
163 #endif
164
165 #if PLATFORM(IOS) || (PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE))
166 #include "VideoFullscreenModel.h"
167 #endif
168
169 namespace WTF {
170 template <>
171 struct LogArgument<WebCore::URL> {
172     static String toString(const WebCore::URL& url)
173     {
174 #if !LOG_DISABLED
175         static const unsigned maximumURLLengthForLogging = 512;
176
177         if (url.string().length() < maximumURLLengthForLogging)
178             return url.string();
179         return url.string().substring(0, maximumURLLengthForLogging) + "...";
180 #else
181         UNUSED_PARAM(url);
182         return "[url]";
183 #endif
184     }
185 };
186 }
187
188
189 namespace WebCore {
190
191 WTF_MAKE_ISO_ALLOCATED_IMPL(HTMLMediaElement);
192
193 using namespace PAL;
194
195 static const Seconds SeekRepeatDelay { 100_ms };
196 static const double SeekTime = 0.2;
197 static const Seconds ScanRepeatDelay { 1.5_s };
198 static const double ScanMaximumRate = 8;
199 static const double AutoplayInterferenceTimeThreshold = 10;
200
201 static const Seconds hideMediaControlsAfterEndedDelay { 6_s };
202
203 static void setFlags(unsigned& value, unsigned flags)
204 {
205     value |= flags;
206 }
207
208 static void clearFlags(unsigned& value, unsigned flags)
209 {
210     value &= ~flags;
211 }
212
213 #if !RELEASE_LOG_DISABLED
214 static String actionName(HTMLMediaElementEnums::DelayedActionType action)
215 {
216     StringBuilder actionBuilder;
217
218 #define ACTION(_actionType) \
219     if (action & (HTMLMediaElementEnums::_actionType)) { \
220         if (!actionBuilder.isEmpty()) \
221         actionBuilder.appendLiteral(", "); \
222         actionBuilder.append(#_actionType); \
223     } \
224
225     ACTION(LoadMediaResource);
226     ACTION(ConfigureTextTracks);
227     ACTION(TextTrackChangesNotification);
228     ACTION(ConfigureTextTrackDisplay);
229     ACTION(CheckPlaybackTargetCompatablity);
230     ACTION(CheckMediaState);
231     ACTION(MediaEngineUpdated);
232     ACTION(UpdatePlayState);
233
234 #undef ACTION
235
236     String name = actionBuilder.toString();
237     ASSERT(!name.isEmpty());
238     return name;
239 }
240 #endif
241
242 #ifndef LOG_CACHED_TIME_WARNINGS
243 // Default to not logging warnings about excessive drift in the cached media time because it adds a
244 // fair amount of overhead and logging.
245 #define LOG_CACHED_TIME_WARNINGS 0
246 #endif
247
248 #if ENABLE(MEDIA_SOURCE)
249 // URL protocol used to signal that the media source API is being used.
250 static const char* mediaSourceBlobProtocol = "blob";
251 #endif
252
253 #if ENABLE(MEDIA_STREAM)
254 // URL protocol used to signal that the media stream API is being used.
255 static const char* mediaStreamBlobProtocol = "blob";
256 #endif
257
258 using namespace HTMLNames;
259
260 String convertEnumerationToString(HTMLMediaElement::ReadyState enumerationValue)
261 {
262     static const NeverDestroyed<String> values[] = {
263         MAKE_STATIC_STRING_IMPL("HAVE_NOTHING"),
264         MAKE_STATIC_STRING_IMPL("HAVE_METADATA"),
265         MAKE_STATIC_STRING_IMPL("HAVE_CURRENT_DATA"),
266         MAKE_STATIC_STRING_IMPL("HAVE_FUTURE_DATA"),
267         MAKE_STATIC_STRING_IMPL("HAVE_ENOUGH_DATA"),
268     };
269     static_assert(static_cast<size_t>(HTMLMediaElementEnums::HAVE_NOTHING) == 0, "HTMLMediaElement::HAVE_NOTHING is not 0 as expected");
270     static_assert(static_cast<size_t>(HTMLMediaElementEnums::HAVE_METADATA) == 1, "HTMLMediaElement::HAVE_METADATA is not 1 as expected");
271     static_assert(static_cast<size_t>(HTMLMediaElementEnums::HAVE_CURRENT_DATA) == 2, "HTMLMediaElement::HAVE_CURRENT_DATA is not 2 as expected");
272     static_assert(static_cast<size_t>(HTMLMediaElementEnums::HAVE_FUTURE_DATA) == 3, "HTMLMediaElement::HAVE_FUTURE_DATA is not 3 as expected");
273     static_assert(static_cast<size_t>(HTMLMediaElementEnums::HAVE_ENOUGH_DATA) == 4, "HTMLMediaElement::HAVE_ENOUGH_DATA is not 4 as expected");
274     ASSERT(static_cast<size_t>(enumerationValue) < WTF_ARRAY_LENGTH(values));
275     return values[static_cast<size_t>(enumerationValue)];
276 }
277
278 String convertEnumerationToString(HTMLMediaElement::NetworkState enumerationValue)
279 {
280     static const NeverDestroyed<String> values[] = {
281         MAKE_STATIC_STRING_IMPL("NETWORK_EMPTY"),
282         MAKE_STATIC_STRING_IMPL("NETWORK_IDLE"),
283         MAKE_STATIC_STRING_IMPL("NETWORK_LOADING"),
284         MAKE_STATIC_STRING_IMPL("NETWORK_NO_SOURCE"),
285     };
286     static_assert(static_cast<size_t>(HTMLMediaElementEnums::NETWORK_EMPTY) == 0, "HTMLMediaElementEnums::NETWORK_EMPTY is not 0 as expected");
287     static_assert(static_cast<size_t>(HTMLMediaElementEnums::NETWORK_IDLE) == 1, "HTMLMediaElementEnums::NETWORK_IDLE is not 1 as expected");
288     static_assert(static_cast<size_t>(HTMLMediaElementEnums::NETWORK_LOADING) == 2, "HTMLMediaElementEnums::NETWORK_LOADING is not 2 as expected");
289     static_assert(static_cast<size_t>(HTMLMediaElementEnums::NETWORK_NO_SOURCE) == 3, "HTMLMediaElementEnums::NETWORK_NO_SOURCE is not 3 as expected");
290     ASSERT(static_cast<size_t>(enumerationValue) < WTF_ARRAY_LENGTH(values));
291     return values[static_cast<size_t>(enumerationValue)];
292 }
293
294 String convertEnumerationToString(HTMLMediaElement::PlaybackWithoutUserGesture enumerationValue)
295 {
296     static const NeverDestroyed<String> values[] = {
297         MAKE_STATIC_STRING_IMPL("None"),
298         MAKE_STATIC_STRING_IMPL("Started"),
299         MAKE_STATIC_STRING_IMPL("Prevented"),
300         MAKE_STATIC_STRING_IMPL("NETWORK_NO_SOURCE"),
301     };
302     static_assert(static_cast<size_t>(HTMLMediaElement::PlaybackWithoutUserGesture::None) == 0, "PlaybackWithoutUserGesture::None is not 0 as expected");
303     static_assert(static_cast<size_t>(HTMLMediaElement::PlaybackWithoutUserGesture::Started) == 1, "PlaybackWithoutUserGesture::Started is not 1 as expected");
304     static_assert(static_cast<size_t>(HTMLMediaElement::PlaybackWithoutUserGesture::Prevented) == 2, "PlaybackWithoutUserGesture::Prevented is not 2 as expected");
305     ASSERT(static_cast<size_t>(enumerationValue) < WTF_ARRAY_LENGTH(values));
306     return values[static_cast<size_t>(enumerationValue)];
307 }
308
309 typedef HashMap<Document*, HashSet<HTMLMediaElement*>> DocumentElementSetMap;
310 static DocumentElementSetMap& documentToElementSetMap()
311 {
312     static NeverDestroyed<DocumentElementSetMap> map;
313     return map;
314 }
315
316 static void addElementToDocumentMap(HTMLMediaElement& element, Document& document)
317 {
318     DocumentElementSetMap& map = documentToElementSetMap();
319     HashSet<HTMLMediaElement*> set = map.take(&document);
320     set.add(&element);
321     map.add(&document, set);
322 }
323
324 static void removeElementFromDocumentMap(HTMLMediaElement& element, Document& document)
325 {
326     DocumentElementSetMap& map = documentToElementSetMap();
327     HashSet<HTMLMediaElement*> set = map.take(&document);
328     set.remove(&element);
329     if (!set.isEmpty())
330         map.add(&document, set);
331 }
332
333 #if ENABLE(VIDEO_TRACK)
334
335 class TrackDisplayUpdateScope {
336 public:
337     TrackDisplayUpdateScope(HTMLMediaElement& element)
338         : m_element(element)
339     {
340         m_element.beginIgnoringTrackDisplayUpdateRequests();
341     }
342     ~TrackDisplayUpdateScope()
343     {
344         m_element.endIgnoringTrackDisplayUpdateRequests();
345     }
346
347 private:
348     HTMLMediaElement& m_element;
349 };
350
351 #endif
352
353 struct HTMLMediaElement::TrackGroup {
354     enum GroupKind { CaptionsAndSubtitles, Description, Chapter, Metadata, Other };
355
356     TrackGroup(GroupKind kind)
357         : kind(kind)
358     {
359     }
360
361     Vector<RefPtr<TextTrack>> tracks;
362     RefPtr<TextTrack> visibleTrack;
363     RefPtr<TextTrack> defaultTrack;
364     GroupKind kind;
365     bool hasSrcLang { false };
366 };
367
368 HashSet<HTMLMediaElement*>& HTMLMediaElement::allMediaElements()
369 {
370     static NeverDestroyed<HashSet<HTMLMediaElement*>> elements;
371     return elements;
372 }
373
374 #if ENABLE(MEDIA_SESSION)
375 typedef HashMap<uint64_t, HTMLMediaElement*> IDToElementMap;
376
377 static IDToElementMap& elementIDsToElements()
378 {
379     static NeverDestroyed<IDToElementMap> map;
380     return map;
381 }
382
383 HTMLMediaElement* HTMLMediaElement::elementWithID(uint64_t id)
384 {
385     if (id == HTMLMediaElementInvalidID)
386         return nullptr;
387
388     return elementIDsToElements().get(id);
389 }
390
391 static uint64_t nextElementID()
392 {
393     static uint64_t elementID = 0;
394     return ++elementID;
395 }
396 #endif
397
398 struct MediaElementSessionInfo {
399     const MediaElementSession* session;
400     MediaElementSession::PlaybackControlsPurpose purpose;
401
402     MonotonicTime timeOfLastUserInteraction;
403     bool canShowControlsManager : 1;
404     bool isVisibleInViewportOrFullscreen : 1;
405     bool isLargeEnoughForMainContent : 1;
406     bool isPlayingAudio : 1;
407 };
408
409 static MediaElementSessionInfo mediaElementSessionInfoForSession(const MediaElementSession& session, MediaElementSession::PlaybackControlsPurpose purpose)
410 {
411     const HTMLMediaElement& element = session.element();
412     return {
413         &session,
414         purpose,
415         session.mostRecentUserInteractionTime(),
416         session.canShowControlsManager(purpose),
417         element.isFullscreen() || element.isVisibleInViewport(),
418         session.isLargeEnoughForMainContent(MediaSessionMainContentPurpose::MediaControls),
419         element.isPlaying() && element.hasAudio() && !element.muted()
420     };
421 }
422
423 static bool preferMediaControlsForCandidateSessionOverOtherCandidateSession(const MediaElementSessionInfo& session, const MediaElementSessionInfo& otherSession)
424 {
425     MediaElementSession::PlaybackControlsPurpose purpose = session.purpose;
426     ASSERT(purpose == otherSession.purpose);
427
428     // For the controls manager, prioritize visible media over offscreen media.
429     if (purpose == MediaElementSession::PlaybackControlsPurpose::ControlsManager && session.isVisibleInViewportOrFullscreen != otherSession.isVisibleInViewportOrFullscreen)
430         return session.isVisibleInViewportOrFullscreen;
431
432     // For Now Playing, prioritize elements that would normally satisfy main content.
433     if (purpose == MediaElementSession::PlaybackControlsPurpose::NowPlaying && session.isLargeEnoughForMainContent != otherSession.isLargeEnoughForMainContent)
434         return session.isLargeEnoughForMainContent;
435
436     // As a tiebreaker, prioritize elements that the user recently interacted with.
437     return session.timeOfLastUserInteraction > otherSession.timeOfLastUserInteraction;
438 }
439
440 static bool mediaSessionMayBeConfusedWithMainContent(const MediaElementSessionInfo& session, MediaElementSession::PlaybackControlsPurpose purpose)
441 {
442     if (purpose == MediaElementSession::PlaybackControlsPurpose::NowPlaying)
443         return session.isPlayingAudio;
444
445     if (!session.isVisibleInViewportOrFullscreen)
446         return false;
447
448     if (!session.isLargeEnoughForMainContent)
449         return false;
450
451     // Even if this video is not a candidate, if it is visible to the user and large enough
452     // to be main content, it poses a risk for being confused with main content.
453     return true;
454 }
455
456 #if !RELEASE_LOG_DISABLED
457 static uint64_t nextLogIdentifier()
458 {
459     static uint64_t logIdentifier = cryptographicallyRandomNumber();
460     return ++logIdentifier;
461 }
462 #endif
463
464 HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document& document, bool createdByParser)
465     : HTMLElement(tagName, document)
466     , ActiveDOMObject(&document)
467     , m_pendingActionTimer(*this, &HTMLMediaElement::pendingActionTimerFired)
468     , m_progressEventTimer(*this, &HTMLMediaElement::progressEventTimerFired)
469     , m_playbackProgressTimer(*this, &HTMLMediaElement::playbackProgressTimerFired)
470     , m_scanTimer(*this, &HTMLMediaElement::scanTimerFired)
471     , m_playbackControlsManagerBehaviorRestrictionsTimer(*this, &HTMLMediaElement::playbackControlsManagerBehaviorRestrictionsTimerFired)
472     , m_seekToPlaybackPositionEndedTimer(*this, &HTMLMediaElement::seekToPlaybackPositionEndedTimerFired)
473     , m_asyncEventQueue(*this)
474     , m_lastTimeUpdateEventMovieTime(MediaTime::positiveInfiniteTime())
475     , m_firstTimePlaying(true)
476     , m_playing(false)
477     , m_isWaitingUntilMediaCanStart(false)
478     , m_shouldDelayLoadEvent(false)
479     , m_haveFiredLoadedData(false)
480     , m_inActiveDocument(true)
481     , m_autoplaying(true)
482     , m_muted(false)
483     , m_explicitlyMuted(false)
484     , m_initiallyMuted(false)
485     , m_paused(true)
486     , m_seeking(false)
487     , m_seekRequested(false)
488     , m_sentStalledEvent(false)
489     , m_sentEndEvent(false)
490     , m_pausedInternal(false)
491     , m_closedCaptionsVisible(false)
492     , m_webkitLegacyClosedCaptionOverride(false)
493     , m_completelyLoaded(false)
494     , m_havePreparedToPlay(false)
495     , m_parsingInProgress(createdByParser)
496     , m_shouldBufferData(true)
497     , m_elementIsHidden(document.hidden())
498     , m_creatingControls(false)
499     , m_receivedLayoutSizeChanged(false)
500     , m_hasEverNotifiedAboutPlaying(false)
501     , m_hasEverHadAudio(false)
502     , m_hasEverHadVideo(false)
503 #if ENABLE(MEDIA_CONTROLS_SCRIPT)
504     , m_mediaControlsDependOnPageScaleFactor(false)
505     , m_haveSetUpCaptionContainer(false)
506 #endif
507     , m_isScrubbingRemotely(false)
508     , m_shouldUnpauseInternalOnResume(false)
509 #if ENABLE(VIDEO_TRACK)
510     , m_tracksAreReady(true)
511     , m_haveVisibleTextTrack(false)
512     , m_processingPreferenceChange(false)
513 #endif
514 #if !RELEASE_LOG_DISABLED
515     , m_logger(&document.logger())
516     , m_logIdentifier(nextLogIdentifier())
517 #endif
518 {
519     allMediaElements().add(this);
520
521     ALWAYS_LOG(LOGIDENTIFIER);
522
523     setHasCustomStyleResolveCallbacks();
524 }
525
526 void HTMLMediaElement::finishInitialization()
527 {
528     m_mediaSession = std::make_unique<MediaElementSession>(*this);
529
530     m_mediaSession->addBehaviorRestriction(MediaElementSession::RequireUserGestureForFullscreen);
531     m_mediaSession->addBehaviorRestriction(MediaElementSession::RequirePageConsentToLoadMedia);
532 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
533     m_mediaSession->addBehaviorRestriction(MediaElementSession::RequireUserGestureToAutoplayToExternalDevice);
534 #endif
535     m_mediaSession->addBehaviorRestriction(MediaElementSession::RequireUserGestureToControlControlsManager);
536     m_mediaSession->addBehaviorRestriction(MediaElementSession::RequirePlaybackToControlControlsManager);
537
538     auto& document = this->document();
539     auto* page = document.page();
540
541     if (document.settings().invisibleAutoplayNotPermitted())
542         m_mediaSession->addBehaviorRestriction(MediaElementSession::InvisibleAutoplayNotPermitted);
543
544     if (document.ownerElement() || !document.isMediaDocument()) {
545         const auto& topDocument = document.topDocument();
546         const bool isProcessingUserGesture = processingUserGestureForMedia();
547         const bool shouldAudioPlaybackRequireUserGesture = topDocument.audioPlaybackRequiresUserGesture() && !isProcessingUserGesture;
548         const bool shouldVideoPlaybackRequireUserGesture = topDocument.videoPlaybackRequiresUserGesture() && !isProcessingUserGesture;
549
550         if (shouldVideoPlaybackRequireUserGesture) {
551             m_mediaSession->addBehaviorRestriction(MediaElementSession::RequireUserGestureForVideoRateChange);
552             if (document.settings().requiresUserGestureToLoadVideo())
553                 m_mediaSession->addBehaviorRestriction(MediaElementSession::RequireUserGestureForLoad);
554         }
555
556         if (page && page->isLowPowerModeEnabled())
557             m_mediaSession->addBehaviorRestriction(MediaElementSession::RequireUserGestureForVideoDueToLowPowerMode);
558
559         if (shouldAudioPlaybackRequireUserGesture)
560             m_mediaSession->addBehaviorRestriction(MediaElementSession::RequireUserGestureForAudioRateChange);
561
562 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
563         if (shouldVideoPlaybackRequireUserGesture || shouldAudioPlaybackRequireUserGesture)
564             m_mediaSession->addBehaviorRestriction(MediaElementSession::RequireUserGestureToShowPlaybackTargetPicker);
565 #endif
566
567         if (!document.settings().mediaDataLoadsAutomatically())
568             m_mediaSession->addBehaviorRestriction(MediaElementSession::AutoPreloadingNotPermitted);
569
570         if (document.settings().mainContentUserGestureOverrideEnabled())
571             m_mediaSession->addBehaviorRestriction(MediaElementSession::OverrideUserGestureRequirementForMainContent);
572     }
573
574 #if PLATFORM(IOS)
575     if (!document.settings().videoPlaybackRequiresUserGesture() && !document.settings().audioPlaybackRequiresUserGesture()) {
576         // Relax RequireUserGestureForFullscreen when videoPlaybackRequiresUserGesture and audioPlaybackRequiresUserGesture is not set:
577         m_mediaSession->removeBehaviorRestriction(MediaElementSession::RequireUserGestureForFullscreen);
578     }
579 #endif
580
581 #if ENABLE(MEDIA_SESSION)
582     m_elementID = nextElementID();
583     elementIDsToElements().add(m_elementID, this);
584
585     setSessionInternal(document.defaultMediaSession());
586 #endif
587
588     registerWithDocument(document);
589
590 #if USE(AUDIO_SESSION) && PLATFORM(MAC)
591     AudioSession::sharedSession().addMutedStateObserver(this);
592 #endif
593
594     mediaSession().clientWillBeginAutoplaying();
595 }
596
597 // FIXME: Remove this code once https://webkit.org/b/185284 is fixed.
598 static unsigned s_destructorCount = 0;
599
600 bool HTMLMediaElement::isRunningDestructor()
601 {
602     return !!s_destructorCount;
603 }
604
605 class HTMLMediaElementDestructorScope {
606 public:
607     HTMLMediaElementDestructorScope() { ++s_destructorCount; }
608     ~HTMLMediaElementDestructorScope() { --s_destructorCount; }
609 };
610
611 HTMLMediaElement::~HTMLMediaElement()
612 {
613     HTMLMediaElementDestructorScope destructorScope;
614     ALWAYS_LOG(LOGIDENTIFIER);
615
616     beginIgnoringTrackDisplayUpdateRequests();
617     allMediaElements().remove(this);
618
619     m_asyncEventQueue.close();
620
621     setShouldDelayLoadEvent(false);
622     unregisterWithDocument(document());
623
624 #if USE(AUDIO_SESSION) && PLATFORM(MAC)
625     AudioSession::sharedSession().removeMutedStateObserver(this);
626 #endif
627
628 #if ENABLE(VIDEO_TRACK)
629     if (m_audioTracks)
630         m_audioTracks->clearElement();
631     if (m_textTracks)
632         m_textTracks->clearElement();
633     if (m_videoTracks)
634         m_videoTracks->clearElement();
635 #endif
636
637 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
638     if (hasEventListeners(eventNames().webkitplaybacktargetavailabilitychangedEvent)) {
639         m_hasPlaybackTargetAvailabilityListeners = false;
640         m_mediaSession->setHasPlaybackTargetAvailabilityListeners(false);
641         updateMediaState();
642     }
643 #endif
644
645     if (m_mediaController) {
646         m_mediaController->removeMediaElement(*this);
647         m_mediaController = nullptr;
648     }
649
650 #if ENABLE(MEDIA_SOURCE)
651     detachMediaSource();
652 #endif
653
654 #if ENABLE(LEGACY_ENCRYPTED_MEDIA)
655     webkitSetMediaKeys(nullptr);
656 #endif
657
658 #if ENABLE(ENCRYPTED_MEDIA)
659     if (m_mediaKeys) {
660         m_mediaKeys->detachCDMClient(*this);
661         if (m_player)
662             m_player->cdmInstanceDetached(m_mediaKeys->cdmInstance());
663     }
664 #endif
665
666 #if ENABLE(MEDIA_CONTROLS_SCRIPT)
667     if (m_isolatedWorld)
668         m_isolatedWorld->clearWrappers();
669 #endif
670
671 #if ENABLE(MEDIA_SESSION)
672     if (m_session) {
673         m_session->removeMediaElement(*this);
674         m_session = nullptr;
675     }
676
677     elementIDsToElements().remove(m_elementID);
678 #endif
679
680     m_seekTaskQueue.close();
681     m_resumeTaskQueue.close();
682     m_promiseTaskQueue.close();
683     m_pauseAfterDetachedTaskQueue.close();
684     m_playbackControlsManagerBehaviorRestrictionsQueue.close();
685     m_resourceSelectionTaskQueue.close();
686     m_visibilityChangeTaskQueue.close();
687 #if ENABLE(ENCRYPTED_MEDIA)
688     m_encryptedMediaQueue.close();
689 #endif
690
691     m_completelyLoaded = true;
692
693     if (m_player) {
694         m_player->invalidate();
695         m_player = nullptr;
696     }
697
698     m_mediaSession = nullptr;
699     schedulePlaybackControlsManagerUpdate();
700 }
701
702 static bool needsAutoplayPlayPauseEventsQuirk(const Document& document)
703 {
704     auto* page = document.page();
705     if (!page || !page->settings().needsSiteSpecificQuirks())
706         return false;
707
708     auto loader = makeRefPtr(document.loader());
709     return loader && loader->allowedAutoplayQuirks().contains(AutoplayQuirk::SynthesizedPauseEvents);
710 }
711
712 RefPtr<HTMLMediaElement> HTMLMediaElement::bestMediaElementForShowingPlaybackControlsManager(MediaElementSession::PlaybackControlsPurpose purpose)
713 {
714     auto allSessions = PlatformMediaSessionManager::sharedManager().currentSessionsMatching([] (const PlatformMediaSession& session) {
715         return is<MediaElementSession>(session);
716     });
717
718     Vector<MediaElementSessionInfo> candidateSessions;
719     bool atLeastOneNonCandidateMayBeConfusedForMainContent = false;
720     for (auto& session : allSessions) {
721         auto mediaElementSessionInfo = mediaElementSessionInfoForSession(downcast<MediaElementSession>(*session), purpose);
722         if (mediaElementSessionInfo.canShowControlsManager)
723             candidateSessions.append(mediaElementSessionInfo);
724         else if (mediaSessionMayBeConfusedWithMainContent(mediaElementSessionInfo, purpose))
725             atLeastOneNonCandidateMayBeConfusedForMainContent = true;
726     }
727
728     if (!candidateSessions.size())
729         return nullptr;
730
731     std::sort(candidateSessions.begin(), candidateSessions.end(), preferMediaControlsForCandidateSessionOverOtherCandidateSession);
732     auto strongestSessionCandidate = candidateSessions.first();
733     if (!strongestSessionCandidate.isVisibleInViewportOrFullscreen && !strongestSessionCandidate.isPlayingAudio && atLeastOneNonCandidateMayBeConfusedForMainContent)
734         return nullptr;
735
736     return &strongestSessionCandidate.session->element();
737 }
738
739 void HTMLMediaElement::registerWithDocument(Document& document)
740 {
741     m_mediaSession->registerWithDocument(document);
742
743     if (m_isWaitingUntilMediaCanStart)
744         document.addMediaCanStartListener(this);
745
746 #if !PLATFORM(IOS)
747     document.registerForMediaVolumeCallbacks(this);
748     document.registerForPrivateBrowsingStateChangedCallbacks(this);
749 #endif
750
751     document.registerForVisibilityStateChangedCallbacks(this);
752
753 #if ENABLE(VIDEO_TRACK)
754     if (m_requireCaptionPreferencesChangedCallbacks)
755         document.registerForCaptionPreferencesChangedCallbacks(this);
756 #endif
757
758 #if ENABLE(MEDIA_CONTROLS_SCRIPT)
759     if (m_mediaControlsDependOnPageScaleFactor)
760         document.registerForPageScaleFactorChangedCallbacks(this);
761     document.registerForUserInterfaceLayoutDirectionChangedCallbacks(*this);
762 #endif
763
764 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
765     document.registerForDocumentSuspensionCallbacks(this);
766 #endif
767
768     document.registerForAllowsMediaDocumentInlinePlaybackChangedCallbacks(*this);
769
770     document.addAudioProducer(this);
771     addElementToDocumentMap(*this, document);
772
773 #if ENABLE(MEDIA_STREAM)
774     document.registerForMediaStreamStateChangeCallbacks(*this);
775 #endif
776
777     document.addApplicationStateChangeListener(*this);
778 }
779
780 void HTMLMediaElement::unregisterWithDocument(Document& document)
781 {
782     m_mediaSession->unregisterWithDocument(document);
783
784     if (m_isWaitingUntilMediaCanStart)
785         document.removeMediaCanStartListener(this);
786
787 #if !PLATFORM(IOS)
788     document.unregisterForMediaVolumeCallbacks(this);
789     document.unregisterForPrivateBrowsingStateChangedCallbacks(this);
790 #endif
791
792     document.unregisterForVisibilityStateChangedCallbacks(this);
793
794 #if ENABLE(VIDEO_TRACK)
795     if (m_requireCaptionPreferencesChangedCallbacks)
796         document.unregisterForCaptionPreferencesChangedCallbacks(this);
797 #endif
798
799 #if ENABLE(MEDIA_CONTROLS_SCRIPT)
800     if (m_mediaControlsDependOnPageScaleFactor)
801         document.unregisterForPageScaleFactorChangedCallbacks(this);
802     document.unregisterForUserInterfaceLayoutDirectionChangedCallbacks(*this);
803 #endif
804
805 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
806     document.unregisterForDocumentSuspensionCallbacks(this);
807 #endif
808
809     document.unregisterForAllowsMediaDocumentInlinePlaybackChangedCallbacks(*this);
810
811     document.removeAudioProducer(this);
812     removeElementFromDocumentMap(*this, document);
813
814 #if ENABLE(MEDIA_STREAM)
815     document.unregisterForMediaStreamStateChangeCallbacks(*this);
816 #endif
817
818     document.removeApplicationStateChangeListener(*this);
819 }
820
821 void HTMLMediaElement::didMoveToNewDocument(Document& oldDocument, Document& newDocument)
822 {
823     ASSERT_WITH_SECURITY_IMPLICATION(&document() == &newDocument);
824     if (m_shouldDelayLoadEvent) {
825         oldDocument.decrementLoadEventDelayCount();
826         newDocument.incrementLoadEventDelayCount();
827     }
828
829     unregisterWithDocument(oldDocument);
830     registerWithDocument(newDocument);
831
832     HTMLElement::didMoveToNewDocument(oldDocument, newDocument);
833     updateShouldAutoplay();
834 }
835
836 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
837 void HTMLMediaElement::prepareForDocumentSuspension()
838 {
839     m_mediaSession->unregisterWithDocument(document());
840 }
841
842 void HTMLMediaElement::resumeFromDocumentSuspension()
843 {
844     m_mediaSession->registerWithDocument(document());
845     updateShouldAutoplay();
846 }
847 #endif
848
849 bool HTMLMediaElement::supportsFocus() const
850 {
851     if (document().isMediaDocument())
852         return false;
853
854     // If no controls specified, we should still be able to focus the element if it has tabIndex.
855     return controls() ||  HTMLElement::supportsFocus();
856 }
857
858 bool HTMLMediaElement::isMouseFocusable() const
859 {
860     return false;
861 }
862
863 void HTMLMediaElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
864 {
865     if (name == srcAttr) {
866         // https://html.spec.whatwg.org/multipage/embedded-content.html#location-of-the-media-resource
867         // Location of the Media Resource
868         // 12 February 2017
869
870         // If a src attribute of a media element is set or changed, the user
871         // agent must invoke the media element's media element load algorithm.
872         if (!value.isNull())
873             prepareForLoad();
874     } else if (name == controlsAttr)
875         configureMediaControls();
876     else if (name == loopAttr)
877         updateSleepDisabling();
878     else if (name == preloadAttr) {
879         if (equalLettersIgnoringASCIICase(value, "none"))
880             m_preload = MediaPlayer::None;
881         else if (equalLettersIgnoringASCIICase(value, "metadata"))
882             m_preload = MediaPlayer::MetaData;
883         else {
884             // The spec does not define an "invalid value default" but "auto" is suggested as the
885             // "missing value default", so use it for everything except "none" and "metadata"
886             m_preload = MediaPlayer::Auto;
887         }
888
889         // The attribute must be ignored if the autoplay attribute is present
890         if (!autoplay() && !m_havePreparedToPlay && m_player)
891             m_player->setPreload(m_mediaSession->effectivePreloadForElement());
892
893     } else if (name == mediagroupAttr)
894         setMediaGroup(value);
895     else if (name == autoplayAttr) {
896         if (processingUserGestureForMedia())
897             removeBehaviorsRestrictionsAfterFirstUserGesture();
898     } else if (name == titleAttr) {
899         if (m_mediaSession)
900             m_mediaSession->clientCharacteristicsChanged();
901     }
902     else
903         HTMLElement::parseAttribute(name, value);
904 }
905
906 void HTMLMediaElement::finishParsingChildren()
907 {
908     HTMLElement::finishParsingChildren();
909     m_parsingInProgress = false;
910
911 #if ENABLE(VIDEO_TRACK)
912     if (childrenOfType<HTMLTrackElement>(*this).first())
913         scheduleDelayedAction(ConfigureTextTracks);
914 #endif
915 }
916
917 bool HTMLMediaElement::rendererIsNeeded(const RenderStyle& style)
918 {
919     return controls() && HTMLElement::rendererIsNeeded(style);
920 }
921
922 RenderPtr<RenderElement> HTMLMediaElement::createElementRenderer(RenderStyle&& style, const RenderTreePosition&)
923 {
924     return createRenderer<RenderMedia>(*this, WTFMove(style));
925 }
926
927 bool HTMLMediaElement::childShouldCreateRenderer(const Node& child) const
928 {
929 #if ENABLE(MEDIA_CONTROLS_SCRIPT)
930     return hasShadowRootParent(child) && HTMLElement::childShouldCreateRenderer(child);
931 #else
932     if (!hasMediaControls())
933         return false;
934     // <media> doesn't allow its content, including shadow subtree, to
935     // be rendered. So this should return false for most of the children.
936     // One exception is a shadow tree built for rendering controls which should be visible.
937     // So we let them go here by comparing its subtree root with one of the controls.
938     return &mediaControls()->treeScope() == &child.treeScope()
939         && hasShadowRootParent(child)
940         && HTMLElement::childShouldCreateRenderer(child);
941 #endif
942 }
943
944 Node::InsertedIntoAncestorResult HTMLMediaElement::insertedIntoAncestor(InsertionType insertionType, ContainerNode& parentOfInsertedTree)
945 {
946     INFO_LOG(LOGIDENTIFIER);
947
948     HTMLElement::insertedIntoAncestor(insertionType, parentOfInsertedTree);
949     if (insertionType.connectedToDocument)
950         setInActiveDocument(true);
951
952     return InsertedIntoAncestorResult::NeedsPostInsertionCallback;
953 }
954
955 void HTMLMediaElement::didFinishInsertingNode()
956 {
957     if (m_inActiveDocument && m_networkState == NETWORK_EMPTY && !attributeWithoutSynchronization(srcAttr).isEmpty())
958         prepareForLoad();
959
960     if (!m_explicitlyMuted) {
961         m_explicitlyMuted = true;
962         m_muted = hasAttributeWithoutSynchronization(mutedAttr);
963         m_mediaSession->canProduceAudioChanged();
964     }
965
966     configureMediaControls();
967 }
968
969 void HTMLMediaElement::pauseAfterDetachedTask()
970 {
971     // If we were re-inserted into an active document, no need to pause.
972     if (m_inActiveDocument)
973         return;
974
975     if (hasMediaControls())
976         mediaControls()->hide();
977     if (m_networkState > NETWORK_EMPTY)
978         pause();
979     if (m_videoFullscreenMode != VideoFullscreenModeNone)
980         exitFullscreen();
981
982     if (!m_player)
983         return;
984
985     size_t extraMemoryCost = m_player->extraMemoryCost();
986     if (extraMemoryCost > m_reportedExtraMemoryCost) {
987         JSC::VM& vm = commonVM();
988         JSC::JSLockHolder lock(vm);
989
990         size_t extraMemoryCostDelta = extraMemoryCost - m_reportedExtraMemoryCost;
991         m_reportedExtraMemoryCost = extraMemoryCost;
992         // FIXME: Adopt reportExtraMemoryVisited, and switch to reportExtraMemoryAllocated.
993         // https://bugs.webkit.org/show_bug.cgi?id=142595
994         vm.heap.deprecatedReportExtraMemory(extraMemoryCostDelta);
995     }
996 }
997
998 void HTMLMediaElement::removedFromAncestor(RemovalType removalType, ContainerNode& oldParentOfRemovedTree)
999 {
1000     INFO_LOG(LOGIDENTIFIER);
1001
1002     setInActiveDocument(false);
1003     if (removalType.disconnectedFromDocument) {
1004         // Pause asynchronously to let the operation that removed us finish, in case we get inserted back into a document.
1005         m_pauseAfterDetachedTaskQueue.enqueueTask(std::bind(&HTMLMediaElement::pauseAfterDetachedTask, this));
1006     }
1007
1008     if (m_mediaSession)
1009         m_mediaSession->clientCharacteristicsChanged();
1010
1011     HTMLElement::removedFromAncestor(removalType, oldParentOfRemovedTree);
1012 }
1013
1014 void HTMLMediaElement::willAttachRenderers()
1015 {
1016     ASSERT(!renderer());
1017 }
1018
1019 inline void HTMLMediaElement::updateRenderer()
1020 {
1021     if (auto* renderer = this->renderer())
1022         renderer->updateFromElement();
1023 }
1024
1025 void HTMLMediaElement::didAttachRenderers()
1026 {
1027     if (auto* renderer = this->renderer()) {
1028         renderer->updateFromElement();
1029         if (m_mediaSession && m_mediaSession->wantsToObserveViewportVisibilityForAutoplay())
1030             renderer->registerForVisibleInViewportCallback();
1031     }
1032     updateShouldAutoplay();
1033 }
1034
1035 void HTMLMediaElement::willDetachRenderers()
1036 {
1037     if (auto* renderer = this->renderer())
1038         renderer->unregisterForVisibleInViewportCallback();
1039 }
1040
1041 void HTMLMediaElement::didDetachRenderers()
1042 {
1043     updateShouldAutoplay();
1044 }
1045
1046 void HTMLMediaElement::didRecalcStyle(Style::Change)
1047 {
1048     updateRenderer();
1049 }
1050
1051 void HTMLMediaElement::scheduleDelayedAction(DelayedActionType actionType)
1052 {
1053     if (!(actionType & m_pendingActionFlags))
1054         ALWAYS_LOG(LOGIDENTIFIER, "setting ", actionName(actionType), " flag");
1055
1056 #if ENABLE(VIDEO_TRACK)
1057     if (actionType & ConfigureTextTracks)
1058         setFlags(m_pendingActionFlags, ConfigureTextTracks);
1059 #endif
1060
1061 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
1062     if (actionType & CheckPlaybackTargetCompatablity)
1063         setFlags(m_pendingActionFlags, CheckPlaybackTargetCompatablity);
1064 #endif
1065
1066     if (actionType & CheckMediaState)
1067         setFlags(m_pendingActionFlags, CheckMediaState);
1068
1069     if (actionType & MediaEngineUpdated)
1070         setFlags(m_pendingActionFlags, MediaEngineUpdated);
1071
1072     if (actionType & UpdatePlayState)
1073         setFlags(m_pendingActionFlags, UpdatePlayState);
1074
1075     m_pendingActionTimer.startOneShot(0_s);
1076 }
1077
1078 void HTMLMediaElement::scheduleNextSourceChild()
1079 {
1080     // Schedule the timer to try the next <source> element WITHOUT resetting state ala prepareForLoad.
1081     m_resourceSelectionTaskQueue.enqueueTask([this] {
1082         loadNextSourceChild();
1083     });
1084 }
1085
1086 void HTMLMediaElement::mediaPlayerActiveSourceBuffersChanged(const MediaPlayer*)
1087 {
1088     m_hasEverHadAudio |= hasAudio();
1089     m_hasEverHadVideo |= hasVideo();
1090 }
1091
1092 void HTMLMediaElement::scheduleEvent(const AtomicString& eventName)
1093 {
1094     RefPtr<Event> event = Event::create(eventName, false, true);
1095
1096     // Don't set the event target, the event queue will set it in GenericEventQueue::timerFired and setting it here
1097     // will trigger an ASSERT if this element has been marked for deletion.
1098
1099     m_asyncEventQueue.enqueueEvent(WTFMove(event));
1100 }
1101
1102 void HTMLMediaElement::scheduleResolvePendingPlayPromises()
1103 {
1104     m_promiseTaskQueue.enqueueTask([this, pendingPlayPromises = WTFMove(m_pendingPlayPromises)] () mutable {
1105         resolvePendingPlayPromises(WTFMove(pendingPlayPromises));
1106     });
1107 }
1108
1109 void HTMLMediaElement::scheduleRejectPendingPlayPromises(Ref<DOMException>&& error)
1110 {
1111     m_promiseTaskQueue.enqueueTask([this, error = WTFMove(error), pendingPlayPromises = WTFMove(m_pendingPlayPromises)] () mutable {
1112         rejectPendingPlayPromises(WTFMove(pendingPlayPromises), WTFMove(error));
1113     });
1114 }
1115
1116 void HTMLMediaElement::rejectPendingPlayPromises(PlayPromiseVector&& pendingPlayPromises, Ref<DOMException>&& error)
1117 {
1118     for (auto& promise : pendingPlayPromises)
1119         promise.rejectType<IDLInterface<DOMException>>(error);
1120 }
1121
1122 void HTMLMediaElement::resolvePendingPlayPromises(PlayPromiseVector&& pendingPlayPromises)
1123 {
1124     for (auto& promise : pendingPlayPromises)
1125         promise.resolve();
1126 }
1127
1128 void HTMLMediaElement::scheduleNotifyAboutPlaying()
1129 {
1130     m_promiseTaskQueue.enqueueTask([this, pendingPlayPromises = WTFMove(m_pendingPlayPromises)] () mutable {
1131         notifyAboutPlaying(WTFMove(pendingPlayPromises));
1132     });
1133 }
1134
1135 void HTMLMediaElement::notifyAboutPlaying(PlayPromiseVector&& pendingPlayPromises)
1136 {
1137     Ref<HTMLMediaElement> protectedThis(*this); // The 'playing' event can make arbitrary DOM mutations.
1138     m_playbackStartedTime = currentMediaTime().toDouble();
1139     m_hasEverNotifiedAboutPlaying = true;
1140     dispatchEvent(Event::create(eventNames().playingEvent, false, true));
1141     resolvePendingPlayPromises(WTFMove(pendingPlayPromises));
1142
1143     schedulePlaybackControlsManagerUpdate();
1144 }
1145
1146 bool HTMLMediaElement::hasEverNotifiedAboutPlaying() const
1147 {
1148     return m_hasEverNotifiedAboutPlaying;
1149 }
1150
1151 void HTMLMediaElement::pendingActionTimerFired()
1152 {
1153     Ref<HTMLMediaElement> protectedThis(*this); // loadNextSourceChild may fire 'beforeload', which can make arbitrary DOM mutations.
1154     PendingActionFlags pendingActions = m_pendingActionFlags;
1155     m_pendingActionFlags = 0;
1156
1157     if (!pendingActions)
1158         return;
1159
1160     ALWAYS_LOG(LOGIDENTIFIER, "processing ", actionName(static_cast<DelayedActionType>(pendingActions)), " flag");
1161
1162 #if ENABLE(VIDEO_TRACK)
1163     if (pendingActions & ConfigureTextTracks)
1164         configureTextTracks();
1165 #endif
1166
1167 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
1168     if (pendingActions & CheckPlaybackTargetCompatablity && m_isPlayingToWirelessTarget && !m_player->canPlayToWirelessPlaybackTarget()) {
1169         INFO_LOG(LOGIDENTIFIER, "calling setShouldPlayToPlaybackTarget(false)");
1170         m_failedToPlayToWirelessTarget = true;
1171         m_player->setShouldPlayToPlaybackTarget(false);
1172     }
1173
1174     if (pendingActions & CheckMediaState)
1175         updateMediaState();
1176 #endif
1177
1178     if (pendingActions & MediaEngineUpdated)
1179         mediaEngineWasUpdated();
1180
1181     if (pendingActions & UpdatePlayState)
1182         updatePlayState();
1183 }
1184
1185 MediaError* HTMLMediaElement::error() const
1186 {
1187     return m_error.get();
1188 }
1189
1190 void HTMLMediaElement::setSrcObject(MediaProvider&& mediaProvider)
1191 {
1192     // FIXME: Setting the srcObject attribute may cause other changes to the media element's internal state:
1193     // Specifically, if srcObject is specified, the UA must use it as the source of media, even if the src
1194     // attribute is also set or children are present. If the value of srcObject is replaced or set to null
1195     // the UA must re-run the media element load algorithm.
1196     //
1197     // https://bugs.webkit.org/show_bug.cgi?id=124896
1198
1199
1200     // https://www.w3.org/TR/html51/semantics-embedded-content.html#dom-htmlmediaelement-srcobject
1201     // 4.7.14.2. Location of the media resource
1202     // srcObject: On setting, it must set the element’s assigned media provider object to the new
1203     // value, and then invoke the element’s media element load algorithm.
1204     m_mediaProvider = WTFMove(mediaProvider);
1205     prepareForLoad();
1206 }
1207
1208 void HTMLMediaElement::setCrossOrigin(const AtomicString& value)
1209 {
1210     setAttributeWithoutSynchronization(crossoriginAttr, value);
1211 }
1212
1213 String HTMLMediaElement::crossOrigin() const
1214 {
1215     return parseCORSSettingsAttribute(attributeWithoutSynchronization(crossoriginAttr));
1216 }
1217
1218 HTMLMediaElement::NetworkState HTMLMediaElement::networkState() const
1219 {
1220     return m_networkState;
1221 }
1222
1223 String HTMLMediaElement::canPlayType(const String& mimeType) const
1224 {
1225     MediaEngineSupportParameters parameters;
1226     ContentType contentType(mimeType);
1227     parameters.type = contentType;
1228     parameters.contentTypesRequiringHardwareSupport = mediaContentTypesRequiringHardwareSupport();
1229     MediaPlayer::SupportsType support = MediaPlayer::supportsType(parameters);
1230     String canPlay;
1231
1232     // 4.8.10.3
1233     switch (support)
1234     {
1235         case MediaPlayer::IsNotSupported:
1236             canPlay = emptyString();
1237             break;
1238         case MediaPlayer::MayBeSupported:
1239             canPlay = "maybe"_s;
1240             break;
1241         case MediaPlayer::IsSupported:
1242             canPlay = "probably"_s;
1243             break;
1244     }
1245
1246     DEBUG_LOG(LOGIDENTIFIER, "[", mimeType, "] -> ", canPlay);
1247
1248     return canPlay;
1249 }
1250
1251 double HTMLMediaElement::getStartDate() const
1252 {
1253     if (!m_player)
1254         return std::numeric_limits<double>::quiet_NaN();
1255     return m_player->getStartDate().toDouble();
1256 }
1257
1258 void HTMLMediaElement::load()
1259 {
1260     Ref<HTMLMediaElement> protectedThis(*this); // prepareForLoad may result in a 'beforeload' event, which can make arbitrary DOM mutations.
1261
1262     INFO_LOG(LOGIDENTIFIER);
1263
1264     if (processingUserGestureForMedia())
1265         removeBehaviorsRestrictionsAfterFirstUserGesture();
1266
1267     prepareForLoad();
1268     m_resourceSelectionTaskQueue.enqueueTask([this] {
1269         prepareToPlay();
1270     });
1271 }
1272
1273 void HTMLMediaElement::prepareForLoad()
1274 {
1275     // https://html.spec.whatwg.org/multipage/embedded-content.html#media-element-load-algorithm
1276     // The Media Element Load Algorithm
1277     // 12 February 2017
1278
1279     INFO_LOG(LOGIDENTIFIER);
1280
1281     // 1 - Abort any already-running instance of the resource selection algorithm for this element.
1282     // Perform the cleanup required for the resource load algorithm to run.
1283     stopPeriodicTimers();
1284     m_pendingActionTimer.stop();
1285     m_resourceSelectionTaskQueue.cancelAllTasks();
1286     // FIXME: Figure out appropriate place to reset LoadTextTrackResource if necessary and set m_pendingActionFlags to 0 here.
1287     m_sentEndEvent = false;
1288     m_sentStalledEvent = false;
1289     m_haveFiredLoadedData = false;
1290     m_completelyLoaded = false;
1291     m_havePreparedToPlay = false;
1292     m_displayMode = Unknown;
1293     m_currentSrc = URL();
1294
1295 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
1296     m_failedToPlayToWirelessTarget = false;
1297 #endif
1298
1299     m_loadState = WaitingForSource;
1300     m_currentSourceNode = nullptr;
1301
1302     if (!document().hasBrowsingContext())
1303         return;
1304
1305     createMediaPlayer();
1306
1307     // 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.
1308     // 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.
1309     // 4 - Remove each task in pending tasks from its task queue
1310     cancelPendingEventsAndCallbacks();
1311
1312     // 5 - If the media element's networkState is set to NETWORK_LOADING or NETWORK_IDLE, queue
1313     // a task to fire a simple event named abort at the media element.
1314     if (m_networkState == NETWORK_LOADING || m_networkState == NETWORK_IDLE)
1315         scheduleEvent(eventNames().abortEvent);
1316
1317     // 6 - If the media element's networkState is not set to NETWORK_EMPTY, then run these substeps
1318     if (m_networkState != NETWORK_EMPTY) {
1319         // 6.1 - Queue a task to fire a simple event named emptied at the media element.
1320         scheduleEvent(eventNames().emptiedEvent);
1321
1322         // 6.2 - If a fetching process is in progress for the media element, the user agent should stop it.
1323         m_networkState = NETWORK_EMPTY;
1324
1325         // 6.3 - If the media element’s assigned media provider object is a MediaSource object, then detach it.
1326 #if ENABLE(MEDIA_SOURCE)
1327         detachMediaSource();
1328 #endif
1329
1330         // 6.4 - Forget the media element's media-resource-specific tracks.
1331         forgetResourceSpecificTracks();
1332
1333         // 6.5 - If readyState is not set to HAVE_NOTHING, then set it to that state.
1334         m_readyState = HAVE_NOTHING;
1335         m_readyStateMaximum = HAVE_NOTHING;
1336
1337         // 6.6 - If the paused attribute is false, then set it to true.
1338         m_paused = true;
1339
1340         // 6.7 - If seeking is true, set it to false.
1341         clearSeeking();
1342
1343         // 6.8 - Set the current playback position to 0.
1344         //       Set the official playback position to 0.
1345         //       If this changed the official playback position, then queue a task to fire a simple event named timeupdate at the media element.
1346         m_lastSeekTime = MediaTime::zeroTime();
1347         m_playedTimeRanges = TimeRanges::create();
1348         // FIXME: Add support for firing this event. e.g., scheduleEvent(eventNames().timeUpdateEvent);
1349
1350         // 4.9 - Set the initial playback position to 0.
1351         // FIXME: Make this less subtle. The position only becomes 0 because of the createMediaPlayer() call
1352         // above.
1353         refreshCachedTime();
1354
1355         invalidateCachedTime();
1356
1357         // 4.10 - Set the timeline offset to Not-a-Number (NaN).
1358         // 4.11 - Update the duration attribute to Not-a-Number (NaN).
1359
1360         updateMediaController();
1361 #if ENABLE(VIDEO_TRACK)
1362         updateActiveTextTrackCues(MediaTime::zeroTime());
1363 #endif
1364     }
1365
1366     // 7 - Set the playbackRate attribute to the value of the defaultPlaybackRate attribute.
1367     setPlaybackRate(defaultPlaybackRate());
1368
1369     // 8 - Set the error attribute to null and the autoplaying flag to true.
1370     m_error = nullptr;
1371     m_autoplaying = true;
1372     mediaSession().clientWillBeginAutoplaying();
1373
1374     if (!MediaPlayer::isAvailable())
1375         noneSupported();
1376     else {
1377         // 9 - Invoke the media element's resource selection algorithm.
1378         // Note, unless the restriction on requiring user action has been removed,
1379         // do not begin downloading data.
1380         if (m_mediaSession->dataLoadingPermitted())
1381             selectMediaResource();
1382     }
1383
1384     // 10 - Note: Playback of any previously playing media resource for this element stops.
1385
1386     configureMediaControls();
1387 }
1388
1389 void HTMLMediaElement::selectMediaResource()
1390 {
1391     // https://www.w3.org/TR/2016/REC-html51-20161101/semantics-embedded-content.html#resource-selection-algorithm
1392     // The Resource Selection Algorithm
1393
1394     // 1. Set the element’s networkState attribute to the NETWORK_NO_SOURCE value.
1395     m_networkState = NETWORK_NO_SOURCE;
1396
1397     // 2. Set the element’s show poster flag to true.
1398     setDisplayMode(Poster);
1399
1400     // 3. Set the media element’s delaying-the-load-event flag to true (this delays the load event).
1401     setShouldDelayLoadEvent(true);
1402
1403     // 4. in parallel await a stable state, allowing the task that invoked this algorithm to continue.
1404     if (m_resourceSelectionTaskQueue.hasPendingTasks())
1405         return;
1406
1407     if (!m_mediaSession->pageAllowsDataLoading()) {
1408         ALWAYS_LOG(LOGIDENTIFIER, "not allowed to load in background, waiting");
1409         setShouldDelayLoadEvent(false);
1410         if (m_isWaitingUntilMediaCanStart)
1411             return;
1412         m_isWaitingUntilMediaCanStart = true;
1413         document().addMediaCanStartListener(this);
1414         return;
1415     }
1416
1417     // Once the page has allowed an element to load media, it is free to load at will. This allows a
1418     // playlist that starts in a foreground tab to continue automatically if the tab is subsequently
1419     // put into the background.
1420     m_mediaSession->removeBehaviorRestriction(MediaElementSession::RequirePageConsentToLoadMedia);
1421
1422     m_resourceSelectionTaskQueue.enqueueTask([this]  {
1423         // 5. If the media element’s blocked-on-parser flag is false, then populate the list of pending text tracks.
1424 #if ENABLE(VIDEO_TRACK)
1425         if (hasMediaControls())
1426             mediaControls()->changedClosedCaptionsVisibility();
1427
1428         // HTMLMediaElement::textTracksAreReady will need "... the text tracks whose mode was not in the
1429         // disabled state when the element's resource selection algorithm last started".
1430         // FIXME: Update this to match "populate the list of pending text tracks" step.
1431         m_textTracksWhenResourceSelectionBegan.clear();
1432         if (m_textTracks) {
1433             for (unsigned i = 0; i < m_textTracks->length(); ++i) {
1434                 RefPtr<TextTrack> track = m_textTracks->item(i);
1435                 if (track->mode() != TextTrack::Mode::Disabled)
1436                     m_textTracksWhenResourceSelectionBegan.append(track);
1437             }
1438         }
1439 #endif
1440
1441         enum Mode { None, Object, Attribute, Children };
1442         Mode mode = None;
1443
1444         if (m_mediaProvider) {
1445             // 6. If the media element has an assigned media provider object, then let mode be object.
1446             mode = Object;
1447         } else if (hasAttributeWithoutSynchronization(srcAttr)) {
1448             //    Otherwise, if the media element has no assigned media provider object but has a src attribute, then let mode be attribute.
1449             mode = Attribute;
1450             ASSERT(m_player);
1451             if (!m_player) {
1452                 ERROR_LOG(LOGIDENTIFIER, " - has srcAttr but m_player is not created");
1453                 return;
1454             }
1455         } else if (auto firstSource = childrenOfType<HTMLSourceElement>(*this).first()) {
1456             //    Otherwise, if the media element does not have an assigned media provider object and does not have a src attribute,
1457             //    but does have a source element child, then let mode be children and let candidate be the first such source element
1458             //    child in tree order.
1459             mode = Children;
1460             m_nextChildNodeToConsider = firstSource;
1461             m_currentSourceNode = nullptr;
1462         } else {
1463             //  Otherwise the media element has no assigned media provider object and has neither a src attribute nor a source
1464             //  element child: set the networkState to NETWORK_EMPTY, and abort these steps; the synchronous section ends.
1465             m_loadState = WaitingForSource;
1466             setShouldDelayLoadEvent(false);
1467             m_networkState = NETWORK_EMPTY;
1468
1469             ALWAYS_LOG(LOGIDENTIFIER, "nothing to load");
1470             return;
1471         }
1472
1473         // 7. Set the media element’s networkState to NETWORK_LOADING.
1474         m_networkState = NETWORK_LOADING;
1475
1476         // 8. Queue a task to fire a simple event named loadstart at the media element.
1477         scheduleEvent(eventNames().loadstartEvent);
1478
1479         // 9. Run the appropriate steps from the following list:
1480         // ↳ If mode is object
1481         if (mode == Object) {
1482             // 1. Set the currentSrc attribute to the empty string.
1483             m_currentSrc = URL();
1484
1485             // 2. End the synchronous section, continuing the remaining steps in parallel.
1486             // 3. Run the resource fetch algorithm with the assigned media provider object.
1487             WTF::visit(WTF::makeVisitor(
1488 #if ENABLE(MEDIA_STREAM)
1489                 [this](RefPtr<MediaStream> stream) { m_mediaStreamSrcObject = stream; },
1490 #endif
1491 #if ENABLE(MEDIA_SOURCE)
1492                 [this](RefPtr<MediaSource> source) { m_mediaSource = source; },
1493 #endif
1494                 [this](RefPtr<Blob> blob) { m_blob = blob; }
1495             ), m_mediaProvider.value());
1496
1497             ContentType contentType;
1498             loadResource(URL(), contentType, String());
1499             ALWAYS_LOG(LOGIDENTIFIER, "using 'srcObject' property");
1500
1501             //    If that algorithm returns without aborting this one, then the load failed.
1502             // 4. Failed with media provider: Reaching this step indicates that the media resource
1503             //    failed to load. Queue a task to run the dedicated media source failure steps.
1504             // 5. Wait for the task queued by the previous step to have executed.
1505             // 6. Abort these steps. The element won’t attempt to load another resource until this
1506             //    algorithm is triggered again.
1507             return;
1508         }
1509
1510         // ↳ If mode is attribute
1511         if (mode == Attribute) {
1512             m_loadState = LoadingFromSrcAttr;
1513
1514             // 1. If the src attribute’s value is the empty string, then end the synchronous section,
1515             //    and jump down to the failed with attribute step below.
1516             // 2. Let absolute URL be the absolute URL that would have resulted from parsing the URL
1517             //    specified by the src attribute’s value relative to the media element when the src
1518             //    attribute was last changed.
1519             URL absoluteURL = getNonEmptyURLAttribute(srcAttr);
1520             if (absoluteURL.isEmpty()) {
1521                 mediaLoadingFailed(MediaPlayer::FormatError);
1522                 ALWAYS_LOG(LOGIDENTIFIER, "empty 'src'");
1523                 return;
1524             }
1525
1526             if (!isSafeToLoadURL(absoluteURL, Complain) || !dispatchBeforeLoadEvent(absoluteURL.string())) {
1527                 mediaLoadingFailed(MediaPlayer::FormatError);
1528                 return;
1529             }
1530
1531             // 3. If absolute URL was obtained successfully, set the currentSrc attribute to absolute URL.
1532             m_currentSrc = absoluteURL;
1533
1534             // 4. End the synchronous section, continuing the remaining steps in parallel.
1535             // 5. If absolute URL was obtained successfully, run the resource fetch algorithm with absolute
1536             //    URL. If that algorithm returns without aborting this one, then the load failed.
1537
1538             // No type or key system information is available when the url comes
1539             // from the 'src' attribute so MediaPlayer
1540             // will have to pick a media engine based on the file extension.
1541             ContentType contentType;
1542             loadResource(absoluteURL, contentType, String());
1543             ALWAYS_LOG(LOGIDENTIFIER, "using 'src' attribute url");
1544
1545             // 6. Failed with attribute: Reaching this step indicates that the media resource failed to load
1546             //    or that the given URL could not be resolved. Queue a task to run the dedicated media source failure steps.
1547             // 7. Wait for the task queued by the previous step to have executed.
1548             // 8. Abort these steps. The element won’t attempt to load another resource until this algorithm is triggered again.
1549             return;
1550         }
1551
1552         // ↳ Otherwise (mode is children)
1553         // (Ctd. in loadNextSourceChild())
1554         loadNextSourceChild();
1555     });
1556 }
1557
1558 void HTMLMediaElement::loadNextSourceChild()
1559 {
1560     ContentType contentType;
1561     String keySystem;
1562     URL mediaURL = selectNextSourceChild(&contentType, &keySystem, Complain);
1563     if (!mediaURL.isValid()) {
1564         waitForSourceChange();
1565         return;
1566     }
1567
1568     // Recreate the media player for the new url
1569     createMediaPlayer();
1570
1571     m_loadState = LoadingFromSourceElement;
1572     loadResource(mediaURL, contentType, keySystem);
1573 }
1574
1575 void HTMLMediaElement::loadResource(const URL& initialURL, ContentType& contentType, const String& keySystem)
1576 {
1577     ASSERT(initialURL.isEmpty() || isSafeToLoadURL(initialURL, Complain));
1578
1579     INFO_LOG(LOGIDENTIFIER, initialURL, contentType.raw(), keySystem);
1580
1581     RefPtr<Frame> frame = document().frame();
1582     if (!frame) {
1583         mediaLoadingFailed(MediaPlayer::FormatError);
1584         return;
1585     }
1586
1587     Page* page = frame->page();
1588     if (!page) {
1589         mediaLoadingFailed(MediaPlayer::FormatError);
1590         return;
1591     }
1592
1593     URL url = initialURL;
1594     if (!url.isEmpty() && !frame->loader().willLoadMediaElementURL(url)) {
1595         mediaLoadingFailed(MediaPlayer::FormatError);
1596         return;
1597     }
1598
1599 #if ENABLE(CONTENT_EXTENSIONS)
1600     if (auto documentLoader = makeRefPtr(frame->loader().documentLoader())) {
1601         if (page->userContentProvider().processContentExtensionRulesForLoad(url, ResourceType::Media, *documentLoader).blockedLoad) {
1602             mediaLoadingFailed(MediaPlayer::FormatError);
1603             return;
1604         }
1605     }
1606 #endif
1607
1608     // The resource fetch algorithm
1609     m_networkState = NETWORK_LOADING;
1610
1611     // If the URL should be loaded from the application cache, pass the URL of the cached file to the media engine.
1612     ApplicationCacheResource* resource = nullptr;
1613     if (!url.isEmpty() && frame->loader().documentLoader()->applicationCacheHost().shouldLoadResourceFromApplicationCache(ResourceRequest(url), resource)) {
1614         // Resources that are not present in the manifest will always fail to load (at least, after the
1615         // cache has been primed the first time), making the testing of offline applications simpler.
1616         if (!resource || resource->path().isEmpty()) {
1617             mediaLoadingFailed(MediaPlayer::NetworkError);
1618             return;
1619         }
1620     }
1621
1622     // Log that we started loading a media element.
1623     page->diagnosticLoggingClient().logDiagnosticMessage(isVideo() ? DiagnosticLoggingKeys::videoKey() : DiagnosticLoggingKeys::audioKey(), DiagnosticLoggingKeys::loadingKey(), ShouldSample::No);
1624
1625     m_firstTimePlaying = true;
1626
1627     // Set m_currentSrc *before* changing to the cache URL, the fact that we are loading from the app
1628     // cache is an internal detail not exposed through the media element API.
1629     m_currentSrc = url;
1630
1631     if (resource) {
1632         url = ApplicationCacheHost::createFileURL(resource->path());
1633         INFO_LOG(LOGIDENTIFIER, "will load ", url, " from app cache");
1634     }
1635
1636     INFO_LOG(LOGIDENTIFIER, "m_currentSrc is ", m_currentSrc);
1637
1638     startProgressEventTimer();
1639
1640     bool privateMode = document().page() && document().page()->usesEphemeralSession();
1641     m_player->setPrivateBrowsingMode(privateMode);
1642
1643     // Reset display mode to force a recalculation of what to show because we are resetting the player.
1644     setDisplayMode(Unknown);
1645
1646     if (!autoplay() && !m_havePreparedToPlay)
1647         m_player->setPreload(m_mediaSession->effectivePreloadForElement());
1648     m_player->setPreservesPitch(m_webkitPreservesPitch);
1649
1650     if (!m_explicitlyMuted) {
1651         m_explicitlyMuted = true;
1652         m_muted = hasAttributeWithoutSynchronization(mutedAttr);
1653         m_mediaSession->canProduceAudioChanged();
1654     }
1655
1656     updateVolume();
1657
1658     bool loadAttempted = false;
1659 #if ENABLE(MEDIA_SOURCE)
1660     if (!m_mediaSource && url.protocolIs(mediaSourceBlobProtocol))
1661         m_mediaSource = MediaSource::lookup(url.string());
1662
1663     if (m_mediaSource) {
1664         loadAttempted = true;
1665         if (!m_mediaSource->attachToElement(*this) || !m_player->load(url, contentType, m_mediaSource.get())) {
1666             // Forget our reference to the MediaSource, so we leave it alone
1667             // while processing remainder of load failure.
1668             m_mediaSource = nullptr;
1669             mediaLoadingFailed(MediaPlayer::FormatError);
1670         }
1671     }
1672 #endif
1673
1674 #if ENABLE(MEDIA_STREAM)
1675     if (!loadAttempted) {
1676         if (!m_mediaStreamSrcObject && url.protocolIs(mediaStreamBlobProtocol))
1677             m_mediaStreamSrcObject = MediaStreamRegistry::shared().lookUp(url);
1678
1679         if (m_mediaStreamSrcObject) {
1680             loadAttempted = true;
1681             if (!m_player->load(m_mediaStreamSrcObject->privateStream()))
1682                 mediaLoadingFailed(MediaPlayer::FormatError);
1683         }
1684     }
1685 #endif
1686
1687     if (!loadAttempted && m_blob) {
1688         loadAttempted = true;
1689         if (!m_player->load(m_blob->url(), contentType, keySystem))
1690             mediaLoadingFailed(MediaPlayer::FormatError);
1691     }
1692
1693     if (!loadAttempted && !m_player->load(url, contentType, keySystem))
1694         mediaLoadingFailed(MediaPlayer::FormatError);
1695
1696     // If there is no poster to display, allow the media engine to render video frames as soon as
1697     // they are available.
1698     updateDisplayState();
1699
1700     updateRenderer();
1701 }
1702
1703 #if ENABLE(VIDEO_TRACK)
1704
1705 static bool trackIndexCompare(TextTrack* a, TextTrack* b)
1706 {
1707     return a->trackIndex() - b->trackIndex() < 0;
1708 }
1709
1710 static bool eventTimeCueCompare(const std::pair<MediaTime, TextTrackCue*>& a, const std::pair<MediaTime, TextTrackCue*>& b)
1711 {
1712     // 12 - Sort the tasks in events in ascending time order (tasks with earlier
1713     // times first).
1714     if (a.first != b.first)
1715         return a.first - b.first < MediaTime::zeroTime();
1716
1717     // If the cues belong to different text tracks, it doesn't make sense to
1718     // compare the two tracks by the relative cue order, so return the relative
1719     // track order.
1720     if (a.second->track() != b.second->track())
1721         return trackIndexCompare(a.second->track(), b.second->track());
1722
1723     // 12 - Further sort tasks in events that have the same time by the
1724     // relative text track cue order of the text track cues associated
1725     // with these tasks.
1726     return a.second->isOrderedBefore(b.second);
1727 }
1728
1729 static bool compareCueInterval(const CueInterval& one, const CueInterval& two)
1730 {
1731     return one.data()->isOrderedBefore(two.data());
1732 }
1733
1734 void HTMLMediaElement::updateActiveTextTrackCues(const MediaTime& movieTime)
1735 {
1736     // 4.8.10.8 Playing the media resource
1737
1738     //  If the current playback position changes while the steps are running,
1739     //  then the user agent must wait for the steps to complete, and then must
1740     //  immediately rerun the steps.
1741     if (ignoreTrackDisplayUpdateRequests())
1742         return;
1743
1744     // 1 - Let current cues be a list of cues, initialized to contain all the
1745     // cues of all the hidden, showing, or showing by default text tracks of the
1746     // media element (not the disabled ones) whose start times are less than or
1747     // equal to the current playback position and whose end times are greater
1748     // than the current playback position.
1749     CueList currentCues;
1750
1751     // The user agent must synchronously unset [the text track cue active] flag
1752     // whenever ... the media element's readyState is changed back to HAVE_NOTHING.
1753     if (m_readyState != HAVE_NOTHING && m_player) {
1754         currentCues = m_cueTree.allOverlaps(m_cueTree.createInterval(movieTime, movieTime));
1755         if (currentCues.size() > 1)
1756             std::sort(currentCues.begin(), currentCues.end(), &compareCueInterval);
1757     }
1758
1759     CueList previousCues;
1760     CueList missedCues;
1761
1762     // 2 - Let other cues be a list of cues, initialized to contain all the cues
1763     // of hidden, showing, and showing by default text tracks of the media
1764     // element that are not present in current cues.
1765     previousCues = m_currentlyActiveCues;
1766
1767     // 3 - Let last time be the current playback position at the time this
1768     // algorithm was last run for this media element, if this is not the first
1769     // time it has run.
1770     MediaTime lastTime = m_lastTextTrackUpdateTime;
1771
1772     // 4 - If the current playback position has, since the last time this
1773     // algorithm was run, only changed through its usual monotonic increase
1774     // during normal playback, then let missed cues be the list of cues in other
1775     // cues whose start times are greater than or equal to last time and whose
1776     // end times are less than or equal to the current playback position.
1777     // Otherwise, let missed cues be an empty list.
1778     if (lastTime >= MediaTime::zeroTime() && m_lastSeekTime < movieTime) {
1779         for (auto& cue : m_cueTree.allOverlaps(m_cueTree.createInterval(lastTime, movieTime))) {
1780             // Consider cues that may have been missed since the last seek time.
1781             if (cue.low() > std::max(m_lastSeekTime, lastTime) && cue.high() < movieTime)
1782                 missedCues.append(cue);
1783         }
1784     }
1785
1786     m_lastTextTrackUpdateTime = movieTime;
1787
1788     // 5 - If the time was reached through the usual monotonic increase of the
1789     // current playback position during normal playback, and if the user agent
1790     // has not fired a timeupdate event at the element in the past 15 to 250ms
1791     // and is not still running event handlers for such an event, then the user
1792     // agent must queue a task to fire a simple event named timeupdate at the
1793     // element. (In the other cases, such as explicit seeks, relevant events get
1794     // fired as part of the overall process of changing the current playback
1795     // position.)
1796     if (!m_paused && m_lastSeekTime <= lastTime)
1797         scheduleTimeupdateEvent(false);
1798
1799     // Explicitly cache vector sizes, as their content is constant from here.
1800     size_t currentCuesSize = currentCues.size();
1801     size_t missedCuesSize = missedCues.size();
1802     size_t previousCuesSize = previousCues.size();
1803
1804     // 6 - If all of the cues in current cues have their text track cue active
1805     // flag set, none of the cues in other cues have their text track cue active
1806     // flag set, and missed cues is empty, then abort these steps.
1807     bool activeSetChanged = missedCuesSize;
1808
1809     for (size_t i = 0; !activeSetChanged && i < previousCuesSize; ++i)
1810         if (!currentCues.contains(previousCues[i]) && previousCues[i].data()->isActive())
1811             activeSetChanged = true;
1812
1813     for (size_t i = 0; i < currentCuesSize; ++i) {
1814         RefPtr<TextTrackCue> cue = currentCues[i].data();
1815
1816         if (cue->isRenderable())
1817             toVTTCue(cue.get())->updateDisplayTree(movieTime);
1818
1819         if (!cue->isActive())
1820             activeSetChanged = true;
1821     }
1822
1823     if (!activeSetChanged)
1824         return;
1825
1826     // 7 - If the time was reached through the usual monotonic increase of the
1827     // current playback position during normal playback, and there are cues in
1828     // other cues that have their text track cue pause-on-exi flag set and that
1829     // either have their text track cue active flag set or are also in missed
1830     // cues, then immediately pause the media element.
1831     for (size_t i = 0; !m_paused && i < previousCuesSize; ++i) {
1832         if (previousCues[i].data()->pauseOnExit()
1833             && previousCues[i].data()->isActive()
1834             && !currentCues.contains(previousCues[i]))
1835             pause();
1836     }
1837
1838     for (size_t i = 0; !m_paused && i < missedCuesSize; ++i) {
1839         if (missedCues[i].data()->pauseOnExit())
1840             pause();
1841     }
1842
1843     // 8 - Let events be a list of tasks, initially empty. Each task in this
1844     // list will be associated with a text track, a text track cue, and a time,
1845     // which are used to sort the list before the tasks are queued.
1846     Vector<std::pair<MediaTime, TextTrackCue*>> eventTasks;
1847
1848     // 8 - Let affected tracks be a list of text tracks, initially empty.
1849     Vector<TextTrack*> affectedTracks;
1850
1851     for (size_t i = 0; i < missedCuesSize; ++i) {
1852         // 9 - For each text track cue in missed cues, prepare an event named enter
1853         // for the TextTrackCue object with the text track cue start time.
1854         eventTasks.append({ missedCues[i].data()->startMediaTime(), missedCues[i].data() });
1855
1856         // 10 - For each text track [...] in missed cues, prepare an event
1857         // named exit for the TextTrackCue object with the  with the later of
1858         // the text track cue end time and the text track cue start time.
1859
1860         // Note: An explicit task is added only if the cue is NOT a zero or
1861         // negative length cue. Otherwise, the need for an exit event is
1862         // checked when these tasks are actually queued below. This doesn't
1863         // affect sorting events before dispatch either, because the exit
1864         // event has the same time as the enter event.
1865         if (missedCues[i].data()->startMediaTime() < missedCues[i].data()->endMediaTime())
1866             eventTasks.append({ missedCues[i].data()->endMediaTime(), missedCues[i].data() });
1867     }
1868
1869     for (size_t i = 0; i < previousCuesSize; ++i) {
1870         // 10 - For each text track cue in other cues that has its text
1871         // track cue active flag set prepare an event named exit for the
1872         // TextTrackCue object with the text track cue end time.
1873         if (!currentCues.contains(previousCues[i]))
1874             eventTasks.append({ previousCues[i].data()->endMediaTime(), previousCues[i].data() });
1875     }
1876
1877     for (size_t i = 0; i < currentCuesSize; ++i) {
1878         // 11 - For each text track cue in current cues that does not have its
1879         // text track cue active flag set, prepare an event named enter for the
1880         // TextTrackCue object with the text track cue start time.
1881         if (!previousCues.contains(currentCues[i]))
1882             eventTasks.append({ currentCues[i].data()->startMediaTime(), currentCues[i].data() });
1883     }
1884
1885     // 12 - Sort the tasks in events in ascending time order (tasks with earlier
1886     // times first).
1887     std::sort(eventTasks.begin(), eventTasks.end(), eventTimeCueCompare);
1888
1889     for (auto& eventTask : eventTasks) {
1890         if (!affectedTracks.contains(eventTask.second->track()))
1891             affectedTracks.append(eventTask.second->track());
1892
1893         // 13 - Queue each task in events, in list order.
1894
1895         // Each event in eventTasks may be either an enterEvent or an exitEvent,
1896         // depending on the time that is associated with the event. This
1897         // correctly identifies the type of the event, if the startTime is
1898         // less than the endTime in the cue.
1899         if (eventTask.second->startTime() >= eventTask.second->endTime()) {
1900             auto enterEvent = Event::create(eventNames().enterEvent, false, false);
1901             enterEvent->setTarget(eventTask.second);
1902             m_asyncEventQueue.enqueueEvent(WTFMove(enterEvent));
1903
1904             auto exitEvent = Event::create(eventNames().exitEvent, false, false);
1905             exitEvent->setTarget(eventTask.second);
1906             m_asyncEventQueue.enqueueEvent(WTFMove(exitEvent));
1907         } else {
1908             RefPtr<Event> event;
1909             if (eventTask.first == eventTask.second->startMediaTime())
1910                 event = Event::create(eventNames().enterEvent, false, false);
1911             else
1912                 event = Event::create(eventNames().exitEvent, false, false);
1913             event->setTarget(eventTask.second);
1914             m_asyncEventQueue.enqueueEvent(WTFMove(event));
1915         }
1916     }
1917
1918     // 14 - Sort affected tracks in the same order as the text tracks appear in
1919     // the media element's list of text tracks, and remove duplicates.
1920     std::sort(affectedTracks.begin(), affectedTracks.end(), trackIndexCompare);
1921
1922     // 15 - For each text track in affected tracks, in the list order, queue a
1923     // task to fire a simple event named cuechange at the TextTrack object, and, ...
1924     for (auto& affectedTrack : affectedTracks) {
1925         auto event = Event::create(eventNames().cuechangeEvent, false, false);
1926         event->setTarget(affectedTrack);
1927         m_asyncEventQueue.enqueueEvent(WTFMove(event));
1928
1929         // ... if the text track has a corresponding track element, to then fire a
1930         // simple event named cuechange at the track element as well.
1931         if (is<LoadableTextTrack>(*affectedTrack)) {
1932             auto event = Event::create(eventNames().cuechangeEvent, false, false);
1933             auto trackElement = makeRefPtr(downcast<LoadableTextTrack>(*affectedTrack).trackElement());
1934             ASSERT(trackElement);
1935             event->setTarget(trackElement);
1936             m_asyncEventQueue.enqueueEvent(WTFMove(event));
1937         }
1938     }
1939
1940     // 16 - Set the text track cue active flag of all the cues in the current
1941     // cues, and unset the text track cue active flag of all the cues in the
1942     // other cues.
1943     for (size_t i = 0; i < currentCuesSize; ++i)
1944         currentCues[i].data()->setIsActive(true);
1945
1946     for (size_t i = 0; i < previousCuesSize; ++i)
1947         if (!currentCues.contains(previousCues[i]))
1948             previousCues[i].data()->setIsActive(false);
1949
1950     // Update the current active cues.
1951     m_currentlyActiveCues = currentCues;
1952
1953     if (activeSetChanged)
1954         updateTextTrackDisplay();
1955 }
1956
1957 bool HTMLMediaElement::textTracksAreReady() const
1958 {
1959     // 4.8.10.12.1 Text track model
1960     // ...
1961     // The text tracks of a media element are ready if all the text tracks whose mode was not
1962     // in the disabled state when the element's resource selection algorithm last started now
1963     // have a text track readiness state of loaded or failed to load.
1964     for (unsigned i = 0; i < m_textTracksWhenResourceSelectionBegan.size(); ++i) {
1965         if (m_textTracksWhenResourceSelectionBegan[i]->readinessState() == TextTrack::Loading
1966             || m_textTracksWhenResourceSelectionBegan[i]->readinessState() == TextTrack::NotLoaded)
1967             return false;
1968     }
1969
1970     return true;
1971 }
1972
1973 void HTMLMediaElement::textTrackReadyStateChanged(TextTrack* track)
1974 {
1975     if (track->readinessState() != TextTrack::Loading
1976         && track->mode() != TextTrack::Mode::Disabled) {
1977         // The display trees exist as long as the track is active, in this case,
1978         // and if the same track is loaded again (for example if the src attribute was changed),
1979         // cues can be accumulated with the old ones, that's why they needs to be flushed
1980         if (hasMediaControls())
1981             mediaControls()->clearTextDisplayContainer();
1982         updateTextTrackDisplay();
1983     }
1984     if (m_player && m_textTracksWhenResourceSelectionBegan.contains(track)) {
1985         if (track->readinessState() != TextTrack::Loading)
1986             setReadyState(m_player->readyState());
1987     } else {
1988         // The track readiness state might have changed as a result of the user
1989         // clicking the captions button. In this case, a check whether all the
1990         // resources have failed loading should be done in order to hide the CC button.
1991         if (hasMediaControls() && track->readinessState() == TextTrack::FailedToLoad)
1992             mediaControls()->refreshClosedCaptionsButtonVisibility();
1993     }
1994 }
1995
1996 void HTMLMediaElement::audioTrackEnabledChanged(AudioTrack& track)
1997 {
1998     if (m_audioTracks && m_audioTracks->contains(track))
1999         m_audioTracks->scheduleChangeEvent();
2000     if (processingUserGestureForMedia())
2001         removeBehaviorsRestrictionsAfterFirstUserGesture(MediaElementSession::AllRestrictions & ~MediaElementSession::RequireUserGestureToControlControlsManager);
2002 }
2003
2004 void HTMLMediaElement::textTrackModeChanged(TextTrack& track)
2005 {
2006     bool trackIsLoaded = true;
2007     if (track.trackType() == TextTrack::TrackElement) {
2008         trackIsLoaded = false;
2009         for (auto& trackElement : childrenOfType<HTMLTrackElement>(*this)) {
2010             if (&trackElement.track() == &track) {
2011                 if (trackElement.readyState() == HTMLTrackElement::LOADING || trackElement.readyState() == HTMLTrackElement::LOADED)
2012                     trackIsLoaded = true;
2013                 break;
2014             }
2015         }
2016     }
2017
2018     // If this is the first added track, create the list of text tracks.
2019     if (!m_textTracks)
2020         m_textTracks = TextTrackList::create(this, ActiveDOMObject::scriptExecutionContext());
2021
2022     // Mark this track as "configured" so configureTextTracks won't change the mode again.
2023     track.setHasBeenConfigured(true);
2024
2025     if (track.mode() != TextTrack::Mode::Disabled && trackIsLoaded)
2026         textTrackAddCues(track, *track.cues());
2027
2028     configureTextTrackDisplay(AssumeTextTrackVisibilityChanged);
2029
2030     if (m_textTracks && m_textTracks->contains(track))
2031         m_textTracks->scheduleChangeEvent();
2032
2033 #if ENABLE(AVF_CAPTIONS)
2034     if (track.trackType() == TextTrack::TrackElement && m_player)
2035         m_player->notifyTrackModeChanged();
2036 #endif
2037 }
2038
2039 void HTMLMediaElement::videoTrackSelectedChanged(VideoTrack& track)
2040 {
2041     if (m_videoTracks && m_videoTracks->contains(track))
2042         m_videoTracks->scheduleChangeEvent();
2043 }
2044
2045 void HTMLMediaElement::textTrackKindChanged(TextTrack& track)
2046 {
2047     if (track.kind() != TextTrack::Kind::Captions && track.kind() != TextTrack::Kind::Subtitles && track.mode() == TextTrack::Mode::Showing)
2048         track.setMode(TextTrack::Mode::Hidden);
2049 }
2050
2051 void HTMLMediaElement::beginIgnoringTrackDisplayUpdateRequests()
2052 {
2053     ++m_ignoreTrackDisplayUpdate;
2054 }
2055
2056 void HTMLMediaElement::endIgnoringTrackDisplayUpdateRequests()
2057 {
2058     ASSERT(m_ignoreTrackDisplayUpdate);
2059     --m_ignoreTrackDisplayUpdate;
2060     if (!m_ignoreTrackDisplayUpdate && m_inActiveDocument)
2061         updateActiveTextTrackCues(currentMediaTime());
2062 }
2063
2064 void HTMLMediaElement::textTrackAddCues(TextTrack& track, const TextTrackCueList& cues)
2065 {
2066     if (track.mode() == TextTrack::Mode::Disabled)
2067         return;
2068
2069     TrackDisplayUpdateScope scope { *this };
2070     for (unsigned i = 0; i < cues.length(); ++i)
2071         textTrackAddCue(track, *cues.item(i));
2072 }
2073
2074 void HTMLMediaElement::textTrackRemoveCues(TextTrack&, const TextTrackCueList& cues)
2075 {
2076     TrackDisplayUpdateScope scope { *this };
2077     for (unsigned i = 0; i < cues.length(); ++i) {
2078         auto& cue = *cues.item(i);
2079         textTrackRemoveCue(*cue.track(), cue);
2080     }
2081 }
2082
2083 void HTMLMediaElement::textTrackAddCue(TextTrack& track, TextTrackCue& cue)
2084 {
2085     if (track.mode() == TextTrack::Mode::Disabled)
2086         return;
2087
2088     // Negative duration cues need be treated in the interval tree as
2089     // zero-length cues.
2090     MediaTime endTime = std::max(cue.startMediaTime(), cue.endMediaTime());
2091
2092     CueInterval interval = m_cueTree.createInterval(cue.startMediaTime(), endTime, &cue);
2093     if (!m_cueTree.contains(interval))
2094         m_cueTree.add(interval);
2095     updateActiveTextTrackCues(currentMediaTime());
2096 }
2097
2098 void HTMLMediaElement::textTrackRemoveCue(TextTrack&, TextTrackCue& cue)
2099 {
2100     // Negative duration cues need to be treated in the interval tree as
2101     // zero-length cues.
2102     MediaTime endTime = std::max(cue.startMediaTime(), cue.endMediaTime());
2103
2104     CueInterval interval = m_cueTree.createInterval(cue.startMediaTime(), endTime, &cue);
2105     m_cueTree.remove(interval);
2106
2107     // Since the cue will be removed from the media element and likely the
2108     // TextTrack might also be destructed, notifying the region of the cue
2109     // removal shouldn't be done.
2110     if (cue.isRenderable())
2111         toVTTCue(&cue)->notifyRegionWhenRemovingDisplayTree(false);
2112
2113     size_t index = m_currentlyActiveCues.find(interval);
2114     if (index != notFound) {
2115         cue.setIsActive(false);
2116         m_currentlyActiveCues.remove(index);
2117     }
2118
2119     if (cue.isRenderable())
2120         toVTTCue(&cue)->removeDisplayTree();
2121     updateActiveTextTrackCues(currentMediaTime());
2122
2123     if (cue.isRenderable())
2124         toVTTCue(&cue)->notifyRegionWhenRemovingDisplayTree(true);
2125 }
2126
2127 #endif
2128
2129 static inline bool isAllowedToLoadMediaURL(HTMLMediaElement& element, const URL& url, bool isInUserAgentShadowTree)
2130 {
2131     // Elements in user agent show tree should load whatever the embedding document policy is.
2132     if (isInUserAgentShadowTree)
2133         return true;
2134
2135     ASSERT(element.document().contentSecurityPolicy());
2136     return element.document().contentSecurityPolicy()->allowMediaFromSource(url);
2137 }
2138
2139 bool HTMLMediaElement::isSafeToLoadURL(const URL& url, InvalidURLAction actionIfInvalid)
2140 {
2141     if (!url.isValid()) {
2142         ERROR_LOG(LOGIDENTIFIER, url, " is invalid");
2143         return false;
2144     }
2145
2146     RefPtr<Frame> frame = document().frame();
2147     if (!frame || !document().securityOrigin().canDisplay(url)) {
2148         if (actionIfInvalid == Complain)
2149             FrameLoader::reportLocalLoadFailed(frame.get(), url.stringCenterEllipsizedToLength());
2150             ERROR_LOG(LOGIDENTIFIER, url , " was rejected by SecurityOrigin");
2151         return false;
2152     }
2153
2154     if (!isAllowedToLoadMediaURL(*this, url, isInUserAgentShadowTree())) {
2155         ERROR_LOG(LOGIDENTIFIER, url, " was rejected by Content Security Policy");
2156         return false;
2157     }
2158
2159     return true;
2160 }
2161
2162 void HTMLMediaElement::startProgressEventTimer()
2163 {
2164     if (m_progressEventTimer.isActive())
2165         return;
2166
2167     m_previousProgressTime = MonotonicTime::now();
2168     // 350ms is not magic, it is in the spec!
2169     m_progressEventTimer.startRepeating(350_ms);
2170 }
2171
2172 void HTMLMediaElement::waitForSourceChange()
2173 {
2174     INFO_LOG(LOGIDENTIFIER);
2175
2176     stopPeriodicTimers();
2177     m_loadState = WaitingForSource;
2178
2179     // 6.17 - Waiting: Set the element's networkState attribute to the NETWORK_NO_SOURCE value
2180     m_networkState = NETWORK_NO_SOURCE;
2181
2182     // 6.18 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
2183     setShouldDelayLoadEvent(false);
2184
2185     updateDisplayState();
2186     updateRenderer();
2187 }
2188
2189 void HTMLMediaElement::noneSupported()
2190 {
2191     if (m_error)
2192         return;
2193
2194     INFO_LOG(LOGIDENTIFIER);
2195
2196     stopPeriodicTimers();
2197     m_loadState = WaitingForSource;
2198     m_currentSourceNode = nullptr;
2199
2200     // 4.8.10.5
2201     // 6 - Reaching this step indicates that the media resource failed to load or that the given
2202     // URL could not be resolved. In one atomic operation, run the following steps:
2203
2204     // 6.1 - Set the error attribute to a new MediaError object whose code attribute is set to
2205     // MEDIA_ERR_SRC_NOT_SUPPORTED.
2206     m_error = MediaError::create(MediaError::MEDIA_ERR_SRC_NOT_SUPPORTED);
2207
2208     // 6.2 - Forget the media element's media-resource-specific text tracks.
2209     forgetResourceSpecificTracks();
2210
2211     // 6.3 - Set the element's networkState attribute to the NETWORK_NO_SOURCE value.
2212     m_networkState = NETWORK_NO_SOURCE;
2213
2214     // 7 - Queue a task to fire a simple event named error at the media element.
2215     scheduleEvent(eventNames().errorEvent);
2216
2217     rejectPendingPlayPromises(WTFMove(m_pendingPlayPromises), DOMException::create(NotSupportedError));
2218
2219 #if ENABLE(MEDIA_SOURCE)
2220     detachMediaSource();
2221 #endif
2222
2223     // 8 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
2224     setShouldDelayLoadEvent(false);
2225
2226     // 9 - Abort these steps. Until the load() method is invoked or the src attribute is changed,
2227     // the element won't attempt to load another resource.
2228
2229     updateDisplayState();
2230     updateRenderer();
2231 }
2232
2233 void HTMLMediaElement::mediaLoadingFailedFatally(MediaPlayer::NetworkState error)
2234 {
2235     // 1 - The user agent should cancel the fetching process.
2236     stopPeriodicTimers();
2237     m_loadState = WaitingForSource;
2238
2239     // 2 - Set the error attribute to a new MediaError object whose code attribute is
2240     // set to MEDIA_ERR_NETWORK/MEDIA_ERR_DECODE.
2241     if (error == MediaPlayer::NetworkError)
2242         m_error = MediaError::create(MediaError::MEDIA_ERR_NETWORK);
2243     else if (error == MediaPlayer::DecodeError)
2244         m_error = MediaError::create(MediaError::MEDIA_ERR_DECODE);
2245     else
2246         ASSERT_NOT_REACHED();
2247
2248     // 3 - Queue a task to fire a simple event named error at the media element.
2249     scheduleEvent(eventNames().errorEvent);
2250
2251 #if ENABLE(MEDIA_SOURCE)
2252     detachMediaSource();
2253 #endif
2254
2255     // 4 - Set the element's networkState attribute to the NETWORK_EMPTY value and queue a
2256     // task to fire a simple event called emptied at the element.
2257     m_networkState = NETWORK_EMPTY;
2258     scheduleEvent(eventNames().emptiedEvent);
2259
2260     // 5 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
2261     setShouldDelayLoadEvent(false);
2262
2263     // 6 - Abort the overall resource selection algorithm.
2264     m_currentSourceNode = nullptr;
2265
2266 #if PLATFORM(COCOA)
2267     if (is<MediaDocument>(document()))
2268         downcast<MediaDocument>(document()).mediaElementSawUnsupportedTracks();
2269 #endif
2270 }
2271
2272 void HTMLMediaElement::cancelPendingEventsAndCallbacks()
2273 {
2274     INFO_LOG(LOGIDENTIFIER);
2275     m_asyncEventQueue.cancelAllEvents();
2276
2277     for (auto& source : childrenOfType<HTMLSourceElement>(*this))
2278         source.cancelPendingErrorEvent();
2279
2280     rejectPendingPlayPromises(WTFMove(m_pendingPlayPromises), DOMException::create(AbortError));
2281 }
2282
2283 void HTMLMediaElement::mediaPlayerNetworkStateChanged(MediaPlayer*)
2284 {
2285     beginProcessingMediaPlayerCallback();
2286     setNetworkState(m_player->networkState());
2287     endProcessingMediaPlayerCallback();
2288 }
2289
2290 static void logMediaLoadRequest(Page* page, const String& mediaEngine, const String& errorMessage, bool succeeded)
2291 {
2292     if (!page)
2293         return;
2294
2295     DiagnosticLoggingClient& diagnosticLoggingClient = page->diagnosticLoggingClient();
2296     if (!succeeded) {
2297         diagnosticLoggingClient.logDiagnosticMessageWithResult(DiagnosticLoggingKeys::mediaLoadingFailedKey(), errorMessage, DiagnosticLoggingResultFail, ShouldSample::No);
2298         return;
2299     }
2300
2301     diagnosticLoggingClient.logDiagnosticMessage(DiagnosticLoggingKeys::mediaLoadedKey(), mediaEngine, ShouldSample::No);
2302
2303     if (!page->hasSeenAnyMediaEngine())
2304         diagnosticLoggingClient.logDiagnosticMessage(DiagnosticLoggingKeys::pageContainsAtLeastOneMediaEngineKey(), emptyString(), ShouldSample::No);
2305
2306     if (!page->hasSeenMediaEngine(mediaEngine))
2307         diagnosticLoggingClient.logDiagnosticMessage(DiagnosticLoggingKeys::pageContainsMediaEngineKey(), mediaEngine, ShouldSample::No);
2308
2309     page->sawMediaEngine(mediaEngine);
2310 }
2311
2312 static String stringForNetworkState(MediaPlayer::NetworkState state)
2313 {
2314     switch (state) {
2315     case MediaPlayer::Empty: return "Empty"_s;
2316     case MediaPlayer::Idle: return "Idle"_s;
2317     case MediaPlayer::Loading: return "Loading"_s;
2318     case MediaPlayer::Loaded: return "Loaded"_s;
2319     case MediaPlayer::FormatError: return "FormatError"_s;
2320     case MediaPlayer::NetworkError: return "NetworkError"_s;
2321     case MediaPlayer::DecodeError: return "DecodeError"_s;
2322     default: return emptyString();
2323     }
2324 }
2325
2326 void HTMLMediaElement::mediaLoadingFailed(MediaPlayer::NetworkState error)
2327 {
2328     stopPeriodicTimers();
2329
2330     // If we failed while trying to load a <source> element, the movie was never parsed, and there are more
2331     // <source> children, schedule the next one
2332     if (m_readyState < HAVE_METADATA && m_loadState == LoadingFromSourceElement) {
2333
2334         // resource selection algorithm
2335         // 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.
2336         if (m_currentSourceNode)
2337             m_currentSourceNode->scheduleErrorEvent();
2338         else
2339             INFO_LOG(LOGIDENTIFIER, "error event not sent, <source> was removed");
2340
2341         // 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.
2342
2343         // 9.Otherwise.11 - Forget the media element's media-resource-specific tracks.
2344         forgetResourceSpecificTracks();
2345
2346         if (havePotentialSourceChild()) {
2347             INFO_LOG(LOGIDENTIFIER, "scheduling next <source>");
2348             scheduleNextSourceChild();
2349         } else {
2350             INFO_LOG(LOGIDENTIFIER, "no more <source> elements, waiting");
2351             waitForSourceChange();
2352         }
2353
2354         return;
2355     }
2356
2357     if ((error == MediaPlayer::NetworkError && m_readyState >= HAVE_METADATA) || error == MediaPlayer::DecodeError)
2358         mediaLoadingFailedFatally(error);
2359     else if ((error == MediaPlayer::FormatError || error == MediaPlayer::NetworkError) && m_loadState == LoadingFromSrcAttr)
2360         noneSupported();
2361
2362     updateDisplayState();
2363     if (hasMediaControls()) {
2364         mediaControls()->reset();
2365         mediaControls()->reportedError();
2366     }
2367
2368     ERROR_LOG(LOGIDENTIFIER, "error = ", static_cast<int>(error));
2369
2370     logMediaLoadRequest(document().page(), String(), stringForNetworkState(error), false);
2371
2372     m_mediaSession->clientCharacteristicsChanged();
2373 }
2374
2375 void HTMLMediaElement::setNetworkState(MediaPlayer::NetworkState state)
2376 {
2377     if (static_cast<int>(state) != static_cast<int>(m_networkState))
2378         ALWAYS_LOG(LOGIDENTIFIER, "new state = ", state, ", current state = ", m_networkState);
2379
2380     if (state == MediaPlayer::Empty) {
2381         // Just update the cached state and leave, we can't do anything.
2382         m_networkState = NETWORK_EMPTY;
2383         return;
2384     }
2385
2386     if (state == MediaPlayer::FormatError || state == MediaPlayer::NetworkError || state == MediaPlayer::DecodeError) {
2387         mediaLoadingFailed(state);
2388         return;
2389     }
2390
2391     if (state == MediaPlayer::Idle) {
2392         if (m_networkState > NETWORK_IDLE) {
2393             changeNetworkStateFromLoadingToIdle();
2394             setShouldDelayLoadEvent(false);
2395         } else {
2396             m_networkState = NETWORK_IDLE;
2397         }
2398     }
2399
2400     if (state == MediaPlayer::Loading) {
2401         if (m_networkState < NETWORK_LOADING || m_networkState == NETWORK_NO_SOURCE)
2402             startProgressEventTimer();
2403         m_networkState = NETWORK_LOADING;
2404     }
2405
2406     if (state == MediaPlayer::Loaded) {
2407         if (m_networkState != NETWORK_IDLE)
2408             changeNetworkStateFromLoadingToIdle();
2409         m_completelyLoaded = true;
2410     }
2411
2412     if (hasMediaControls())
2413         mediaControls()->updateStatusDisplay();
2414 }
2415
2416 void HTMLMediaElement::changeNetworkStateFromLoadingToIdle()
2417 {
2418     m_progressEventTimer.stop();
2419     if (hasMediaControls() && m_player->didLoadingProgress())
2420         mediaControls()->bufferingProgressed();
2421
2422     // Schedule one last progress event so we guarantee that at least one is fired
2423     // for files that load very quickly.
2424     scheduleEvent(eventNames().progressEvent);
2425     scheduleEvent(eventNames().suspendEvent);
2426     m_networkState = NETWORK_IDLE;
2427 }
2428
2429 void HTMLMediaElement::mediaPlayerReadyStateChanged(MediaPlayer*)
2430 {
2431     beginProcessingMediaPlayerCallback();
2432
2433     setReadyState(m_player->readyState());
2434
2435     endProcessingMediaPlayerCallback();
2436 }
2437
2438 SuccessOr<MediaPlaybackDenialReason> HTMLMediaElement::canTransitionFromAutoplayToPlay() const
2439 {
2440     if (isAutoplaying()
2441         && mediaSession().autoplayPermitted()
2442         && paused()
2443         && autoplay()
2444         && !pausedForUserInteraction()
2445         && !document().isSandboxed(SandboxAutomaticFeatures)
2446         && m_readyState == HAVE_ENOUGH_DATA)
2447         return mediaSession().playbackPermitted();
2448
2449     ALWAYS_LOG(LOGIDENTIFIER, "page consent required");
2450     return MediaPlaybackDenialReason::PageConsentRequired;
2451 }
2452
2453 void HTMLMediaElement::dispatchPlayPauseEventsIfNeedsQuirks()
2454 {
2455     auto& document = this->document();
2456     if (!needsAutoplayPlayPauseEventsQuirk(document) && !needsAutoplayPlayPauseEventsQuirk(document.topDocument()))
2457         return;
2458
2459     ALWAYS_LOG(LOGIDENTIFIER);
2460     scheduleEvent(eventNames().playingEvent);
2461     scheduleEvent(eventNames().pauseEvent);
2462 }
2463
2464 void HTMLMediaElement::setReadyState(MediaPlayer::ReadyState state)
2465 {
2466     // Set "wasPotentiallyPlaying" BEFORE updating m_readyState, potentiallyPlaying() uses it
2467     bool wasPotentiallyPlaying = potentiallyPlaying();
2468
2469     ReadyState oldState = m_readyState;
2470     ReadyState newState = static_cast<ReadyState>(state);
2471
2472 #if ENABLE(VIDEO_TRACK)
2473     bool tracksAreReady = textTracksAreReady();
2474
2475     if (newState == oldState && m_tracksAreReady == tracksAreReady)
2476         return;
2477
2478     m_tracksAreReady = tracksAreReady;
2479 #else
2480     if (newState == oldState)
2481         return;
2482     bool tracksAreReady = true;
2483 #endif
2484
2485     ALWAYS_LOG(LOGIDENTIFIER, "new state = ", state, ", current state = ", m_readyState);
2486
2487     if (tracksAreReady)
2488         m_readyState = newState;
2489     else {
2490         // If a media file has text tracks the readyState may not progress beyond HAVE_FUTURE_DATA until
2491         // the text tracks are ready, regardless of the state of the media file.
2492         if (newState <= HAVE_METADATA)
2493             m_readyState = newState;
2494         else
2495             m_readyState = HAVE_CURRENT_DATA;
2496     }
2497
2498     if (oldState > m_readyStateMaximum)
2499         m_readyStateMaximum = oldState;
2500
2501     if (m_networkState == NETWORK_EMPTY)
2502         return;
2503
2504     if (m_seeking) {
2505         // 4.8.10.9, step 11
2506         if (wasPotentiallyPlaying && m_readyState < HAVE_FUTURE_DATA)
2507             scheduleEvent(eventNames().waitingEvent);
2508
2509         // 4.8.10.10 step 14 & 15.
2510         if (m_seekRequested && !m_player->seeking() && m_readyState >= HAVE_CURRENT_DATA)
2511             finishSeek();
2512     } else {
2513         if (wasPotentiallyPlaying && m_readyState < HAVE_FUTURE_DATA) {
2514             // 4.8.10.8
2515             invalidateCachedTime();
2516             scheduleTimeupdateEvent(false);
2517             scheduleEvent(eventNames().waitingEvent);
2518         }
2519     }
2520
2521     if (m_readyState >= HAVE_METADATA && oldState < HAVE_METADATA) {
2522         prepareMediaFragmentURI();
2523         scheduleEvent(eventNames().durationchangeEvent);
2524         scheduleResizeEvent();
2525         scheduleEvent(eventNames().loadedmetadataEvent);
2526 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
2527         if (hasEventListeners(eventNames().webkitplaybacktargetavailabilitychangedEvent))
2528             enqueuePlaybackTargetAvailabilityChangedEvent();
2529 #endif
2530         m_initiallyMuted = m_volume < 0.05 || muted();
2531
2532         if (hasMediaControls())
2533             mediaControls()->loadedMetadata();
2534         updateRenderer();
2535
2536         if (is<MediaDocument>(document()))
2537             downcast<MediaDocument>(document()).mediaElementNaturalSizeChanged(expandedIntSize(m_player->naturalSize()));
2538
2539         logMediaLoadRequest(document().page(), m_player->engineDescription(), String(), true);
2540
2541 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
2542         updateMediaState(UpdateState::Asynchronously);
2543 #endif
2544
2545         m_mediaSession->clientCharacteristicsChanged();
2546     }
2547
2548     bool shouldUpdateDisplayState = false;
2549
2550     if (m_readyState >= HAVE_CURRENT_DATA && oldState < HAVE_CURRENT_DATA) {
2551         if (!m_haveFiredLoadedData) {
2552             m_haveFiredLoadedData = true;
2553             scheduleEvent(eventNames().loadeddataEvent);
2554             // FIXME: It's not clear that it's correct to skip these two operations just
2555             // because m_haveFiredLoadedData is already true. At one time we were skipping
2556             // the call to setShouldDelayLoadEvent, which was definitely incorrect.
2557             shouldUpdateDisplayState = true;
2558             applyMediaFragmentURI();
2559         }
2560         setShouldDelayLoadEvent(false);
2561     }
2562
2563     if (m_readyState == HAVE_FUTURE_DATA && oldState <= HAVE_CURRENT_DATA && tracksAreReady) {
2564         scheduleEvent(eventNames().canplayEvent);
2565         shouldUpdateDisplayState = true;
2566     }
2567
2568     if (m_readyState == HAVE_ENOUGH_DATA && oldState < HAVE_ENOUGH_DATA && tracksAreReady) {
2569         if (oldState <= HAVE_CURRENT_DATA)
2570             scheduleEvent(eventNames().canplayEvent);
2571
2572         scheduleEvent(eventNames().canplaythroughEvent);
2573
2574         auto success = canTransitionFromAutoplayToPlay();
2575         if (success) {
2576             m_paused = false;
2577             invalidateCachedTime();
2578             setPlaybackWithoutUserGesture(PlaybackWithoutUserGesture::Started);
2579             m_playbackStartedTime = currentMediaTime().toDouble();
2580             scheduleEvent(eventNames().playEvent);
2581         } else if (success.value() == MediaPlaybackDenialReason::UserGestureRequired) {
2582             ALWAYS_LOG(LOGIDENTIFIER, "Autoplay blocked, user gesture required");
2583             setPlaybackWithoutUserGesture(PlaybackWithoutUserGesture::Prevented);
2584         }
2585
2586         shouldUpdateDisplayState = true;
2587     }
2588
2589     // If we transition to the Future Data state and we're about to begin playing, ensure playback is actually permitted first,
2590     // honoring any playback denial reasons such as the requirement of a user gesture.
2591     if (m_readyState == HAVE_FUTURE_DATA && oldState < HAVE_FUTURE_DATA && potentiallyPlaying() && !m_mediaSession->playbackPermitted()) {
2592         auto canTransition = canTransitionFromAutoplayToPlay();
2593         if (canTransition && canTransition.value() == MediaPlaybackDenialReason::UserGestureRequired)
2594             ALWAYS_LOG(LOGIDENTIFIER, "Autoplay blocked, user gesture required");
2595
2596         pauseInternal();
2597         setPlaybackWithoutUserGesture(PlaybackWithoutUserGesture::Prevented);
2598     }
2599
2600     if (shouldUpdateDisplayState) {
2601         updateDisplayState();
2602         if (hasMediaControls()) {
2603             mediaControls()->refreshClosedCaptionsButtonVisibility();
2604             mediaControls()->updateStatusDisplay();
2605         }
2606     }
2607
2608     updatePlayState();
2609     updateMediaController();
2610 #if ENABLE(VIDEO_TRACK)
2611     updateActiveTextTrackCues(currentMediaTime());
2612 #endif
2613 }
2614
2615 #if ENABLE(LEGACY_ENCRYPTED_MEDIA)
2616 RefPtr<ArrayBuffer> HTMLMediaElement::mediaPlayerCachedKeyForKeyId(const String& keyId) const
2617 {
2618     return m_webKitMediaKeys ? m_webKitMediaKeys->cachedKeyForKeyId(keyId) : nullptr;
2619 }
2620
2621 bool HTMLMediaElement::mediaPlayerKeyNeeded(MediaPlayer*, Uint8Array* initData)
2622 {
2623     if (!RuntimeEnabledFeatures::sharedFeatures().legacyEncryptedMediaAPIEnabled())
2624         return false;
2625
2626     if (!hasEventListeners("webkitneedkey")
2627 #if ENABLE(ENCRYPTED_MEDIA)
2628         // Only fire an error if ENCRYPTED_MEDIA is not enabled, to give clients of the 
2629         // "encrypted" event a chance to handle it without resulting in a synthetic error.
2630         && !RuntimeEnabledFeatures::sharedFeatures().encryptedMediaAPIEnabled()
2631 #endif
2632         ) {
2633         m_error = MediaError::create(MediaError::MEDIA_ERR_ENCRYPTED);
2634         scheduleEvent(eventNames().errorEvent);
2635         return false;
2636     }
2637
2638     auto event = WebKitMediaKeyNeededEvent::create(eventNames().webkitneedkeyEvent, initData);
2639     event->setTarget(this);
2640     m_asyncEventQueue.enqueueEvent(WTFMove(event));
2641
2642     return true;
2643 }
2644
2645 String HTMLMediaElement::mediaPlayerMediaKeysStorageDirectory() const
2646 {
2647     auto* page = document().page();
2648     if (!page || page->usesEphemeralSession())
2649         return emptyString();
2650
2651     String storageDirectory = document().settings().mediaKeysStorageDirectory();
2652     if (storageDirectory.isEmpty())
2653         return emptyString();
2654
2655     return FileSystem::pathByAppendingComponent(storageDirectory, document().securityOrigin().data().databaseIdentifier());
2656 }
2657
2658 void HTMLMediaElement::webkitSetMediaKeys(WebKitMediaKeys* mediaKeys)
2659 {
2660     if (!RuntimeEnabledFeatures::sharedFeatures().legacyEncryptedMediaAPIEnabled())
2661         return;
2662
2663     if (m_webKitMediaKeys == mediaKeys)
2664         return;
2665
2666     if (m_webKitMediaKeys)
2667         m_webKitMediaKeys->setMediaElement(nullptr);
2668     m_webKitMediaKeys = mediaKeys;
2669     if (m_webKitMediaKeys)
2670         m_webKitMediaKeys->setMediaElement(this);
2671 }
2672
2673 void HTMLMediaElement::keyAdded()
2674 {
2675     if (!RuntimeEnabledFeatures::sharedFeatures().legacyEncryptedMediaAPIEnabled())
2676         return;
2677
2678     if (m_player)
2679         m_player->keyAdded();
2680 }
2681
2682 #endif
2683
2684 #if ENABLE(ENCRYPTED_MEDIA)
2685
2686 MediaKeys* HTMLMediaElement::mediaKeys() const
2687 {
2688     return m_mediaKeys.get();
2689 }
2690
2691 void HTMLMediaElement::setMediaKeys(MediaKeys* mediaKeys, Ref<DeferredPromise>&& promise)
2692 {
2693     // https://w3c.github.io/encrypted-media/#dom-htmlmediaelement-setmediakeys
2694     // W3C Editor's Draft 23 June 2017
2695
2696     // 1. If this object's attaching media keys value is true, return a promise rejected with an InvalidStateError.
2697     if (m_attachingMediaKeys) {
2698         promise->reject(InvalidStateError);
2699         return;
2700     }
2701
2702     // 2. If mediaKeys and the mediaKeys attribute are the same object, return a resolved promise.
2703     if (mediaKeys == m_mediaKeys) {
2704         promise->resolve();
2705         return;
2706     }
2707
2708     // 3. Let this object's attaching media keys value be true.
2709     m_attachingMediaKeys = true;
2710
2711     // 4. Let promise be a new promise.
2712     // 5. Run the following steps in parallel:
2713     m_encryptedMediaQueue.enqueueTask([this, mediaKeys = RefPtr<MediaKeys>(mediaKeys), promise = WTFMove(promise)]() mutable {
2714         // 5.1. If all the following conditions hold:
2715         //      - mediaKeys is not null,
2716         //      - the CDM instance represented by mediaKeys is already in use by another media element
2717         //      - the user agent is unable to use it with this element
2718         //      then let this object's attaching media keys value be false and reject promise with a QuotaExceededError.
2719         // FIXME: ^
2720
2721         // 5.2. If the mediaKeys attribute is not null, run the following steps:
2722         if (m_mediaKeys) {
2723             // 5.2.1. If the user agent or CDM do not support removing the association, let this object's attaching media keys value be false and reject promise with a NotSupportedError.
2724             // 5.2.2. If the association cannot currently be removed, let this object's attaching media keys value be false and reject promise with an InvalidStateError.
2725             // 5.2.3. Stop using the CDM instance represented by the mediaKeys attribute to decrypt media data and remove the association with the media element.
2726             // 5.2.4. If the preceding step failed, let this object's attaching media keys value be false and reject promise with the appropriate error name.
2727             // FIXME: ^
2728
2729             m_mediaKeys->detachCDMClient(*this);
2730             if (m_player)
2731                 m_player->cdmInstanceDetached(m_mediaKeys->cdmInstance());
2732         }
2733
2734         // 5.3. If mediaKeys is not null, run the following steps:
2735         if (mediaKeys) {
2736             // 5.3.1. Associate the CDM instance represented by mediaKeys with the media element for decrypting media data.
2737             mediaKeys->attachCDMClient(*this);
2738             if (m_player)
2739                 m_player->cdmInstanceAttached(mediaKeys->cdmInstance());
2740
2741             // 5.3.2. If the preceding step failed, run the following steps:
2742             //   5.3.2.1. Set the mediaKeys attribute to null.
2743             //   5.3.2.2. Let this object's attaching media keys value be false.
2744             //   5.3.2.3. Reject promise with a new DOMException whose name is the appropriate error name.
2745             // FIXME: ^
2746
2747             // 5.3.3. Queue a task to run the Attempt to Resume Playback If Necessary algorithm on the media element.
2748             m_encryptedMediaQueue.enqueueTask([this] {
2749                 attemptToResumePlaybackIfNecessary();
2750             });
2751         }
2752
2753         // 5.4. Set the mediaKeys attribute to mediaKeys.
2754         // 5.5. Let this object's attaching media keys value be false.
2755         // 5.6. Resolve promise.
2756         m_mediaKeys = WTFMove(mediaKeys);
2757         m_attachingMediaKeys = false;
2758         promise->resolve();
2759     });
2760
2761     // 6. Return promise.
2762 }
2763
2764 void HTMLMediaElement::mediaPlayerInitializationDataEncountered(const String& initDataType, RefPtr<ArrayBuffer>&& initData)
2765 {
2766     if (!RuntimeEnabledFeatures::sharedFeatures().encryptedMediaAPIEnabled())
2767         return;
2768
2769     // https://w3c.github.io/encrypted-media/#initdata-encountered
2770     // W3C Editor's Draft 23 June 2017
2771
2772     // 1. Let the media element be the specified HTMLMediaElement object.
2773     // 2. Let initDataType be the empty string.
2774     // 3. Let initData be null.
2775     // 4. If the media data is CORS-same-origin and not mixed content, run the following steps:
2776     //   4.1. Let initDataType be the string representing the Initialization Data Type of the Initialization Data.
2777     //   4.2. Let initData be the Initialization Data.
2778     // FIXME: ^
2779
2780     // 5. Queue a task to create an event named encrypted that does not bubble and is not cancellable using the
2781     //    MediaEncryptedEvent interface with its type attribute set to encrypted and its isTrusted attribute
2782     //    initialized to true, and dispatch it at the media element.
2783     //    The event interface MediaEncryptedEvent has:
2784     //      initDataType = initDataType
2785     //      initData = initData
2786     MediaEncryptedEventInit initializer { initDataType, WTFMove(initData) };
2787     m_asyncEventQueue.enqueueEvent(MediaEncryptedEvent::create(eventNames().encryptedEvent, initializer, Event::IsTrusted::Yes));
2788 }
2789
2790 void HTMLMediaElement::attemptToDecrypt()
2791 {
2792     // https://w3c.github.io/encrypted-media/#attempt-to-decrypt
2793     // W3C Editor's Draft 23 June 2017
2794
2795     // 1. Let the media element be the specified HTMLMediaElement object.
2796     // 2. If the media element's encrypted block queue is empty, abort these steps.
2797     // FIXME: ^
2798
2799     // 3. If the media element's mediaKeys attribute is not null, run the following steps:
2800     if (m_mediaKeys) {
2801         // 3.1. Let media keys be the MediaKeys object referenced by that attribute.
2802         // 3.2. Let cdm be the CDM instance represented by media keys's cdm instance value.
2803         auto& cdmInstance = m_mediaKeys->cdmInstance();
2804
2805         // 3.3. If cdm is no longer usable for any reason, run the following steps:
2806         //   3.3.1. Run the media data is corrupted steps of the resource fetch algorithm.
2807         //   3.3.2. Run the CDM Unavailable algorithm on media keys.
2808         //   3.3.3. Abort these steps.
2809         // FIXME: ^
2810
2811         // 3.4. If there is at least one MediaKeySession created by the media keys that is not closed, run the following steps:
2812         if (m_mediaKeys->hasOpenSessions()) {
2813             // Continued in MediaPlayer::attemptToDecryptWithInstance().
2814             if (m_player)
2815                 m_player->attemptToDecryptWithInstance(cdmInstance);
2816         }
2817     }
2818
2819     // 4. Set the media element's decryption blocked waiting for key value to true.
2820     // FIXME: ^
2821 }
2822
2823 void HTMLMediaElement::attemptToResumePlaybackIfNecessary()
2824 {
2825     // https://w3c.github.io/encrypted-media/#resume-playback
2826     // W3C Editor's Draft 23 June 2017
2827
2828     // 1. Let the media element be the specified HTMLMediaElement object.
2829     // 2. If the media element's playback blocked waiting for key is false, abort these steps.
2830     // FIXME: ^
2831
2832     // 3. Run the Attempt to Decrypt algorithm on the media element.
2833     attemptToDecrypt();
2834
2835     // 4. If the user agent can advance the current playback position in the direction of playback:
2836     //   4.1. Set the media element's decryption blocked waiting for key value to false.
2837     //   4.2. Set the media element's playback blocked waiting for key value to false.
2838     //   4.3. Set the media element's readyState value to HAVE_CURRENT_DATA, HAVE_FUTURE_DATA or HAVE_ENOUGH_DATA as appropriate.
2839     // FIXME: ^
2840 }
2841
2842 void HTMLMediaElement::cdmClientAttemptToResumePlaybackIfNecessary()
2843 {
2844     attemptToResumePlaybackIfNecessary();
2845 }
2846
2847 #endif // ENABLE(ENCRYPTED_MEDIA)
2848
2849 void HTMLMediaElement::progressEventTimerFired()
2850 {
2851     ASSERT(m_player);
2852     if (m_networkState != NETWORK_LOADING)
2853         return;
2854
2855     MonotonicTime time = MonotonicTime::now();
2856     Seconds timedelta = time - m_previousProgressTime;
2857
2858     if (m_player->didLoadingProgress()) {
2859         scheduleEvent(eventNames().progressEvent);
2860         m_previousProgressTime = time;
2861         m_sentStalledEvent = false;
2862         updateRenderer();
2863         if (hasMediaControls())
2864             mediaControls()->bufferingProgressed();
2865     } else if (timedelta > 3_s && !m_sentStalledEvent) {
2866         scheduleEvent(eventNames().stalledEvent);
2867         m_sentStalledEvent = true;
2868         setShouldDelayLoadEvent(false);
2869     }
2870 }
2871
2872 void HTMLMediaElement::rewind(double timeDelta)
2873 {
2874     setCurrentTime(std::max(currentMediaTime() - MediaTime::createWithDouble(timeDelta), minTimeSeekable()));
2875 }
2876
2877 void HTMLMediaElement::returnToRealtime()
2878 {
2879     setCurrentTime(maxTimeSeekable());
2880 }
2881
2882 void HTMLMediaElement::addPlayedRange(const MediaTime& start, const MediaTime& end)
2883 {
2884     DEBUG_LOG(LOGIDENTIFIER, MediaTimeRange { start, end });
2885     if (!m_playedTimeRanges)
2886         m_playedTimeRanges = TimeRanges::create();
2887     m_playedTimeRanges->ranges().add(start, end);
2888 }
2889
2890 bool HTMLMediaElement::supportsScanning() const
2891 {
2892     return m_player ? m_player->supportsScanning() : false;
2893 }
2894
2895 void HTMLMediaElement::prepareToPlay()
2896 {
2897     INFO_LOG(LOGIDENTIFIER);
2898     if (m_havePreparedToPlay || !document().hasBrowsingContext())
2899         return;
2900     m_havePreparedToPlay = true;
2901     if (m_player)
2902         m_player->prepareToPlay();
2903 }
2904
2905 void HTMLMediaElement::fastSeek(double time)
2906 {
2907     fastSeek(MediaTime::createWithDouble(time));
2908 }
2909
2910 void HTMLMediaElement::fastSeek(const MediaTime& time)
2911 {
2912     INFO_LOG(LOGIDENTIFIER, time);
2913     // 4.7.10.9 Seeking
2914     // 9. If the approximate-for-speed flag is set, adjust the new playback position to a value that will
2915     // allow for playback to resume promptly. If new playback position before this step is before current
2916     // playback position, then the adjusted new playback position must also be before the current playback
2917     // position. Similarly, if the new playback position before this step is after current playback position,
2918     // then the adjusted new playback position must also be after the current playback position.
2919     refreshCachedTime();
2920
2921     MediaTime delta = time - currentMediaTime();
2922     MediaTime negativeTolerance = delta < MediaTime::zeroTime() ? MediaTime::positiveInfiniteTime() : delta;
2923     seekWithTolerance(time, negativeTolerance, MediaTime::zeroTime(), true);
2924 }
2925
2926 void HTMLMediaElement::seek(const MediaTime& time)
2927 {
2928     INFO_LOG(LOGIDENTIFIER, time);
2929     seekWithTolerance(time, MediaTime::zeroTime(), MediaTime::zeroTime(), true);
2930 }
2931
2932 void HTMLMediaElement::seekInternal(const MediaTime& time)
2933 {
2934     INFO_LOG(LOGIDENTIFIER, time);
2935     seekWithTolerance(time, MediaTime::zeroTime(), MediaTime::zeroTime(), false);
2936 }
2937
2938 void HTMLMediaElement::seekWithTolerance(const MediaTime& inTime, const MediaTime& negativeTolerance, const MediaTime& positiveTolerance, bool fromDOM)
2939 {
2940     // 4.8.10.9 Seeking
2941     MediaTime time = inTime;
2942
2943     // 1 - Set the media element's show poster flag to false.
2944     setDisplayMode(Video);
2945
2946     // 2 - If the media element's readyState is HAVE_NOTHING, abort these steps.
2947     if (m_readyState == HAVE_NOTHING || !m_player)
2948         return;
2949
2950     // If the media engine has been told to postpone loading data, let it go ahead now.
2951     if (m_preload < MediaPlayer::Auto && m_readyState < HAVE_FUTURE_DATA)
2952         prepareToPlay();
2953
2954     // Get the current time before setting m_seeking, m_lastSeekTime is returned once it is set.
2955     refreshCachedTime();
2956     MediaTime now = currentMediaTime();
2957
2958     // 3 - If the element's seeking IDL attribute is true, then another instance of this algorithm is
2959     // already running. Abort that other instance of the algorithm without waiting for the step that
2960     // it is running to complete.
2961     if (m_seekTaskQueue.hasPendingTasks()) {
2962         INFO_LOG(LOGIDENTIFIER, "cancelling pending seeks");
2963         m_seekTaskQueue.cancelAllTasks();
2964         if (m_pendingSeek) {
2965             now = m_pendingSeek->now;
2966             m_pendingSeek = nullptr;
2967         }
2968         m_pendingSeekType = NoSeek;
2969     }
2970
2971     // 4 - Set the seeking IDL attribute to true.
2972     // The flag will be cleared when the engine tells us the time has actually changed.
2973     m_seeking = true;
2974     if (m_playing) {
2975         if (m_lastSeekTime < now)
2976             addPlayedRange(m_lastSeekTime, now);
2977     }
2978     m_lastSeekTime = time;
2979
2980     // 5 - If the seek was in response to a DOM method call or setting of an IDL attribute, then continue
2981     // the script. The remainder of these steps must be run asynchronously.
2982     m_pendingSeek = std::make_unique<PendingSeek>(now, time, negativeTolerance, positiveTolerance);
2983     if (fromDOM) {
2984         INFO_LOG(LOGIDENTIFIER, "enqueuing seek from ", now, " to ", time);
2985         m_seekTaskQueue.enqueueTask(std::bind(&HTMLMediaElement::seekTask, this));
2986     } else
2987         seekTask();
2988
2989     if (processingUserGestureForMedia())
2990         m_mediaSession->removeBehaviorRestriction(MediaElementSession::RequireUserGestureToControlControlsManager);
2991 }
2992
2993 void HTMLMediaElement::seekTask()
2994 {
2995     INFO_LOG(LOGIDENTIFIER);
2996
2997     if (!m_player) {
2998         clearSeeking();
2999         return;
3000     }
3001
3002     ASSERT(m_pendingSeek);
3003     MediaTime now = m_pendingSeek->now;
3004     MediaTime time = m_pendingSeek->targetTime;
3005     MediaTime negativeTolerance = m_pendingSeek->negativeTolerance;
3006     MediaTime positiveTolerance = m_pendingSeek->positiveTolerance;
3007     m_pendingSeek = nullptr;
3008
3009     ASSERT(negativeTolerance >= MediaTime::zeroTime());
3010
3011     // 6 - If the new playback position is later than the end of the media resource, then let it be the end
3012     // of the media resource instead.
3013     time = std::min(time, durationMediaTime());
3014
3015     // 7 - If the new playback position is less than the earliest possible position, let it be that position instead.
3016     MediaTime earliestTime = m_player->startTime();
3017     time = std::max(time, earliestTime);
3018
3019     // Ask the media engine for the time value in the movie's time scale before comparing with current time. This
3020     // is necessary because if the seek time is not equal to currentTime but the delta is less than the movie's
3021     // time scale, we will ask the media engine to "seek" to the current movie time, which may be a noop and
3022     // not generate a timechanged callback. This means m_seeking will never be cleared and we will never
3023     // fire a 'seeked' event.
3024     if (willLog(WTFLogLevelDebug)) {
3025         MediaTime mediaTime = m_player->mediaTimeForTimeValue(time);
3026         if (time != mediaTime)
3027             DEBUG_LOG(LOGIDENTIFIER, time, " media timeline equivalent is ", mediaTime);
3028     }
3029
3030     time = m_player->mediaTimeForTimeValue(time);
3031
3032     // 8 - If the (possibly now changed) new playback position is not in one of the ranges given in the
3033     // seekable attribute, then let it be the position in one of the ranges given in the seekable attribute
3034     // that is the nearest to the new playback position. ... If there are no ranges given in the seekable
3035     // attribute then set the seeking IDL attribute to false and abort these steps.
3036     RefPtr<TimeRanges> seekableRanges = seekable();
3037     bool noSeekRequired = !seekableRanges->length();
3038
3039     // Short circuit seeking to the current time by just firing the events if no seek is required.
3040     // Don't skip calling the media engine if 1) we are in poster mode (because a seek should always cancel
3041     // poster display), or 2) if there is a pending fast seek, or 3) if this seek is not an exact seek
3042     SeekType thisSeekType = (negativeTolerance == MediaTime::zeroTime() && positiveTolerance == MediaTime::zeroTime()) ? Precise : Fast;
3043     if (!noSeekRequired && time == now && thisSeekType == Precise && m_pendingSeekType != Fast && displayMode() != Poster)
3044         noSeekRequired = true;
3045
3046 #if ENABLE(MEDIA_SOURCE)
3047     // Always notify the media engine of a seek if the source is not closed. This ensures that the source is
3048     // always in a flushed state when the 'seeking' event fires.
3049     if (m_mediaSource && !m_mediaSource->isClosed())
3050         noSeekRequired = false;
3051 #endif
3052
3053     if (noSeekRequired) {
3054         INFO_LOG(LOGIDENTIFIER, "seek to ", time, " ignored");
3055         if (time == now) {
3056             scheduleEvent(eventNames().seekingEvent);
3057             scheduleTimeupdateEvent(false);
3058             scheduleEvent(eventNames().seekedEvent);
3059         }
3060         clearSeeking();
3061         return;
3062     }
3063     time = seekableRanges->ranges().nearest(time);
3064
3065     m_sentEndEvent = false;
3066     m_lastSeekTime = time;
3067     m_pendingSeekType = thisSeekType;
3068     m_seeking = true;
3069
3070     // 10 - Queue a task to fire a simple event named seeking at the element.
3071     scheduleEvent(eventNames().seekingEvent);
3072
3073     // 11 - Set the current playback position to the given new playback position
3074     m_seekRequested = true;
3075     m_player->seekWithTolerance(time, negativeTolerance, positiveTolerance);
3076
3077     // 12 - Wait until the user agent has established whether or not the media data for the new playback
3078     // position is available, and, if it is, until it has decoded enough data to play back that position.
3079     // 13 - Await a stable state. The synchronous section consists of all the remaining steps of this algorithm.
3080 }
3081
3082 void HTMLMediaElement::clearSeeking()
3083 {
3084     m_seeking = false;
3085     m_seekRequested = false;
3086     m_pendingSeekType = NoSeek;
3087     invalidateCachedTime();
3088 }
3089
3090 void HTMLMediaElement::finishSeek()
3091 {
3092     // 4.8.10.9 Seeking
3093     // 14 - Set the seeking IDL attribute to false.
3094     clearSeeking();
3095
3096     INFO_LOG(LOGIDENTIFIER, "current time = ", currentMediaTime());
3097
3098     // 15 - Run the time maches on steps.
3099     // Handled by mediaPlayerTimeChanged().
3100
3101     // 16 - Queue a task to fire a simple event named timeupdate at the element.
3102     scheduleEvent(eventNames().timeupdateEvent);
3103
3104     // 17 - Queue a task to fire a simple event named seeked at the element.
3105     scheduleEvent(eventNames().seekedEvent);
3106
3107     if (m_mediaSession)
3108         m_mediaSession->clientCharacteristicsChanged();
3109
3110 #if ENABLE(MEDIA_SOURCE)
3111     if (m_mediaSource)
3112         m_mediaSource->monitorSourceBuffers();
3113 #endif
3114 }
3115
3116 HTMLMediaElement::ReadyState HTMLMediaElement::readyState() const
3117 {
3118     return m_readyState;
3119 }
3120
3121 MediaPlayer::MovieLoadType HTMLMediaElement::movieLoadType() const
3122 {
3123     return m_player ? m_player->movieLoadType() : MediaPlayer::Unknown;
3124 }
3125
3126 bool HTMLMediaElement::hasAudio() const
3127 {
3128     return m_player ? m_player->hasAudio() : false;
3129 }
3130
3131 bool HTMLMediaElement::seeking() const
3132 {
3133     return m_seeking;
3134 }
3135
3136 void HTMLMediaElement::refreshCachedTime() const
3137 {
3138     if (!m_player)
3139         return;
3140
3141     m_cachedTime = m_player->currentTime();
3142     if (!m_cachedTime) {
3143         // Do not use m_cachedTime until the media engine returns a non-zero value because we can't
3144         // estimate current time until playback actually begins.
3145         invalidateCachedTime();
3146         return;
3147     }
3148
3149     m_clockTimeAtLastCachedTimeUpdate = MonotonicTime::now();
3150 }
3151
3152 void HTMLMediaElement::invalidateCachedTime() const
3153 {
3154     m_cachedTime = MediaTime::invalidTime();
3155     if (!m_player || !m_player->maximumDurationToCacheMediaTime())
3156         return;
3157
3158     // Don't try to cache movie time when playback first starts as the time reported by the engine
3159     // sometimes fluctuates for a short amount of time, so the cached time will be off if we take it
3160     // too early.
3161     static const Seconds minimumTimePlayingBeforeCacheSnapshot = 500_ms;
3162
3163     m_minimumClockTimeToUpdateCachedTime = MonotonicTime::now() + minimumTimePlayingBeforeCacheSnapshot;
3164 }
3165
3166 // playback state
3167 double HTMLMediaElement::currentTime() const
3168 {
3169     return currentMediaTime().toDouble();
3170 }
3171
3172 MediaTime HTMLMediaElement::currentMediaTime() const
3173 {
3174 #if LOG_CACHED_TIME_WARNINGS
3175     static const MediaTime minCachedDeltaForWarning = MediaTime::create(1, 100);
3176 #endif
3177
3178     if (!m_player)
3179         return MediaTime::zeroTime();
3180
3181     if (m_seeking) {
3182         INFO_LOG(LOGIDENTIFIER, "seeking, returning", m_lastSeekTime);
3183         return m_lastSeekTime;
3184     }
3185
3186     if (m_cachedTime.isValid() && m_paused) {
3187 #if LOG_CACHED_TIME_WARNINGS
3188         MediaTime delta = m_cachedTime - m_player->currentTime();
3189         if (delta > minCachedDeltaForWarning)
3190             WARNING_LOG(LOGIDENTIFIER, "cached time is ", delta, " seconds off of media time when paused");
3191 #endif
3192         return m_cachedTime;
3193     }
3194
3195     // Is it too soon use a cached time?
3196     MonotonicTime now = MonotonicTime::now();
3197     double maximumDurationToCacheMediaTime = m_player->maximumDurationToCacheMediaTime();
3198
3199     if (maximumDurationToCacheMediaTime && m_cachedTime.isValid() && !m_paused && now > m_minimumClockTimeToUpdateCachedTime) {
3200         Seconds clockDelta = now - m_clockTimeAtLastCachedTimeUpdate;
3201
3202         // Not too soon, use the cached time only if it hasn't expired.
3203         if (clockDelta.seconds() < maximumDurationToCacheMediaTime) {
3204             MediaTime adjustedCacheTime = m_cachedTime + MediaTime::createWithDouble(effectivePlaybackRate() * clockDelta.seconds());
3205
3206 #if LOG_CACHED_TIME_WARNINGS
3207             MediaTime delta = adjustedCacheTime - m_player->currentTime();
3208             if (delta > minCachedDeltaForWarning)
3209                 WARNING_LOG(LOGIDENTIFIER, "cached time is ", delta, " seconds off of media time when playing");
3210 #endif
3211             return adjustedCacheTime;
3212         }
3213     }
3214
3215 #if LOG_CACHED_TIME_WARNINGS
3216     if (maximumDurationToCacheMediaTime && now > m_minimumClockTimeToUpdateCachedTime && m_cachedTime != MediaPlayer::invalidTime()) {
3217         Seconds clockDelta = now - m_clockTimeAtLastCachedTimeUpdate;
3218         MediaTime delta = m_cachedTime + MediaTime::createWithDouble(effectivePlaybackRate() * clockDelta.seconds()) - m_player->currentTime();
3219         WARNING_LOG(LOGIDENTIFIER, "cached time was ", delta, " seconds off of media time when it expired");
3220     }
3221 #endif
3222
3223     refreshCachedTime();
3224
3225     if (m_cachedTime.isInvalid())
3226         return MediaTime::zeroTime();
3227
3228     return m_cachedTime;
3229 }
3230
3231 void HTMLMediaElement::setCurrentTime(double time)
3232 {
3233     setCurrentTime(MediaTime::createWithDouble(time));
3234 }
3235
3236 void HTMLMediaElement::setCurrentTimeWithTolerance(double time, double toleranceBefore, double toleranceAfter)
3237 {
3238     seekWithTolerance(MediaTime::createWithDouble(time), MediaTime::createWithDouble(toleranceBefore), MediaTime::createWithDouble(toleranceAfter), true);
3239 }
3240
3241 void HTMLMediaElement::setCurrentTime(const MediaTime& time)
3242 {
3243     if (m_mediaController)
3244         return;
3245
3246     seekInternal(time);
3247 }
3248
3249 ExceptionOr<void> HTMLMediaElement::setCurrentTimeForBindings(double time)
3250 {
3251     if (m_mediaController)
3252         return Exception { InvalidStateError };
3253     seek(MediaTime::createWithDouble(time));
3254     return { };
3255 }
3256
3257 double HTMLMediaElement::duration() const
3258 {
3259     return durationMediaTime().toDouble();
3260 }
3261
3262 MediaTime HTMLMediaElement::durationMediaTime() const
3263 {
3264     if (m_player && m_readyState >= HAVE_METADATA)
3265         return m_player->duration();
3266
3267     return MediaTime::invalidTime();
3268 }
3269
3270 bool HTMLMediaElement::paused() const
3271 {
3272     // As of this writing, JavaScript garbage collection calls this function directly. In the past
3273     // we had problems where this was called on an object after a bad cast. The assertion below
3274     // made our regression test detect the problem, so we should keep it because of that. But note
3275     // that the value of the assertion relies on the compiler not being smart enough to know that
3276     // isHTMLUnknownElement is guaranteed to return false for an HTMLMediaElement.
3277     ASSERT(!isHTMLUnknownElement());
3278
3279     return m_paused;
3280 }
3281
3282 double HTMLMediaElement::defaultPlaybackRate() const
3283 {
3284 #if ENABLE(MEDIA_STREAM)
3285     // http://w3c.github.io/mediacapture-main/#mediastreams-in-media-elements
3286     // "defaultPlaybackRate" - On setting: ignored. On getting: return 1.0
3287     // A MediaStream is not seekable. Therefore, this attribute must always have the
3288     // value 1.0 and any attempt to alter it must be ignored. Note that this also means
3289     // that the ratechange event will not fire.
3290     if (m_mediaStreamSrcObject)
3291         return 1;
3292 #endif
3293
3294     return m_defaultPlaybackRate;
3295 }
3296
3297 void HTMLMediaElement::setDefaultPlaybackRate(double rate)
3298 {
3299 #if ENABLE(MEDIA_STREAM)
3300     // http://w3c.github.io/mediacapture-main/#mediastreams-in-media-elements
3301     // "defaultPlaybackRate" - On setting: ignored. On getting: return 1.0
3302     // A MediaStream is not seekable. Therefore, this attribute must always have the
3303     // value 1.0 and any attempt to alter it must be ignored. Note that this also means
3304     // that the ratechange event will not fire.
3305     if (m_mediaStreamSrcObject)
3306         return;
3307 #endif
3308
3309     if (m_defaultPlaybackRate == rate)
3310         return;
3311
3312     ALWAYS_LOG(LOGIDENTIFIER, rate);
3313     m_defaultPlaybackRate = rate;
3314     scheduleEvent(eventNames().ratechangeEvent);
3315 }
3316
3317 double HTMLMediaElement::effectivePlaybackRate() const
3318 {
3319     return m_mediaController ? m_mediaController->playbackRate() : m_reportedPlaybackRate;
3320 }
3321
3322 double HTMLMediaElement::requestedPlaybackRate() const
3323 {
3324     return m_mediaController ? m_mediaController->playbackRate() : m_requestedPlaybackRate;
3325 }
3326
3327 double HTMLMediaElement::playbackRate() const
3328 {
3329 #if ENABLE(MEDIA_STREAM)
3330     // http://w3c.github.io/mediacapture-main/#mediastreams-in-media-elements
3331     // "playbackRate" - A MediaStream is not seekable. Therefore, this attribute must always
3332     // have the value 1.0 and any attempt to alter it must be ignored. Note that this also
3333     // means that the ratechange event will not fire.
3334     if (m_mediaStreamSrcObject)
3335         return 1;
3336 #endif
3337
3338     return m_requestedPlaybackRate;
3339 }
3340
3341 void HTMLMediaElement::setPlaybackRate(double rate)
3342 {
3343     ALWAYS_LOG(LOGIDENTIFIER, rate);
3344
3345 #if ENABLE(MEDIA_STREAM)
3346     // http://w3c.github.io/mediacapture-main/#mediastreams-in-media-elements
3347     // "playbackRate" - A MediaStream is not seekable. Therefore, this attribute must always
3348     // have the value 1.0 and any attempt to alter it must be ignored. Note that this also
3349     // means that the ratechange event will not fire.
3350     if (m_mediaStreamSrcObject)
3351         return;
3352 #endif
3353
3354     if (m_player && potentiallyPlaying() && m_player->rate() != rate && !m_mediaController)
3355         m_player->setRate(rate);
3356
3357     if (m_requestedPlaybackRate != rate) {
3358         m_reportedPlaybackRate = m_requestedPlaybackRate = rate;
3359         invalidateCachedTime();
3360         scheduleEvent(eventNames().ratechangeEvent);
3361     }
3362 }
3363
3364 void HTMLMediaElement::updatePlaybackRate()
3365 {
3366     double requestedRate = requestedPlaybackRate();
3367     if (m_player && potentiallyPlaying() && m_player->rate() != requestedRate)
3368         m_player->setRate(requestedRate);
3369 }
3370
3371 bool HTMLMediaElement::webkitPreservesPitch() const
3372 {
3373     return m_webkitPreservesPitch;
3374 }
3375
3376 void HTMLMediaElement::setWebkitPreservesPitch(bool preservesPitch)
3377 {
3378     INFO_LOG(LOGIDENTIFIER, preservesPitch);
3379
3380     m_webkitPreservesPitch = preservesPitch;
3381
3382     if (!m_player)
3383         return;
3384
3385     m_player->setPreservesPitch(preservesPitch);
3386 }
3387
3388 bool HTMLMediaElement::ended() const
3389 {
3390 #if ENABLE(MEDIA_STREAM)
3391     // http://w3c.github.io/mediacapture-main/#mediastreams-in-media-elements
3392     // When the MediaStream state moves from the active to the inactive state, the User Agent
3393     // must raise an ended event on the HTMLMediaElement and set its ended attribute to true.
3394     if (m_mediaStreamSrcObject && m_player && m_player->ended())
3395         return true;
3396 #endif
3397
3398     // 4.8.10.8 Playing the media resource
3399     // The ended attribute must return true if the media element has ended
3400     // playback and the direction of playback is forwards, and false otherwise.
3401     return endedPlayback() && requestedPlaybackRate() > 0;
3402 }
3403
3404 bool HTMLMediaElement::autoplay() const
3405 {
3406     return hasAttributeWithoutSynchronization(autoplayAttr);
3407 }
3408
3409 String HTMLMediaElement::preload() const
3410 {
3411 #if ENABLE(MEDIA_STREAM)
3412     // http://w3c.github.io/mediacapture-main/#mediastreams-in-media-elements
3413     // "preload" - On getting: none. On setting: ignored.
3414     if (m_mediaStreamSrcObject)
3415         return "none"_s;
3416 #endif
3417
3418     switch (m_preload) {
3419     case MediaPlayer::None:
3420         return "none"_s;
3421     case MediaPlayer::MetaData:
3422         return "metadata"_s;
3423     case MediaPlayer::Auto:
3424         return "auto"_s;
3425     }
3426
3427     ASSERT_NOT_REACHED();
3428     return String();
3429 }
3430
3431 void HTMLMediaElement::setPreload(const String& preload)
3432 {
3433     INFO_LOG(LOGIDENTIFIER, preload);
3434 #if ENABLE(MEDIA_STREAM)
3435     // http://w3c.github.io/mediacapture-main/#mediastreams-in-media-elements
3436     // "preload" - On getting: none. On setting: ignored.
3437     if (m_mediaStreamSrcObject)
3438         return;
3439 #endif
3440
3441     setAttributeWithoutSynchronization(preloadAttr, preload);
3442 }
3443
3444 void HTMLMediaElement::play(DOMPromiseDeferred<void>&& promise)
3445 {
3446     ALWAYS_LOG(LOGIDENTIFIER);
3447
3448     auto success = m_mediaSession->playbackPermitted();
3449     if (!success) {
3450         if (success.value() == MediaPlaybackDenialReason::UserGestureRequired)
3451             setPlaybackWithoutUserGesture(PlaybackWithoutUserGesture::Prevented);
3452         promise.reject(NotAllowedError);
3453         return;
3454     }
3455
3456     if (m_error && m_error->code() == MediaError::MEDIA_ERR_SRC_NOT_SUPPORTED) {
3457         promise.reject(NotSupportedError, "The operation is not supported.");
3458         return;
3459     }
3460
3461     if (processingUserGestureForMedia())
3462         removeBehaviorsRestrictionsAfterFirstUserGesture();
3463
3464     m_pendingPlayPromises.append(WTFMove(promise));
3465     playInternal();
3466 }
3467
3468 void HTMLMediaElement::play()
3469 {
3470     ALWAYS_LOG(LOGIDENTIFIER);
3471
3472     auto success = m_mediaSession->playbackPermitted();
3473     if (!success) {