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