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