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