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