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