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