b446242d5012f3a4b952a2f87ae270d388b23275
[WebKit-https.git] / Source / WebCore / html / HTMLMediaElement.cpp
1 /*
2  * Copyright (C) 2007-2015 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 #if ENABLE(VIDEO)
28 #include "HTMLMediaElement.h"
29
30 #include "ApplicationCacheHost.h"
31 #include "ApplicationCacheResource.h"
32 #include "Attribute.h"
33 #include "CSSPropertyNames.h"
34 #include "CSSValueKeywords.h"
35 #include "ChromeClient.h"
36 #include "ClientRect.h"
37 #include "ClientRectList.h"
38 #include "ContentSecurityPolicy.h"
39 #include "ContentType.h"
40 #include "CookieJar.h"
41 #include "DiagnosticLoggingClient.h"
42 #include "DiagnosticLoggingKeys.h"
43 #include "DisplaySleepDisabler.h"
44 #include "Document.h"
45 #include "DocumentLoader.h"
46 #include "ElementIterator.h"
47 #include "EventNames.h"
48 #include "ExceptionCodePlaceholder.h"
49 #include "FrameLoader.h"
50 #include "FrameLoaderClient.h"
51 #include "FrameView.h"
52 #include "HTMLSourceElement.h"
53 #include "HTMLVideoElement.h"
54 #include "JSHTMLMediaElement.h"
55 #include "Language.h"
56 #include "Logging.h"
57 #include "MIMETypeRegistry.h"
58 #include "MainFrame.h"
59 #include "MediaController.h"
60 #include "MediaControls.h"
61 #include "MediaDocument.h"
62 #include "MediaError.h"
63 #include "MediaFragmentURIParser.h"
64 #include "MediaKeyEvent.h"
65 #include "MediaList.h"
66 #include "MediaPlayer.h"
67 #include "MediaQueryEvaluator.h"
68 #include "MediaResourceLoader.h"
69 #include "MemoryPressureHandler.h"
70 #include "NetworkingContext.h"
71 #include "NoEventDispatchAssertion.h"
72 #include "PageGroup.h"
73 #include "PageThrottler.h"
74 #include "PlatformMediaSessionManager.h"
75 #include "ProgressTracker.h"
76 #include "RenderLayerCompositor.h"
77 #include "RenderVideo.h"
78 #include "RenderView.h"
79 #include "ResourceLoadInfo.h"
80 #include "ScriptController.h"
81 #include "ScriptSourceCode.h"
82 #include "SecurityPolicy.h"
83 #include "SessionID.h"
84 #include "Settings.h"
85 #include "ShadowRoot.h"
86 #include "TimeRanges.h"
87 #include "UserContentController.h"
88 #include <limits>
89 #include <runtime/Uint8Array.h>
90 #include <wtf/CurrentTime.h>
91 #include <wtf/MathExtras.h>
92 #include <wtf/Ref.h>
93 #include <wtf/text/CString.h>
94
95 #if ENABLE(VIDEO_TRACK)
96 #include "AudioTrackList.h"
97 #include "HTMLTrackElement.h"
98 #include "InbandGenericTextTrack.h"
99 #include "InbandTextTrackPrivate.h"
100 #include "InbandWebVTTTextTrack.h"
101 #include "RuntimeEnabledFeatures.h"
102 #include "TextTrackCueList.h"
103 #include "TextTrackList.h"
104 #include "VideoTrackList.h"
105 #endif
106
107 #if ENABLE(WEB_AUDIO)
108 #include "AudioSourceProvider.h"
109 #include "MediaElementAudioSourceNode.h"
110 #endif
111
112 #if PLATFORM(IOS) || (PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE))
113 #include "WebVideoFullscreenInterface.h"
114 #endif
115
116 #if PLATFORM(IOS)
117 #include "RuntimeApplicationChecks.h"
118 #include "WebVideoFullscreenInterfaceAVKit.h"
119 #endif
120
121 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
122 #include "WebKitPlaybackTargetAvailabilityEvent.h"
123 #endif
124
125 #if ENABLE(MEDIA_SESSION)
126 #include "MediaSession.h"
127 #endif
128
129 #if ENABLE(MEDIA_SOURCE)
130 #include "DOMWindow.h"
131 #include "MediaSource.h"
132 #include "Performance.h"
133 #include "VideoPlaybackQuality.h"
134 #endif
135
136 #if ENABLE(MEDIA_STREAM)
137 #include "DOMURL.h"
138 #include "MediaStream.h"
139 #include "MediaStreamRegistry.h"
140 #endif
141
142 #if ENABLE(ENCRYPTED_MEDIA_V2)
143 #include "MediaKeyNeededEvent.h"
144 #include "MediaKeys.h"
145 #endif
146
147 #if ENABLE(MEDIA_CONTROLS_SCRIPT)
148 #include "JSMediaControlsHost.h"
149 #include "MediaControlsHost.h"
150 #include "ScriptGlobalObject.h"
151 #include <bindings/ScriptObject.h>
152 #endif
153
154 namespace WebCore {
155
156 static const double SeekRepeatDelay = 0.1;
157 static const double SeekTime = 0.2;
158 static const double ScanRepeatDelay = 1.5;
159 static const double ScanMaximumRate = 8;
160
161 static void setFlags(unsigned& value, unsigned flags)
162 {
163     value |= flags;
164 }
165
166 static void clearFlags(unsigned& value, unsigned flags)
167 {
168     value &= ~flags;
169 }
170     
171 #if !LOG_DISABLED
172 static String urlForLoggingMedia(const URL& url)
173 {
174     static const unsigned maximumURLLengthForLogging = 128;
175
176     if (url.string().length() < maximumURLLengthForLogging)
177         return url.string();
178     return url.string().substring(0, maximumURLLengthForLogging) + "...";
179 }
180
181 static const char* boolString(bool val)
182 {
183     return val ? "true" : "false";
184 }
185
186 static String actionName(HTMLMediaElementEnums::DelayedActionType action)
187 {
188     StringBuilder actionBuilder;
189
190 #define ACTION(_actionType) \
191     if (action & (HTMLMediaElementEnums::_actionType)) { \
192         if (!actionBuilder.isEmpty()) \
193         actionBuilder.append(", "); \
194         actionBuilder.append(#_actionType); \
195     } \
196
197     ACTION(LoadMediaResource);
198     ACTION(ConfigureTextTracks);
199     ACTION(TextTrackChangesNotification);
200     ACTION(ConfigureTextTrackDisplay);
201     ACTION(CheckPlaybackTargetCompatablity);
202     ACTION(CheckMediaState);
203     ACTION(MediaEngineUpdated);
204
205     return actionBuilder.toString();
206
207 #undef ACTION
208 }
209
210 #endif
211
212 #ifndef LOG_MEDIA_EVENTS
213 // Default to not logging events because so many are generated they can overwhelm the rest of 
214 // the logging.
215 #define LOG_MEDIA_EVENTS 0
216 #endif
217
218 #ifndef LOG_CACHED_TIME_WARNINGS
219 // Default to not logging warnings about excessive drift in the cached media time because it adds a
220 // fair amount of overhead and logging.
221 #define LOG_CACHED_TIME_WARNINGS 0
222 #endif
223
224 #if ENABLE(MEDIA_SOURCE)
225 // URL protocol used to signal that the media source API is being used.
226 static const char* mediaSourceBlobProtocol = "blob";
227 #endif
228
229 #if ENABLE(MEDIA_STREAM)
230 // URL protocol used to signal that the media stream API is being used.
231 static const char* mediaStreamBlobProtocol = "blob";
232 #endif
233
234 using namespace HTMLNames;
235
236 typedef HashMap<Document*, HashSet<HTMLMediaElement*>> DocumentElementSetMap;
237 static DocumentElementSetMap& documentToElementSetMap()
238 {
239     static NeverDestroyed<DocumentElementSetMap> map;
240     return map;
241 }
242
243 static void addElementToDocumentMap(HTMLMediaElement& element, Document& document)
244 {
245     DocumentElementSetMap& map = documentToElementSetMap();
246     HashSet<HTMLMediaElement*> set = map.take(&document);
247     set.add(&element);
248     map.add(&document, set);
249 }
250
251 static void removeElementFromDocumentMap(HTMLMediaElement& element, Document& document)
252 {
253     DocumentElementSetMap& map = documentToElementSetMap();
254     HashSet<HTMLMediaElement*> set = map.take(&document);
255     set.remove(&element);
256     if (!set.isEmpty())
257         map.add(&document, set);
258 }
259
260 #if ENABLE(ENCRYPTED_MEDIA)
261 static ExceptionCode exceptionCodeForMediaKeyException(MediaPlayer::MediaKeyException exception)
262 {
263     switch (exception) {
264     case MediaPlayer::NoError:
265         return 0;
266     case MediaPlayer::InvalidPlayerState:
267         return INVALID_STATE_ERR;
268     case MediaPlayer::KeySystemNotSupported:
269         return NOT_SUPPORTED_ERR;
270     }
271
272     ASSERT_NOT_REACHED();
273     return INVALID_STATE_ERR;
274 }
275 #endif
276
277 #if ENABLE(VIDEO_TRACK)
278 class TrackDisplayUpdateScope {
279 public:
280     TrackDisplayUpdateScope(HTMLMediaElement* mediaElement)
281     {
282         m_mediaElement = mediaElement;
283         m_mediaElement->beginIgnoringTrackDisplayUpdateRequests();
284     }
285     ~TrackDisplayUpdateScope()
286     {
287         ASSERT(m_mediaElement);
288         m_mediaElement->endIgnoringTrackDisplayUpdateRequests();
289     }
290     
291 private:
292     HTMLMediaElement* m_mediaElement;
293 };
294 #endif
295
296 struct HTMLMediaElement::TrackGroup {
297     enum GroupKind { CaptionsAndSubtitles, Description, Chapter, Metadata, Other };
298
299     TrackGroup(GroupKind kind)
300         : visibleTrack(0)
301         , defaultTrack(0)
302         , kind(kind)
303         , hasSrcLang(false)
304     {
305     }
306
307     Vector<RefPtr<TextTrack>> tracks;
308     RefPtr<TextTrack> visibleTrack;
309     RefPtr<TextTrack> defaultTrack;
310     GroupKind kind;
311     bool hasSrcLang;
312 };
313
314 HashSet<HTMLMediaElement*>& HTMLMediaElement::allMediaElements()
315 {
316     static NeverDestroyed<HashSet<HTMLMediaElement*>> elements;
317     return elements;
318 }
319
320 #if ENABLE(MEDIA_SESSION)
321 typedef HashMap<uint64_t, HTMLMediaElement*> IDToElementMap;
322
323 static IDToElementMap& elementIDsToElements()
324 {
325     static NeverDestroyed<IDToElementMap> map;
326     return map;
327 }
328
329 HTMLMediaElement* HTMLMediaElement::elementWithID(uint64_t id)
330 {
331     if (id == HTMLMediaElementInvalidID)
332         return nullptr;
333     
334     return elementIDsToElements().get(id);
335 }
336
337 static uint64_t nextElementID()
338 {
339     static uint64_t elementID = 0;
340     return ++elementID;
341 }
342 #endif
343
344 HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document& document, bool createdByParser)
345     : HTMLElement(tagName, document)
346     , ActiveDOMObject(&document)
347     , m_pendingActionTimer(*this, &HTMLMediaElement::pendingActionTimerFired)
348     , m_progressEventTimer(*this, &HTMLMediaElement::progressEventTimerFired)
349     , m_playbackProgressTimer(*this, &HTMLMediaElement::playbackProgressTimerFired)
350     , m_scanTimer(*this, &HTMLMediaElement::scanTimerFired)
351     , m_seekTaskQueue(document)
352     , m_resizeTaskQueue(document)
353     , m_shadowDOMTaskQueue(document)
354     , m_playedTimeRanges()
355     , m_asyncEventQueue(*this)
356     , m_requestedPlaybackRate(1)
357     , m_reportedPlaybackRate(1)
358     , m_defaultPlaybackRate(1)
359     , m_webkitPreservesPitch(true)
360     , m_networkState(NETWORK_EMPTY)
361     , m_readyState(HAVE_NOTHING)
362     , m_readyStateMaximum(HAVE_NOTHING)
363     , m_volume(1.0f)
364     , m_volumeInitialized(false)
365     , m_previousProgressTime(std::numeric_limits<double>::max())
366     , m_clockTimeAtLastUpdateEvent(0)
367     , m_lastTimeUpdateEventMovieTime(MediaTime::positiveInfiniteTime())
368     , m_loadState(WaitingForSource)
369     , m_videoFullscreenMode(VideoFullscreenModeNone)
370 #if PLATFORM(IOS) || (PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE))
371     , m_videoFullscreenGravity(MediaPlayer::VideoGravityResizeAspect)
372 #endif
373     , m_preload(MediaPlayer::Auto)
374     , m_displayMode(Unknown)
375     , m_processingMediaPlayerCallback(0)
376 #if ENABLE(MEDIA_SOURCE)
377     , m_droppedVideoFrames(0)
378 #endif
379     , m_clockTimeAtLastCachedTimeUpdate(0)
380     , m_minimumClockTimeToUpdateCachedTime(0)
381     , m_pendingActionFlags(0)
382     , m_actionAfterScan(Nothing)
383     , m_scanType(Scan)
384     , m_scanDirection(Forward)
385     , m_firstTimePlaying(true)
386     , m_playing(false)
387     , m_isWaitingUntilMediaCanStart(false)
388     , m_shouldDelayLoadEvent(false)
389     , m_haveFiredLoadedData(false)
390     , m_inActiveDocument(true)
391     , m_autoplaying(true)
392     , m_muted(false)
393     , m_explicitlyMuted(false)
394     , m_initiallyMuted(false)
395     , m_paused(true)
396     , m_seeking(false)
397     , m_sentStalledEvent(false)
398     , m_sentEndEvent(false)
399     , m_pausedInternal(false)
400     , m_sendProgressEvents(true)
401     , m_closedCaptionsVisible(false)
402     , m_webkitLegacyClosedCaptionOverride(false)
403     , m_completelyLoaded(false)
404     , m_havePreparedToPlay(false)
405     , m_parsingInProgress(createdByParser)
406     , m_elementIsHidden(document.hidden())
407     , m_creatingControls(false)
408 #if ENABLE(MEDIA_CONTROLS_SCRIPT)
409     , m_mediaControlsDependOnPageScaleFactor(false)
410     , m_haveSetUpCaptionContainer(false)
411 #endif
412 #if ENABLE(VIDEO_TRACK)
413     , m_tracksAreReady(true)
414     , m_haveVisibleTextTrack(false)
415     , m_processingPreferenceChange(false)
416     , m_lastTextTrackUpdateTime(MediaTime(-1, 1))
417     , m_captionDisplayMode(CaptionUserPreferences::Automatic)
418     , m_audioTracks(0)
419     , m_textTracks(0)
420     , m_videoTracks(0)
421     , m_ignoreTrackDisplayUpdate(0)
422 #endif
423 #if ENABLE(WEB_AUDIO)
424     , m_audioSourceNode(0)
425 #endif
426     , m_mediaSession(std::make_unique<MediaElementSession>(*this))
427     , m_reportedExtraMemoryCost(0)
428 #if ENABLE(MEDIA_STREAM)
429     , m_mediaStreamSrcObject(nullptr)
430 #endif
431 {
432     allMediaElements().add(this);
433
434     LOG(Media, "HTMLMediaElement::HTMLMediaElement(%p)", this);
435     setHasCustomStyleResolveCallbacks();
436
437     m_mediaSession->addBehaviorRestriction(MediaElementSession::RequireUserGestureForFullscreen);
438     m_mediaSession->addBehaviorRestriction(MediaElementSession::RequirePageConsentToLoadMedia);
439 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
440     m_mediaSession->addBehaviorRestriction(MediaElementSession::RequireUserGestureToAutoplayToExternalDevice);
441 #endif
442
443     Settings* settings = document.settings();
444 #if PLATFORM(IOS)
445     m_sendProgressEvents = false;
446 #endif
447     if (!settings || settings->videoPlaybackRequiresUserGesture()) {
448         // Allow autoplay in a MediaDocument that is not in an iframe.
449         if (document.ownerElement() || !document.isMediaDocument()) {
450             m_mediaSession->addBehaviorRestriction(MediaElementSession::RequireUserGestureForVideoRateChange);
451             m_mediaSession->addBehaviorRestriction(MediaElementSession::RequireUserGestureForLoad);
452         }
453 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
454         m_mediaSession->addBehaviorRestriction(MediaElementSession::RequireUserGestureToShowPlaybackTargetPicker);
455 #endif
456     }
457 #if PLATFORM(IOS)
458     else {
459         // Relax RequireUserGestureForFullscreen when videoPlaybackRequiresUserGesture is not set:
460         m_mediaSession->removeBehaviorRestriction(MediaElementSession::RequireUserGestureForFullscreen);
461     }
462 #endif
463     if (settings && settings->invisibleAutoplayNotPermitted())
464         m_mediaSession->addBehaviorRestriction(MediaElementSession::InvisibleAutoplayNotPermitted);
465
466     if (settings && settings->audioPlaybackRequiresUserGesture())
467         m_mediaSession->addBehaviorRestriction(MediaElementSession::RequireUserGestureForAudioRateChange);
468
469     if (!settings || !settings->mediaDataLoadsAutomatically())
470         m_mediaSession->addBehaviorRestriction(MediaElementSession::AutoPreloadingNotPermitted);
471
472 #if ENABLE(VIDEO_TRACK)
473     if (document.page())
474         m_captionDisplayMode = document.page()->group().captionPreferences().captionDisplayMode();
475 #endif
476
477     if (settings && settings->mainContentUserGestureOverrideEnabled())
478         m_mediaSession->addBehaviorRestriction(MediaElementSession::OverrideUserGestureRequirementForMainContent);
479
480 #if ENABLE(MEDIA_SESSION)
481     m_elementID = nextElementID();
482     elementIDsToElements().add(m_elementID, this);
483
484     setSessionInternal(document.defaultMediaSession());
485 #endif
486
487     registerWithDocument(document);
488 }
489
490 HTMLMediaElement::~HTMLMediaElement()
491 {
492     LOG(Media, "HTMLMediaElement::~HTMLMediaElement(%p)", this);
493
494     allMediaElements().remove(this);
495
496     m_asyncEventQueue.close();
497
498     setShouldDelayLoadEvent(false);
499     unregisterWithDocument(document());
500
501 #if ENABLE(VIDEO_TRACK)
502     if (m_audioTracks) {
503         m_audioTracks->clearElement();
504         for (unsigned i = 0; i < m_audioTracks->length(); ++i)
505             m_audioTracks->item(i)->clearClient();
506     }
507     if (m_textTracks)
508         m_textTracks->clearElement();
509     if (m_textTracks) {
510         for (unsigned i = 0; i < m_textTracks->length(); ++i)
511             m_textTracks->item(i)->clearClient();
512     }
513     if (m_videoTracks) {
514         m_videoTracks->clearElement();
515         for (unsigned i = 0; i < m_videoTracks->length(); ++i)
516             m_videoTracks->item(i)->clearClient();
517     }
518 #endif
519
520 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
521     if (hasEventListeners(eventNames().webkitplaybacktargetavailabilitychangedEvent)) {
522         m_hasPlaybackTargetAvailabilityListeners = false;
523         m_mediaSession->setHasPlaybackTargetAvailabilityListeners(*this, false);
524         updateMediaState();
525     }
526 #endif
527
528     if (m_mediaController) {
529         m_mediaController->removeMediaElement(this);
530         m_mediaController = nullptr;
531     }
532
533 #if ENABLE(MEDIA_SOURCE)
534     closeMediaSource();
535 #endif
536
537 #if ENABLE(ENCRYPTED_MEDIA_V2)
538     setMediaKeys(0);
539 #endif
540
541 #if ENABLE(MEDIA_CONTROLS_SCRIPT)
542     if (m_isolatedWorld)
543         m_isolatedWorld->clearWrappers();
544 #endif
545
546 #if ENABLE(MEDIA_SESSION)
547     if (m_session) {
548         m_session->removeMediaElement(*this);
549         m_session = nullptr;
550     }
551
552     elementIDsToElements().remove(m_elementID);
553 #endif
554
555     m_seekTaskQueue.close();
556
557     m_completelyLoaded = true;
558 }
559
560 void HTMLMediaElement::registerWithDocument(Document& document)
561 {
562     m_mediaSession->registerWithDocument(document);
563
564     if (m_isWaitingUntilMediaCanStart)
565         document.addMediaCanStartListener(this);
566
567 #if !PLATFORM(IOS)
568     document.registerForMediaVolumeCallbacks(this);
569     document.registerForPrivateBrowsingStateChangedCallbacks(this);
570 #endif
571
572     document.registerForVisibilityStateChangedCallbacks(this);
573
574 #if ENABLE(VIDEO_TRACK)
575     if (m_requireCaptionPreferencesChangedCallbacks)
576         document.registerForCaptionPreferencesChangedCallbacks(this);
577 #endif
578
579 #if ENABLE(MEDIA_CONTROLS_SCRIPT)
580     if (m_mediaControlsDependOnPageScaleFactor)
581         document.registerForPageScaleFactorChangedCallbacks(this);
582 #endif
583
584 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
585     document.registerForDocumentSuspensionCallbacks(this);
586 #endif
587
588     document.registerForAllowsMediaDocumentInlinePlaybackChangedCallbacks(*this);
589
590     document.addAudioProducer(this);
591     addElementToDocumentMap(*this, document);
592 }
593
594 void HTMLMediaElement::unregisterWithDocument(Document& document)
595 {
596     m_mediaSession->unregisterWithDocument(document);
597
598     if (m_isWaitingUntilMediaCanStart)
599         document.removeMediaCanStartListener(this);
600
601 #if !PLATFORM(IOS)
602     document.unregisterForMediaVolumeCallbacks(this);
603     document.unregisterForPrivateBrowsingStateChangedCallbacks(this);
604 #endif
605
606     document.unregisterForVisibilityStateChangedCallbacks(this);
607
608 #if ENABLE(VIDEO_TRACK)
609     if (m_requireCaptionPreferencesChangedCallbacks)
610         document.unregisterForCaptionPreferencesChangedCallbacks(this);
611 #endif
612
613 #if ENABLE(MEDIA_CONTROLS_SCRIPT)
614     if (m_mediaControlsDependOnPageScaleFactor)
615         document.unregisterForPageScaleFactorChangedCallbacks(this);
616 #endif
617
618 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
619     document.unregisterForDocumentSuspensionCallbacks(this);
620 #endif
621
622     document.unregisterForAllowsMediaDocumentInlinePlaybackChangedCallbacks(*this);
623
624     document.removeAudioProducer(this);
625     removeElementFromDocumentMap(*this, document);
626 }
627
628 void HTMLMediaElement::didMoveToNewDocument(Document* oldDocument)
629 {
630     if (m_shouldDelayLoadEvent) {
631         if (oldDocument)
632             oldDocument->decrementLoadEventDelayCount();
633         document().incrementLoadEventDelayCount();
634     }
635
636     if (oldDocument)
637         unregisterWithDocument(*oldDocument);
638
639     registerWithDocument(document());
640
641     HTMLElement::didMoveToNewDocument(oldDocument);
642     updateShouldAutoplay();
643 }
644
645 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
646 void HTMLMediaElement::prepareForDocumentSuspension()
647 {
648     m_mediaSession->unregisterWithDocument(document());
649 }
650
651 void HTMLMediaElement::resumeFromDocumentSuspension()
652 {
653     m_mediaSession->registerWithDocument(document());
654     updateShouldAutoplay();
655 }
656 #endif
657
658 bool HTMLMediaElement::hasCustomFocusLogic() const
659 {
660     return true;
661 }
662
663 bool HTMLMediaElement::supportsFocus() const
664 {
665     if (document().isMediaDocument())
666         return false;
667
668     // If no controls specified, we should still be able to focus the element if it has tabIndex.
669     return controls() ||  HTMLElement::supportsFocus();
670 }
671
672 bool HTMLMediaElement::isMouseFocusable() const
673 {
674     return false;
675 }
676
677 void HTMLMediaElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
678 {
679     if (name == srcAttr) {
680 #if PLATFORM(IOS)
681         // Note, unless the restriction on requiring user action has been removed,
682         // do not begin downloading data on iOS.
683         if (!value.isNull() && m_mediaSession->dataLoadingPermitted(*this))
684 #else
685         // Trigger a reload, as long as the 'src' attribute is present.
686         if (!value.isNull())
687 #endif
688         {
689             clearMediaPlayer(LoadMediaResource);
690             scheduleDelayedAction(LoadMediaResource);
691         }
692     } else if (name == controlsAttr)
693         configureMediaControls();
694     else if (name == loopAttr)
695         updateSleepDisabling();
696     else if (name == preloadAttr) {
697         if (equalLettersIgnoringASCIICase(value, "none"))
698             m_preload = MediaPlayer::None;
699         else if (equalLettersIgnoringASCIICase(value, "metadata"))
700             m_preload = MediaPlayer::MetaData;
701         else {
702             // The spec does not define an "invalid value default" but "auto" is suggested as the
703             // "missing value default", so use it for everything except "none" and "metadata"
704             m_preload = MediaPlayer::Auto;
705         }
706
707         // The attribute must be ignored if the autoplay attribute is present
708         if (!autoplay() && m_player)
709             m_player->setPreload(m_mediaSession->effectivePreloadForElement(*this));
710
711     } else if (name == mediagroupAttr)
712         setMediaGroup(value);
713     else
714         HTMLElement::parseAttribute(name, value);
715 }
716
717 void HTMLMediaElement::finishParsingChildren()
718 {
719     HTMLElement::finishParsingChildren();
720     m_parsingInProgress = false;
721     
722 #if ENABLE(VIDEO_TRACK)
723     if (!RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled())
724         return;
725
726     if (descendantsOfType<HTMLTrackElement>(*this).first())
727         scheduleDelayedAction(ConfigureTextTracks);
728 #endif
729 }
730
731 bool HTMLMediaElement::rendererIsNeeded(const RenderStyle& style)
732 {
733     return controls() && HTMLElement::rendererIsNeeded(style);
734 }
735
736 RenderPtr<RenderElement> HTMLMediaElement::createElementRenderer(Ref<RenderStyle>&& style, const RenderTreePosition&)
737 {
738     return createRenderer<RenderMedia>(*this, WTFMove(style));
739 }
740
741 bool HTMLMediaElement::childShouldCreateRenderer(const Node& child) const
742 {
743 #if ENABLE(MEDIA_CONTROLS_SCRIPT)
744     return hasShadowRootParent(child) && HTMLElement::childShouldCreateRenderer(child);
745 #else
746     if (!hasMediaControls())
747         return false;
748     // <media> doesn't allow its content, including shadow subtree, to
749     // be rendered. So this should return false for most of the children.
750     // One exception is a shadow tree built for rendering controls which should be visible.
751     // So we let them go here by comparing its subtree root with one of the controls.
752     return &mediaControls()->treeScope() == &child.treeScope()
753         && hasShadowRootParent(child)
754         && HTMLElement::childShouldCreateRenderer(child);
755 #endif
756 }
757
758 Node::InsertionNotificationRequest HTMLMediaElement::insertedInto(ContainerNode& insertionPoint)
759 {
760     LOG(Media, "HTMLMediaElement::insertedInto(%p)", this);
761
762     HTMLElement::insertedInto(insertionPoint);
763     if (insertionPoint.inDocument()) {
764         m_inActiveDocument = true;
765
766 #if PLATFORM(IOS)
767         if (m_networkState == NETWORK_EMPTY && !fastGetAttribute(srcAttr).isEmpty() && m_mediaSession->dataLoadingPermitted(*this))
768 #else
769         if (m_networkState == NETWORK_EMPTY && !fastGetAttribute(srcAttr).isEmpty())
770 #endif
771             scheduleDelayedAction(LoadMediaResource);
772     }
773
774     if (!m_explicitlyMuted) {
775         m_explicitlyMuted = true;
776         m_muted = fastHasAttribute(mutedAttr);
777     }
778
779     configureMediaControls();
780     return InsertionDone;
781 }
782
783 void HTMLMediaElement::removedFrom(ContainerNode& insertionPoint)
784 {
785     LOG(Media, "HTMLMediaElement::removedFrom(%p)", this);
786
787     m_inActiveDocument = false;
788     if (insertionPoint.inDocument()) {
789         if (hasMediaControls())
790             mediaControls()->hide();
791         if (m_networkState > NETWORK_EMPTY)
792             pause();
793         if (m_videoFullscreenMode != VideoFullscreenModeNone)
794             exitFullscreen();
795
796         if (m_player) {
797             size_t extraMemoryCost = m_player->extraMemoryCost();
798             if (extraMemoryCost > m_reportedExtraMemoryCost) {
799                 JSC::VM& vm = JSDOMWindowBase::commonVM();
800                 JSC::JSLockHolder lock(vm);
801
802                 size_t extraMemoryCostDelta = extraMemoryCost - m_reportedExtraMemoryCost;
803                 m_reportedExtraMemoryCost = extraMemoryCost;
804                 // FIXME: Adopt reportExtraMemoryVisited, and switch to reportExtraMemoryAllocated.
805                 // https://bugs.webkit.org/show_bug.cgi?id=142595
806                 vm.heap.deprecatedReportExtraMemory(extraMemoryCostDelta);
807             }
808         }
809     }
810
811     HTMLElement::removedFrom(insertionPoint);
812 }
813
814 void HTMLMediaElement::willAttachRenderers()
815 {
816     ASSERT(!renderer());
817 }
818
819 void HTMLMediaElement::didAttachRenderers()
820 {
821     if (RenderElement* renderer = this->renderer()) {
822         renderer->updateFromElement();
823         if (m_mediaSession->hasBehaviorRestriction(MediaElementSession::InvisibleAutoplayNotPermitted)
824             || m_mediaSession->hasBehaviorRestriction(MediaElementSession::OverrideUserGestureRequirementForMainContent))
825             renderer->registerForVisibleInViewportCallback();
826     }
827     updateShouldAutoplay();
828 }
829
830 void HTMLMediaElement::willDetachRenderers()
831 {
832     if (renderer())
833         renderer()->unregisterForVisibleInViewportCallback();
834 }
835
836 void HTMLMediaElement::didDetachRenderers()
837 {
838     updateShouldAutoplay();
839 }
840
841 void HTMLMediaElement::didRecalcStyle(Style::Change)
842 {
843     if (renderer())
844         renderer()->updateFromElement();
845 }
846
847 void HTMLMediaElement::scheduleDelayedAction(DelayedActionType actionType)
848 {
849     LOG(Media, "HTMLMediaElement::scheduleDelayedAction(%p) - setting %s flag", this, actionName(actionType).utf8().data());
850
851     if ((actionType & LoadMediaResource) && !(m_pendingActionFlags & LoadMediaResource)) {
852         prepareForLoad();
853         setFlags(m_pendingActionFlags, LoadMediaResource);
854     }
855
856 #if ENABLE(VIDEO_TRACK)
857     if (RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled() && (actionType & ConfigureTextTracks))
858         setFlags(m_pendingActionFlags, ConfigureTextTracks);
859 #endif
860
861 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
862     if (actionType & CheckPlaybackTargetCompatablity)
863         setFlags(m_pendingActionFlags, CheckPlaybackTargetCompatablity);
864 #endif
865
866     if (actionType & CheckMediaState)
867         setFlags(m_pendingActionFlags, CheckMediaState);
868
869     if (actionType & MediaEngineUpdated)
870         setFlags(m_pendingActionFlags, MediaEngineUpdated);
871
872     m_pendingActionTimer.startOneShot(0);
873 }
874
875 void HTMLMediaElement::scheduleNextSourceChild()
876 {
877     // Schedule the timer to try the next <source> element WITHOUT resetting state ala prepareForLoad.
878     LOG(Media, "HTMLMediaElement::scheduleNextSourceChild(%p) - setting %s flag", this, actionName(LoadMediaResource).utf8().data());
879     setFlags(m_pendingActionFlags, LoadMediaResource);
880     m_pendingActionTimer.startOneShot(0);
881 }
882
883 void HTMLMediaElement::scheduleEvent(const AtomicString& eventName)
884 {
885 #if LOG_MEDIA_EVENTS
886     LOG(Media, "HTMLMediaElement::scheduleEvent(%p) - scheduling '%s'", this, eventName.string().ascii().data());
887 #endif
888     RefPtr<Event> event = Event::create(eventName, false, true);
889     
890     // Don't set the event target, the event queue will set it in GenericEventQueue::timerFired and setting it here
891     // will trigger an ASSERT if this element has been marked for deletion.
892
893     m_asyncEventQueue.enqueueEvent(event.release());
894 }
895
896 void HTMLMediaElement::pendingActionTimerFired()
897 {
898     Ref<HTMLMediaElement> protect(*this); // loadNextSourceChild may fire 'beforeload', which can make arbitrary DOM mutations.
899     PendingActionFlags pendingActions = m_pendingActionFlags;
900     m_pendingActionFlags = 0;
901
902 #if ENABLE(VIDEO_TRACK)
903     if (RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled() && (pendingActions & ConfigureTextTracks))
904         configureTextTracks();
905 #endif
906
907     if (pendingActions & LoadMediaResource) {
908         if (m_loadState == LoadingFromSourceElement)
909             loadNextSourceChild();
910         else
911             loadInternal();
912     }
913
914 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
915     if (pendingActions & CheckPlaybackTargetCompatablity && m_player && m_player->isCurrentPlaybackTargetWireless() && !m_player->canPlayToWirelessPlaybackTarget()) {
916         LOG(Media, "HTMLMediaElement::pendingActionTimerFired(%p) - calling setShouldPlayToPlaybackTarget(false)", this);
917         m_failedToPlayToWirelessTarget = true;
918         m_player->setShouldPlayToPlaybackTarget(false);
919     }
920
921     if (pendingActions & CheckMediaState)
922         updateMediaState();
923 #endif
924
925     if (pendingActions & MediaEngineUpdated)
926         mediaEngineWasUpdated();
927 }
928
929 PassRefPtr<MediaError> HTMLMediaElement::error() const 
930 {
931     return m_error;
932 }
933
934 void HTMLMediaElement::setSrc(const String& url)
935 {
936     setAttribute(srcAttr, url);
937 }
938
939 #if ENABLE(MEDIA_STREAM)
940 void HTMLMediaElement::setSrcObject(ScriptExecutionContext& context, MediaStream* mediaStream)
941 {
942     // FIXME: Setting the srcObject attribute may cause other changes to the media element's internal state:
943     // Specifically, if srcObject is specified, the UA must use it as the source of media, even if the src
944     // attribute is also set or children are present. If the value of srcObject is replaced or set to null
945     // the UA must re-run the media element load algorithm.
946     //
947     // https://bugs.webkit.org/show_bug.cgi?id=124896
948
949     m_mediaStreamSrcObject = mediaStream;
950     setSrc(DOMURL::createPublicURL(context, mediaStream));
951 }
952 #endif
953
954 HTMLMediaElement::NetworkState HTMLMediaElement::networkState() const
955 {
956     return m_networkState;
957 }
958
959 String HTMLMediaElement::canPlayType(const String& mimeType, const String& keySystem, const URL& url) const
960 {
961     MediaEngineSupportParameters parameters;
962     ContentType contentType(mimeType);
963     parameters.type = contentType.type().convertToASCIILowercase();
964     parameters.codecs = contentType.parameter(ASCIILiteral("codecs"));
965     parameters.url = url;
966 #if ENABLE(ENCRYPTED_MEDIA)
967     parameters.keySystem = keySystem;
968 #else
969     UNUSED_PARAM(keySystem);
970 #endif
971     MediaPlayer::SupportsType support = MediaPlayer::supportsType(parameters, this);
972     String canPlay;
973
974     // 4.8.10.3
975     switch (support)
976     {
977         case MediaPlayer::IsNotSupported:
978             canPlay = emptyString();
979             break;
980         case MediaPlayer::MayBeSupported:
981             canPlay = ASCIILiteral("maybe");
982             break;
983         case MediaPlayer::IsSupported:
984             canPlay = ASCIILiteral("probably");
985             break;
986     }
987     
988     LOG(Media, "HTMLMediaElement::canPlayType(%p) - [%s, %s, %s] -> %s", this, mimeType.utf8().data(), keySystem.utf8().data(), url.stringCenterEllipsizedToLength().utf8().data(), canPlay.utf8().data());
989
990     return canPlay;
991 }
992
993 double HTMLMediaElement::getStartDate() const
994 {
995     return m_player->getStartDate().toDouble();
996 }
997
998 void HTMLMediaElement::load()
999 {
1000     Ref<HTMLMediaElement> protect(*this); // loadInternal may result in a 'beforeload' event, which can make arbitrary DOM mutations.
1001     
1002     LOG(Media, "HTMLMediaElement::load(%p)", this);
1003     
1004     if (!m_mediaSession->dataLoadingPermitted(*this))
1005         return;
1006     if (ScriptController::processingUserGestureForMedia())
1007         removeBehaviorsRestrictionsAfterFirstUserGesture();
1008
1009     prepareForLoad();
1010     loadInternal();
1011     prepareToPlay();
1012 }
1013
1014 void HTMLMediaElement::prepareForLoad()
1015 {
1016     LOG(Media, "HTMLMediaElement::prepareForLoad(%p)", this);
1017
1018     // Perform the cleanup required for the resource load algorithm to run.
1019     stopPeriodicTimers();
1020     m_pendingActionTimer.stop();
1021     // FIXME: Figure out appropriate place to reset LoadTextTrackResource if necessary and set m_pendingActionFlags to 0 here.
1022     m_pendingActionFlags &= ~LoadMediaResource;
1023     m_sentEndEvent = false;
1024     m_sentStalledEvent = false;
1025     m_haveFiredLoadedData = false;
1026     m_completelyLoaded = false;
1027     m_havePreparedToPlay = false;
1028     m_displayMode = Unknown;
1029     m_currentSrc = URL();
1030
1031 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
1032     m_failedToPlayToWirelessTarget = false;
1033 #endif
1034
1035     // 1 - Abort any already-running instance of the resource selection algorithm for this element.
1036     m_loadState = WaitingForSource;
1037     m_currentSourceNode = nullptr;
1038
1039     // 2 - If there are any tasks from the media element's media element event task source in 
1040     // one of the task queues, then remove those tasks.
1041     cancelPendingEventsAndCallbacks();
1042
1043     // 3 - If the media element's networkState is set to NETWORK_LOADING or NETWORK_IDLE, queue
1044     // a task to fire a simple event named abort at the media element.
1045     if (m_networkState == NETWORK_LOADING || m_networkState == NETWORK_IDLE)
1046         scheduleEvent(eventNames().abortEvent);
1047
1048 #if ENABLE(MEDIA_SOURCE)
1049     closeMediaSource();
1050 #endif
1051
1052     createMediaPlayer();
1053
1054     // 4 - If the media element's networkState is not set to NETWORK_EMPTY, then run these substeps
1055     if (m_networkState != NETWORK_EMPTY) {
1056         // 4.1 - Queue a task to fire a simple event named emptied at the media element.
1057         scheduleEvent(eventNames().emptiedEvent);
1058
1059         // 4.2 - If a fetching process is in progress for the media element, the user agent should stop it.
1060         m_networkState = NETWORK_EMPTY;
1061
1062         // 4.3 - Forget the media element's media-resource-specific tracks.
1063         forgetResourceSpecificTracks();
1064
1065         // 4.4 - If readyState is not set to HAVE_NOTHING, then set it to that state.
1066         m_readyState = HAVE_NOTHING;
1067         m_readyStateMaximum = HAVE_NOTHING;
1068
1069         // 4.5 - If the paused attribute is false, then set it to true.
1070         m_paused = true;
1071
1072         // 4.6 - If seeking is true, set it to false.
1073         clearSeeking();
1074
1075         // 4.7 - Set the current playback position to 0.
1076         //       Set the official playback position to 0.
1077         //       If this changed the official playback position, then queue a task to fire a simple event named timeupdate at the media element.
1078         // FIXME: Add support for firing this event. e.g., scheduleEvent(eventNames().timeUpdateEvent);
1079
1080         // 4.8 - Set the initial playback position to 0.
1081         // FIXME: Make this less subtle. The position only becomes 0 because of the createMediaPlayer() call
1082         // above.
1083         refreshCachedTime();
1084
1085         invalidateCachedTime();
1086
1087         // 4.9 - Set the timeline offset to Not-a-Number (NaN).
1088         // 4.10 - Update the duration attribute to Not-a-Number (NaN).
1089
1090         updateMediaController();
1091 #if ENABLE(VIDEO_TRACK)
1092         if (RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled())
1093             updateActiveTextTrackCues(MediaTime::zeroTime());
1094 #endif
1095     }
1096
1097     // 5 - Set the playbackRate attribute to the value of the defaultPlaybackRate attribute.
1098     setPlaybackRate(defaultPlaybackRate());
1099
1100     // 6 - Set the error attribute to null and the autoplaying flag to true.
1101     m_error = nullptr;
1102     m_autoplaying = true;
1103     mediaSession().clientWillBeginAutoplaying();
1104
1105     // 7 - Invoke the media element's resource selection algorithm.
1106
1107     // 8 - Note: Playback of any previously playing media resource for this element stops.
1108
1109     // The resource selection algorithm
1110     // 1 - Set the networkState to NETWORK_NO_SOURCE
1111     m_networkState = NETWORK_NO_SOURCE;
1112
1113     // 2 - Asynchronously await a stable state.
1114
1115     m_playedTimeRanges = TimeRanges::create();
1116
1117     // FIXME: Investigate whether these can be moved into m_networkState != NETWORK_EMPTY block above
1118     // so they are closer to the relevant spec steps.
1119     m_lastSeekTime = MediaTime::zeroTime();
1120
1121     // The spec doesn't say to block the load event until we actually run the asynchronous section
1122     // algorithm, but do it now because we won't start that until after the timer fires and the 
1123     // event may have already fired by then.
1124     MediaPlayer::Preload effectivePreload = m_mediaSession->effectivePreloadForElement(*this);
1125     if (effectivePreload != MediaPlayer::None)
1126         setShouldDelayLoadEvent(true);
1127
1128 #if PLATFORM(IOS)
1129     if (effectivePreload != MediaPlayer::None && m_mediaSession->allowsAutomaticMediaDataLoading(*this))
1130         prepareToPlay();
1131 #endif
1132
1133     configureMediaControls();
1134 }
1135
1136 void HTMLMediaElement::loadInternal()
1137 {
1138     // Some of the code paths below this function dispatch the BeforeLoad event. This ASSERT helps
1139     // us catch those bugs more quickly without needing all the branches to align to actually
1140     // trigger the event.
1141     ASSERT(!NoEventDispatchAssertion::isEventDispatchForbidden());
1142
1143     // If we can't start a load right away, start it later.
1144     if (!m_mediaSession->pageAllowsDataLoading(*this)) {
1145         setShouldDelayLoadEvent(false);
1146         if (m_isWaitingUntilMediaCanStart)
1147             return;
1148         document().addMediaCanStartListener(this);
1149         m_isWaitingUntilMediaCanStart = true;
1150         return;
1151     }
1152
1153     clearFlags(m_pendingActionFlags, LoadMediaResource);
1154
1155     // Once the page has allowed an element to load media, it is free to load at will. This allows a 
1156     // playlist that starts in a foreground tab to continue automatically if the tab is subsequently 
1157     // put into the background.
1158     m_mediaSession->removeBehaviorRestriction(MediaElementSession::RequirePageConsentToLoadMedia);
1159
1160 #if ENABLE(VIDEO_TRACK)
1161     if (hasMediaControls())
1162         mediaControls()->changedClosedCaptionsVisibility();
1163
1164     // HTMLMediaElement::textTracksAreReady will need "... the text tracks whose mode was not in the
1165     // disabled state when the element's resource selection algorithm last started".
1166     if (RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled()) {
1167         m_textTracksWhenResourceSelectionBegan.clear();
1168         if (m_textTracks) {
1169             for (unsigned i = 0; i < m_textTracks->length(); ++i) {
1170                 TextTrack* track = m_textTracks->item(i);
1171                 if (track->mode() != TextTrack::disabledKeyword())
1172                     m_textTracksWhenResourceSelectionBegan.append(track);
1173             }
1174         }
1175     }
1176 #endif
1177
1178     selectMediaResource();
1179 }
1180
1181 void HTMLMediaElement::selectMediaResource()
1182 {
1183     LOG(Media, "HTMLMediaElement::selectMediaResource(%p)", this);
1184
1185     enum Mode { attribute, children };
1186
1187     // 3 - If the media element has a src attribute, then let mode be attribute.
1188     Mode mode = attribute;
1189     if (!fastHasAttribute(srcAttr)) {
1190         // Otherwise, if the media element does not have a src attribute but has a source 
1191         // element child, then let mode be children and let candidate be the first such 
1192         // source element child in tree order.
1193         if (auto firstSource = childrenOfType<HTMLSourceElement>(*this).first()) {
1194             mode = children;
1195             m_nextChildNodeToConsider = firstSource;
1196             m_currentSourceNode = nullptr;
1197         } else {
1198             // Otherwise the media element has neither a src attribute nor a source element 
1199             // child: set the networkState to NETWORK_EMPTY, and abort these steps; the 
1200             // synchronous section ends.
1201             m_loadState = WaitingForSource;
1202             setShouldDelayLoadEvent(false);
1203             m_networkState = NETWORK_EMPTY;
1204
1205             LOG(Media, "HTMLMediaElement::selectMediaResource(%p) - nothing to load", this);
1206             return;
1207         }
1208     }
1209
1210     // 4 - Set the media element's delaying-the-load-event flag to true (this delays the load event), 
1211     // and set its networkState to NETWORK_LOADING.
1212     setShouldDelayLoadEvent(true);
1213     m_networkState = NETWORK_LOADING;
1214
1215     // 5 - Queue a task to fire a simple event named loadstart at the media element.
1216     scheduleEvent(eventNames().loadstartEvent);
1217
1218     // 6 - If mode is attribute, then run these substeps
1219     if (mode == attribute) {
1220         m_loadState = LoadingFromSrcAttr;
1221
1222         // If the src attribute's value is the empty string ... jump down to the failed step below
1223         URL mediaURL = getNonEmptyURLAttribute(srcAttr);
1224         if (mediaURL.isEmpty()) {
1225             mediaLoadingFailed(MediaPlayer::FormatError);
1226             LOG(Media, "HTMLMediaElement::selectMediaResource(%p) -  empty 'src'", this);
1227             return;
1228         }
1229
1230         if (!isSafeToLoadURL(mediaURL, Complain) || !dispatchBeforeLoadEvent(mediaURL.string())) {
1231             mediaLoadingFailed(MediaPlayer::FormatError);
1232             return;
1233         }
1234
1235         // No type or key system information is available when the url comes
1236         // from the 'src' attribute so MediaPlayer
1237         // will have to pick a media engine based on the file extension.
1238         ContentType contentType((String()));
1239         loadResource(mediaURL, contentType, String());
1240         LOG(Media, "HTMLMediaElement::selectMediaResource(%p) - using 'src' attribute url", this);
1241         return;
1242     }
1243
1244     // Otherwise, the source elements will be used
1245     loadNextSourceChild();
1246 }
1247
1248 void HTMLMediaElement::loadNextSourceChild()
1249 {
1250     ContentType contentType((String()));
1251     String keySystem;
1252     URL mediaURL = selectNextSourceChild(&contentType, &keySystem, Complain);
1253     if (!mediaURL.isValid()) {
1254         waitForSourceChange();
1255         return;
1256     }
1257
1258     // Recreate the media player for the new url
1259     createMediaPlayer();
1260
1261     m_loadState = LoadingFromSourceElement;
1262     loadResource(mediaURL, contentType, keySystem);
1263 }
1264
1265 void HTMLMediaElement::loadResource(const URL& initialURL, ContentType& contentType, const String& keySystem)
1266 {
1267     ASSERT(isSafeToLoadURL(initialURL, Complain));
1268
1269     LOG(Media, "HTMLMediaElement::loadResource(%p) - %s, %s, %s", this, urlForLoggingMedia(initialURL).utf8().data(), contentType.raw().utf8().data(), keySystem.utf8().data());
1270
1271     Frame* frame = document().frame();
1272     if (!frame) {
1273         mediaLoadingFailed(MediaPlayer::FormatError);
1274         return;
1275     }
1276
1277     Page* page = frame->page();
1278     if (!page) {
1279         mediaLoadingFailed(MediaPlayer::FormatError);
1280         return;
1281     }
1282
1283     URL url = initialURL;
1284     if (!frame->loader().willLoadMediaElementURL(url)) {
1285         mediaLoadingFailed(MediaPlayer::FormatError);
1286         return;
1287     }
1288
1289 #if ENABLE(CONTENT_EXTENSIONS)
1290     ResourceRequest request(url);
1291     DocumentLoader* documentLoader = frame->loader().documentLoader();
1292
1293     if (documentLoader && page->userContentProvider().processContentExtensionRulesForLoad(request, ResourceType::Media, *documentLoader) == ContentExtensions::BlockedStatus::Blocked) {
1294         request = { };
1295         mediaLoadingFailed(MediaPlayer::FormatError);
1296         return;
1297     }
1298 #endif
1299
1300     // The resource fetch algorithm 
1301     m_networkState = NETWORK_LOADING;
1302
1303     // If the url should be loaded from the application cache, pass the url of the cached file
1304     // to the media engine.
1305     ApplicationCacheHost* cacheHost = frame->loader().documentLoader()->applicationCacheHost();
1306     ApplicationCacheResource* resource = 0;
1307     if (cacheHost && cacheHost->shouldLoadResourceFromApplicationCache(ResourceRequest(url), resource)) {
1308         // Resources that are not present in the manifest will always fail to load (at least, after the
1309         // cache has been primed the first time), making the testing of offline applications simpler.
1310         if (!resource || resource->path().isEmpty()) {
1311             mediaLoadingFailed(MediaPlayer::NetworkError);
1312             return;
1313         }
1314     }
1315
1316     // Log that we started loading a media element.
1317     if (Frame* frame = document().frame())
1318         frame->mainFrame().diagnosticLoggingClient().logDiagnosticMessageWithValue(DiagnosticLoggingKeys::mediaKey(), isVideo() ? DiagnosticLoggingKeys::videoKey() : DiagnosticLoggingKeys::audioKey(), DiagnosticLoggingKeys::loadingKey(), ShouldSample::No);
1319
1320     m_firstTimePlaying = true;
1321
1322     // Set m_currentSrc *before* changing to the cache url, the fact that we are loading from the app
1323     // cache is an internal detail not exposed through the media element API.
1324     m_currentSrc = url;
1325
1326     if (resource) {
1327         url = ApplicationCacheHost::createFileURL(resource->path());
1328         LOG(Media, "HTMLMediaElement::loadResource(%p) - will load from app cache -> %s", this, urlForLoggingMedia(url).utf8().data());
1329     }
1330
1331     LOG(Media, "HTMLMediaElement::loadResource(%p) - m_currentSrc -> %s", this, urlForLoggingMedia(m_currentSrc).utf8().data());
1332
1333     if (m_sendProgressEvents)
1334         startProgressEventTimer();
1335
1336     bool privateMode = document().page() && document().page()->usesEphemeralSession();
1337     m_player->setPrivateBrowsingMode(privateMode);
1338
1339     // Reset display mode to force a recalculation of what to show because we are resetting the player.
1340     setDisplayMode(Unknown);
1341
1342     if (!autoplay())
1343         m_player->setPreload(m_mediaSession->effectivePreloadForElement(*this));
1344     m_player->setPreservesPitch(m_webkitPreservesPitch);
1345
1346     if (!m_explicitlyMuted) {
1347         m_explicitlyMuted = true;
1348         m_muted = fastHasAttribute(mutedAttr);
1349     }
1350
1351     updateVolume();
1352
1353     bool loadAttempted = false;
1354 #if ENABLE(MEDIA_SOURCE)
1355     ASSERT(!m_mediaSource);
1356
1357     if (url.protocolIs(mediaSourceBlobProtocol))
1358         m_mediaSource = MediaSource::lookup(url.string());
1359
1360     if (m_mediaSource) {
1361         if (m_mediaSource->attachToElement(this))
1362             m_player->load(url, contentType, m_mediaSource.get());
1363         else {
1364             // Forget our reference to the MediaSource, so we leave it alone
1365             // while processing remainder of load failure.
1366             m_mediaSource = nullptr;
1367             mediaLoadingFailed(MediaPlayer::FormatError);
1368         }
1369         loadAttempted = true;
1370     }
1371 #endif
1372
1373 #if ENABLE(MEDIA_STREAM)
1374     if (!loadAttempted) {
1375         if (!m_mediaStreamSrcObject && url.protocolIs(mediaStreamBlobProtocol))
1376             m_mediaStreamSrcObject = MediaStreamRegistry::shared().lookUp(url);
1377
1378         if (m_mediaStreamSrcObject) {
1379             loadAttempted = true;
1380             if (!m_player->load(m_mediaStreamSrcObject->privateStream()))
1381                 mediaLoadingFailed(MediaPlayer::FormatError);
1382         }
1383     }
1384 #endif
1385
1386     if (!loadAttempted && !m_player->load(url, contentType, keySystem))
1387         mediaLoadingFailed(MediaPlayer::FormatError);
1388
1389     // If there is no poster to display, allow the media engine to render video frames as soon as
1390     // they are available.
1391     updateDisplayState();
1392
1393     if (renderer())
1394         renderer()->updateFromElement();
1395 }
1396
1397 #if ENABLE(VIDEO_TRACK)
1398 static bool trackIndexCompare(TextTrack* a,
1399                               TextTrack* b)
1400 {
1401     return a->trackIndex() - b->trackIndex() < 0;
1402 }
1403
1404 static bool eventTimeCueCompare(const std::pair<MediaTime, TextTrackCue*>& a, const std::pair<MediaTime, TextTrackCue*>& b)
1405 {
1406     // 12 - Sort the tasks in events in ascending time order (tasks with earlier
1407     // times first).
1408     if (a.first != b.first)
1409         return a.first - b.first < MediaTime::zeroTime();
1410
1411     // If the cues belong to different text tracks, it doesn't make sense to
1412     // compare the two tracks by the relative cue order, so return the relative
1413     // track order.
1414     if (a.second->track() != b.second->track())
1415         return trackIndexCompare(a.second->track(), b.second->track());
1416
1417     // 12 - Further sort tasks in events that have the same time by the
1418     // relative text track cue order of the text track cues associated
1419     // with these tasks.
1420     return a.second->cueIndex() - b.second->cueIndex() < 0;
1421 }
1422
1423 static bool compareCueInterval(const CueInterval& one, const CueInterval& two)
1424 {
1425     return one.data()->isOrderedBefore(two.data());
1426 };
1427
1428
1429 void HTMLMediaElement::updateActiveTextTrackCues(const MediaTime& movieTime)
1430 {
1431     // 4.8.10.8 Playing the media resource
1432
1433     //  If the current playback position changes while the steps are running,
1434     //  then the user agent must wait for the steps to complete, and then must
1435     //  immediately rerun the steps.
1436     if (ignoreTrackDisplayUpdateRequests())
1437         return;
1438
1439     LOG(Media, "HTMLMediaElement::updateActiveTextTrackCues(%p)", this);
1440
1441     // 1 - Let current cues be a list of cues, initialized to contain all the
1442     // cues of all the hidden, showing, or showing by default text tracks of the
1443     // media element (not the disabled ones) whose start times are less than or
1444     // equal to the current playback position and whose end times are greater
1445     // than the current playback position.
1446     CueList currentCues;
1447
1448     // The user agent must synchronously unset [the text track cue active] flag
1449     // whenever ... the media element's readyState is changed back to HAVE_NOTHING.
1450     if (m_readyState != HAVE_NOTHING && m_player) {
1451         currentCues = m_cueTree.allOverlaps(m_cueTree.createInterval(movieTime, movieTime));
1452         if (currentCues.size() > 1)
1453             std::sort(currentCues.begin(), currentCues.end(), &compareCueInterval);
1454     }
1455
1456     CueList previousCues;
1457     CueList missedCues;
1458
1459     // 2 - Let other cues be a list of cues, initialized to contain all the cues
1460     // of hidden, showing, and showing by default text tracks of the media
1461     // element that are not present in current cues.
1462     previousCues = m_currentlyActiveCues;
1463
1464     // 3 - Let last time be the current playback position at the time this
1465     // algorithm was last run for this media element, if this is not the first
1466     // time it has run.
1467     MediaTime lastTime = m_lastTextTrackUpdateTime;
1468
1469     // 4 - If the current playback position has, since the last time this
1470     // algorithm was run, only changed through its usual monotonic increase
1471     // during normal playback, then let missed cues be the list of cues in other
1472     // cues whose start times are greater than or equal to last time and whose
1473     // end times are less than or equal to the current playback position.
1474     // Otherwise, let missed cues be an empty list.
1475     if (lastTime >= MediaTime::zeroTime() && m_lastSeekTime < movieTime) {
1476         CueList potentiallySkippedCues =
1477             m_cueTree.allOverlaps(m_cueTree.createInterval(lastTime, movieTime));
1478
1479         for (size_t i = 0; i < potentiallySkippedCues.size(); ++i) {
1480             MediaTime cueStartTime = potentiallySkippedCues[i].low();
1481             MediaTime cueEndTime = potentiallySkippedCues[i].high();
1482
1483             // Consider cues that may have been missed since the last seek time.
1484             if (cueStartTime > std::max(m_lastSeekTime, lastTime) && cueEndTime < movieTime)
1485                 missedCues.append(potentiallySkippedCues[i]);
1486         }
1487     }
1488
1489     m_lastTextTrackUpdateTime = movieTime;
1490
1491     // 5 - If the time was reached through the usual monotonic increase of the
1492     // current playback position during normal playback, and if the user agent
1493     // has not fired a timeupdate event at the element in the past 15 to 250ms
1494     // and is not still running event handlers for such an event, then the user
1495     // agent must queue a task to fire a simple event named timeupdate at the
1496     // element. (In the other cases, such as explicit seeks, relevant events get
1497     // fired as part of the overall process of changing the current playback
1498     // position.)
1499     if (!m_paused && m_lastSeekTime <= lastTime)
1500         scheduleTimeupdateEvent(false);
1501
1502     // Explicitly cache vector sizes, as their content is constant from here.
1503     size_t currentCuesSize = currentCues.size();
1504     size_t missedCuesSize = missedCues.size();
1505     size_t previousCuesSize = previousCues.size();
1506
1507     // 6 - If all of the cues in current cues have their text track cue active
1508     // flag set, none of the cues in other cues have their text track cue active
1509     // flag set, and missed cues is empty, then abort these steps.
1510     bool activeSetChanged = missedCuesSize;
1511
1512     for (size_t i = 0; !activeSetChanged && i < previousCuesSize; ++i)
1513         if (!currentCues.contains(previousCues[i]) && previousCues[i].data()->isActive())
1514             activeSetChanged = true;
1515
1516     for (size_t i = 0; i < currentCuesSize; ++i) {
1517         TextTrackCue* cue = currentCues[i].data();
1518
1519         if (cue->isRenderable())
1520             toVTTCue(cue)->updateDisplayTree(movieTime);
1521
1522         if (!cue->isActive())
1523             activeSetChanged = true;
1524     }
1525
1526     if (!activeSetChanged)
1527         return;
1528
1529     // 7 - If the time was reached through the usual monotonic increase of the
1530     // current playback position during normal playback, and there are cues in
1531     // other cues that have their text track cue pause-on-exi flag set and that
1532     // either have their text track cue active flag set or are also in missed
1533     // cues, then immediately pause the media element.
1534     for (size_t i = 0; !m_paused && i < previousCuesSize; ++i) {
1535         if (previousCues[i].data()->pauseOnExit()
1536             && previousCues[i].data()->isActive()
1537             && !currentCues.contains(previousCues[i]))
1538             pause();
1539     }
1540
1541     for (size_t i = 0; !m_paused && i < missedCuesSize; ++i) {
1542         if (missedCues[i].data()->pauseOnExit())
1543             pause();
1544     }
1545
1546     // 8 - Let events be a list of tasks, initially empty. Each task in this
1547     // list will be associated with a text track, a text track cue, and a time,
1548     // which are used to sort the list before the tasks are queued.
1549     Vector<std::pair<MediaTime, TextTrackCue*>> eventTasks;
1550
1551     // 8 - Let affected tracks be a list of text tracks, initially empty.
1552     Vector<TextTrack*> affectedTracks;
1553
1554     for (size_t i = 0; i < missedCuesSize; ++i) {
1555         // 9 - For each text track cue in missed cues, prepare an event named enter
1556         // for the TextTrackCue object with the text track cue start time.
1557         eventTasks.append(std::make_pair(missedCues[i].data()->startMediaTime(),
1558                                          missedCues[i].data()));
1559
1560         // 10 - For each text track [...] in missed cues, prepare an event
1561         // named exit for the TextTrackCue object with the  with the later of
1562         // the text track cue end time and the text track cue start time.
1563
1564         // Note: An explicit task is added only if the cue is NOT a zero or
1565         // negative length cue. Otherwise, the need for an exit event is
1566         // checked when these tasks are actually queued below. This doesn't
1567         // affect sorting events before dispatch either, because the exit
1568         // event has the same time as the enter event.
1569         if (missedCues[i].data()->startMediaTime() < missedCues[i].data()->endMediaTime())
1570             eventTasks.append(std::make_pair(missedCues[i].data()->endMediaTime(), missedCues[i].data()));
1571     }
1572
1573     for (size_t i = 0; i < previousCuesSize; ++i) {
1574         // 10 - For each text track cue in other cues that has its text
1575         // track cue active flag set prepare an event named exit for the
1576         // TextTrackCue object with the text track cue end time.
1577         if (!currentCues.contains(previousCues[i]))
1578             eventTasks.append(std::make_pair(previousCues[i].data()->endMediaTime(),
1579                                              previousCues[i].data()));
1580     }
1581
1582     for (size_t i = 0; i < currentCuesSize; ++i) {
1583         // 11 - For each text track cue in current cues that does not have its
1584         // text track cue active flag set, prepare an event named enter for the
1585         // TextTrackCue object with the text track cue start time.
1586         if (!previousCues.contains(currentCues[i]))
1587             eventTasks.append(std::make_pair(currentCues[i].data()->startMediaTime(),
1588                                              currentCues[i].data()));
1589     }
1590
1591     // 12 - Sort the tasks in events in ascending time order (tasks with earlier
1592     // times first).
1593     std::sort(eventTasks.begin(), eventTasks.end(), eventTimeCueCompare);
1594
1595     for (size_t i = 0; i < eventTasks.size(); ++i) {
1596         if (!affectedTracks.contains(eventTasks[i].second->track()))
1597             affectedTracks.append(eventTasks[i].second->track());
1598
1599         // 13 - Queue each task in events, in list order.
1600         RefPtr<Event> event;
1601
1602         // Each event in eventTasks may be either an enterEvent or an exitEvent,
1603         // depending on the time that is associated with the event. This
1604         // correctly identifies the type of the event, if the startTime is
1605         // less than the endTime in the cue.
1606         if (eventTasks[i].second->startTime() >= eventTasks[i].second->endTime()) {
1607             event = Event::create(eventNames().enterEvent, false, false);
1608             event->setTarget(eventTasks[i].second);
1609             m_asyncEventQueue.enqueueEvent(event.release());
1610
1611             event = Event::create(eventNames().exitEvent, false, false);
1612             event->setTarget(eventTasks[i].second);
1613             m_asyncEventQueue.enqueueEvent(event.release());
1614         } else {
1615             if (eventTasks[i].first == eventTasks[i].second->startMediaTime())
1616                 event = Event::create(eventNames().enterEvent, false, false);
1617             else
1618                 event = Event::create(eventNames().exitEvent, false, false);
1619
1620             event->setTarget(eventTasks[i].second);
1621             m_asyncEventQueue.enqueueEvent(event.release());
1622         }
1623     }
1624
1625     // 14 - Sort affected tracks in the same order as the text tracks appear in
1626     // the media element's list of text tracks, and remove duplicates.
1627     std::sort(affectedTracks.begin(), affectedTracks.end(), trackIndexCompare);
1628
1629     // 15 - For each text track in affected tracks, in the list order, queue a
1630     // task to fire a simple event named cuechange at the TextTrack object, and, ...
1631     for (size_t i = 0; i < affectedTracks.size(); ++i) {
1632         RefPtr<Event> event = Event::create(eventNames().cuechangeEvent, false, false);
1633         event->setTarget(affectedTracks[i]);
1634
1635         m_asyncEventQueue.enqueueEvent(event.release());
1636
1637         // ... if the text track has a corresponding track element, to then fire a
1638         // simple event named cuechange at the track element as well.
1639         if (is<LoadableTextTrack>(*affectedTracks[i])) {
1640             RefPtr<Event> event = Event::create(eventNames().cuechangeEvent, false, false);
1641             HTMLTrackElement* trackElement = downcast<LoadableTextTrack>(*affectedTracks[i]).trackElement();
1642             ASSERT(trackElement);
1643             event->setTarget(trackElement);
1644             
1645             m_asyncEventQueue.enqueueEvent(event.release());
1646         }
1647     }
1648
1649     // 16 - Set the text track cue active flag of all the cues in the current
1650     // cues, and unset the text track cue active flag of all the cues in the
1651     // other cues.
1652     for (size_t i = 0; i < currentCuesSize; ++i)
1653         currentCues[i].data()->setIsActive(true);
1654
1655     for (size_t i = 0; i < previousCuesSize; ++i)
1656         if (!currentCues.contains(previousCues[i]))
1657             previousCues[i].data()->setIsActive(false);
1658
1659     // Update the current active cues.
1660     m_currentlyActiveCues = currentCues;
1661
1662     if (activeSetChanged)
1663         updateTextTrackDisplay();
1664 }
1665
1666 bool HTMLMediaElement::textTracksAreReady() const
1667 {
1668     // 4.8.10.12.1 Text track model
1669     // ...
1670     // The text tracks of a media element are ready if all the text tracks whose mode was not 
1671     // in the disabled state when the element's resource selection algorithm last started now
1672     // have a text track readiness state of loaded or failed to load.
1673     for (unsigned i = 0; i < m_textTracksWhenResourceSelectionBegan.size(); ++i) {
1674         if (m_textTracksWhenResourceSelectionBegan[i]->readinessState() == TextTrack::Loading
1675             || m_textTracksWhenResourceSelectionBegan[i]->readinessState() == TextTrack::NotLoaded)
1676             return false;
1677     }
1678
1679     return true;
1680 }
1681
1682 void HTMLMediaElement::textTrackReadyStateChanged(TextTrack* track)
1683 {
1684     if (m_player && m_textTracksWhenResourceSelectionBegan.contains(track)) {
1685         if (track->readinessState() != TextTrack::Loading)
1686             setReadyState(m_player->readyState());
1687     } else {
1688         // The track readiness state might have changed as a result of the user
1689         // clicking the captions button. In this case, a check whether all the
1690         // resources have failed loading should be done in order to hide the CC button.
1691         if (hasMediaControls() && track->readinessState() == TextTrack::FailedToLoad)
1692             mediaControls()->refreshClosedCaptionsButtonVisibility();
1693     }
1694 }
1695
1696 void HTMLMediaElement::audioTrackEnabledChanged(AudioTrack* track)
1697 {
1698     if (!RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled())
1699         return;
1700     if (m_audioTracks && m_audioTracks->contains(track))
1701         m_audioTracks->scheduleChangeEvent();
1702 }
1703
1704 void HTMLMediaElement::textTrackModeChanged(TextTrack* track)
1705 {
1706     bool trackIsLoaded = true;
1707     if (track->trackType() == TextTrack::TrackElement) {
1708         trackIsLoaded = false;
1709         for (auto& trackElement : childrenOfType<HTMLTrackElement>(*this)) {
1710             if (trackElement.track() == track) {
1711                 if (trackElement.readyState() == HTMLTrackElement::LOADING || trackElement.readyState() == HTMLTrackElement::LOADED)
1712                     trackIsLoaded = true;
1713                 break;
1714             }
1715         }
1716     }
1717
1718     // If this is the first added track, create the list of text tracks.
1719     if (!m_textTracks)
1720         m_textTracks = TextTrackList::create(this, ActiveDOMObject::scriptExecutionContext());
1721     
1722     // Mark this track as "configured" so configureTextTracks won't change the mode again.
1723     track->setHasBeenConfigured(true);
1724     
1725     if (track->mode() != TextTrack::disabledKeyword() && trackIsLoaded)
1726         textTrackAddCues(track, track->cues());
1727
1728     configureTextTrackDisplay(AssumeTextTrackVisibilityChanged);
1729
1730     if (m_textTracks && m_textTracks->contains(track))
1731         m_textTracks->scheduleChangeEvent();
1732
1733 #if ENABLE(AVF_CAPTIONS)
1734     if (track->trackType() == TextTrack::TrackElement && m_player)
1735         m_player->notifyTrackModeChanged();
1736 #endif
1737 }
1738
1739 void HTMLMediaElement::videoTrackSelectedChanged(VideoTrack* track)
1740 {
1741     if (!RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled())
1742         return;
1743     if (m_videoTracks && m_videoTracks->contains(track))
1744         m_videoTracks->scheduleChangeEvent();
1745 }
1746
1747 void HTMLMediaElement::textTrackKindChanged(TextTrack* track)
1748 {
1749     if (track->kind() != TextTrack::captionsKeyword() && track->kind() != TextTrack::subtitlesKeyword() && track->mode() == TextTrack::showingKeyword())
1750         track->setMode(TextTrack::hiddenKeyword());
1751 }
1752
1753 void HTMLMediaElement::beginIgnoringTrackDisplayUpdateRequests()
1754 {
1755     ++m_ignoreTrackDisplayUpdate;
1756 }
1757
1758 void HTMLMediaElement::endIgnoringTrackDisplayUpdateRequests()
1759 {
1760     ASSERT(m_ignoreTrackDisplayUpdate);
1761     --m_ignoreTrackDisplayUpdate;
1762     if (!m_ignoreTrackDisplayUpdate && m_inActiveDocument)
1763         updateActiveTextTrackCues(currentMediaTime());
1764 }
1765
1766 void HTMLMediaElement::textTrackAddCues(TextTrack* track, const TextTrackCueList* cues) 
1767 {
1768     if (track->mode() == TextTrack::disabledKeyword())
1769         return;
1770
1771     TrackDisplayUpdateScope scope(this);
1772     for (size_t i = 0; i < cues->length(); ++i)
1773         textTrackAddCue(track, cues->item(i));
1774 }
1775
1776 void HTMLMediaElement::textTrackRemoveCues(TextTrack*, const TextTrackCueList* cues) 
1777 {
1778     TrackDisplayUpdateScope scope(this);
1779     for (size_t i = 0; i < cues->length(); ++i)
1780         textTrackRemoveCue(cues->item(i)->track(), cues->item(i));
1781 }
1782
1783 void HTMLMediaElement::textTrackAddCue(TextTrack* track, PassRefPtr<TextTrackCue> prpCue)
1784 {
1785     if (track->mode() == TextTrack::disabledKeyword())
1786         return;
1787
1788     RefPtr<TextTrackCue> cue = prpCue;
1789
1790     // Negative duration cues need be treated in the interval tree as
1791     // zero-length cues.
1792     MediaTime endTime = std::max(cue->startMediaTime(), cue->endMediaTime());
1793
1794     CueInterval interval = m_cueTree.createInterval(cue->startMediaTime(), endTime, cue.get());
1795     if (!m_cueTree.contains(interval))
1796         m_cueTree.add(interval);
1797     updateActiveTextTrackCues(currentMediaTime());
1798 }
1799
1800 void HTMLMediaElement::textTrackRemoveCue(TextTrack*, PassRefPtr<TextTrackCue> prpCue)
1801 {
1802     RefPtr<TextTrackCue> cue = prpCue;
1803     // Negative duration cues need to be treated in the interval tree as
1804     // zero-length cues.
1805     MediaTime endTime = std::max(cue->startMediaTime(), cue->endMediaTime());
1806
1807     CueInterval interval = m_cueTree.createInterval(cue->startMediaTime(), endTime, cue.get());
1808     m_cueTree.remove(interval);
1809
1810     // Since the cue will be removed from the media element and likely the
1811     // TextTrack might also be destructed, notifying the region of the cue
1812     // removal shouldn't be done.
1813     if (cue->isRenderable())
1814         toVTTCue(cue.get())->notifyRegionWhenRemovingDisplayTree(false);
1815
1816     size_t index = m_currentlyActiveCues.find(interval);
1817     if (index != notFound) {
1818         cue->setIsActive(false);
1819         m_currentlyActiveCues.remove(index);
1820     }
1821
1822     if (cue->isRenderable())
1823         toVTTCue(cue.get())->removeDisplayTree();
1824     updateActiveTextTrackCues(currentMediaTime());
1825
1826     if (cue->isRenderable())
1827         toVTTCue(cue.get())->notifyRegionWhenRemovingDisplayTree(true);
1828 }
1829
1830 #endif
1831
1832 bool HTMLMediaElement::isSafeToLoadURL(const URL& url, InvalidURLAction actionIfInvalid)
1833 {
1834     if (!url.isValid()) {
1835         LOG(Media, "HTMLMediaElement::isSafeToLoadURL(%p) - %s -> FALSE because url is invalid", this, urlForLoggingMedia(url).utf8().data());
1836         return false;
1837     }
1838
1839     Frame* frame = document().frame();
1840     if (!frame || !document().securityOrigin()->canDisplay(url)) {
1841         if (actionIfInvalid == Complain)
1842             FrameLoader::reportLocalLoadFailed(frame, url.stringCenterEllipsizedToLength());
1843         LOG(Media, "HTMLMediaElement::isSafeToLoadURL(%p) - %s -> FALSE rejected by SecurityOrigin", this, urlForLoggingMedia(url).utf8().data());
1844         return false;
1845     }
1846
1847     if (!document().contentSecurityPolicy()->allowMediaFromSource(url, isInUserAgentShadowTree())) {
1848         LOG(Media, "HTMLMediaElement::isSafeToLoadURL(%p) - %s -> rejected by Content Security Policy", this, urlForLoggingMedia(url).utf8().data());
1849         return false;
1850     }
1851
1852     return true;
1853 }
1854
1855 void HTMLMediaElement::startProgressEventTimer()
1856 {
1857     if (m_progressEventTimer.isActive())
1858         return;
1859
1860     m_previousProgressTime = monotonicallyIncreasingTime();
1861     // 350ms is not magic, it is in the spec!
1862     m_progressEventTimer.startRepeating(0.350);
1863 }
1864
1865 void HTMLMediaElement::waitForSourceChange()
1866 {
1867     LOG(Media, "HTMLMediaElement::waitForSourceChange(%p)", this);
1868
1869     stopPeriodicTimers();
1870     m_loadState = WaitingForSource;
1871
1872     // 6.17 - Waiting: Set the element's networkState attribute to the NETWORK_NO_SOURCE value
1873     m_networkState = NETWORK_NO_SOURCE;
1874
1875     // 6.18 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
1876     setShouldDelayLoadEvent(false);
1877
1878     updateDisplayState();
1879
1880     if (renderer())
1881         renderer()->updateFromElement();
1882 }
1883
1884 void HTMLMediaElement::noneSupported()
1885 {
1886     LOG(Media, "HTMLMediaElement::noneSupported(%p)", this);
1887
1888     stopPeriodicTimers();
1889     m_loadState = WaitingForSource;
1890     m_currentSourceNode = nullptr;
1891
1892     // 4.8.10.5 
1893     // 6 - Reaching this step indicates that the media resource failed to load or that the given 
1894     // URL could not be resolved. In one atomic operation, run the following steps:
1895
1896     // 6.1 - Set the error attribute to a new MediaError object whose code attribute is set to
1897     // MEDIA_ERR_SRC_NOT_SUPPORTED.
1898     m_error = MediaError::create(MediaError::MEDIA_ERR_SRC_NOT_SUPPORTED);
1899
1900     // 6.2 - Forget the media element's media-resource-specific text tracks.
1901     forgetResourceSpecificTracks();
1902
1903     // 6.3 - Set the element's networkState attribute to the NETWORK_NO_SOURCE value.
1904     m_networkState = NETWORK_NO_SOURCE;
1905
1906     // 7 - Queue a task to fire a simple event named error at the media element.
1907     scheduleEvent(eventNames().errorEvent);
1908
1909 #if ENABLE(MEDIA_SOURCE)
1910     closeMediaSource();
1911 #endif
1912
1913     // 8 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
1914     setShouldDelayLoadEvent(false);
1915
1916     // 9 - Abort these steps. Until the load() method is invoked or the src attribute is changed, 
1917     // the element won't attempt to load another resource.
1918
1919     updateDisplayState();
1920
1921     if (renderer())
1922         renderer()->updateFromElement();
1923 }
1924
1925 void HTMLMediaElement::mediaLoadingFailedFatally(MediaPlayer::NetworkState error)
1926 {
1927     LOG(Media, "HTMLMediaElement::mediaLoadingFailedFatally(%p) - error = %d", this, static_cast<int>(error));
1928
1929     // 1 - The user agent should cancel the fetching process.
1930     stopPeriodicTimers();
1931     m_loadState = WaitingForSource;
1932
1933     // 2 - Set the error attribute to a new MediaError object whose code attribute is 
1934     // set to MEDIA_ERR_NETWORK/MEDIA_ERR_DECODE.
1935     if (error == MediaPlayer::NetworkError)
1936         m_error = MediaError::create(MediaError::MEDIA_ERR_NETWORK);
1937     else if (error == MediaPlayer::DecodeError)
1938         m_error = MediaError::create(MediaError::MEDIA_ERR_DECODE);
1939     else
1940         ASSERT_NOT_REACHED();
1941
1942     // 3 - Queue a task to fire a simple event named error at the media element.
1943     scheduleEvent(eventNames().errorEvent);
1944
1945 #if ENABLE(MEDIA_SOURCE)
1946     closeMediaSource();
1947 #endif
1948
1949     // 4 - Set the element's networkState attribute to the NETWORK_EMPTY value and queue a
1950     // task to fire a simple event called emptied at the element.
1951     m_networkState = NETWORK_EMPTY;
1952     scheduleEvent(eventNames().emptiedEvent);
1953
1954     // 5 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
1955     setShouldDelayLoadEvent(false);
1956
1957     // 6 - Abort the overall resource selection algorithm.
1958     m_currentSourceNode = nullptr;
1959
1960 #if PLATFORM(COCOA)
1961     if (is<MediaDocument>(document()))
1962         downcast<MediaDocument>(document()).mediaElementSawUnsupportedTracks();
1963 #endif
1964 }
1965
1966 void HTMLMediaElement::cancelPendingEventsAndCallbacks()
1967 {
1968     LOG(Media, "HTMLMediaElement::cancelPendingEventsAndCallbacks(%p)", this);
1969     m_asyncEventQueue.cancelAllEvents();
1970
1971     for (auto& source : childrenOfType<HTMLSourceElement>(*this))
1972         source.cancelPendingErrorEvent();
1973 }
1974
1975 void HTMLMediaElement::mediaPlayerNetworkStateChanged(MediaPlayer*)
1976 {
1977     beginProcessingMediaPlayerCallback();
1978     setNetworkState(m_player->networkState());
1979     endProcessingMediaPlayerCallback();
1980 }
1981
1982 static void logMediaLoadRequest(Page* page, const String& mediaEngine, const String& errorMessage, bool succeeded)
1983 {
1984     if (!page)
1985         return;
1986
1987     DiagnosticLoggingClient& diagnosticLoggingClient = page->mainFrame().diagnosticLoggingClient();
1988     if (!succeeded) {
1989         diagnosticLoggingClient.logDiagnosticMessageWithResult(DiagnosticLoggingKeys::mediaLoadingFailedKey(), errorMessage, DiagnosticLoggingResultFail, ShouldSample::No);
1990         return;
1991     }
1992
1993     diagnosticLoggingClient.logDiagnosticMessage(DiagnosticLoggingKeys::mediaLoadedKey(), mediaEngine, ShouldSample::No);
1994
1995     if (!page->hasSeenAnyMediaEngine())
1996         diagnosticLoggingClient.logDiagnosticMessage(DiagnosticLoggingKeys::pageContainsAtLeastOneMediaEngineKey(), emptyString(), ShouldSample::No);
1997
1998     if (!page->hasSeenMediaEngine(mediaEngine))
1999         diagnosticLoggingClient.logDiagnosticMessage(DiagnosticLoggingKeys::pageContainsMediaEngineKey(), mediaEngine, ShouldSample::No);
2000
2001     page->sawMediaEngine(mediaEngine);
2002 }
2003
2004 static String stringForNetworkState(MediaPlayer::NetworkState state)
2005 {
2006     switch (state) {
2007     case MediaPlayer::Empty: return ASCIILiteral("Empty");
2008     case MediaPlayer::Idle: return ASCIILiteral("Idle");
2009     case MediaPlayer::Loading: return ASCIILiteral("Loading");
2010     case MediaPlayer::Loaded: return ASCIILiteral("Loaded");
2011     case MediaPlayer::FormatError: return ASCIILiteral("FormatError");
2012     case MediaPlayer::NetworkError: return ASCIILiteral("NetworkError");
2013     case MediaPlayer::DecodeError: return ASCIILiteral("DecodeError");
2014     default: return emptyString();
2015     }
2016 }
2017
2018 void HTMLMediaElement::mediaLoadingFailed(MediaPlayer::NetworkState error)
2019 {
2020     stopPeriodicTimers();
2021     
2022     // If we failed while trying to load a <source> element, the movie was never parsed, and there are more
2023     // <source> children, schedule the next one
2024     if (m_readyState < HAVE_METADATA && m_loadState == LoadingFromSourceElement) {
2025         
2026         // resource selection algorithm
2027         // 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.
2028         if (m_currentSourceNode)
2029             m_currentSourceNode->scheduleErrorEvent();
2030         else
2031             LOG(Media, "HTMLMediaElement::setNetworkState(%p) - error event not sent, <source> was removed", this);
2032         
2033         // 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.
2034         
2035         // 9.Otherwise.11 - Forget the media element's media-resource-specific tracks.
2036         forgetResourceSpecificTracks();
2037
2038         if (havePotentialSourceChild()) {
2039             LOG(Media, "HTMLMediaElement::setNetworkState(%p) - scheduling next <source>", this);
2040             scheduleNextSourceChild();
2041         } else {
2042             LOG(Media, "HTMLMediaElement::setNetworkState(%p) - no more <source> elements, waiting", this);
2043             waitForSourceChange();
2044         }
2045         
2046         return;
2047     }
2048     
2049     if ((error == MediaPlayer::NetworkError && m_readyState >= HAVE_METADATA) || error == MediaPlayer::DecodeError)
2050         mediaLoadingFailedFatally(error);
2051     else if ((error == MediaPlayer::FormatError || error == MediaPlayer::NetworkError) && m_loadState == LoadingFromSrcAttr)
2052         noneSupported();
2053     
2054     updateDisplayState();
2055     if (hasMediaControls()) {
2056         mediaControls()->reset();
2057         mediaControls()->reportedError();
2058     }
2059
2060     logMediaLoadRequest(document().page(), String(), stringForNetworkState(error), false);
2061
2062     m_mediaSession->clientCharacteristicsChanged();
2063 }
2064
2065 void HTMLMediaElement::setNetworkState(MediaPlayer::NetworkState state)
2066 {
2067     LOG(Media, "HTMLMediaElement::setNetworkState(%p) - new state = %d, current state = %d", this, static_cast<int>(state), static_cast<int>(m_networkState));
2068
2069     if (state == MediaPlayer::Empty) {
2070         // Just update the cached state and leave, we can't do anything.
2071         m_networkState = NETWORK_EMPTY;
2072         return;
2073     }
2074
2075     if (state == MediaPlayer::FormatError || state == MediaPlayer::NetworkError || state == MediaPlayer::DecodeError) {
2076         mediaLoadingFailed(state);
2077         return;
2078     }
2079
2080     if (state == MediaPlayer::Idle) {
2081         if (m_networkState > NETWORK_IDLE) {
2082             changeNetworkStateFromLoadingToIdle();
2083             setShouldDelayLoadEvent(false);
2084         } else {
2085             m_networkState = NETWORK_IDLE;
2086         }
2087     }
2088
2089     if (state == MediaPlayer::Loading) {
2090         if (m_networkState < NETWORK_LOADING || m_networkState == NETWORK_NO_SOURCE)
2091             startProgressEventTimer();
2092         m_networkState = NETWORK_LOADING;
2093     }
2094
2095     if (state == MediaPlayer::Loaded) {
2096         if (m_networkState != NETWORK_IDLE)
2097             changeNetworkStateFromLoadingToIdle();
2098         m_completelyLoaded = true;
2099     }
2100
2101     if (hasMediaControls())
2102         mediaControls()->updateStatusDisplay();
2103 }
2104
2105 void HTMLMediaElement::changeNetworkStateFromLoadingToIdle()
2106 {
2107     m_progressEventTimer.stop();
2108     if (hasMediaControls() && m_player->didLoadingProgress())
2109         mediaControls()->bufferingProgressed();
2110
2111     // Schedule one last progress event so we guarantee that at least one is fired
2112     // for files that load very quickly.
2113     scheduleEvent(eventNames().progressEvent);
2114     scheduleEvent(eventNames().suspendEvent);
2115     m_networkState = NETWORK_IDLE;
2116 }
2117
2118 void HTMLMediaElement::mediaPlayerReadyStateChanged(MediaPlayer*)
2119 {
2120     beginProcessingMediaPlayerCallback();
2121
2122     setReadyState(m_player->readyState());
2123
2124     endProcessingMediaPlayerCallback();
2125 }
2126
2127 bool HTMLMediaElement::canTransitionFromAutoplayToPlay() const
2128 {
2129     return isAutoplaying()
2130         && paused()
2131         && autoplay()
2132         && !pausedForUserInteraction()
2133         && !document().isSandboxed(SandboxAutomaticFeatures)
2134         && mediaSession().playbackPermitted(*this);
2135 }
2136
2137 void HTMLMediaElement::setReadyState(MediaPlayer::ReadyState state)
2138 {
2139     LOG(Media, "HTMLMediaElement::setReadyState(%p) - new state = %d, current state = %d,", this, static_cast<int>(state), static_cast<int>(m_readyState));
2140
2141     // Set "wasPotentiallyPlaying" BEFORE updating m_readyState, potentiallyPlaying() uses it
2142     bool wasPotentiallyPlaying = potentiallyPlaying();
2143
2144     ReadyState oldState = m_readyState;
2145     ReadyState newState = static_cast<ReadyState>(state);
2146
2147 #if ENABLE(VIDEO_TRACK)
2148     bool tracksAreReady = !RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled() || textTracksAreReady();
2149
2150     if (newState == oldState && m_tracksAreReady == tracksAreReady)
2151         return;
2152
2153     m_tracksAreReady = tracksAreReady;
2154 #else
2155     if (newState == oldState)
2156         return;
2157     bool tracksAreReady = true;
2158 #endif
2159     
2160     if (tracksAreReady)
2161         m_readyState = newState;
2162     else {
2163         // If a media file has text tracks the readyState may not progress beyond HAVE_FUTURE_DATA until
2164         // the text tracks are ready, regardless of the state of the media file.
2165         if (newState <= HAVE_METADATA)
2166             m_readyState = newState;
2167         else
2168             m_readyState = HAVE_CURRENT_DATA;
2169     }
2170     
2171     if (oldState > m_readyStateMaximum)
2172         m_readyStateMaximum = oldState;
2173
2174     if (m_networkState == NETWORK_EMPTY)
2175         return;
2176
2177     if (m_seeking) {
2178         // 4.8.10.9, step 11
2179         if (wasPotentiallyPlaying && m_readyState < HAVE_FUTURE_DATA)
2180             scheduleEvent(eventNames().waitingEvent);
2181
2182         // 4.8.10.10 step 14 & 15.
2183         if (!m_player->seeking() && m_readyState >= HAVE_CURRENT_DATA)
2184             finishSeek();
2185     } else {
2186         if (wasPotentiallyPlaying && m_readyState < HAVE_FUTURE_DATA) {
2187             // 4.8.10.8
2188             invalidateCachedTime();
2189             scheduleTimeupdateEvent(false);
2190             scheduleEvent(eventNames().waitingEvent);
2191         }
2192     }
2193
2194     if (m_readyState >= HAVE_METADATA && oldState < HAVE_METADATA) {
2195         prepareMediaFragmentURI();
2196         scheduleEvent(eventNames().durationchangeEvent);
2197         scheduleResizeEvent();
2198         scheduleEvent(eventNames().loadedmetadataEvent);
2199 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
2200         if (hasEventListeners(eventNames().webkitplaybacktargetavailabilitychangedEvent))
2201             enqueuePlaybackTargetAvailabilityChangedEvent();
2202 #endif
2203         m_initiallyMuted = m_volume < 0.05 || muted();
2204
2205         if (hasMediaControls())
2206             mediaControls()->loadedMetadata();
2207         if (renderer())
2208             renderer()->updateFromElement();
2209
2210         if (is<MediaDocument>(document()))
2211             downcast<MediaDocument>(document()).mediaElementNaturalSizeChanged(expandedIntSize(m_player->naturalSize()));
2212
2213         logMediaLoadRequest(document().page(), m_player->engineDescription(), String(), true);
2214
2215 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
2216         updateMediaState(UpdateMediaState::Asynchronously);
2217 #endif
2218
2219         m_mediaSession->clientCharacteristicsChanged();
2220     }
2221
2222     bool shouldUpdateDisplayState = false;
2223
2224     if (m_readyState >= HAVE_CURRENT_DATA && oldState < HAVE_CURRENT_DATA && !m_haveFiredLoadedData) {
2225         m_haveFiredLoadedData = true;
2226         shouldUpdateDisplayState = true;
2227         scheduleEvent(eventNames().loadeddataEvent);
2228         setShouldDelayLoadEvent(false);
2229         applyMediaFragmentURI();
2230     }
2231
2232     bool isPotentiallyPlaying = potentiallyPlaying();
2233     if (m_readyState == HAVE_FUTURE_DATA && oldState <= HAVE_CURRENT_DATA && tracksAreReady) {
2234         scheduleEvent(eventNames().canplayEvent);
2235         if (isPotentiallyPlaying)
2236             scheduleEvent(eventNames().playingEvent);
2237         shouldUpdateDisplayState = true;
2238     }
2239
2240     if (m_readyState == HAVE_ENOUGH_DATA && oldState < HAVE_ENOUGH_DATA && tracksAreReady) {
2241         if (oldState <= HAVE_CURRENT_DATA)
2242             scheduleEvent(eventNames().canplayEvent);
2243
2244         scheduleEvent(eventNames().canplaythroughEvent);
2245
2246         if (isPotentiallyPlaying && oldState <= HAVE_CURRENT_DATA)
2247             scheduleEvent(eventNames().playingEvent);
2248
2249         if (canTransitionFromAutoplayToPlay()) {
2250             m_paused = false;
2251             invalidateCachedTime();
2252             scheduleEvent(eventNames().playEvent);
2253             scheduleEvent(eventNames().playingEvent);
2254         }
2255
2256         shouldUpdateDisplayState = true;
2257     }
2258
2259     if (shouldUpdateDisplayState) {
2260         updateDisplayState();
2261         if (hasMediaControls()) {
2262             mediaControls()->refreshClosedCaptionsButtonVisibility();
2263             mediaControls()->updateStatusDisplay();
2264         }
2265     }
2266
2267     updatePlayState();
2268     updateMediaController();
2269 #if ENABLE(VIDEO_TRACK)
2270     if (RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled())
2271         updateActiveTextTrackCues(currentMediaTime());
2272 #endif
2273 }
2274
2275 #if ENABLE(ENCRYPTED_MEDIA)
2276 void HTMLMediaElement::mediaPlayerKeyAdded(MediaPlayer*, const String& keySystem, const String& sessionId)
2277 {
2278     Ref<Event> event = MediaKeyEvent::create(eventNames().webkitkeyaddedEvent, keySystem, sessionId, nullptr, nullptr, emptyString(), nullptr, 0);
2279     event->setTarget(this);
2280     m_asyncEventQueue.enqueueEvent(WTFMove(event));
2281 }
2282
2283 void HTMLMediaElement::mediaPlayerKeyError(MediaPlayer*, const String& keySystem, const String& sessionId, MediaPlayerClient::MediaKeyErrorCode errorCode, unsigned short systemCode)
2284 {
2285     MediaKeyError::Code mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_UNKNOWN;
2286     switch (errorCode) {
2287     case MediaPlayerClient::UnknownError:
2288         mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_UNKNOWN;
2289         break;
2290     case MediaPlayerClient::ClientError:
2291         mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_CLIENT;
2292         break;
2293     case MediaPlayerClient::ServiceError:
2294         mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_SERVICE;
2295         break;
2296     case MediaPlayerClient::OutputError:
2297         mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_OUTPUT;
2298         break;
2299     case MediaPlayerClient::HardwareChangeError:
2300         mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_HARDWARECHANGE;
2301         break;
2302     case MediaPlayerClient::DomainError:
2303         mediaKeyErrorCode = MediaKeyError::MEDIA_KEYERR_DOMAIN;
2304         break;
2305     }
2306
2307     Ref<Event> event = MediaKeyEvent::create(eventNames().webkitkeyerrorEvent, keySystem, sessionId, nullptr, nullptr, emptyString(), MediaKeyError::create(mediaKeyErrorCode), systemCode);
2308     event->setTarget(this);
2309     m_asyncEventQueue.enqueueEvent(WTFMove(event));
2310 }
2311
2312 void HTMLMediaElement::mediaPlayerKeyMessage(MediaPlayer*, const String& keySystem, const String& sessionId, const unsigned char* message, unsigned messageLength, const URL& defaultURL)
2313 {
2314     Ref<Event> event = MediaKeyEvent::create(eventNames().webkitkeymessageEvent, keySystem, sessionId, nullptr, Uint8Array::create(message, messageLength), defaultURL, nullptr, 0);
2315     event->setTarget(this);
2316     m_asyncEventQueue.enqueueEvent(WTFMove(event));
2317 }
2318
2319 bool HTMLMediaElement::mediaPlayerKeyNeeded(MediaPlayer*, const String& keySystem, const String& sessionId, const unsigned char* initData, unsigned initDataLength)
2320 {
2321     if (!hasEventListeners(eventNames().webkitneedkeyEvent)) {
2322         m_error = MediaError::create(MediaError::MEDIA_ERR_ENCRYPTED);
2323         scheduleEvent(eventNames().errorEvent);
2324         return false;
2325     }
2326
2327     Ref<Event> event = MediaKeyEvent::create(eventNames().webkitneedkeyEvent, keySystem, sessionId, Uint8Array::create(initData, initDataLength), nullptr, emptyString(), nullptr, 0);
2328     event->setTarget(this);
2329     m_asyncEventQueue.enqueueEvent(WTFMove(event));
2330     return true;
2331 }
2332 #endif
2333
2334 #if ENABLE(ENCRYPTED_MEDIA_V2)
2335 RefPtr<ArrayBuffer> HTMLMediaElement::mediaPlayerCachedKeyForKeyId(const String& keyId) const
2336 {
2337     return m_mediaKeys ? m_mediaKeys->cachedKeyForKeyId(keyId) : nullptr;
2338 }
2339
2340 bool HTMLMediaElement::mediaPlayerKeyNeeded(MediaPlayer*, Uint8Array* initData)
2341 {
2342     if (!hasEventListeners("webkitneedkey")) {
2343         m_error = MediaError::create(MediaError::MEDIA_ERR_ENCRYPTED);
2344         scheduleEvent(eventNames().errorEvent);
2345         return false;
2346     }
2347
2348     RefPtr<Event> event = MediaKeyNeededEvent::create(eventNames().webkitneedkeyEvent, initData);
2349     event->setTarget(this);
2350     m_asyncEventQueue.enqueueEvent(event.release());
2351
2352     return true;
2353 }
2354
2355 String HTMLMediaElement::mediaPlayerMediaKeysStorageDirectory() const
2356 {
2357     Settings* settings = document().settings();
2358     if (!settings)
2359         return emptyString();
2360
2361     String storageDirectory = settings->mediaKeysStorageDirectory();
2362     if (storageDirectory.isEmpty())
2363         return emptyString();
2364
2365     SecurityOrigin* origin = document().securityOrigin();
2366     if (!origin)
2367         return emptyString();
2368
2369     return pathByAppendingComponent(storageDirectory, origin->databaseIdentifier());
2370 }
2371
2372 void HTMLMediaElement::setMediaKeys(MediaKeys* mediaKeys)
2373 {
2374     if (m_mediaKeys == mediaKeys)
2375         return;
2376
2377     if (m_mediaKeys)
2378         m_mediaKeys->setMediaElement(0);
2379     m_mediaKeys = mediaKeys;
2380     if (m_mediaKeys)
2381         m_mediaKeys->setMediaElement(this);
2382 }
2383
2384 void HTMLMediaElement::keyAdded()
2385 {
2386     if (m_player)
2387         m_player->keyAdded();
2388 }
2389 #endif
2390
2391 void HTMLMediaElement::progressEventTimerFired()
2392 {
2393     ASSERT(m_player);
2394     if (m_networkState != NETWORK_LOADING)
2395         return;
2396
2397     double time = monotonicallyIncreasingTime();
2398     double timedelta = time - m_previousProgressTime;
2399
2400     if (m_player->didLoadingProgress()) {
2401         scheduleEvent(eventNames().progressEvent);
2402         m_previousProgressTime = time;
2403         m_sentStalledEvent = false;
2404         if (renderer())
2405             renderer()->updateFromElement();
2406         if (hasMediaControls())
2407             mediaControls()->bufferingProgressed();
2408     } else if (timedelta > 3.0 && !m_sentStalledEvent) {
2409         scheduleEvent(eventNames().stalledEvent);
2410         m_sentStalledEvent = true;
2411         setShouldDelayLoadEvent(false);
2412     }
2413 }
2414
2415 void HTMLMediaElement::rewind(double timeDelta)
2416 {
2417     LOG(Media, "HTMLMediaElement::rewind(%p) - %f", this, timeDelta);
2418     setCurrentTime(std::max(currentMediaTime() - MediaTime::createWithDouble(timeDelta), minTimeSeekable()));
2419 }
2420
2421 void HTMLMediaElement::returnToRealtime()
2422 {
2423     LOG(Media, "HTMLMediaElement::returnToRealtime(%p)", this);
2424     setCurrentTime(maxTimeSeekable());
2425 }
2426
2427 void HTMLMediaElement::addPlayedRange(const MediaTime& start, const MediaTime& end)
2428 {
2429     LOG(Media, "HTMLMediaElement::addPlayedRange(%p) - [%s, %s]", this, toString(start).utf8().data(), toString(end).utf8().data());
2430     if (!m_playedTimeRanges)
2431         m_playedTimeRanges = TimeRanges::create();
2432     m_playedTimeRanges->ranges().add(start, end);
2433 }  
2434
2435 bool HTMLMediaElement::supportsScanning() const
2436 {
2437     return m_player ? m_player->supportsScanning() : false;
2438 }
2439
2440 void HTMLMediaElement::prepareToPlay()
2441 {
2442     LOG(Media, "HTMLMediaElement::prepareToPlay(%p)", this);
2443     if (m_havePreparedToPlay)
2444         return;
2445     m_havePreparedToPlay = true;
2446     m_player->prepareToPlay();
2447 }
2448
2449 void HTMLMediaElement::fastSeek(double time)
2450 {
2451     fastSeek(MediaTime::createWithDouble(time));
2452 }
2453
2454 void HTMLMediaElement::fastSeek(const MediaTime& time)
2455 {
2456     LOG(Media, "HTMLMediaElement::fastSeek(%p) - %s", this, toString(time).utf8().data());
2457     // 4.7.10.9 Seeking
2458     // 9. If the approximate-for-speed flag is set, adjust the new playback position to a value that will
2459     // allow for playback to resume promptly. If new playback position before this step is before current
2460     // playback position, then the adjusted new playback position must also be before the current playback
2461     // position. Similarly, if the new playback position before this step is after current playback position,
2462     // then the adjusted new playback position must also be after the current playback position.
2463     refreshCachedTime();
2464     MediaTime delta = time - currentMediaTime();
2465     MediaTime negativeTolerance = delta >= MediaTime::zeroTime() ? delta : MediaTime::positiveInfiniteTime();
2466     MediaTime positiveTolerance = delta < MediaTime::zeroTime() ? -delta : MediaTime::positiveInfiniteTime();
2467
2468     seekWithTolerance(time, negativeTolerance, positiveTolerance, true);
2469 }
2470
2471 void HTMLMediaElement::seek(const MediaTime& time)
2472 {
2473     LOG(Media, "HTMLMediaElement::seek(%p) - %s", this, toString(time).utf8().data());
2474     seekWithTolerance(time, MediaTime::zeroTime(), MediaTime::zeroTime(), true);
2475 }
2476
2477 void HTMLMediaElement::seekInternal(const MediaTime& time)
2478 {
2479     LOG(Media, "HTMLMediaElement::seekInternal(%p) - %s", this, toString(time).utf8().data());
2480     seekWithTolerance(time, MediaTime::zeroTime(), MediaTime::zeroTime(), false);
2481 }
2482
2483 void HTMLMediaElement::seekWithTolerance(const MediaTime& inTime, const MediaTime& negativeTolerance, const MediaTime& positiveTolerance, bool fromDOM)
2484 {
2485     // 4.8.10.9 Seeking
2486     MediaTime time = inTime;
2487
2488     // 1 - Set the media element's show poster flag to false.
2489     setDisplayMode(Video);
2490
2491     // 2 - If the media element's readyState is HAVE_NOTHING, abort these steps.
2492     if (m_readyState == HAVE_NOTHING || !m_player)
2493         return;
2494
2495     // If the media engine has been told to postpone loading data, let it go ahead now.
2496     if (m_preload < MediaPlayer::Auto && m_readyState < HAVE_FUTURE_DATA)
2497         prepareToPlay();
2498
2499     // Get the current time before setting m_seeking, m_lastSeekTime is returned once it is set.
2500     refreshCachedTime();
2501     MediaTime now = currentMediaTime();
2502
2503     // 3 - If the element's seeking IDL attribute is true, then another instance of this algorithm is
2504     // already running. Abort that other instance of the algorithm without waiting for the step that
2505     // it is running to complete.
2506     if (m_seekTaskQueue.hasPendingTasks()) {
2507         LOG(Media, "HTMLMediaElement::seekWithTolerance(%p) - cancelling pending seeks", this);
2508         m_seekTaskQueue.cancelAllTasks();
2509         if (m_pendingSeek) {
2510             now = m_pendingSeek->now;
2511             m_pendingSeek = nullptr;
2512         }
2513         m_pendingSeekType = NoSeek;
2514     }
2515
2516     // 4 - Set the seeking IDL attribute to true.
2517     // The flag will be cleared when the engine tells us the time has actually changed.
2518     m_seeking = true;
2519     if (m_playing) {
2520         if (m_lastSeekTime < now)
2521             addPlayedRange(m_lastSeekTime, now);
2522     }
2523     m_lastSeekTime = time;
2524
2525     // 5 - If the seek was in response to a DOM method call or setting of an IDL attribute, then continue
2526     // the script. The remainder of these steps must be run asynchronously.
2527     m_pendingSeek = std::make_unique<PendingSeek>(now, time, negativeTolerance, positiveTolerance);
2528     if (fromDOM) {
2529         LOG(Media, "HTMLMediaElement::seekWithTolerance(%p) - enqueuing seek from %s to %s", this, toString(now).utf8().data(), toString(time).utf8().data());
2530         m_seekTaskQueue.enqueueTask(std::bind(&HTMLMediaElement::seekTask, this));
2531     } else
2532         seekTask();
2533 }
2534
2535 void HTMLMediaElement::seekTask()
2536 {
2537     LOG(Media, "HTMLMediaElement::seekTask(%p)", this);
2538
2539     if (!m_player) {
2540         clearSeeking();
2541         return;
2542     }
2543
2544     ASSERT(m_pendingSeek);
2545     MediaTime now = m_pendingSeek->now;
2546     MediaTime time = m_pendingSeek->targetTime;
2547     MediaTime negativeTolerance = m_pendingSeek->negativeTolerance;
2548     MediaTime positiveTolerance = m_pendingSeek->positiveTolerance;
2549     m_pendingSeek = nullptr;
2550
2551     // 6 - If the new playback position is later than the end of the media resource, then let it be the end 
2552     // of the media resource instead.
2553     time = std::min(time, durationMediaTime());
2554
2555     // 7 - If the new playback position is less than the earliest possible position, let it be that position instead.
2556     MediaTime earliestTime = m_player->startTime();
2557     time = std::max(time, earliestTime);
2558
2559     // Ask the media engine for the time value in the movie's time scale before comparing with current time. This
2560     // is necessary because if the seek time is not equal to currentTime but the delta is less than the movie's
2561     // time scale, we will ask the media engine to "seek" to the current movie time, which may be a noop and
2562     // not generate a timechanged callback. This means m_seeking will never be cleared and we will never 
2563     // fire a 'seeked' event.
2564 #if !LOG_DISABLED
2565     MediaTime mediaTime = m_player->mediaTimeForTimeValue(time);
2566     if (time != mediaTime)
2567         LOG(Media, "HTMLMediaElement::seekTask(%p) - %s - media timeline equivalent is %s", this, toString(time).utf8().data(), toString(mediaTime).utf8().data());
2568 #endif
2569     time = m_player->mediaTimeForTimeValue(time);
2570
2571     // 8 - If the (possibly now changed) new playback position is not in one of the ranges given in the
2572     // seekable attribute, then let it be the position in one of the ranges given in the seekable attribute 
2573     // that is the nearest to the new playback position. ... If there are no ranges given in the seekable
2574     // attribute then set the seeking IDL attribute to false and abort these steps.
2575     RefPtr<TimeRanges> seekableRanges = seekable();
2576     bool noSeekRequired = !seekableRanges->length();
2577
2578     // Short circuit seeking to the current time by just firing the events if no seek is required.
2579     // Don't skip calling the media engine if 1) we are in poster mode (because a seek should always cancel
2580     // poster display), or 2) if there is a pending fast seek, or 3) if this seek is not an exact seek
2581     SeekType thisSeekType = (negativeTolerance == MediaTime::zeroTime() && positiveTolerance == MediaTime::zeroTime()) ? Precise : Fast;
2582     if (!noSeekRequired && time == now && thisSeekType == Precise && m_pendingSeekType != Fast && displayMode() != Poster)
2583         noSeekRequired = true;
2584
2585 #if ENABLE(MEDIA_SOURCE)
2586     // Always notify the media engine of a seek if the source is not closed. This ensures that the source is
2587     // always in a flushed state when the 'seeking' event fires.
2588     if (m_mediaSource && !m_mediaSource->isClosed())
2589         noSeekRequired = false;
2590 #endif
2591
2592     if (noSeekRequired) {
2593         LOG(Media, "HTMLMediaElement::seekTask(%p) - seek to %s ignored", this, toString(time).utf8().data());
2594         if (time == now) {
2595             scheduleEvent(eventNames().seekingEvent);
2596             scheduleTimeupdateEvent(false);
2597             scheduleEvent(eventNames().seekedEvent);
2598         }
2599         clearSeeking();
2600         return;
2601     }
2602     time = seekableRanges->ranges().nearest(time);
2603
2604     m_sentEndEvent = false;
2605     m_lastSeekTime = time;
2606     m_pendingSeekType = thisSeekType;
2607     m_seeking = true;
2608
2609     // 10 - Queue a task to fire a simple event named seeking at the element.
2610     scheduleEvent(eventNames().seekingEvent);
2611
2612     // 11 - Set the current playback position to the given new playback position
2613     m_player->seekWithTolerance(time, negativeTolerance, positiveTolerance);
2614
2615     // 12 - Wait until the user agent has established whether or not the media data for the new playback
2616     // position is available, and, if it is, until it has decoded enough data to play back that position.
2617     // 13 - Await a stable state. The synchronous section consists of all the remaining steps of this algorithm.
2618 }
2619
2620 void HTMLMediaElement::clearSeeking()
2621 {
2622     m_seeking = false;
2623     m_pendingSeekType = NoSeek;
2624     invalidateCachedTime();
2625 }
2626
2627 void HTMLMediaElement::finishSeek()
2628 {
2629     // 4.8.10.9 Seeking
2630     // 14 - Set the seeking IDL attribute to false.
2631     clearSeeking();
2632
2633     LOG(Media, "HTMLMediaElement::finishSeek(%p) - current time = %s", this, toString(currentMediaTime()).utf8().data());
2634
2635     // 15 - Run the time maches on steps.
2636     // Handled by mediaPlayerTimeChanged().
2637
2638     // 16 - Queue a task to fire a simple event named timeupdate at the element.
2639     scheduleEvent(eventNames().timeupdateEvent);
2640
2641     // 17 - Queue a task to fire a simple event named seeked at the element.
2642     scheduleEvent(eventNames().seekedEvent);
2643
2644 #if ENABLE(MEDIA_SOURCE)
2645     if (m_mediaSource)
2646         m_mediaSource->monitorSourceBuffers();
2647 #endif
2648 }
2649
2650 HTMLMediaElement::ReadyState HTMLMediaElement::readyState() const
2651 {
2652     return m_readyState;
2653 }
2654
2655 MediaPlayer::MovieLoadType HTMLMediaElement::movieLoadType() const
2656 {
2657     return m_player ? m_player->movieLoadType() : MediaPlayer::Unknown;
2658 }
2659
2660 bool HTMLMediaElement::hasAudio() const
2661 {
2662     return m_player ? m_player->hasAudio() : false;
2663 }
2664
2665 bool HTMLMediaElement::seeking() const
2666 {
2667     return m_seeking;
2668 }
2669
2670 void HTMLMediaElement::refreshCachedTime() const
2671 {
2672     if (!m_player)
2673         return;
2674
2675     m_cachedTime = m_player->currentTime();
2676     if (!m_cachedTime) {
2677         // Do not use m_cachedTime until the media engine returns a non-zero value because we can't 
2678         // estimate current time until playback actually begins. 
2679         invalidateCachedTime(); 
2680         return; 
2681     } 
2682
2683     m_clockTimeAtLastCachedTimeUpdate = monotonicallyIncreasingTime();
2684 }
2685
2686 void HTMLMediaElement::invalidateCachedTime() const
2687 {
2688     m_cachedTime = MediaTime::invalidTime();
2689     if (!m_player || !m_player->maximumDurationToCacheMediaTime())
2690         return;
2691
2692 #if !LOG_DISABLED
2693     if (m_cachedTime.isValid())
2694         LOG(Media, "HTMLMediaElement::invalidateCachedTime(%p)", this);
2695 #endif
2696
2697     // Don't try to cache movie time when playback first starts as the time reported by the engine
2698     // sometimes fluctuates for a short amount of time, so the cached time will be off if we take it
2699     // too early.
2700     static const double minimumTimePlayingBeforeCacheSnapshot = 0.5;
2701
2702     m_minimumClockTimeToUpdateCachedTime = monotonicallyIncreasingTime() + minimumTimePlayingBeforeCacheSnapshot;
2703 }
2704
2705 // playback state
2706 double HTMLMediaElement::currentTime() const
2707 {
2708     return currentMediaTime().toDouble();
2709 }
2710
2711 MediaTime HTMLMediaElement::currentMediaTime() const
2712 {
2713 #if LOG_CACHED_TIME_WARNINGS
2714     static const MediaTime minCachedDeltaForWarning = MediaTime::create(1, 100);
2715 #endif
2716
2717     if (!m_player)
2718         return MediaTime::zeroTime();
2719
2720     if (m_seeking) {
2721         LOG(Media, "HTMLMediaElement::currentTime(%p) - seeking, returning %s", this, toString(m_lastSeekTime).utf8().data());
2722         return m_lastSeekTime;
2723     }
2724
2725     if (m_cachedTime.isValid() && m_paused) {
2726 #if LOG_CACHED_TIME_WARNINGS
2727         MediaTime delta = m_cachedTime - m_player->currentTime();
2728         if (delta > minCachedDeltaForWarning)
2729             LOG(Media, "HTMLMediaElement::currentTime(%p) - WARNING, cached time is %s seconds off of media time when paused", this, toString(delta).utf8().data());
2730 #endif
2731         return m_cachedTime;
2732     }
2733
2734     // Is it too soon use a cached time?
2735     double now = monotonicallyIncreasingTime();
2736     double maximumDurationToCacheMediaTime = m_player->maximumDurationToCacheMediaTime();
2737
2738     if (maximumDurationToCacheMediaTime && m_cachedTime.isValid() && !m_paused && now > m_minimumClockTimeToUpdateCachedTime) {
2739         double clockDelta = now - m_clockTimeAtLastCachedTimeUpdate;
2740
2741         // Not too soon, use the cached time only if it hasn't expired.
2742         if (clockDelta < maximumDurationToCacheMediaTime) {
2743             MediaTime adjustedCacheTime = m_cachedTime + MediaTime::createWithDouble(effectivePlaybackRate() * clockDelta);
2744
2745 #if LOG_CACHED_TIME_WARNINGS
2746             MediaTime delta = adjustedCacheTime - m_player->currentTime();
2747             if (delta > minCachedDeltaForWarning)
2748                 LOG(Media, "HTMLMediaElement::currentTime(%p) - WARNING, cached time is %f seconds off of media time when playing", this, delta);
2749 #endif
2750             return adjustedCacheTime;
2751         }
2752     }
2753
2754 #if LOG_CACHED_TIME_WARNINGS
2755     if (maximumDurationToCacheMediaTime && now > m_minimumClockTimeToUpdateCachedTime && m_cachedTime != MediaPlayer::invalidTime()) {
2756         double clockDelta = now - m_clockTimeAtLastCachedTimeUpdate;
2757         MediaTime delta = m_cachedTime + MediaTime::createWithDouble(effectivePlaybackRate() * clockDelta) - m_player->currentTime();
2758         LOG(Media, "HTMLMediaElement::currentTime(%p) - cached time was %s seconds off of media time when it expired", this, toString(delta).utf8().data());
2759     }
2760 #endif
2761
2762     refreshCachedTime();
2763
2764     if (m_cachedTime.isInvalid())
2765         return MediaTime::zeroTime();
2766     
2767     return m_cachedTime;
2768 }
2769
2770 void HTMLMediaElement::setCurrentTime(double time)
2771 {
2772     setCurrentTime(MediaTime::createWithDouble(time));
2773 }
2774
2775 void HTMLMediaElement::setCurrentTime(const MediaTime& time)
2776 {
2777     if (m_mediaController)
2778         return;
2779
2780     seekInternal(time);
2781 }
2782
2783 void HTMLMediaElement::setCurrentTime(double time, ExceptionCode& ec)
2784 {
2785     // On setting, if the media element has a current media controller, then the user agent must
2786     // throw an InvalidStateError exception
2787     if (m_mediaController) {
2788         ec = INVALID_STATE_ERR;
2789         return;
2790     }
2791
2792     seek(MediaTime::createWithDouble(time));
2793 }
2794
2795 double HTMLMediaElement::duration() const
2796 {
2797     return durationMediaTime().toDouble();
2798 }
2799
2800 MediaTime HTMLMediaElement::durationMediaTime() const
2801 {
2802     if (m_player && m_readyState >= HAVE_METADATA)
2803         return m_player->duration();
2804
2805     return MediaTime::invalidTime();
2806 }
2807
2808 bool HTMLMediaElement::paused() const
2809 {
2810     // As of this writing, JavaScript garbage collection calls this function directly. In the past
2811     // we had problems where this was called on an object after a bad cast. The assertion below
2812     // made our regression test detect the problem, so we should keep it because of that. But note
2813     // that the value of the assertion relies on the compiler not being smart enough to know that
2814     // isHTMLUnknownElement is guaranteed to return false for an HTMLMediaElement.
2815     ASSERT(!isHTMLUnknownElement());
2816
2817     return m_paused;
2818 }
2819
2820 double HTMLMediaElement::defaultPlaybackRate() const
2821 {
2822 #if ENABLE(MEDIA_STREAM)
2823     // http://w3c.github.io/mediacapture-main/#mediastreams-in-media-elements
2824     // "defaultPlaybackRate" - On setting: ignored. On getting: return 1.0
2825     // A MediaStream is not seekable. Therefore, this attribute must always have the
2826     // value 1.0 and any attempt to alter it must be ignored. Note that this also means
2827     // that the ratechange event will not fire.
2828     if (m_mediaStreamSrcObject)
2829         return 1;
2830 #endif
2831
2832     return m_defaultPlaybackRate;
2833 }
2834
2835 void HTMLMediaElement::setDefaultPlaybackRate(double rate)
2836 {
2837 #if ENABLE(MEDIA_STREAM)
2838     // http://w3c.github.io/mediacapture-main/#mediastreams-in-media-elements
2839     // "defaultPlaybackRate" - On setting: ignored. On getting: return 1.0
2840     // A MediaStream is not seekable. Therefore, this attribute must always have the
2841     // value 1.0 and any attempt to alter it must be ignored. Note that this also means
2842     // that the ratechange event will not fire.
2843     if (m_mediaStreamSrcObject)
2844         return;
2845 #endif
2846
2847     if (m_defaultPlaybackRate != rate) {
2848         LOG(Media, "HTMLMediaElement::setDefaultPlaybackRate(%p) - %f", this, rate);
2849         m_defaultPlaybackRate = rate;
2850         scheduleEvent(eventNames().ratechangeEvent);
2851     }
2852 }
2853
2854 double HTMLMediaElement::effectivePlaybackRate() const
2855 {
2856     return m_mediaController ? m_mediaController->playbackRate() : m_reportedPlaybackRate;
2857 }
2858
2859 double HTMLMediaElement::requestedPlaybackRate() const
2860 {
2861     return m_mediaController ? m_mediaController->playbackRate() : m_requestedPlaybackRate;
2862 }
2863
2864 double HTMLMediaElement::playbackRate() const
2865 {
2866 #if ENABLE(MEDIA_STREAM)
2867     // http://w3c.github.io/mediacapture-main/#mediastreams-in-media-elements
2868     // "playbackRate" - A MediaStream is not seekable. Therefore, this attribute must always
2869     // have the value 1.0 and any attempt to alter it must be ignored. Note that this also
2870     // means that the ratechange event will not fire.
2871     if (m_mediaStreamSrcObject)
2872         return 1;
2873 #endif
2874
2875     return m_requestedPlaybackRate;
2876 }
2877
2878 void HTMLMediaElement::setPlaybackRate(double rate)
2879 {
2880     LOG(Media, "HTMLMediaElement::setPlaybackRate(%p) - %f", this, rate);
2881
2882 #if ENABLE(MEDIA_STREAM)
2883     // http://w3c.github.io/mediacapture-main/#mediastreams-in-media-elements
2884     // "playbackRate" - A MediaStream is not seekable. Therefore, this attribute must always
2885     // have the value 1.0 and any attempt to alter it must be ignored. Note that this also
2886     // means that the ratechange event will not fire.
2887     if (m_mediaStreamSrcObject)
2888         return;
2889 #endif
2890
2891     if (m_player && potentiallyPlaying() && m_player->rate() != rate && !m_mediaController)
2892         m_player->setRate(rate);
2893
2894     if (m_requestedPlaybackRate != rate) {
2895         m_reportedPlaybackRate = m_requestedPlaybackRate = rate;
2896         invalidateCachedTime();
2897         scheduleEvent(eventNames().ratechangeEvent);
2898     }
2899 }
2900
2901 void HTMLMediaElement::updatePlaybackRate()
2902 {
2903     double requestedRate = requestedPlaybackRate();
2904     if (m_player && potentiallyPlaying() && m_player->rate() != requestedRate)
2905         m_player->setRate(requestedRate);
2906 }
2907
2908 bool HTMLMediaElement::webkitPreservesPitch() const
2909 {
2910     return m_webkitPreservesPitch;
2911 }
2912
2913 void HTMLMediaElement::setWebkitPreservesPitch(bool preservesPitch)
2914 {
2915     LOG(Media, "HTMLMediaElement::setWebkitPreservesPitch(%p) - %s", this, boolString(preservesPitch));
2916
2917     m_webkitPreservesPitch = preservesPitch;
2918     
2919     if (!m_player)
2920         return;
2921
2922     m_player->setPreservesPitch(preservesPitch);
2923 }
2924
2925 bool HTMLMediaElement::ended() const
2926 {
2927 #if ENABLE(MEDIA_STREAM)
2928     // http://w3c.github.io/mediacapture-main/#mediastreams-in-media-elements
2929     // When the MediaStream state moves from the active to the inactive state, the User Agent
2930     // must raise an ended event on the HTMLMediaElement and set its ended attribute to true.
2931     if (m_mediaStreamSrcObject && m_player && m_player->ended())
2932         return true;
2933 #endif
2934
2935     // 4.8.10.8 Playing the media resource
2936     // The ended attribute must return true if the media element has ended 
2937     // playback and the direction of playback is forwards, and false otherwise.
2938     return endedPlayback() && requestedPlaybackRate() > 0;
2939 }
2940
2941 bool HTMLMediaElement::autoplay() const
2942 {
2943     return fastHasAttribute(autoplayAttr);
2944 }
2945
2946 String HTMLMediaElement::preload() const
2947 {
2948 #if ENABLE(MEDIA_STREAM)
2949     // http://w3c.github.io/mediacapture-main/#mediastreams-in-media-elements
2950     // "preload" - On getting: none. On setting: ignored.
2951     if (m_mediaStreamSrcObject)
2952         return ASCIILiteral("none");
2953 #endif
2954
2955     switch (m_preload) {
2956     case MediaPlayer::None:
2957         return ASCIILiteral("none");
2958     case MediaPlayer::MetaData:
2959         return ASCIILiteral("metadata");
2960     case MediaPlayer::Auto:
2961         return ASCIILiteral("auto");
2962     }
2963
2964     ASSERT_NOT_REACHED();
2965     return String();
2966 }
2967
2968 void HTMLMediaElement::setPreload(const String& preload)
2969 {
2970     LOG(Media, "HTMLMediaElement::setPreload(%p) - %s", this, preload.utf8().data());
2971 #if ENABLE(MEDIA_STREAM)
2972     // http://w3c.github.io/mediacapture-main/#mediastreams-in-media-elements
2973     // "preload" - On getting: none. On setting: ignored.
2974     if (m_mediaStreamSrcObject)
2975         return;
2976 #endif
2977
2978     setAttribute(preloadAttr, preload);
2979 }
2980
2981 void HTMLMediaElement::play()
2982 {
2983     LOG(Media, "HTMLMediaElement::play(%p)", this);
2984
2985     if (!m_mediaSession->playbackPermitted(*this))
2986         return;
2987     if (ScriptController::processingUserGestureForMedia())
2988         removeBehaviorsRestrictionsAfterFirstUserGesture();
2989
2990     playInternal();
2991 }
2992
2993 void HTMLMediaElement::playInternal()
2994 {
2995     LOG(Media, "HTMLMediaElement::playInternal(%p)", this);
2996     
2997     if (!m_mediaSession->clientWillBeginPlayback()) {
2998         LOG(Media, "  returning because of interruption");
2999         return;
3000     }
3001
3002     // 4.8.10.9. Playing the media resource
3003     if (!m_player || m_networkState == NETWORK_EMPTY)
3004         scheduleDelayedAction(LoadMediaResource);
3005
3006     if (endedPlayback())
3007         seekInternal(MediaTime::zeroTime());
3008
3009     if (m_mediaController)
3010         m_mediaController->bringElementUpToSpeed(this);
3011
3012     if (m_paused) {
3013         m_paused = false;
3014         invalidateCachedTime();
3015         scheduleEvent(eventNames().playEvent);
3016
3017         if (m_readyState <= HAVE_CURRENT_DATA)
3018             scheduleEvent(eventNames().waitingEvent);
3019         else if (m_readyState >= HAVE_FUTURE_DATA)
3020             scheduleEvent(eventNames().playingEvent);
3021
3022 #if ENABLE(MEDIA_SESSION)
3023         // 6.3 Activating a media session from a media element
3024         // When the play() method is invoked, the paused attribute is true, and the readyState attribute has the value
3025         // HAVE_FUTURE_DATA or HAVE_ENOUGH_DATA, then
3026         // 1. Let media session be the value of the current media session.
3027         // 2. If we are not currently in media session's list of active participating media elements then append
3028         //    ourselves to this list.
3029         // 3. Let activated be the result of running the media session invocation algorithm for media session.
3030         // 4. If activated is failure, pause ourselves.
3031         if (m_readyState == HAVE_ENOUGH_DATA || m_readyState == HAVE_FUTURE_DATA) {
3032             if (m_session) {
3033                 m_session->addActiveMediaElement(*this);
3034
3035                 if (m_session->kindEnum() == MediaSession::Kind::Content) {
3036                     if (Page* page = document().page())
3037                         page->chrome().client().focusedContentMediaElementDidChange(m_elementID);
3038                 }
3039
3040                 if (!m_session->invoke()) {
3041                     pause();
3042                     return;
3043                 }
3044             }
3045         }
3046 #endif
3047     }
3048     m_autoplaying = false;
3049     updatePlayState();
3050     updateMediaController();
3051 }
3052
3053 void HTMLMediaElement::pause()
3054 {
3055     LOG(Media, "HTMLMediaElement::pause(%p)", this);
3056
3057     if (!m_mediaSession->playbackPermitted(*this))
3058         return;
3059
3060     pauseInternal();
3061 }
3062
3063
3064 void HTMLMediaElement::pauseInternal()
3065 {
3066     LOG(Media, "HTMLMediaElement::pauseInternal(%p)", this);
3067
3068     if (!m_mediaSession->clientWillPausePlayback()) {
3069         LOG(Media, "  returning because of interruption");
3070         return;
3071     }
3072
3073     // 4.8.10.9. Playing the media resource
3074     if (!m_player || m_networkState == NETWORK_EMPTY) {
3075         // Unless the restriction on media requiring user action has been lifted
3076         // don't trigger loading if a script calls pause().
3077         if (!m_mediaSession->playbackPermitted(*this))
3078             return;
3079         scheduleDelayedAction(LoadMediaResource);
3080     }
3081
3082     m_autoplaying = false;
3083
3084     if (!m_paused) {
3085         m_paused = true;
3086         scheduleTimeupdateEvent(false);
3087         scheduleEvent(eventNames().pauseEvent);
3088
3089         if (MemoryPressureHandler::singleton().isUnderMemoryPressure())
3090             purgeBufferedDataIfPossible();
3091     }
3092
3093     updatePlayState();
3094 }
3095
3096 #if ENABLE(MEDIA_SOURCE)
3097 void HTMLMediaElement::closeMediaSource()
3098 {
3099     if (!m_mediaSource)
3100         return;
3101
3102     m_mediaSource->close();
3103     m_mediaSource = nullptr;
3104 }
3105 #endif
3106
3107 #if ENABLE(ENCRYPTED_MEDIA)
3108 void HTMLMediaElement::webkitGenerateKeyRequest(const String& keySystem, PassRefPtr<Uint8Array> initData, ExceptionCode& ec)
3109 {
3110 #if ENABLE(ENCRYPTED_MEDIA_V2)
3111     static bool firstTime = true;
3112     if (firstTime && scriptExecutionContext()) {
3113         scriptExecutionContext()->addConsoleMessage(MessageSource::JS, MessageLevel::Warning, ASCIILiteral("'HTMLMediaElement.webkitGenerateKeyRequest()' is deprecated.  Use 'MediaKeys.createSession()' instead."));
3114         firstTime = false;
3115     }
3116 #endif
3117
3118     if (keySystem.isEmpty()) {
3119         ec = SYNTAX_ERR;
3120         return;
3121     }
3122
3123     if (!m_player) {
3124         ec = INVALID_STATE_ERR;
3125         return;
3126     }
3127
3128     const unsigned char* initDataPointer = 0;
3129     unsigned initDataLength = 0;
3130     if (initData) {
3131         initDataPointer = initData->data();
3132         initDataLength = initData->length();
3133     }
3134
3135     MediaPlayer::MediaKeyException result = m_player->generateKeyRequest(keySystem, initDataPointer, initDataLength);
3136     ec = exceptionCodeForMediaKeyException(result);
3137 }
3138
3139 void HTMLMediaElement::webkitGenerateKeyRequest(const String& keySystem, ExceptionCode& ec)
3140 {
3141     webkitGenerateKeyRequest(keySystem, Uint8Array::create(0), ec);
3142 }
3143
3144 void HTMLMediaElement::webkitAddKey(const String& keySystem, PassRefPtr<Uint8Array> key, PassRefPtr<Uint8Array> initData, const String& sessionId, ExceptionCode& ec)
3145 {
3146 #if ENABLE(ENCRYPTED_MEDIA_V2)
3147     static bool firstTime = true;
3148     if (firstTime && scriptExecutionContext()) {
3149         scriptExecutionContext()->addConsoleMessage(MessageSource::JS, MessageLevel::Warning, ASCIILiteral("'HTMLMediaElement.webkitAddKey()' is deprecated.  Use 'MediaKeySession.update()' instead."));
3150         firstTime = false;
3151     }
3152 #endif
3153
3154     if (keySystem.isEmpty()) {
3155         ec = SYNTAX_ERR;
3156         return;
3157     }
3158
3159     if (!key) {
3160         ec = SYNTAX_ERR;
3161         return;
3162     }
3163
3164     if (!key->length()) {
3165         ec = TYPE_MISMATCH_ERR;
3166         return;
3167     }
3168
3169     if (!m_player) {
3170         ec = INVALID_STATE_ERR;
3171         return;
3172     }
3173
3174     const unsigned char* initDataPointer = 0;
3175     unsigned initDataLength = 0;
3176     if (initData) {
3177         initDataPointer = initData->data();
3178         initDataLength = initData->length();
3179     }
3180
3181     MediaPlayer::MediaKeyException result = m_player->addKey(keySystem, key->data(), key->length(), initDataPointer, initDataLength, sessionId);
3182     ec = exceptionCodeForMediaKeyException(result);
3183 }
3184
3185 void HTMLMediaElement::webkitAddKey(const String& keySystem, PassRefPtr<Uint8Array> key, ExceptionCode& ec)
3186 {
3187     webkitAddKey(keySystem, key, Uint8Array::create(0), String(), ec);
3188 }
3189
3190 void HTMLMediaElement::webkitCancelKeyRequest(const String& keySystem, const String& sessionId, ExceptionCode& ec)
3191 {
3192     if (keySystem.isEmpty()) {
3193         ec = SYNTAX_ERR;
3194         return;
3195     }
3196
3197     if (!m_player) {
3198         ec = INVALID_STATE_ERR;
3199         return;
3200     }
3201
3202     MediaPlayer::MediaKeyException result = m_player->cancelKeyRequest(keySystem, sessionId);
3203     ec = exceptionCodeForMediaKeyException(result);
3204 }
3205
3206 #endif
3207
3208 bool HTMLMediaElement::loop() const
3209 {
3210     return fastHasAttribute(loopAttr);
3211 }
3212
3213 void HTMLMediaElement::setLoop(bool b)
3214 {
3215     LOG(Media, "HTMLMediaElement::setLoop(%p) - %s", this, boolString(b));
3216     setBooleanAttribute(loopAttr, b);
3217 }
3218
3219 bool HTMLMediaElement::controls() const
3220 {
3221     Frame* frame = document().frame();
3222
3223     // always show controls when scripting is disabled
3224     if (frame && !frame->script().canExecuteScripts(NotAboutToExecuteScript))
3225         return true;
3226
3227     return fastHasAttribute(controlsAttr);
3228 }
3229
3230 void HTMLMediaElement::setControls(bool b)
3231 {
3232     LOG(Media, "HTMLMediaElement::setControls(%p) - %s", this, boolString(b));
3233     setBooleanAttribute(controlsAttr, b);
3234 }
3235
3236 double HTMLMediaElement::volume() const
3237 {
3238     return m_volume;
3239 }
3240
3241 void HTMLMediaElement::setVolume(double vol, ExceptionCode& ec)
3242 {
3243     LOG(Media, "HTMLMediaElement::setVolume(%p) - %f", this, vol);
3244
3245     if (vol < 0.0f || vol > 1.0f) {
3246         ec = INDEX_SIZE_ERR;
3247         return;
3248     }
3249     
3250 #if !PLATFORM(IOS)
3251     if (m_volume != vol) {
3252         m_volume = vol;
3253         m_volumeInitialized = true;
3254         updateVolume();
3255         scheduleEvent(eventNames().volumechangeEvent);
3256     }
3257 #endif
3258 }
3259
3260 bool HTMLMediaElement::muted() const
3261 {
3262     return m_explicitlyMuted ? m_muted : fastHasAttribute(mutedAttr);
3263 }
3264
3265 void HTMLMediaElement::setMuted(bool muted)
3266 {
3267     LOG(Media, "HTMLMediaElement::setMuted(%p) - %s", this, boolString(muted));
3268
3269 #if PLATFORM(IOS)
3270     UNUSED_PARAM(muted);
3271 #else
3272     if (m_muted != muted || !m_explicitlyMuted) {
3273         m_muted = muted;
3274         m_explicitlyMuted = true;
3275         // Avoid recursion when the player reports volume changes.
3276         if (!processingMediaPlayerCallback()) {
3277             if (m_player) {
3278                 m_player->setMuted(effectiveMuted());
3279                 if (hasMediaControls())
3280                     mediaControls()->changedMute();
3281             }
3282         }
3283         scheduleEvent(eventNames().volumechangeEvent);
3284
3285 #if ENABLE(MEDIA_SESSION)
3286         document().updateIsPlayingMedia(m_elementID);
3287 #else
3288         document().updateIsPlayingMedia();
3289 #endif
3290
3291 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
3292         updateMediaState(UpdateMediaState::Asynchronously);
3293 #endif
3294     }
3295 #endif
3296 }
3297
3298 void HTMLMediaElement::togglePlayState()
3299 {
3300     LOG(Media, "HTMLMediaElement::togglePlayState(%p) - canPlay() is %s", this, boolString(canPlay()));
3301
3302     // We can safely call the internal play/pause methods, which don't check restrictions, because
3303     // this method is only called from the built-in media controller
3304     if (canPlay()) {
3305         updatePlaybackRate();
3306         playInternal();
3307     } else 
3308         pauseInternal();
3309 }
3310
3311 void HTMLMediaElement::beginScrubbing()
3312 {
3313     LOG(Media, "HTMLMediaElement::beginScrubbing(%p) - paused() is %s", this, boolString(paused()));
3314
3315     if (!paused()) {
3316         if (ended()) {
3317             // Because a media element stays in non-paused state when it reaches end, playback resumes 
3318             // when the slider is dragged from the end to another position unless we pause first. Do 
3319             // a "hard pause" so an event is generated, since we want to stay paused after scrubbing finishes.
3320             pause();
3321         } else {
3322             // Not at the end but we still want to pause playback so the media engine doesn't try to
3323             // continue playing during scrubbing. Pause without generating an event as we will 
3324             // unpause after scrubbing finishes.
3325             setPausedInternal(true);
3326         }
3327     }
3328 }
3329
3330 void HTMLMediaElement::endScrubbing()
3331 {
3332     LOG(Media, "HTMLMediaElement::endScrubbing(%p) - m_pausedInternal is %s", this, boolString(m_pausedInternal));
3333
3334     if (m_pausedInternal)
3335         setPausedInternal(false);
3336 }
3337
3338 void HTMLMediaElement::beginScanning(ScanDirection direction)
3339 {
3340     m_scanType = supportsScanning() ? Scan : Seek;
3341     m_scanDirection = direction;
3342
3343     if (m_scanType == Seek) {
3344         // Scanning by seeking requires the video to be paused during scanning.
3345         m_actionAfterScan = paused() ? Nothing : Play;
3346         pause();
3347     } else {
3348         // Scanning by scanning requires the video to be playing during scanninging.
3349         m_actionAfterScan = paused() ? Pause : Nothing;
3350         play();
3351         setPlaybackRate(nextScanRate());
3352     }
3353
3354     m_scanTimer.start(0, m_scanType == Seek ? SeekRepeatDelay : ScanRepeatDelay);
3355 }
3356
3357 void HTMLMediaElement::endScanning()
3358 {
3359     if (m_scanType == Scan)
3360         setPlaybackRate(defaultPlaybackRate());
3361
3362     if (m_actionAfterScan == Play)
3363         play();
3364     else if (m_actionAfterScan == Pause)
3365         pause();
3366
3367     if (m_scanTimer.isActive())
3368         m_scanTimer.stop();
3369 }
3370
3371 double HTMLMediaElement::nextScanRate()
3372 {
3373     double rate = std::min(ScanMaximumRate, fabs(playbackRate() * 2));
3374     if (m_scanDirection == Backward)
3375         rate *= -1;
3376 #if PLATFORM(IOS)
3377     rate = std::min(std::max(rate, minFastReverseRate()), maxFastForwardRate());
3378 #endif
3379     return rate;
3380 }
3381
3382 void HTMLMediaElement::scanTimerFired()
3383 {
3384     if (m_scanType == Seek) {
3385         double seekTime = m_scanDirection == Forward ? SeekTime : -SeekTime;
3386         setCurrentTime(currentTime() + seekTime);
3387     } else
3388         setPlaybackRate(nextScanRate());
3389 }
3390
3391 // The spec says to fire periodic timeupdate events (those sent while playing) every
3392 // "15 to 250ms", we choose the slowest frequency
3393 static const double maxTimeupdateEventFrequency = 0.25;
3394
3395 void HTMLMediaElement::startPlaybackProgressTimer()
3396 {
3397     if (m_playbackProgressTimer.isActive())
3398         return;
3399
3400     m_previousProgressTime = monotonicallyIncreasingTime();
3401     m_playbackProgressTimer.startRepeating(maxTimeupdateEventFrequency);
3402 }
3403
3404 void HTMLMediaElement::playbackProgressTimerFired()
3405 {
3406     ASSERT(m_player);
3407
3408     if (m_fragmentEndTime.isValid() && currentMediaTime() >= m_fragmentEndTime && requestedPlaybackRate() > 0) {
3409         m_fragmentEndTime = MediaTime::invalidTime();
3410         if (!m_mediaController && !m_paused) {
3411             // changes paused to true and fires a simple event named pause at the media element.
3412             pauseInternal();
3413         }
3414     }
3415     
3416     scheduleTimeupdateEvent(true);
3417
3418     if (!requestedPlaybackRate())
3419         return;
3420
3421     if (!m_paused && hasMediaControls())
3422         mediaControls()->playbackProgressed();
3423
3424 #if ENABLE(VIDEO_TRACK)
3425     if (RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled())
3426         updateActiveTextTrackCues(currentMediaTime());
3427 #endif
3428
3429 #if ENABLE(MEDIA_SOURCE)
3430     if (m_mediaSource)
3431         m_mediaSource->monitorSourceBuffers();
3432 #endif
3433 }
3434
3435 void HTMLMediaElement::scheduleTimeupdateEvent(bool periodicEvent)
3436 {
3437     double now = monotonicallyIncreasingTime();
3438     double timedelta = now - m_clockTimeAtLastUpdateEvent;
3439
3440     // throttle the periodic events
3441     if (periodicEvent && timedelta < maxTimeupdateEventFrequency)
3442         return;
3443
3444     // Some media engines make multiple "time changed" callbacks at the same time, but we only want one
3445     // event at a given time so filter here
3446     MediaTime movieTime = currentMediaTime();
3447     if (movieTime != m_lastTimeUpdateEventMovieTime) {
3448         scheduleEvent(eventNames().timeupdateEvent);
3449         m_clockTimeAtLastUpdateEvent = now;
3450         m_lastTimeUpdateEventMovieTime = movieTime;
3451     }
3452 }
3453
3454 bool HTMLMediaElement::canPlay() const
3455 {
3456     return paused() || ended() || m_readyState < HAVE_METADATA;
3457 }
3458
3459 double HTMLMediaElement::percentLoaded() const
3460 {
3461     if (!m_player)
3462         return 0;
3463     MediaTime duration = m_player->duration();
3464
3465     if (!duration || duration.isPositiveInfinite() || duration.isNegativeInfinite())
3466         return 0;
3467
3468     MediaTime buffered = MediaTime::zeroTime();
3469     bool ignored;
3470     std::unique_ptr<PlatformTimeRanges> timeRanges = m_player->buffered();
3471     for (unsigned i = 0; i < timeRanges->length(); ++i) {
3472         MediaTime start = timeRanges->start(i, ignored);
3473         MediaTime end = timeRanges->end(i, ignored);
3474         buffered += end - start;
3475     }
3476     return buffered.toDouble() / duration.toDouble();
3477 }
3478
3479 #if ENABLE(VIDEO_TRACK)
3480
3481 void HTMLMediaElement::mediaPlayerDidAddAudioTrack(PassRefPtr<AudioTrackPrivate> prpTrack)
3482 {
3483     if (isPlaying() && !m_mediaSession->playbackPermitted(*this))
3484         pauseInternal();
3485
3486     if (!RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled())
3487         return;
3488
3489     addAudioTrack(AudioTrack::create(this, prpTrack));
3490 }
3491
3492 void HTMLMediaElement::mediaPlayerDidAddTextTrack(PassRefPtr<InbandTextTrackPrivate> prpTrack)
3493 {
3494     if (!RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled())
3495         return;
3496     
3497     // 4.8.10.12.2 Sourcing in-band text tracks
3498     // 1. Associate the relevant data with a new text track and its corresponding new TextTrack object.
3499     RefPtr<InbandTextTrack> textTrack = InbandTextTrack::create(ActiveDOMObject::scriptExecutionContext(), this, prpTrack);
3500     textTrack->setMediaElement(this);
3501     
3502     // 2. Set the new text track's kind, label, and language based on the semantics of the relevant data,
3503     // as defined by the relevant specification. If there is no label in that data, then the label must
3504     // be set to the empty string.
3505     // 3. Associate the text track list of cues with the rules for updating the text track rendering appropriate
3506     // for the format in question.
3507     // 4. If the new text track's kind is metadata, then set the text track in-band metadata track dispatch type
3508     // as follows, based on the type of the media resource:
3509     // 5. Populate the new text track's list of cues with the cues parsed so far, folllowing the guidelines for exposing
3510     // cues, and begin updating it dynamically as necessary.
3511     //   - Thess are all done by the media engine.
3512     
3513     // 6. Set the new text track's readiness state to loaded.
3514     textTrack->setReadinessState(TextTrack::Loaded);
3515     
3516     // 7. Set the new text track's mode to the mode consistent with the user's preferences and the requirements of
3517     // the relevant specification for the data.
3518     //  - This will happen in configureTextTracks()
3519     scheduleDelayedAction(ConfigureTextTracks);
3520     
3521     // 8. Add the new text track to the media element's list of text tracks.
3522     // 9. Fire an event with the name addtrack, that does not bubble and is not cancelable, and that uses the TrackEvent
3523     // interface, with the track attribute initialized to the text track's TextTrack object, at the media element's
3524     // textTracks attribute's TextTrackList object.
3525     addTextTrack(textTrack.release());
3526 }
3527
3528 void HTMLMediaElement::mediaPlayerDidAddVideoTrack(PassRefPtr<VideoTrackPrivate> prpTrack)
3529 {
3530     if (!RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled())
3531         return;
3532
3533     addVideoTrack(VideoTrack::create(this, prpTrack));
3534 }
3535
3536 void HTMLMediaElement::mediaPlayerDidRemoveAudioTrack(PassRefPtr<AudioTrackPrivate> prpTrack)
3537 {
3538     prpTrack->willBeRemoved();
3539 }
3540
3541 void HTMLMediaElement::mediaPlayerDidRemoveTextTrack(PassRefPtr<InbandTextTrackPrivate> prpTrack)
3542 {
3543     prpTrack->willBeRemoved();
3544 }
3545
3546 void HTMLMediaElement::mediaPlayerDidRemoveVideoTrack(PassRefPtr<VideoTrackPrivate> prpTrack)
3547 {
3548     prpTrack->willBeRemoved();
3549 }
3550
3551 void HTMLMediaElement::closeCaptionTracksChanged()
3552 {
3553     if (hasMediaControls())
3554         mediaControls()->closedCaptionTracksChanged();
3555 }
3556
3557 void HTMLMediaElement::addAudioTrack(PassRefPtr<AudioTrack> track)
3558 {
3559     if (!RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled())
3560         return;
3561
3562     audioTracks()->append(track);
3563 }
3564
3565 void HTMLMediaElement::addTextTrack(PassRefPtr<TextTrack> track)
3566 {
3567     if (!RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled())
3568         return;
3569
3570     if (!m_requireCaptionPreferencesChangedCallbacks) {
3571         m_requireCaptionPreferencesChangedCallbacks = true;
3572         Document& document = this->document();
3573         document.registerForCaptionPreferencesChangedCallbacks(this);
3574         if (Page* page = document.page())
3575             m_captionDisplayMode = page->group().captionPreferences().captionDisplayMode();
3576     }
3577
3578     textTracks()->append(track);
3579
3580     closeCaptionTracksChanged();
3581 }
3582
3583 void HTMLMediaElement::addVideoTrack(PassRefPtr<VideoTrack> track)
3584 {
3585     if (!RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled())
3586         return;
3587
3588     videoTracks()->append(track);
3589 }
3590
3591 void HTMLMediaElement::removeAudioTrack(AudioTrack* track)
3592 {
3593     if (!RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled())
3594         return;
3595
3596     m_audioTracks->remove(track);
3597 }
3598
3599 void HTMLMediaElement::removeTextTrack(TextTrack* track, bool scheduleEvent)
3600 {
3601     if (!RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled())
3602         return;
3603
3604     TrackDisplayUpdateScope scope(this);
3605     if (TextTrackCueList* cues = track->cues())
3606         textTrackRemoveCues(track, cues);
3607     track->clearClient();
3608     if (m_textTracks)
3609         m_textTracks->remove(track, scheduleEvent);
3610
3611     closeCaptionTracksChanged();
3612 }
3613
3614 void HTMLMediaElement::removeVideoTrack(VideoTrack* track)
3615 {
3616     if (!RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled())
3617         return;
3618
3619     m_videoTracks->remove(track);
3620 }
3621
3622 void HTMLMediaElement::forgetResourceSpecificTracks()
3623 {
3624     while (m_audioTracks &&  m_audioTracks->length())
3625         removeAudioTrack(m_audioTracks->lastItem());
3626
3627     if (m_textTracks) {
3628         TrackDisplayUpdateScope scope(this);
3629         for (int i = m_textTracks->length() - 1; i >= 0; --i) {
3630             TextTrack* track = m_textTracks->item(i);
3631
3632             if (track->trackType() == TextTrack::InBand)
3633                 removeTextTrack(track, false);
3634         }
3635     }
3636
3637     while (m_videoTracks &&  m_videoTracks->length())
3638         removeVideoTrack(m_videoTracks->lastItem());
3639 }
3640
3641 PassRefPtr<TextTrack> HTMLMediaElement::addTextTrack(const String& kind, const String& label, const String& language, ExceptionCode& ec)
3642 {
3643     if (!RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled())
3644         return 0;
3645
3646     // 4.8.10.12.4 Text track API
3647     // The addTextTrack(kind, label, language) method of media elements, when invoked, must run the following steps:
3648
3649     // 1. If kind is not one of the following strings, then throw a SyntaxError exception and abort these steps
3650     if (!TextTrack::isValidKindKeyword(kind)) {
3651         ec = SYNTAX_ERR;
3652         return 0;
3653     }
3654
3655     // 2. If the label argument was omitted, let label be the empty string.
3656     // 3. If the language argument was omitted, let language be the empty string.
3657     // 4. Create a new TextTrack object.
3658
3659     // 5. Create a new text track corresponding to the new object, and set its text track kind to kind, its text 
3660     // track label to label, its text track language to language...
3661     RefPtr<TextTrack> textTrack = TextTrack::create(ActiveDOMObject::scriptExecutionContext(), this, kind, emptyString(), label, language);
3662
3663     // Note, due to side effects when changing track parameters, we have to
3664     // first append the track to the text track list.
3665
3666     // 6. Add the new text track to the media element's list of text tracks.
3667     addTextTrack(textTrack);
3668
3669     // ... its text track readiness state to the text track loaded state ...
3670     textTrack->setReadinessState(TextTrack::Loaded);
3671
3672     // ... its text track mode to the text track hidden mode, and its text track list of cues to an empty list ...
3673     textTrack->setMode(TextTrack::hiddenKeyword());
3674
3675     return textTrack.release();
3676 }
3677
3678 AudioTrackList* HTMLMediaElement::audioTracks()
3679 {
3680     if (!RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled())
3681         return 0;
3682
3683     if (!m_audioTracks)
3684         m_audioTracks = AudioTrackList::create(this, ActiveDOMObject::scriptExecutionContext());
3685
3686     return m_audioTracks.get();
3687 }
3688
3689 TextTrackList* HTMLMediaElement::textTracks() 
3690 {
3691     if (!RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled())
3692         return 0;
3693
3694     if (!m_textTracks)
3695         m_textTracks = TextTrackList::create(this, ActiveDOMObject::scriptExecutionContext());
3696
3697     return m_textTracks.get();
3698 }
3699
3700 VideoTrackList* HTMLMediaElement::videoTracks()
3701 {
3702     if (!RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled())
3703         return 0;
3704
3705     if (!m_videoTracks)
3706         m_videoTracks = VideoTrackList::create(this, ActiveDOMObject::scriptExecutionContext());
3707
3708     return m_videoTracks.get();
3709 }
3710
3711 void HTMLMediaElement::didAddTextTrack(HTMLTrackElement* trackElement)
3712 {
3713     ASSERT(trackElement->hasTagName(trackTag));
3714
3715     if (!RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled())
3716         return;
3717
3718     // 4.8.10.12.3 Sourcing out-of-band text tracks
3719     // When a track element's parent element changes and the new parent is a media element, 
3720     // then the user agent must add the track element's corresponding text track to the 
3721     // media element's list of text tracks ... [continues in TextTrackList::append]
3722     RefPtr<TextTrack> textTrack = trackElement->track();
3723     if (!textTrack)
3724         return;
3725     
3726     addTextTrack(textTrack.release());
3727     
3728     // Do not schedule the track loading until parsing finishes so we don't start before all tracks
3729     // in the markup have been added.
3730     if (!m_parsingInProgress)
3731         scheduleDelayedAction(ConfigureTextTracks);
3732
3733     if (hasMediaControls())
3734         mediaControls()->closedCaptionTracksChanged();
3735 }
3736
3737 void HTMLMediaElement::didRemoveTextTrack(HTMLTrackElement* trackElement)
3738 {
3739     ASSERT(trackElement->hasTagName(trackTag));
3740
3741     if (!RuntimeEnabledFeatures::sharedFeatures().webkitVideoTrackEnabled())
3742         return;
3743
3744 #if !LOG_DISABLED
3745     if (trackElement->hasTagName(trackTag)) {
3746         URL url = trackElement->getNonEmptyURLAttribute(srcAttr);
3747         LOG(Media, "HTMLMediaElement::didRemoveTrack(%p) - 'src' is %s", this, urlForLoggingMedia(url).utf8().data());
3748     }
3749 #endif
3750
3751     RefPtr<TextTrack> textTrack = trackElement->track();
3752     if (!textTrack)
3753         return;
3754     
3755     textTrack->setHasBeenConfigured(false);
3756
3757     if (!m_textTracks)
3758         return;
3759     
3760     // 4.8.10.12.3 Sourcing out-of-band text tracks
3761     // When a track element's parent element changes and the old parent was a media element, 
3762     // then the user agent must remove the track element's corresponding text track from the 
3763     // media element's list of text tracks.
3764     removeTextTrack(textTrack.get());
3765
3766     size_t index = m_textTracksWhenResourceSelectionBegan.find(textTrack.get());
3767     if (index != notFound)
3768         m_textTracksWhenResourceSelectionBegan.remove(index);
3769 }
3770
3771 void HTMLMediaElement::configureTextTrackGroup(const TrackGroup& group)
3772 {
3773     ASSERT(group.tracks.size());
3774
3775     LOG(Media, "HTMLMediaElement::configureTextTrackGroup(%p)", this);
3776
3777     Page* page = document().page();
3778     CaptionUserPreferences* captionPreferences = page ? &page->group().captionPreferences() : 0;
3779     CaptionUserPreferences::CaptionDisplayMode displayMode = captionPreferences ? captionPreferences->captionDisplayMode() : CaptionUserPreferences::Automatic;
3780
3781     // First, find the track in the group that should be enabled (if any).
3782     Vector<RefPtr<TextTrack>> currentlyEnabledTracks;
3783     RefPtr<TextTrack> trackToEnable;
3784     RefPtr<TextTrack> defaultTrack;
3785     RefPtr<TextTrack> fallbackTrack;
3786     RefPtr<TextTrack> forcedSubitleTrack;
3787     int highestTrackScore = 0;
3788     int highestForcedScore = 0;
3789
3790     // If there is a visible track, it has already been configured so it won't be considered in the loop below. We don't want to choose another
3791     // track if it is less suitable, and we do want to disable it if another track is more suitable.
3792     int alreadyVisibleTrackScore = 0;
3793     if (group.visibleTrack && captionPreferences) {
3794         alreadyVisibleTrackScore = captionPreferences->textTrackSelectionScore(group.visibleTrack.get(), this);
3795         currentlyEnabledTracks.append(group.visibleTrack);
3796     }
3797
3798     for (size_t i = 0; i < group.tracks.size(); ++i) {
3799         RefPtr<TextTrack> textTrack = group.tracks[i];
3800
3801         if (m_processingPreferenceChange && textTrack->mode() == TextTrack::showingKeyword())
3802             currentlyEnabledTracks.append(textTrack);
3803
3804         int trackScore = captionPreferences ? captionPreferences->textTrackSelectionScore(textTrack.get(), this) : 0;
3805         LOG(Media, "HTMLMediaElement::configureTextTrackGroup(%p) -  '%s' track with language '%s' has score %i", this, textTrack->kind().string().utf8().data(), textTrack->language().string().utf8().data(), trackScore);
3806
3807         if (trackScore) {
3808
3809             // * If the text track kind is { [subtitles or captions] [descriptions] } and the user has indicated an interest in having a
3810             // track with this text track kind, text track language, and text track label enabled, and there is no
3811             // other text track in the media element's list of text tracks with a text track kind of either subtitles
3812             // or captions whose text track mode is showing
3813             // ...
3814             // * If the text track kind is chapters and the text track language is one that the user agent has reason
3815             // to believe is appropriate for the user, and there is no other text track in the media element's list of
3816             // text tracks with a text track kind of chapters whose text track mode is showing
3817             //    Let the text track mode be showing.
3818             if (trackScore > highestTrackScore && trackScore > alreadyVisibleTrackScore) {
3819                 highestTrackScore = trackScore;
3820                 trackToEnable = textTrack;
3821             }
3822
3823             if (!defaultTrack && textTrack->isDefault())
3824                 defaultTrack = textTrack;
3825             if (!defaultTrack && !fallbackTrack)
3826                 fallbackTrack = textTrack;
3827             if (textTrack->containsOnlyForcedSubtitles() && trackScore > highestForcedScore) {
3828                 forcedSubitleTrack = textTrack;
3829                 highestForcedScore = trackScore;
3830             }
3831         } else if (!group.visibleTrack && !defaultTrack && textTrack->isDefault()) {
3832             // * If the track element has a default attribute specified, and there is no other text track in the media
3833             // element's list of text tracks whose text track mode is showing or showing by default
3834             //    Let the text track mode be showing by default.
3835             if (group.kind != TrackGroup::CaptionsAndSubtitles || displayMode != CaptionUserPreferences::ForcedOnly)
3836                 defaultTrack = textTrack;
3837         }
3838     }
3839
3840     if (displayMode != CaptionUserPreferences::Manual) {
3841         if (!trackToEnable && defaultTrack)
3842             trackToEnable = defaultTrack;
3843
3844         // If no track matches the user's preferred language, none was marked as 'default', and there is a forced subtitle track
3845         // in the same language as the language of the primary audio track, enable it.
3846         if (!trackToEnable && forcedSubitleTrack)
3847             trackToEnable = forcedSubitleTrack;
3848
3849         // If no track matches, don't disable an already visible track unless preferences say they all should be off.
3850         if (group.kind != TrackGroup::CaptionsAndSubtitles || displayMode != CaptionUserPreferences::ForcedOnly) {
3851             if (!trackToEnable && !defaultTrack && group.visibleTrack)
3852                 trackToEnable = group.visibleTrack;
3853         }
3854
3855         // If no track matches the user's preferred language and non was marked 'default', enable the first track
3856         // because the user has explicitly stated a preference for this kind of track.
3857         if (!trackToEnable && fallbackTrack)
3858             trackToEnable = fallbackTrack;
3859
3860         if (trackToEnable)
3861             m_subtitleTrackLanguage = trackToEnable->language();
3862         else
3863             m_subtitleTrackLanguage = emptyString();
3864     }
3865
3866     if (currentlyEnabledTracks.size()) {
3867         for (size_t i = 0; i < currentlyEnabledTracks.size(); ++i) {
3868             RefPtr<TextTrack> textTrack = currentlyEnabledTracks[i];
3869             if (textTrack != trackToEnable)
3870                 textTrack->setMode(TextTrack::disabledKeyword());
3871         }
3872     }
3873
3874     if (trackToEnable) {
3875         trackToEnable->setMode(TextTrack::showingKeyword());
3876
3877         // If user preferences indicate we should always display captions, make sure we reflect the
3878         // proper status via the webkitClosedCaptionsVisible API call:
3879         if (!webkitClosedCaptionsVisible() && closedCaptionsVisible() && displayMode == CaptionUserPreferences::AlwaysOn)
3880             m_webkitLegacyClosedCaptionOverride = true;
3881     }
3882
3883     m_processingPreferenceChange = false;
3884 }
3885
3886 static JSC::JSValue controllerJSValue(JSC::ExecState& exec, JSDOMGlobalObject& globalObject, HTMLMediaElement& media)
3887 {
3888     auto mediaJSWrapper = toJS(&exec, &globalObject, &media);
3889     
3890     // Retrieve the controller through the JS object graph
3891     JSC::JSObject* mediaJSWrapperObject = JSC::jsDynamicCast<JSC::JSObject*>(mediaJSWrapper);
3892     if (!mediaJSWrapperObject)
3893         return JSC::jsNull();
3894     
3895     JSC::Identifier controlsHost = JSC::Identifier::fromString(&exec.vm(), "controlsHost");
3896     JSC::JSValue controlsHostJSWrapper = mediaJSWrapperObject->get(&exec, controlsHost);
3897     if (exec.hadException())
3898         return JSC::jsNull();
3899
3900     JSC::JSObject* controlsHostJSWrapperObject = JSC::jsDynamicCast<JSC::JSObject*>(controlsHostJSWrapper);
3901     if (!controlsHostJSWrapperObject)
3902         return JSC::jsNull();
3903
3904     JSC::Identifier controllerID = JSC::Identifier::fromString(&exec.vm(), "controller");
3905     JSC::JSValue controllerJSWrapper = controlsHostJSWrapperObject->get(&exec, controllerID);
3906     if (exec.hadException())
3907         return JSC::jsNull();
3908
3909     return controllerJSWrapper;
3910 }
3911
3912 void HTMLMediaElement::ensureMediaControlsShadowRoot()
3913 {
3914     ASSERT(!m_creatingControls);
3915     m_creatingControls = true;
3916     ensureUserAgentShadowRoot();
3917     m_creatingControls = false;
3918 }
3919
3920 void HTMLMediaElement::updateCaptionContainer()
3921 {
3922     LOG(Media, "HTMLMediaElement::updateCaptionContainer(%p)", this);
3923 #if ENABLE(MEDIA_CONTROLS_SCRIPT)
3924     if (m_haveSetUpCaptionContainer)
3925         return;
3926
3927     Page* page = document().page();
3928     if (!page)
3929         return;
3930
3931     DOMWrapperWorld& world = ensureIsolatedWorld();
3932
3933     if (!ensureMediaControlsInjectedScript())
3934         return;
3935
3936     ensureMediaControlsShadowRoot();
3937
3938     if (!m_mediaControlsHost)
3939         m_mediaControlsHost = MediaControlsHost::create(this);
3940
3941     ScriptController& scriptController = document().frame()->script();
3942     JSDOMGlobalObject* globalObject = JSC::jsCast<JSDOMGlobalObject*>(scriptController.globalObject(world));
3943     JSC::ExecState* exec = globalObject->globalExec();
3944     JSC::JSLockHolder lock(exec);
3945
3946     JSC::JSValue controllerValue = controllerJSValue(*exec, *globalObject, *this);
3947     JSC::JSObject* controllerObject = JSC::jsDynamicCast<JSC::JSObject*>(controllerValue);
3948     if (!controllerObject)
3949         return;
3950
3951     // The media controls script must provide a method on the Controller object with the following details.
3952     // Name: updateCaptionContainer
3953     // Parameters:
3954     //     None
3955     // Return value:
3956     //     None
3957     JSC::JSValue methodValue = controllerObject->get(exec, JSC::Identifier::fromString(exec, "updateCaptionContainer"));
3958     JSC::JSObject* methodObject = JSC::jsDynamicCast<JSC::JSObject*>(methodValue);
3959     if (!methodObject)
3960         return;
3961
3962     JSC::CallData callData;
3963     JSC::CallType callType = methodObject->methodTable()->getCallData(methodObject, callData);
3964     if (callType == JSC::CallType::None)
3965         return;
3966
3967     JSC::MarkedArgumentBuffer noArguments;
3968     JSC::call(exec, methodObject, callType, callData, controllerObject, noArguments);
3969     exec->clearException();
3970
3971     m_haveSetUpCaptionContainer = true;
3972 #endif
3973 }
3974
3975 void HTMLMediaElement::layoutSizeChanged()
3976 {
3977 #if ENABLE(MEDIA_CONTROLS_SCRIPT)
3978     RefPtr<HTMLMediaElement> strongThis = this;
3979     std::function<void()> task = [strongThis] {
3980         if (ShadowRoot* root = strongThis->userAgentShadowRoot())
3981             root->dispatchEvent(Event::create("resize", false, false));
3982     };
3983     m_resizeTaskQueue.enqueueTask(task);
3984 #endif
3985 }
3986
3987 void HTMLMediaElement::visibilityDidChange()
3988 {
3989     updateShouldAutoplay();
3990 }
3991
3992 void HTMLMediaElement::setSelectedTextTrack(TextTrack* trackToSelect)
3993 {
3994     TextTrackList* trackList = textTracks();
3995     if (!trackList || !trackList->length())
3996         return;
3997
3998     if (trackToSelect != TextTrack::captionMenuOffItem() && trackToSelect != TextTrack::captionMenuAutomaticItem()) {
3999         if (!trackList->contains(trackToSelect))
4000             return;
4001         
4002         for (int i = 0, length = trackList->length(); i < length; ++i) {
4003             TextTrack* track = trackList->item(i);
4004             if (!trackToSelect || track != trackToSelect)
4005                 track->setMode(TextTrack::disabledKeyword());
4006             else
4007                 track->setMode(TextTrack::showingKeyword());
4008         }
4009     } else if (trackToSelect == TextTrack::captionMenuOffItem()) {
4010         for (int i = 0, length = trackList->length(); i < length; ++i)
4011             trackList->item(i)->setMode(TextTrack::disabledKeyword());
4012     }
4013
4014     if (!document().page())
4015         return;
4016
4017     auto& captionPreferences = document().page()->group().captionPreferences();
4018     CaptionUserPreferences::CaptionDisplayMode displayMode;
4019     if (trackToSelect == TextTrack::captionMenuOffItem())
4020         displayMode = CaptionUserPreferences::ForcedOnly;
4021     else if (trackToSelect == TextTrack::captionMenuAutomaticItem())
4022         displayMode = CaptionUserPreferences::Automatic;
4023     else {
4024         displayMode = CaptionUserPreferences::AlwaysOn;
4025         if (trackToSelect->language().length())
4026             captionPreferences.setPreferredLanguage(trackToSelect->language());
4027     }
4028
4029     captionPreferences.setCaptionDisplayMode(displayMode);
4030 }
4031
4032 void HTMLMediaElement::configureTextTracks()
4033 {
4034     TrackGroup captionAndSubtitleTracks(TrackGroup::CaptionsAndSubtitles);
4035     TrackGroup descriptionTracks(TrackGroup::Description);
4036     TrackGroup chapterTracks(TrackGroup::Chapter);
4037     TrackGroup metadataTracks(TrackGroup::Metadata);
4038     TrackGroup otherTracks(TrackGroup::Other);
4039
4040     if (!m_textTracks)
4041         return;
4042
4043     for (size_t i = 0; i < m_textTracks->length(); ++i) {
4044         RefPtr<TextTrack> textTrack = m_textTracks->item(i);
4045         if (!textTrack)
4046             continue;
4047
4048         String kind = textTrack->kind();
4049         TrackGroup* currentGroup;
4050         if (kind == TextTrack::subtitlesKeyword() || kind == TextTrack::captionsKeyword() || kind == TextTrack::forcedKeyword())
4051             currentGroup = &captionAndSubtitleTracks;
4052         else if (kind == TextTrack::descriptionsKeyword())
4053             currentGroup = &descriptionTracks;
4054         else if (kind == TextTrack::chaptersKeyword())
4055             currentGroup = &chapterTracks;
4056         else if (kind == TextTrack::metadataKeyword())
4057             currentGroup = &metadataTracks;
4058         else
4059             currentGroup = &otherTracks;
4060
4061         if (!currentGroup->visibleTrack && textTrack->mode() == TextTrack::showingKeyword())
4062             currentGroup->visibleTrack = textTrack;
4063         if (!currentGroup->defaultTrack && textTrack->isDefault())
4064             currentGroup->defaultTrack = textTrack;
4065
4066         // Do not add this track to the group if it has already been automatically configured
4067         // as we only want to call configureTextTrack once per track so that adding another 
4068         // track after the initial configuration doesn't reconfigure every track - only those 
4069         // that should be changed by the new addition. For example all metadata tracks are 
4070         // disabled by default, and we don't want a track that has been enabled by script 
4071         // to be disabled automatically when a new metadata track is added later.
4072         if (textTrack->hasBeenConfigured())
4073             continue;
4074         
4075         if (textTrack->language().length())
4076             currentGroup->hasSrcLang = true;
4077         currentGroup->tracks.append(textTrack);
4078     }
4079     
4080     if (captionAndSubtitleTracks.tracks.size())
4081         configureTextTrackGroup(captionAndSubtitleTracks);
4082     if (descriptionTracks.tracks.size())
4083         configureTextTrackGroup(descriptionTracks);
4084     if (chapterTracks.tracks.size())
4085         configureTextTrackGroup(chapterTracks);
4086     if (metadataTracks.tracks.size())
4087         configureTextTrackGroup(metadataTracks);
4088     if (otherTracks.tracks.size())
4089         configureTextTrackGroup(otherTracks);
4090
4091     updateCaptionContainer();
4092     configureTextTrackDisplay();
4093     if (hasMediaControls())
4094         mediaControls()->closedCaptionTracksChanged();
4095 }
4096 #endif
4097
4098 bool HTMLMediaElement::havePotentialSourceChild()
4099 {
4100     // Stash the current <source> node and next nodes so we can restore them after checking