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