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