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