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