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