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