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