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