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