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