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