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