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