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