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