Element child and descendant iterators
[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 "ChildIterator.h"
34 #include "Chrome.h"
35 #include "ChromeClient.h"
36 #include "ClientRect.h"
37 #include "ClientRectList.h"
38 #include "ContentSecurityPolicy.h"
39 #include "ContentType.h"
40 #include "CSSPropertyNames.h"
41 #include "CSSValueKeywords.h"
42 #include "DiagnosticLoggingKeys.h"
43 #include "DocumentLoader.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 "Page.h"
71 #include "PageActivityAssertionToken.h"
72 #include "PageGroup.h"
73 #include "RenderVideo.h"
74 #include "RenderView.h"
75 #include "ScriptController.h"
76 #include "ScriptEventListener.h"
77 #include "SecurityPolicy.h"
78 #include "Settings.h"
79 #include "ShadowRoot.h"
80 #include "TimeRanges.h"
81 #include <limits>
82 #include <runtime/Uint8Array.h>
83 #include <wtf/CurrentTime.h>
84 #include <wtf/MathExtras.h>
85 #include <wtf/NonCopyingSort.h>
86 #include <wtf/text/CString.h>
87
88 #if USE(ACCELERATED_COMPOSITING)
89 #include "RenderLayerCompositor.h"
90 #endif
91
92 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
93 #include "RenderEmbeddedObject.h"
94 #include "Widget.h"
95 #endif
96
97 #if ENABLE(VIDEO_TRACK)
98 #include "AudioTrackList.h"
99 #include "AudioTrackPrivate.h"
100 #include "CaptionUserPreferences.h"
101 #include "HTMLTrackElement.h"
102 #include "InbandTextTrack.h"
103 #include "InbandTextTrackPrivate.h"
104 #include "RuntimeEnabledFeatures.h"
105 #include "TextTrackCueList.h"
106 #include "TextTrackList.h"
107 #include "VideoTrackList.h"
108 #include "VideoTrackPrivate.h"
109 #endif
110
111 #if ENABLE(WEB_AUDIO)
112 #include "AudioSourceProvider.h"
113 #include "MediaElementAudioSourceNode.h"
114 #endif
115
116 #if PLATFORM(MAC)
117 #include "DisplaySleepDisabler.h"
118 #endif
119
120 #if ENABLE(MEDIA_SOURCE)
121 #include "MediaSource.h"
122 #include "MediaSourceRegistry.h"
123 #endif
124
125 #if ENABLE(MEDIA_STREAM)
126 #include "MediaStreamRegistry.h"
127 #endif
128
129 #if ENABLE(ENCRYPTED_MEDIA_V2)
130 #include "MediaKeyNeededEvent.h"
131 #include "MediaKeys.h"
132 #endif
133
134 #if USE(PLATFORM_TEXT_TRACK_MENU)
135 #include "PlatformTextTrack.h"
136 #endif
137
138 #if USE(AUDIO_SESSION)
139 #include "AudioSessionManager.h"
140 #endif
141
142 using namespace std;
143
144 namespace WebCore {
145
146 static void setFlags(unsigned& value, unsigned flags)
147 {
148     value |= flags;
149 }
150
151 static void clearFlags(unsigned& value, unsigned flags)
152 {
153     value &= ~flags;
154 }
155     
156 #if !LOG_DISABLED
157 static String urlForLoggingMedia(const KURL& url)
158 {
159     static const unsigned maximumURLLengthForLogging = 128;
160
161     if (url.string().length() < maximumURLLengthForLogging)
162         return url.string();
163     return url.string().substring(0, maximumURLLengthForLogging) + "...";
164 }
165
166 static const char* boolString(bool val)
167 {
168     return val ? "true" : "false";
169 }
170 #endif
171
172 #ifndef LOG_MEDIA_EVENTS
173 // Default to not logging events because so many are generated they can overwhelm the rest of 
174 // the logging.
175 #define LOG_MEDIA_EVENTS 0
176 #endif
177
178 #ifndef LOG_CACHED_TIME_WARNINGS
179 // Default to not logging warnings about excessive drift in the cached media time because it adds a
180 // fair amount of overhead and logging.
181 #define LOG_CACHED_TIME_WARNINGS 0
182 #endif
183
184 #if ENABLE(MEDIA_SOURCE)
185 // URL protocol used to signal that the media source API is being used.
186 static const char* mediaSourceBlobProtocol = "blob";
187 #endif
188
189 using namespace HTMLNames;
190 using namespace std;
191
192 typedef HashMap<Document*, HashSet<HTMLMediaElement*> > DocumentElementSetMap;
193 static DocumentElementSetMap& documentToElementSetMap()
194 {
195     DEFINE_STATIC_LOCAL(DocumentElementSetMap, map, ());
196     return map;
197 }
198
199 static void addElementToDocumentMap(HTMLMediaElement* element, Document* document)
200 {
201     DocumentElementSetMap& map = documentToElementSetMap();
202     HashSet<HTMLMediaElement*> set = map.take(document);
203     set.add(element);
204     map.add(document, set);
205 }
206
207 static void removeElementFromDocumentMap(HTMLMediaElement* element, Document* document)
208 {
209     DocumentElementSetMap& map = documentToElementSetMap();
210     HashSet<HTMLMediaElement*> set = map.take(document);
211     set.remove(element);
212     if (!set.isEmpty())
213         map.add(document, set);
214 }
215
216 #if ENABLE(ENCRYPTED_MEDIA)
217 static ExceptionCode exceptionCodeForMediaKeyException(MediaPlayer::MediaKeyException exception)
218 {
219     switch (exception) {
220     case MediaPlayer::NoError:
221         return 0;
222     case MediaPlayer::InvalidPlayerState:
223         return INVALID_STATE_ERR;
224     case MediaPlayer::KeySystemNotSupported:
225         return NOT_SUPPORTED_ERR;
226     }
227
228     ASSERT_NOT_REACHED();
229     return INVALID_STATE_ERR;
230 }
231 #endif
232
233 #if ENABLE(VIDEO_TRACK)
234 class TrackDisplayUpdateScope {
235 public:
236     TrackDisplayUpdateScope(HTMLMediaElement* mediaElement)
237     {
238         m_mediaElement = mediaElement;
239         m_mediaElement->beginIgnoringTrackDisplayUpdateRequests();
240     }
241     ~TrackDisplayUpdateScope()
242     {
243         ASSERT(m_mediaElement);
244         m_mediaElement->endIgnoringTrackDisplayUpdateRequests();
245     }
246     
247 private:
248     HTMLMediaElement* m_mediaElement;
249 };
250 #endif
251
252 HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document* document, bool createdByParser)
253     : HTMLElement(tagName, document)
254     , ActiveDOMObject(document)
255     , m_loadTimer(this, &HTMLMediaElement::loadTimerFired)
256     , m_progressEventTimer(this, &HTMLMediaElement::progressEventTimerFired)
257     , m_playbackProgressTimer(this, &HTMLMediaElement::playbackProgressTimerFired)
258     , m_playedTimeRanges()
259     , m_asyncEventQueue(GenericEventQueue::create(this))
260     , m_playbackRate(1.0f)
261     , m_defaultPlaybackRate(1.0f)
262     , m_webkitPreservesPitch(true)
263     , m_networkState(NETWORK_EMPTY)
264     , m_readyState(HAVE_NOTHING)
265     , m_readyStateMaximum(HAVE_NOTHING)
266     , m_volume(1.0f)
267     , m_lastSeekTime(0)
268     , m_previousProgressTime(numeric_limits<double>::max())
269     , m_clockTimeAtLastUpdateEvent(0)
270     , m_lastTimeUpdateEventMovieTime(numeric_limits<double>::max())
271     , m_loadState(WaitingForSource)
272 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
273     , m_proxyWidget(0)
274 #endif
275     , m_restrictions(RequireUserGestureForFullscreenRestriction | RequirePageConsentToLoadMediaRestriction)
276     , m_preload(MediaPlayer::Auto)
277     , m_displayMode(Unknown)
278     , m_processingMediaPlayerCallback(0)
279     , m_cachedTime(MediaPlayer::invalidTime())
280     , m_clockTimeAtLastCachedTimeUpdate(0)
281     , m_minimumClockTimeToUpdateCachedTime(0)
282     , m_fragmentStartTime(MediaPlayer::invalidTime())
283     , m_fragmentEndTime(MediaPlayer::invalidTime())
284     , m_pendingActionFlags(0)
285     , m_playing(false)
286     , m_isWaitingUntilMediaCanStart(false)
287     , m_shouldDelayLoadEvent(false)
288     , m_haveFiredLoadedData(false)
289     , m_inActiveDocument(true)
290     , m_autoplaying(true)
291     , m_muted(false)
292     , m_paused(true)
293     , m_seeking(false)
294     , m_sentStalledEvent(false)
295     , m_sentEndEvent(false)
296     , m_pausedInternal(false)
297     , m_sendProgressEvents(true)
298     , m_isFullscreen(false)
299     , m_closedCaptionsVisible(false)
300     , m_webkitLegacyClosedCaptionOverride(false)
301 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
302     , m_needWidgetUpdate(false)
303 #endif
304     , m_dispatchingCanPlayEvent(false)
305     , m_loadInitiatedByUserGesture(false)
306     , m_completelyLoaded(false)
307     , m_havePreparedToPlay(false)
308     , m_parsingInProgress(createdByParser)
309 #if ENABLE(VIDEO_TRACK)
310     , m_tracksAreReady(true)
311     , m_haveVisibleTextTrack(false)
312     , m_processingPreferenceChange(false)
313     , m_lastTextTrackUpdateTime(-1)
314     , m_captionDisplayMode(CaptionUserPreferences::Automatic)
315     , m_audioTracks(0)
316     , m_textTracks(0)
317     , m_videoTracks(0)
318     , m_ignoreTrackDisplayUpdate(0)
319 #endif
320 #if ENABLE(WEB_AUDIO)
321     , m_audioSourceNode(0)
322 #endif
323 #if USE(AUDIO_SESSION)
324     , m_audioSessionManagerToken(AudioSessionManagerToken::create(tagName == videoTag ? AudioSessionManager::Video : AudioSessionManager::Audio))
325 #endif
326     , m_reportedExtraMemoryCost(0)
327 {
328     LOG(Media, "HTMLMediaElement::HTMLMediaElement");
329     setHasCustomStyleResolveCallbacks();
330
331     document->registerForMediaVolumeCallbacks(this);
332     document->registerForPrivateBrowsingStateChangedCallbacks(this);
333
334     if (document->settings() && document->settings()->mediaPlaybackRequiresUserGesture()) {
335         addBehaviorRestriction(RequireUserGestureForRateChangeRestriction);
336         addBehaviorRestriction(RequireUserGestureForLoadRestriction);
337     }
338
339     addElementToDocumentMap(this, document);
340
341 #if ENABLE(VIDEO_TRACK)
342     document->registerForCaptionPreferencesChangedCallbacks(this);
343     if (document->page())
344         m_captionDisplayMode = document->page()->group().captionPreferences()->captionDisplayMode();
345 #endif
346 }
347
348 HTMLMediaElement::~HTMLMediaElement()
349 {
350     LOG(Media, "HTMLMediaElement::~HTMLMediaElement");
351
352     m_asyncEventQueue->close();
353
354     if (m_isWaitingUntilMediaCanStart)
355         document()->removeMediaCanStartListener(this);
356     setShouldDelayLoadEvent(false);
357     document()->unregisterForMediaVolumeCallbacks(this);
358     document()->unregisterForPrivateBrowsingStateChangedCallbacks(this);
359 #if ENABLE(VIDEO_TRACK)
360     document()->unregisterForCaptionPreferencesChangedCallbacks(this);
361     if (m_audioTracks) {
362         m_audioTracks->clearElement();
363         for (unsigned i = 0; i < m_audioTracks->length(); ++i)
364             m_audioTracks->item(i)->clearClient();
365     }
366     if (m_textTracks)
367         m_textTracks->clearElement();
368     if (m_textTracks) {
369         for (unsigned i = 0; i < m_textTracks->length(); ++i)
370             m_textTracks->item(i)->clearClient();
371     }
372     if (m_videoTracks) {
373         m_videoTracks->clearElement();
374         for (unsigned i = 0; i < m_videoTracks->length(); ++i)
375             m_videoTracks->item(i)->clearClient();
376     }
377 #endif
378
379     if (m_mediaController)
380         m_mediaController->removeMediaElement(this);
381
382 #if ENABLE(MEDIA_SOURCE)
383     setSourceState(MediaSource::closedKeyword());
384 #endif
385
386 #if ENABLE(ENCRYPTED_MEDIA_V2)
387     setMediaKeys(0);
388 #endif
389
390     removeElementFromDocumentMap(this, document());
391
392     m_completelyLoaded = true;
393     if (m_player)
394         m_player->clearMediaPlayerClient();
395 }
396
397 void HTMLMediaElement::didMoveToNewDocument(Document* oldDocument)
398 {
399     if (m_isWaitingUntilMediaCanStart) {
400         if (oldDocument)
401             oldDocument->removeMediaCanStartListener(this);
402         document()->addMediaCanStartListener(this);
403     }
404
405     if (m_shouldDelayLoadEvent) {
406         if (oldDocument)
407             oldDocument->decrementLoadEventDelayCount();
408         document()->incrementLoadEventDelayCount();
409     }
410
411     if (oldDocument) {
412         oldDocument->unregisterForMediaVolumeCallbacks(this);
413         removeElementFromDocumentMap(this, oldDocument);
414     }
415
416     document()->registerForMediaVolumeCallbacks(this);
417     addElementToDocumentMap(this, document());
418
419     HTMLElement::didMoveToNewDocument(oldDocument);
420 }
421
422 bool HTMLMediaElement::hasCustomFocusLogic() const
423 {
424     return true;
425 }
426
427 bool HTMLMediaElement::supportsFocus() const
428 {
429     if (document()->isMediaDocument())
430         return false;
431
432     // If no controls specified, we should still be able to focus the element if it has tabIndex.
433     return controls() ||  HTMLElement::supportsFocus();
434 }
435
436 bool HTMLMediaElement::isMouseFocusable() const
437 {
438     return false;
439 }
440
441 void HTMLMediaElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
442 {
443     if (name == srcAttr) {
444         // Trigger a reload, as long as the 'src' attribute is present.
445         if (!value.isNull()) {
446             clearMediaPlayer(LoadMediaResource);
447             scheduleDelayedAction(LoadMediaResource);
448         }
449     } else if (name == controlsAttr)
450         configureMediaControls();
451 #if PLATFORM(MAC)
452     else if (name == loopAttr)
453         updateDisableSleep();
454 #endif
455     else if (name == preloadAttr) {
456         if (equalIgnoringCase(value, "none"))
457             m_preload = MediaPlayer::None;
458         else if (equalIgnoringCase(value, "metadata"))
459             m_preload = MediaPlayer::MetaData;
460         else {
461             // The spec does not define an "invalid value default" but "auto" is suggested as the
462             // "missing value default", so use it for everything except "none" and "metadata"
463             m_preload = MediaPlayer::Auto;
464         }
465
466         // The attribute must be ignored if the autoplay attribute is present
467         if (!autoplay() && m_player)
468             m_player->setPreload(m_preload);
469
470     } else if (name == mediagroupAttr)
471         setMediaGroup(value);
472     else if (name == onabortAttr)
473         setAttributeEventListener(eventNames().abortEvent, createAttributeEventListener(this, name, value));
474     else if (name == onbeforeloadAttr)
475         setAttributeEventListener(eventNames().beforeloadEvent, createAttributeEventListener(this, name, value));
476     else if (name == oncanplayAttr)
477         setAttributeEventListener(eventNames().canplayEvent, createAttributeEventListener(this, name, value));
478     else if (name == oncanplaythroughAttr)
479         setAttributeEventListener(eventNames().canplaythroughEvent, createAttributeEventListener(this, name, value));
480     else if (name == ondurationchangeAttr)
481         setAttributeEventListener(eventNames().durationchangeEvent, createAttributeEventListener(this, name, value));
482     else if (name == onemptiedAttr)
483         setAttributeEventListener(eventNames().emptiedEvent, createAttributeEventListener(this, name, value));
484     else if (name == onendedAttr)
485         setAttributeEventListener(eventNames().endedEvent, createAttributeEventListener(this, name, value));
486     else if (name == onerrorAttr)
487         setAttributeEventListener(eventNames().errorEvent, createAttributeEventListener(this, name, value));
488     else if (name == onloadeddataAttr)
489         setAttributeEventListener(eventNames().loadeddataEvent, createAttributeEventListener(this, name, value));
490     else if (name == onloadedmetadataAttr)
491         setAttributeEventListener(eventNames().loadedmetadataEvent, createAttributeEventListener(this, name, value));
492     else if (name == onloadstartAttr)
493         setAttributeEventListener(eventNames().loadstartEvent, createAttributeEventListener(this, name, value));
494     else if (name == onpauseAttr)
495         setAttributeEventListener(eventNames().pauseEvent, createAttributeEventListener(this, name, value));
496     else if (name == onplayAttr)
497         setAttributeEventListener(eventNames().playEvent, createAttributeEventListener(this, name, value));
498     else if (name == onplayingAttr)
499         setAttributeEventListener(eventNames().playingEvent, createAttributeEventListener(this, name, value));
500     else if (name == onprogressAttr)
501         setAttributeEventListener(eventNames().progressEvent, createAttributeEventListener(this, name, value));
502     else if (name == onratechangeAttr)
503         setAttributeEventListener(eventNames().ratechangeEvent, createAttributeEventListener(this, name, value));
504     else if (name == onseekedAttr)
505         setAttributeEventListener(eventNames().seekedEvent, createAttributeEventListener(this, name, value));
506     else if (name == onseekingAttr)
507         setAttributeEventListener(eventNames().seekingEvent, createAttributeEventListener(this, name, value));
508     else if (name == onstalledAttr)
509         setAttributeEventListener(eventNames().stalledEvent, createAttributeEventListener(this, name, value));
510     else if (name == onsuspendAttr)
511         setAttributeEventListener(eventNames().suspendEvent, createAttributeEventListener(this, name, value));
512     else if (name == ontimeupdateAttr)
513         setAttributeEventListener(eventNames().timeupdateEvent, createAttributeEventListener(this, name, value));
514     else if (name == onvolumechangeAttr)
515         setAttributeEventListener(eventNames().volumechangeEvent, createAttributeEventListener(this, name, value));
516     else if (name == onwaitingAttr)
517         setAttributeEventListener(eventNames().waitingEvent, createAttributeEventListener(this, name, value));
518     else if (name == onwebkitbeginfullscreenAttr)
519         setAttributeEventListener(eventNames().webkitbeginfullscreenEvent, createAttributeEventListener(this, name, value));
520     else if (name == onwebkitendfullscreenAttr)
521         setAttributeEventListener(eventNames().webkitendfullscreenEvent, createAttributeEventListener(this, name, value));
522     else
523         HTMLElement::parseAttribute(name, value);
524 }
525
526 void HTMLMediaElement::finishParsingChildren()
527 {
528     HTMLElement::finishParsingChildren();
529     m_parsingInProgress = false;
530
531 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
532     document()->updateStyleIfNeeded();
533     createMediaPlayerProxy();
534 #endif
535     
536 #if ENABLE(VIDEO_TRACK)
537     if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled())
538         return;
539
540     if (Traversal<HTMLTrackElement>::firstWithin(this))
541         scheduleDelayedAction(ConfigureTextTracks);
542 #endif
543 }
544
545 bool HTMLMediaElement::rendererIsNeeded(const RenderStyle& style)
546 {
547 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
548     UNUSED_PARAM(context);
549     Frame* frame = document()->frame();
550     if (!frame)
551         return false;
552
553     return true;
554 #else
555     return controls() && HTMLElement::rendererIsNeeded(style);
556 #endif
557 }
558
559 RenderObject* HTMLMediaElement::createRenderer(RenderArena* arena, RenderStyle*)
560 {
561 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
562     // Setup the renderer if we already have a proxy widget.
563     RenderEmbeddedObject* mediaRenderer = new (arena) RenderEmbeddedObject(this);
564     if (m_proxyWidget) {
565         mediaRenderer->setWidget(m_proxyWidget);
566
567         if (Frame* frame = document()->frame())
568             frame->loader().client().showMediaPlayerProxyPlugin(m_proxyWidget.get());
569     }
570     return mediaRenderer;
571 #else
572     return new (arena) RenderMedia(this);
573 #endif
574 }
575
576 bool HTMLMediaElement::childShouldCreateRenderer(const Node* child) const
577 {
578     if (!hasMediaControls())
579         return false;
580     // <media> doesn't allow its content, including shadow subtree, to
581     // be rendered. So this should return false for most of the children.
582     // One exception is a shadow tree built for rendering controls which should be visible.
583     // So we let them go here by comparing its subtree root with one of the controls.
584     return mediaControls()->treeScope() == child->treeScope()
585         && hasShadowRootParent(child)
586         && HTMLElement::childShouldCreateRenderer(child);
587 }
588
589 Node::InsertionNotificationRequest HTMLMediaElement::insertedInto(ContainerNode* insertionPoint)
590 {
591     LOG(Media, "HTMLMediaElement::insertedInto");
592
593     HTMLElement::insertedInto(insertionPoint);
594     if (insertionPoint->inDocument()) {
595         m_inActiveDocument = true;
596
597         if (m_networkState == NETWORK_EMPTY && !getAttribute(srcAttr).isEmpty())
598             scheduleDelayedAction(LoadMediaResource);
599     }
600
601     configureMediaControls();
602     return InsertionDone;
603 }
604
605 void HTMLMediaElement::removedFrom(ContainerNode* insertionPoint)
606 {
607     LOG(Media, "HTMLMediaElement::removedFrom");
608
609     m_inActiveDocument = false;
610     if (insertionPoint->inDocument()) {
611         configureMediaControls();
612         if (m_networkState > NETWORK_EMPTY)
613             pause();
614         if (m_isFullscreen)
615             exitFullscreen();
616
617         if (m_player) {
618             JSC::VM* vm = JSDOMWindowBase::commonVM();
619             JSC::JSLockHolder lock(vm);
620
621             size_t extraMemoryCost = m_player->extraMemoryCost();
622             size_t extraMemoryCostDelta = extraMemoryCost - m_reportedExtraMemoryCost;
623             m_reportedExtraMemoryCost = extraMemoryCost;
624
625             if (extraMemoryCostDelta > 0)
626                 vm->heap.reportExtraMemoryCost(extraMemoryCostDelta);
627         }
628     }
629
630     HTMLElement::removedFrom(insertionPoint);
631 }
632
633 void HTMLMediaElement::willAttachRenderers()
634 {
635     ASSERT(!attached());
636
637 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
638     m_needWidgetUpdate = true;
639 #endif
640 }
641
642 void HTMLMediaElement::didAttachRenderers()
643 {
644     if (renderer())
645         renderer()->updateFromElement();
646 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
647     else if (m_proxyWidget) {
648         if (Frame* frame = document()->frame())
649             frame->loader().client().hideMediaPlayerProxyPlugin(m_proxyWidget.get());
650     }
651 #endif
652 }
653
654 void HTMLMediaElement::didRecalcStyle(Style::Change)
655 {
656     if (renderer())
657         renderer()->updateFromElement();
658 }
659
660 void HTMLMediaElement::scheduleDelayedAction(DelayedActionType actionType)
661 {
662     LOG(Media, "HTMLMediaElement::scheduleLoad");
663
664     if ((actionType & LoadMediaResource) && !(m_pendingActionFlags & LoadMediaResource)) {
665 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
666         createMediaPlayerProxy();
667 #endif
668         
669         prepareForLoad();
670         setFlags(m_pendingActionFlags, LoadMediaResource);
671     }
672
673 #if ENABLE(VIDEO_TRACK)
674     if (RuntimeEnabledFeatures::webkitVideoTrackEnabled() && (actionType & ConfigureTextTracks))
675         setFlags(m_pendingActionFlags, ConfigureTextTracks);
676 #endif
677
678 #if USE(PLATFORM_TEXT_TRACK_MENU)
679     if (actionType & TextTrackChangesNotification)
680         setFlags(m_pendingActionFlags, TextTrackChangesNotification);
681 #endif
682
683     m_loadTimer.startOneShot(0);
684 }
685
686 void HTMLMediaElement::scheduleNextSourceChild()
687 {
688     // Schedule the timer to try the next <source> element WITHOUT resetting state ala prepareForLoad.
689     setFlags(m_pendingActionFlags, LoadMediaResource);
690     m_loadTimer.startOneShot(0);
691 }
692
693 void HTMLMediaElement::scheduleEvent(const AtomicString& eventName)
694 {
695 #if LOG_MEDIA_EVENTS
696     LOG(Media, "HTMLMediaElement::scheduleEvent - scheduling '%s'", eventName.string().ascii().data());
697 #endif
698     RefPtr<Event> event = Event::create(eventName, false, true);
699     
700     // Don't set the event target, the event queue will set it in GenericEventQueue::timerFired and setting it here
701     // will trigger an ASSERT if this element has been marked for deletion.
702
703     m_asyncEventQueue->enqueueEvent(event.release());
704 }
705
706 void HTMLMediaElement::loadTimerFired(Timer<HTMLMediaElement>*)
707 {
708     RefPtr<HTMLMediaElement> protect(this); // loadNextSourceChild may fire 'beforeload', which can make arbitrary DOM mutations.
709
710 #if ENABLE(VIDEO_TRACK)
711     if (RuntimeEnabledFeatures::webkitVideoTrackEnabled() && (m_pendingActionFlags & ConfigureTextTracks))
712         configureTextTracks();
713 #endif
714
715     if (m_pendingActionFlags & LoadMediaResource) {
716         if (m_loadState == LoadingFromSourceElement)
717             loadNextSourceChild();
718         else
719             loadInternal();
720     }
721
722 #if USE(PLATFORM_TEXT_TRACK_MENU)
723     if (RuntimeEnabledFeatures::webkitVideoTrackEnabled() && (m_pendingActionFlags & TextTrackChangesNotification))
724         notifyMediaPlayerOfTextTrackChanges();
725 #endif
726
727     m_pendingActionFlags = 0;
728 }
729
730 PassRefPtr<MediaError> HTMLMediaElement::error() const 
731 {
732     return m_error;
733 }
734
735 void HTMLMediaElement::setSrc(const String& url)
736 {
737     setAttribute(srcAttr, url);
738 }
739
740 HTMLMediaElement::NetworkState HTMLMediaElement::networkState() const
741 {
742     return m_networkState;
743 }
744
745 String HTMLMediaElement::canPlayType(const String& mimeType, const String& keySystem, const KURL& url) const
746 {
747     MediaPlayer::SupportsType support = MediaPlayer::supportsType(ContentType(mimeType), keySystem, url, this);
748     String canPlay;
749
750     // 4.8.10.3
751     switch (support)
752     {
753         case MediaPlayer::IsNotSupported:
754             canPlay = emptyString();
755             break;
756         case MediaPlayer::MayBeSupported:
757             canPlay = ASCIILiteral("maybe");
758             break;
759         case MediaPlayer::IsSupported:
760             canPlay = ASCIILiteral("probably");
761             break;
762     }
763     
764     LOG(Media, "HTMLMediaElement::canPlayType(%s, %s, %s) -> %s", mimeType.utf8().data(), keySystem.utf8().data(), url.stringCenterEllipsizedToLength().utf8().data(), canPlay.utf8().data());
765
766     return canPlay;
767 }
768
769 void HTMLMediaElement::load()
770 {
771     RefPtr<HTMLMediaElement> protect(this); // loadInternal may result in a 'beforeload' event, which can make arbitrary DOM mutations.
772     
773     LOG(Media, "HTMLMediaElement::load()");
774     
775     if (userGestureRequiredForLoad() && !ScriptController::processingUserGesture())
776         return;
777     
778     m_loadInitiatedByUserGesture = ScriptController::processingUserGesture();
779     if (m_loadInitiatedByUserGesture)
780         removeBehaviorsRestrictionsAfterFirstUserGesture();
781     prepareForLoad();
782     loadInternal();
783     prepareToPlay();
784 }
785
786 void HTMLMediaElement::prepareForLoad()
787 {
788     LOG(Media, "HTMLMediaElement::prepareForLoad");
789
790     // Perform the cleanup required for the resource load algorithm to run.
791     stopPeriodicTimers();
792     m_loadTimer.stop();
793     m_sentEndEvent = false;
794     m_sentStalledEvent = false;
795     m_haveFiredLoadedData = false;
796     m_completelyLoaded = false;
797     m_havePreparedToPlay = false;
798     m_displayMode = Unknown;
799
800     // 1 - Abort any already-running instance of the resource selection algorithm for this element.
801     m_loadState = WaitingForSource;
802     m_currentSourceNode = 0;
803
804     // 2 - If there are any tasks from the media element's media element event task source in 
805     // one of the task queues, then remove those tasks.
806     cancelPendingEventsAndCallbacks();
807
808     // 3 - If the media element's networkState is set to NETWORK_LOADING or NETWORK_IDLE, queue
809     // a task to fire a simple event named abort at the media element.
810     if (m_networkState == NETWORK_LOADING || m_networkState == NETWORK_IDLE)
811         scheduleEvent(eventNames().abortEvent);
812
813 #if ENABLE(MEDIA_SOURCE)
814     setSourceState(MediaSource::closedKeyword());
815 #endif
816
817 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
818     createMediaPlayer();
819 #else
820     if (m_player)
821         m_player->cancelLoad();
822     else
823         createMediaPlayerProxy();
824 #endif
825
826     // 4 - If the media element's networkState is not set to NETWORK_EMPTY, then run these substeps
827     if (m_networkState != NETWORK_EMPTY) {
828         m_networkState = NETWORK_EMPTY;
829         m_readyState = HAVE_NOTHING;
830         m_readyStateMaximum = HAVE_NOTHING;
831         refreshCachedTime();
832         m_paused = true;
833         m_seeking = false;
834         invalidateCachedTime();
835         scheduleEvent(eventNames().emptiedEvent);
836         updateMediaController();
837 #if ENABLE(VIDEO_TRACK)
838         if (RuntimeEnabledFeatures::webkitVideoTrackEnabled())
839             updateActiveTextTrackCues(0);
840 #endif
841     }
842
843     // 5 - Set the playbackRate attribute to the value of the defaultPlaybackRate attribute.
844     setPlaybackRate(defaultPlaybackRate());
845
846     // 6 - Set the error attribute to null and the autoplaying flag to true.
847     m_error = 0;
848     m_autoplaying = true;
849
850     // 7 - Invoke the media element's resource selection algorithm.
851
852     // 8 - Note: Playback of any previously playing media resource for this element stops.
853
854     // The resource selection algorithm
855     // 1 - Set the networkState to NETWORK_NO_SOURCE
856     m_networkState = NETWORK_NO_SOURCE;
857
858     // 2 - Asynchronously await a stable state.
859
860     m_playedTimeRanges = TimeRanges::create();
861     m_lastSeekTime = 0;
862
863     // The spec doesn't say to block the load event until we actually run the asynchronous section
864     // algorithm, but do it now because we won't start that until after the timer fires and the 
865     // event may have already fired by then.
866     if (m_preload != MediaPlayer::None)
867         setShouldDelayLoadEvent(true);
868
869     configureMediaControls();
870 }
871
872 void HTMLMediaElement::loadInternal()
873 {
874     // Some of the code paths below this function dispatch the BeforeLoad event. This ASSERT helps
875     // us catch those bugs more quickly without needing all the branches to align to actually
876     // trigger the event.
877     ASSERT(!NoEventDispatchAssertion::isEventDispatchForbidden());
878
879     // If we can't start a load right away, start it later.
880     Page* page = document()->page();
881     if (pageConsentRequiredForLoad() && page && !page->canStartMedia()) {
882         setShouldDelayLoadEvent(false);
883         if (m_isWaitingUntilMediaCanStart)
884             return;
885         document()->addMediaCanStartListener(this);
886         m_isWaitingUntilMediaCanStart = true;
887         return;
888     }
889
890     clearFlags(m_pendingActionFlags, LoadMediaResource);
891
892     // Once the page has allowed an element to load media, it is free to load at will. This allows a 
893     // playlist that starts in a foreground tab to continue automatically if the tab is subsequently 
894     // put in the the background.
895     removeBehaviorRestriction(RequirePageConsentToLoadMediaRestriction);
896
897 #if ENABLE(VIDEO_TRACK)
898     if (hasMediaControls())
899         mediaControls()->changedClosedCaptionsVisibility();
900
901     // HTMLMediaElement::textTracksAreReady will need "... the text tracks whose mode was not in the
902     // disabled state when the element's resource selection algorithm last started".
903     if (RuntimeEnabledFeatures::webkitVideoTrackEnabled()) {
904         m_textTracksWhenResourceSelectionBegan.clear();
905         if (m_textTracks) {
906             for (unsigned i = 0; i < m_textTracks->length(); ++i) {
907                 TextTrack* track = m_textTracks->item(i);
908                 if (track->mode() != TextTrack::disabledKeyword())
909                     m_textTracksWhenResourceSelectionBegan.append(track);
910             }
911         }
912     }
913 #endif
914
915     selectMediaResource();
916 }
917
918 void HTMLMediaElement::selectMediaResource()
919 {
920     LOG(Media, "HTMLMediaElement::selectMediaResource");
921
922     enum Mode { attribute, children };
923
924     // 3 - If the media element has a src attribute, then let mode be attribute.
925     Mode mode = attribute;
926     if (!fastHasAttribute(srcAttr)) {
927         // Otherwise, if the media element does not have a src attribute but has a source 
928         // element child, then let mode be children and let candidate be the first such 
929         // source element child in tree order.
930         auto source = childrenOfType<HTMLSourceElement>(this).begin();
931         if (source != childrenOfType<HTMLSourceElement>(this).end()) {
932             mode = children;
933             m_nextChildNodeToConsider = &*source;
934             m_currentSourceNode = 0;
935         } else {
936             // Otherwise the media element has neither a src attribute nor a source element 
937             // child: set the networkState to NETWORK_EMPTY, and abort these steps; the 
938             // synchronous section ends.
939             m_loadState = WaitingForSource;
940             setShouldDelayLoadEvent(false);
941             m_networkState = NETWORK_EMPTY;
942
943             LOG(Media, "HTMLMediaElement::selectMediaResource, nothing to load");
944             return;
945         }
946     }
947
948     // 4 - Set the media element's delaying-the-load-event flag to true (this delays the load event), 
949     // and set its networkState to NETWORK_LOADING.
950     setShouldDelayLoadEvent(true);
951     m_networkState = NETWORK_LOADING;
952
953     // 5 - Queue a task to fire a simple event named loadstart at the media element.
954     scheduleEvent(eventNames().loadstartEvent);
955
956     // 6 - If mode is attribute, then run these substeps
957     if (mode == attribute) {
958         m_loadState = LoadingFromSrcAttr;
959
960         // If the src attribute's value is the empty string ... jump down to the failed step below
961         KURL mediaURL = getNonEmptyURLAttribute(srcAttr);
962         if (mediaURL.isEmpty()) {
963             mediaLoadingFailed(MediaPlayer::FormatError);
964             LOG(Media, "HTMLMediaElement::selectMediaResource, empty 'src'");
965             return;
966         }
967
968         if (!isSafeToLoadURL(mediaURL, Complain) || !dispatchBeforeLoadEvent(mediaURL.string())) {
969             mediaLoadingFailed(MediaPlayer::FormatError);
970             return;
971         }
972
973         // No type or key system information is available when the url comes
974         // from the 'src' attribute so MediaPlayer
975         // will have to pick a media engine based on the file extension.
976         ContentType contentType((String()));
977         loadResource(mediaURL, contentType, String());
978         LOG(Media, "HTMLMediaElement::selectMediaResource, using 'src' attribute url");
979         return;
980     }
981
982     // Otherwise, the source elements will be used
983     loadNextSourceChild();
984 }
985
986 void HTMLMediaElement::loadNextSourceChild()
987 {
988     ContentType contentType((String()));
989     String keySystem;
990     KURL mediaURL = selectNextSourceChild(&contentType, &keySystem, Complain);
991     if (!mediaURL.isValid()) {
992         waitForSourceChange();
993         return;
994     }
995
996 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
997     // Recreate the media player for the new url
998     createMediaPlayer();
999 #endif
1000
1001     m_loadState = LoadingFromSourceElement;
1002     loadResource(mediaURL, contentType, keySystem);
1003 }
1004
1005 static KURL createFileURLForApplicationCacheResource(const String& path)
1006 {
1007     // KURL should have a function to create a url from a path, but it does not. This function
1008     // is not suitable because KURL::setPath uses encodeWithURLEscapeSequences, which it notes
1009     // does not correctly escape '#' and '?'. This function works for our purposes because
1010     // app cache media files are always created with encodeForFileName(createCanonicalUUIDString()).
1011
1012 #if USE(CF) && PLATFORM(WIN)
1013     RetainPtr<CFURLRef> cfURL = adoptCF(CFURLCreateWithFileSystemPath(0, path.createCFString().get(), kCFURLWindowsPathStyle, false));
1014     KURL url(cfURL.get());
1015 #else
1016     KURL url;
1017
1018     url.setProtocol(ASCIILiteral("file"));
1019     url.setPath(path);
1020 #endif
1021     return url;
1022 }
1023
1024 void HTMLMediaElement::loadResource(const KURL& initialURL, ContentType& contentType, const String& keySystem)
1025 {
1026     ASSERT(isSafeToLoadURL(initialURL, Complain));
1027
1028     LOG(Media, "HTMLMediaElement::loadResource(%s, %s, %s)", urlForLoggingMedia(initialURL).utf8().data(), contentType.raw().utf8().data(), keySystem.utf8().data());
1029
1030     Frame* frame = document()->frame();
1031     if (!frame) {
1032         mediaLoadingFailed(MediaPlayer::FormatError);
1033         return;
1034     }
1035
1036     KURL url = initialURL;
1037     if (!frame->loader().willLoadMediaElementURL(url)) {
1038         mediaLoadingFailed(MediaPlayer::FormatError);
1039         return;
1040     }
1041     
1042     // The resource fetch algorithm 
1043     m_networkState = NETWORK_LOADING;
1044
1045     // If the url should be loaded from the application cache, pass the url of the cached file
1046     // to the media engine.
1047     ApplicationCacheHost* cacheHost = frame->loader().documentLoader()->applicationCacheHost();
1048     ApplicationCacheResource* resource = 0;
1049     if (cacheHost && cacheHost->shouldLoadResourceFromApplicationCache(ResourceRequest(url), resource)) {
1050         // Resources that are not present in the manifest will always fail to load (at least, after the
1051         // cache has been primed the first time), making the testing of offline applications simpler.
1052         if (!resource || resource->path().isEmpty()) {
1053             mediaLoadingFailed(MediaPlayer::NetworkError);
1054             return;
1055         }
1056     }
1057
1058     // Set m_currentSrc *before* changing to the cache url, the fact that we are loading from the app
1059     // cache is an internal detail not exposed through the media element API.
1060     m_currentSrc = url;
1061
1062     if (resource) {
1063         url = createFileURLForApplicationCacheResource(resource->path());
1064         LOG(Media, "HTMLMediaElement::loadResource - will load from app cache -> %s", urlForLoggingMedia(url).utf8().data());
1065     }
1066
1067     LOG(Media, "HTMLMediaElement::loadResource - m_currentSrc -> %s", urlForLoggingMedia(m_currentSrc).utf8().data());
1068
1069 #if ENABLE(MEDIA_STREAM)
1070     if (MediaStreamRegistry::registry().lookupMediaStreamDescriptor(url.string()))
1071         removeBehaviorRestriction(RequireUserGestureForRateChangeRestriction);
1072 #endif
1073
1074     if (m_sendProgressEvents) 
1075         startProgressEventTimer();
1076
1077     Settings* settings = document()->settings();
1078     bool privateMode = !settings || settings->privateBrowsingEnabled();
1079     m_player->setPrivateBrowsingMode(privateMode);
1080
1081     // Reset display mode to force a recalculation of what to show because we are resetting the player.
1082     setDisplayMode(Unknown);
1083
1084     if (!autoplay())
1085         m_player->setPreload(m_preload);
1086     m_player->setPreservesPitch(m_webkitPreservesPitch);
1087
1088     if (fastHasAttribute(mutedAttr))
1089         m_muted = true;
1090     updateVolume();
1091
1092 #if ENABLE(MEDIA_SOURCE)
1093     ASSERT(!m_mediaSource);
1094
1095     if (url.protocolIs(mediaSourceBlobProtocol))
1096         m_mediaSource = MediaSourceRegistry::registry().lookupMediaSource(url.string());
1097
1098     if (m_mediaSource) {
1099         if (!m_player->load(url, m_mediaSource))
1100             mediaLoadingFailed(MediaPlayer::FormatError);
1101     } else
1102 #endif
1103     if (!m_player->load(url, contentType, keySystem))
1104         mediaLoadingFailed(MediaPlayer::FormatError);
1105
1106     // If there is no poster to display, allow the media engine to render video frames as soon as
1107     // they are available.
1108     updateDisplayState();
1109
1110     if (renderer())
1111         renderer()->updateFromElement();
1112 }
1113
1114 #if ENABLE(VIDEO_TRACK)
1115 static bool trackIndexCompare(TextTrack* a,
1116                               TextTrack* b)
1117 {
1118     return a->trackIndex() - b->trackIndex() < 0;
1119 }
1120
1121 static bool eventTimeCueCompare(const std::pair<double, TextTrackCue*>& a,
1122                                 const std::pair<double, TextTrackCue*>& b)
1123 {
1124     // 12 - Sort the tasks in events in ascending time order (tasks with earlier
1125     // times first).
1126     if (a.first != b.first)
1127         return a.first - b.first < 0;
1128
1129     // If the cues belong to different text tracks, it doesn't make sense to
1130     // compare the two tracks by the relative cue order, so return the relative
1131     // track order.
1132     if (a.second->track() != b.second->track())
1133         return trackIndexCompare(a.second->track(), b.second->track());
1134
1135     // 12 - Further sort tasks in events that have the same time by the
1136     // relative text track cue order of the text track cues associated
1137     // with these tasks.
1138     return a.second->cueIndex() - b.second->cueIndex() < 0;
1139 }
1140
1141 static bool compareCueInterval(const CueInterval& one, const CueInterval& two)
1142 {
1143     return one.data()->isOrderedBefore(two.data());
1144 };
1145
1146
1147 void HTMLMediaElement::updateActiveTextTrackCues(double movieTime)
1148 {
1149     // 4.8.10.8 Playing the media resource
1150
1151     //  If the current playback position changes while the steps are running,
1152     //  then the user agent must wait for the steps to complete, and then must
1153     //  immediately rerun the steps.
1154     if (ignoreTrackDisplayUpdateRequests())
1155         return;
1156
1157     LOG(Media, "HTMLMediaElement::updateActiveTextTracks");
1158
1159     // 1 - Let current cues be a list of cues, initialized to contain all the
1160     // cues of all the hidden, showing, or showing by default text tracks of the
1161     // media element (not the disabled ones) whose start times are less than or
1162     // equal to the current playback position and whose end times are greater
1163     // than the current playback position.
1164     CueList currentCues;
1165
1166     // The user agent must synchronously unset [the text track cue active] flag
1167     // whenever ... the media element's readyState is changed back to HAVE_NOTHING.
1168     if (m_readyState != HAVE_NOTHING && m_player) {
1169         currentCues = m_cueTree.allOverlaps(m_cueTree.createInterval(movieTime, movieTime));
1170         std::sort(currentCues.begin(), currentCues.end(), &compareCueInterval);
1171     }
1172
1173     CueList previousCues;
1174     CueList missedCues;
1175
1176     // 2 - Let other cues be a list of cues, initialized to contain all the cues
1177     // of hidden, showing, and showing by default text tracks of the media
1178     // element that are not present in current cues.
1179     previousCues = m_currentlyActiveCues;
1180
1181     // 3 - Let last time be the current playback position at the time this
1182     // algorithm was last run for this media element, if this is not the first
1183     // time it has run.
1184     double lastTime = m_lastTextTrackUpdateTime;
1185
1186     // 4 - If the current playback position has, since the last time this
1187     // algorithm was run, only changed through its usual monotonic increase
1188     // during normal playback, then let missed cues be the list of cues in other
1189     // cues whose start times are greater than or equal to last time and whose
1190     // end times are less than or equal to the current playback position.
1191     // Otherwise, let missed cues be an empty list.
1192     if (lastTime >= 0 && m_lastSeekTime < movieTime) {
1193         CueList potentiallySkippedCues =
1194             m_cueTree.allOverlaps(m_cueTree.createInterval(lastTime, movieTime));
1195
1196         for (size_t i = 0; i < potentiallySkippedCues.size(); ++i) {
1197             double cueStartTime = potentiallySkippedCues[i].low();
1198             double cueEndTime = potentiallySkippedCues[i].high();
1199
1200             // Consider cues that may have been missed since the last seek time.
1201             if (cueStartTime > max(m_lastSeekTime, lastTime) && cueEndTime < movieTime)
1202                 missedCues.append(potentiallySkippedCues[i]);
1203         }
1204     }
1205
1206     m_lastTextTrackUpdateTime = movieTime;
1207
1208     // 5 - If the time was reached through the usual monotonic increase of the
1209     // current playback position during normal playback, and if the user agent
1210     // has not fired a timeupdate event at the element in the past 15 to 250ms
1211     // and is not still running event handlers for such an event, then the user
1212     // agent must queue a task to fire a simple event named timeupdate at the
1213     // element. (In the other cases, such as explicit seeks, relevant events get
1214     // fired as part of the overall process of changing the current playback
1215     // position.)
1216     if (m_lastSeekTime <= lastTime)
1217         scheduleTimeupdateEvent(false);
1218
1219     // Explicitly cache vector sizes, as their content is constant from here.
1220     size_t currentCuesSize = currentCues.size();
1221     size_t missedCuesSize = missedCues.size();
1222     size_t previousCuesSize = previousCues.size();
1223
1224     // 6 - If all of the cues in current cues have their text track cue active
1225     // flag set, none of the cues in other cues have their text track cue active
1226     // flag set, and missed cues is empty, then abort these steps.
1227     bool activeSetChanged = missedCuesSize;
1228
1229     for (size_t i = 0; !activeSetChanged && i < previousCuesSize; ++i)
1230         if (!currentCues.contains(previousCues[i]) && previousCues[i].data()->isActive())
1231             activeSetChanged = true;
1232
1233     for (size_t i = 0; i < currentCuesSize; ++i) {
1234         currentCues[i].data()->updateDisplayTree(movieTime);
1235
1236         if (!currentCues[i].data()->isActive())
1237             activeSetChanged = true;
1238     }
1239
1240     if (!activeSetChanged)
1241         return;
1242
1243     // 7 - If the time was reached through the usual monotonic increase of the
1244     // current playback position during normal playback, and there are cues in
1245     // other cues that have their text track cue pause-on-exi flag set and that
1246     // either have their text track cue active flag set or are also in missed
1247     // cues, then immediately pause the media element.
1248     for (size_t i = 0; !m_paused && i < previousCuesSize; ++i) {
1249         if (previousCues[i].data()->pauseOnExit()
1250             && previousCues[i].data()->isActive()
1251             && !currentCues.contains(previousCues[i]))
1252             pause();
1253     }
1254
1255     for (size_t i = 0; !m_paused && i < missedCuesSize; ++i) {
1256         if (missedCues[i].data()->pauseOnExit())
1257             pause();
1258     }
1259
1260     // 8 - Let events be a list of tasks, initially empty. Each task in this
1261     // list will be associated with a text track, a text track cue, and a time,
1262     // which are used to sort the list before the tasks are queued.
1263     Vector<std::pair<double, TextTrackCue*> > eventTasks;
1264
1265     // 8 - Let affected tracks be a list of text tracks, initially empty.
1266     Vector<TextTrack*> affectedTracks;
1267
1268     for (size_t i = 0; i < missedCuesSize; ++i) {
1269         // 9 - For each text track cue in missed cues, prepare an event named enter
1270         // for the TextTrackCue object with the text track cue start time.
1271         eventTasks.append(std::make_pair(missedCues[i].data()->startTime(),
1272                                          missedCues[i].data()));
1273
1274         // 10 - For each text track [...] in missed cues, prepare an event
1275         // named exit for the TextTrackCue object with the  with the later of
1276         // the text track cue end time and the text track cue start time.
1277
1278         // Note: An explicit task is added only if the cue is NOT a zero or
1279         // negative length cue. Otherwise, the need for an exit event is
1280         // checked when these tasks are actually queued below. This doesn't
1281         // affect sorting events before dispatch either, because the exit
1282         // event has the same time as the enter event.
1283         if (missedCues[i].data()->startTime() < missedCues[i].data()->endTime())
1284             eventTasks.append(std::make_pair(missedCues[i].data()->endTime(),
1285                                              missedCues[i].data()));
1286     }
1287
1288     for (size_t i = 0; i < previousCuesSize; ++i) {
1289         // 10 - For each text track cue in other cues that has its text
1290         // track cue active flag set prepare an event named exit for the
1291         // TextTrackCue object with the text track cue end time.
1292         if (!currentCues.contains(previousCues[i]))
1293             eventTasks.append(std::make_pair(previousCues[i].data()->endTime(),
1294                                              previousCues[i].data()));
1295     }
1296
1297     for (size_t i = 0; i < currentCuesSize; ++i) {
1298         // 11 - For each text track cue in current cues that does not have its
1299         // text track cue active flag set, prepare an event named enter for the
1300         // TextTrackCue object with the text track cue start time.
1301         if (!previousCues.contains(currentCues[i]))
1302             eventTasks.append(std::make_pair(currentCues[i].data()->startTime(),
1303                                              currentCues[i].data()));
1304     }
1305
1306     // 12 - Sort the tasks in events in ascending time order (tasks with earlier
1307     // times first).
1308     nonCopyingSort(eventTasks.begin(), eventTasks.end(), eventTimeCueCompare);
1309
1310     for (size_t i = 0; i < eventTasks.size(); ++i) {
1311         if (!affectedTracks.contains(eventTasks[i].second->track()))
1312             affectedTracks.append(eventTasks[i].second->track());
1313
1314         // 13 - Queue each task in events, in list order.
1315         RefPtr<Event> event;
1316
1317         // Each event in eventTasks may be either an enterEvent or an exitEvent,
1318         // depending on the time that is associated with the event. This
1319         // correctly identifies the type of the event, if the startTime is
1320         // less than the endTime in the cue.
1321         if (eventTasks[i].second->startTime() >= eventTasks[i].second->endTime()) {
1322             event = Event::create(eventNames().enterEvent, false, false);
1323             event->setTarget(eventTasks[i].second);
1324             m_asyncEventQueue->enqueueEvent(event.release());
1325
1326             event = Event::create(eventNames().exitEvent, false, false);
1327             event->setTarget(eventTasks[i].second);
1328             m_asyncEventQueue->enqueueEvent(event.release());
1329         } else {
1330             if (eventTasks[i].first == eventTasks[i].second->startTime())
1331                 event = Event::create(eventNames().enterEvent, false, false);
1332             else
1333                 event = Event::create(eventNames().exitEvent, false, false);
1334
1335             event->setTarget(eventTasks[i].second);
1336             m_asyncEventQueue->enqueueEvent(event.release());
1337         }
1338     }
1339
1340     // 14 - Sort affected tracks in the same order as the text tracks appear in
1341     // the media element's list of text tracks, and remove duplicates.
1342     nonCopyingSort(affectedTracks.begin(), affectedTracks.end(), trackIndexCompare);
1343
1344     // 15 - For each text track in affected tracks, in the list order, queue a
1345     // task to fire a simple event named cuechange at the TextTrack object, and, ...
1346     for (size_t i = 0; i < affectedTracks.size(); ++i) {
1347         RefPtr<Event> event = Event::create(eventNames().cuechangeEvent, false, false);
1348         event->setTarget(affectedTracks[i]);
1349
1350         m_asyncEventQueue->enqueueEvent(event.release());
1351
1352         // ... if the text track has a corresponding track element, to then fire a
1353         // simple event named cuechange at the track element as well.
1354         if (affectedTracks[i]->trackType() == TextTrack::TrackElement) {
1355             RefPtr<Event> event = Event::create(eventNames().cuechangeEvent, false, false);
1356             HTMLTrackElement* trackElement = static_cast<LoadableTextTrack*>(affectedTracks[i])->trackElement();
1357             ASSERT(trackElement);
1358             event->setTarget(trackElement);
1359             
1360             m_asyncEventQueue->enqueueEvent(event.release());
1361         }
1362     }
1363
1364     // 16 - Set the text track cue active flag of all the cues in the current
1365     // cues, and unset the text track cue active flag of all the cues in the
1366     // other cues.
1367     for (size_t i = 0; i < currentCuesSize; ++i)
1368         currentCues[i].data()->setIsActive(true);
1369
1370     for (size_t i = 0; i < previousCuesSize; ++i)
1371         if (!currentCues.contains(previousCues[i]))
1372             previousCues[i].data()->setIsActive(false);
1373
1374     // Update the current active cues.
1375     m_currentlyActiveCues = currentCues;
1376
1377     if (activeSetChanged)
1378         updateTextTrackDisplay();
1379 }
1380
1381 bool HTMLMediaElement::textTracksAreReady() const
1382 {
1383     // 4.8.10.12.1 Text track model
1384     // ...
1385     // The text tracks of a media element are ready if all the text tracks whose mode was not 
1386     // in the disabled state when the element's resource selection algorithm last started now
1387     // have a text track readiness state of loaded or failed to load.
1388     for (unsigned i = 0; i < m_textTracksWhenResourceSelectionBegan.size(); ++i) {
1389         if (m_textTracksWhenResourceSelectionBegan[i]->readinessState() == TextTrack::Loading
1390             || m_textTracksWhenResourceSelectionBegan[i]->readinessState() == TextTrack::NotLoaded)
1391             return false;
1392     }
1393
1394     return true;
1395 }
1396
1397 void HTMLMediaElement::textTrackReadyStateChanged(TextTrack* track)
1398 {
1399     if (m_player && m_textTracksWhenResourceSelectionBegan.contains(track)) {
1400         if (track->readinessState() != TextTrack::Loading)
1401             setReadyState(m_player->readyState());
1402     } else {
1403         // The track readiness state might have changed as a result of the user
1404         // clicking the captions button. In this case, a check whether all the
1405         // resources have failed loading should be done in order to hide the CC button.
1406         if (hasMediaControls() && track->readinessState() == TextTrack::FailedToLoad)
1407             mediaControls()->refreshClosedCaptionsButtonVisibility();
1408     }
1409 }
1410
1411 void HTMLMediaElement::audioTrackEnabledChanged(AudioTrack*)
1412 {
1413     // We will want to change the media controls here once they exist
1414 }
1415
1416 void HTMLMediaElement::textTrackModeChanged(TextTrack* track)
1417 {
1418     bool trackIsLoaded = true;
1419     if (track->trackType() == TextTrack::TrackElement) {
1420         trackIsLoaded = false;
1421         auto end = childrenOfType<HTMLTrackElement>(this).end();
1422         for (auto trackElement = childrenOfType<HTMLTrackElement>(this).begin(); trackElement != end; ++trackElement) {
1423             if (trackElement->track() == track) {
1424                 if (trackElement->readyState() == HTMLTrackElement::LOADING || trackElement->readyState() == HTMLTrackElement::LOADED)
1425                     trackIsLoaded = true;
1426                 break;
1427             }
1428         }
1429     }
1430
1431     // If this is the first added track, create the list of text tracks.
1432     if (!m_textTracks)
1433         m_textTracks = TextTrackList::create(this, ActiveDOMObject::scriptExecutionContext());
1434     
1435     // Mark this track as "configured" so configureTextTracks won't change the mode again.
1436     track->setHasBeenConfigured(true);
1437     
1438     if (track->mode() != TextTrack::disabledKeyword() && trackIsLoaded)
1439         textTrackAddCues(track, track->cues());
1440
1441 #if USE(PLATFORM_TEXT_TRACK_MENU)
1442     if (platformTextTrackMenu())
1443         platformTextTrackMenu()->trackWasSelected(track->platformTextTrack());
1444 #endif
1445     
1446     configureTextTrackDisplay(AssumeTextTrackVisibilityChanged);
1447 }
1448
1449 void HTMLMediaElement::videoTrackSelectedChanged(VideoTrack*)
1450 {
1451     // We will want to change the media controls here once they exist
1452 }
1453
1454 void HTMLMediaElement::textTrackKindChanged(TextTrack* track)
1455 {
1456     if (track->kind() != TextTrack::captionsKeyword() && track->kind() != TextTrack::subtitlesKeyword() && track->mode() == TextTrack::showingKeyword())
1457         track->setMode(TextTrack::hiddenKeyword());
1458 }
1459
1460 void HTMLMediaElement::beginIgnoringTrackDisplayUpdateRequests()
1461 {
1462     ++m_ignoreTrackDisplayUpdate;
1463 }
1464
1465 void HTMLMediaElement::endIgnoringTrackDisplayUpdateRequests()
1466 {
1467     ASSERT(m_ignoreTrackDisplayUpdate);
1468     --m_ignoreTrackDisplayUpdate;
1469     if (!m_ignoreTrackDisplayUpdate && m_inActiveDocument)
1470         updateActiveTextTrackCues(currentTime());
1471 }
1472
1473 void HTMLMediaElement::textTrackAddCues(TextTrack* track, const TextTrackCueList* cues) 
1474 {
1475     if (track->mode() == TextTrack::disabledKeyword())
1476         return;
1477
1478     TrackDisplayUpdateScope scope(this);
1479     for (size_t i = 0; i < cues->length(); ++i)
1480         textTrackAddCue(track, cues->item(i));
1481 }
1482
1483 void HTMLMediaElement::textTrackRemoveCues(TextTrack*, const TextTrackCueList* cues) 
1484 {
1485     TrackDisplayUpdateScope scope(this);
1486     for (size_t i = 0; i < cues->length(); ++i)
1487         textTrackRemoveCue(cues->item(i)->track(), cues->item(i));
1488 }
1489
1490 void HTMLMediaElement::textTrackAddCue(TextTrack* track, PassRefPtr<TextTrackCue> cue)
1491 {
1492     if (track->mode() == TextTrack::disabledKeyword())
1493         return;
1494
1495     // Negative duration cues need be treated in the interval tree as
1496     // zero-length cues.
1497     double endTime = max(cue->startTime(), cue->endTime());
1498
1499     CueInterval interval = m_cueTree.createInterval(cue->startTime(), endTime, cue.get());
1500     if (!m_cueTree.contains(interval))
1501         m_cueTree.add(interval);
1502     updateActiveTextTrackCues(currentTime());
1503 }
1504
1505 void HTMLMediaElement::textTrackRemoveCue(TextTrack*, PassRefPtr<TextTrackCue> cue)
1506 {
1507     // Negative duration cues need to be treated in the interval tree as
1508     // zero-length cues.
1509     double endTime = max(cue->startTime(), cue->endTime());
1510
1511     CueInterval interval = m_cueTree.createInterval(cue->startTime(), endTime, cue.get());
1512     m_cueTree.remove(interval);
1513
1514     size_t index = m_currentlyActiveCues.find(interval);
1515     if (index != notFound) {
1516         cue->setIsActive(false);
1517         m_currentlyActiveCues.remove(index);
1518     }
1519
1520     cue->removeDisplayTree();
1521     updateActiveTextTrackCues(currentTime());
1522 }
1523
1524 #endif
1525
1526 bool HTMLMediaElement::isSafeToLoadURL(const KURL& url, InvalidURLAction actionIfInvalid)
1527 {
1528     if (!url.isValid()) {
1529         LOG(Media, "HTMLMediaElement::isSafeToLoadURL(%s) -> FALSE because url is invalid", urlForLoggingMedia(url).utf8().data());
1530         return false;
1531     }
1532
1533     Frame* frame = document()->frame();
1534     if (!frame || !document()->securityOrigin()->canDisplay(url)) {
1535         if (actionIfInvalid == Complain)
1536             FrameLoader::reportLocalLoadFailed(frame, url.stringCenterEllipsizedToLength());
1537         LOG(Media, "HTMLMediaElement::isSafeToLoadURL(%s) -> FALSE rejected by SecurityOrigin", urlForLoggingMedia(url).utf8().data());
1538         return false;
1539     }
1540
1541     if (!document()->contentSecurityPolicy()->allowMediaFromSource(url)) {
1542         LOG(Media, "HTMLMediaElement::isSafeToLoadURL(%s) -> rejected by Content Security Policy", urlForLoggingMedia(url).utf8().data());
1543         return false;
1544     }
1545
1546     return true;
1547 }
1548
1549 void HTMLMediaElement::startProgressEventTimer()
1550 {
1551     if (m_progressEventTimer.isActive())
1552         return;
1553
1554     m_previousProgressTime = monotonicallyIncreasingTime();
1555     // 350ms is not magic, it is in the spec!
1556     m_progressEventTimer.startRepeating(0.350);
1557 }
1558
1559 void HTMLMediaElement::waitForSourceChange()
1560 {
1561     LOG(Media, "HTMLMediaElement::waitForSourceChange");
1562
1563     stopPeriodicTimers();
1564     m_loadState = WaitingForSource;
1565
1566     // 6.17 - Waiting: Set the element's networkState attribute to the NETWORK_NO_SOURCE value
1567     m_networkState = NETWORK_NO_SOURCE;
1568
1569     // 6.18 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
1570     setShouldDelayLoadEvent(false);
1571
1572     updateDisplayState();
1573
1574     if (renderer())
1575         renderer()->updateFromElement();
1576 }
1577
1578 void HTMLMediaElement::noneSupported()
1579 {
1580     LOG(Media, "HTMLMediaElement::noneSupported");
1581
1582     stopPeriodicTimers();
1583     m_loadState = WaitingForSource;
1584     m_currentSourceNode = 0;
1585
1586     // 4.8.10.5 
1587     // 6 - Reaching this step indicates that the media resource failed to load or that the given 
1588     // URL could not be resolved. In one atomic operation, run the following steps:
1589
1590     // 6.1 - Set the error attribute to a new MediaError object whose code attribute is set to
1591     // MEDIA_ERR_SRC_NOT_SUPPORTED.
1592     m_error = MediaError::create(MediaError::MEDIA_ERR_SRC_NOT_SUPPORTED);
1593
1594     // 6.2 - Forget the media element's media-resource-specific text tracks.
1595
1596     // 6.3 - Set the element's networkState attribute to the NETWORK_NO_SOURCE value.
1597     m_networkState = NETWORK_NO_SOURCE;
1598
1599     // 7 - Queue a task to fire a simple event named error at the media element.
1600     scheduleEvent(eventNames().errorEvent);
1601
1602 #if ENABLE(MEDIA_SOURCE)
1603     setSourceState(MediaSource::closedKeyword());
1604 #endif
1605
1606     // 8 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
1607     setShouldDelayLoadEvent(false);
1608
1609     // 9 - Abort these steps. Until the load() method is invoked or the src attribute is changed, 
1610     // the element won't attempt to load another resource.
1611
1612     updateDisplayState();
1613
1614     if (renderer())
1615         renderer()->updateFromElement();
1616 }
1617
1618 void HTMLMediaElement::mediaEngineError(PassRefPtr<MediaError> err)
1619 {
1620     LOG(Media, "HTMLMediaElement::mediaEngineError(%d)", static_cast<int>(err->code()));
1621
1622     // 1 - The user agent should cancel the fetching process.
1623     stopPeriodicTimers();
1624     m_loadState = WaitingForSource;
1625
1626     // 2 - Set the error attribute to a new MediaError object whose code attribute is 
1627     // set to MEDIA_ERR_NETWORK/MEDIA_ERR_DECODE.
1628     m_error = err;
1629
1630     // 3 - Queue a task to fire a simple event named error at the media element.
1631     scheduleEvent(eventNames().errorEvent);
1632
1633 #if ENABLE(MEDIA_SOURCE)
1634     setSourceState(MediaSource::closedKeyword());
1635 #endif
1636
1637     // 4 - Set the element's networkState attribute to the NETWORK_EMPTY value and queue a
1638     // task to fire a simple event called emptied at the element.
1639     m_networkState = NETWORK_EMPTY;
1640     scheduleEvent(eventNames().emptiedEvent);
1641
1642     // 5 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
1643     setShouldDelayLoadEvent(false);
1644
1645     // 6 - Abort the overall resource selection algorithm.
1646     m_currentSourceNode = 0;
1647 }
1648
1649 void HTMLMediaElement::cancelPendingEventsAndCallbacks()
1650 {
1651     LOG(Media, "HTMLMediaElement::cancelPendingEventsAndCallbacks");
1652     m_asyncEventQueue->cancelAllEvents();
1653
1654     for (auto source = Traversal<HTMLSourceElement>::firstChild(this); source; source = Traversal<HTMLSourceElement>::nextSibling(source))
1655         source->cancelPendingErrorEvent();
1656 }
1657
1658 Document* HTMLMediaElement::mediaPlayerOwningDocument()
1659 {
1660     return document();
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& chromeClient = page->chrome().client();
1676
1677     if (!succeeded) {
1678         chromeClient.logDiagnosticMessage(DiagnosticLoggingKeys::mediaLoadingFailedKey(), errorMessage, DiagnosticLoggingKeys::failKey());
1679         return;
1680     }
1681
1682     chromeClient.logDiagnosticMessage(DiagnosticLoggingKeys::mediaLoadedKey(), mediaEngine, DiagnosticLoggingKeys::noopKey());
1683
1684     if (!page->hasSeenAnyMediaEngine())
1685         chromeClient.logDiagnosticMessage(DiagnosticLoggingKeys::pageContainsAtLeastOneMediaEngineKey(), emptyString(), DiagnosticLoggingKeys::noopKey());
1686
1687     if (!page->hasSeenMediaEngine(mediaEngine))
1688         chromeClient.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 = monotonicallyIncreasingTime();
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_clockTimeAtLastCachedTimeUpdate = monotonicallyIncreasingTime();
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_minimumClockTimeToUpdateCachedTime = monotonicallyIncreasingTime() + 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 = monotonicallyIncreasingTime();
2290     double maximumDurationToCacheMediaTime = m_player->maximumDurationToCacheMediaTime();
2291
2292     if (maximumDurationToCacheMediaTime && m_cachedTime != MediaPlayer::invalidTime() && !m_paused && now > m_minimumClockTimeToUpdateCachedTime) {
2293         double clockDelta = now - m_clockTimeAtLastCachedTimeUpdate;
2294
2295         // Not too soon, use the cached time only if it hasn't expired.
2296         if (clockDelta < maximumDurationToCacheMediaTime) {
2297             double adjustedCacheTime = m_cachedTime + (m_playbackRate * clockDelta);
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_minimumClockTimeToUpdateCachedTime && m_cachedTime != MediaPlayer::invalidTime()) {
2310         double clockDelta = now - m_clockTimeAtLastCachedTimeUpdate;
2311         double delta = m_cachedTime + (m_playbackRate * clockDelta) - 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 = monotonicallyIncreasingTime();
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 = monotonicallyIncreasingTime();
2816     double timedelta = now - m_clockTimeAtLastUpdateEvent;
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_clockTimeAtLastUpdateEvent = 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(TextTrack::captionMenuOffItem());
2934         return;
2935     }
2936
2937     TextTrack* textTrack;
2938     if (platformTrack == PlatformTextTrack::captionMenuOffItem())
2939         textTrack = TextTrack::captionMenuOffItem();
2940     else if (platformTrack == PlatformTextTrack::captionMenuAutomaticItem())
2941         textTrack = TextTrack::captionMenuAutomaticItem();
2942     else {
2943         size_t i;
2944         for (i = 0; i < m_textTracks->length(); ++i) {
2945             textTrack = m_textTracks->item(i);
2946             
2947             if (textTrack->platformTextTrack() == platformTrack)
2948                 break;
2949         }
2950         if (i == m_textTracks->length())
2951             return;
2952     }
2953
2954     setSelectedTextTrack(textTrack);
2955 }
2956
2957 Vector<RefPtr<PlatformTextTrack> > HTMLMediaElement::platformTextTracks()
2958 {
2959     if (!m_textTracks || !m_textTracks->length())
2960         return Vector<RefPtr<PlatformTextTrack> >();
2961     
2962     Vector<RefPtr<PlatformTextTrack> > platformTracks;
2963     for (size_t i = 0; i < m_textTracks->length(); ++i)
2964         platformTracks.append(m_textTracks->item(i)->platformTextTrack());
2965     
2966     return platformTracks;
2967 }
2968
2969 void HTMLMediaElement::notifyMediaPlayerOfTextTrackChanges()
2970 {
2971     if (!m_textTracks || !m_textTracks->length() || !platformTextTrackMenu())
2972         return;
2973     
2974     m_platformMenu->tracksDidChange();
2975 }
2976
2977 PlatformTextTrackMenuInterface* HTMLMediaElement::platformTextTrackMenu()
2978 {
2979     if (m_platformMenu)
2980         return m_platformMenu.get();
2981
2982     if (!m_player || !m_player->implementsTextTrackControls())
2983         return 0;
2984
2985     m_platformMenu = m_player->textTrackMenu();
2986     if (!m_platformMenu)
2987         return 0;
2988
2989     m_platformMenu->setClient(this);
2990
2991     return m_platformMenu.get();
2992 }
2993 #endif // #if USE(PLATFORM_TEXT_TRACK_MENU)
2994     
2995 void HTMLMediaElement::closeCaptionTracksChanged()
2996 {
2997     if (hasMediaControls())
2998         mediaControls()->closedCaptionTracksChanged();
2999
3000 #if USE(PLATFORM_TEXT_TRACK_MENU)
3001     if (m_player && m_player->implementsTextTrackControls())
3002         scheduleDelayedAction(TextTrackChangesNotification);
3003 #endif
3004 }
3005
3006 void HTMLMediaElement::addAudioTrack(PassRefPtr<AudioTrack> track)
3007 {
3008     if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled())
3009         return;
3010
3011     audioTracks()->append(track);
3012 }
3013
3014 void HTMLMediaElement::addTextTrack(PassRefPtr<TextTrack> track)
3015 {
3016     if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled())
3017         return;
3018
3019     textTracks()->append(track);
3020
3021     closeCaptionTracksChanged();
3022 }
3023
3024 void HTMLMediaElement::addVideoTrack(PassRefPtr<VideoTrack> track)
3025 {
3026     if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled())
3027         return;
3028
3029     videoTracks()->append(track);
3030 }
3031
3032 void HTMLMediaElement::removeAudioTrack(AudioTrack* track)
3033 {
3034     if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled())
3035         return;
3036
3037     m_audioTracks->remove(track);
3038 }
3039
3040 void HTMLMediaElement::removeTextTrack(TextTrack* track)
3041 {
3042     if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled())
3043         return;
3044
3045     TrackDisplayUpdateScope scope(this);
3046     TextTrackCueList* cues = track->cues();
3047     if (cues)
3048         textTrackRemoveCues(track, cues);
3049     track->clearClient();
3050     m_textTracks->remove(track);
3051
3052     closeCaptionTracksChanged();
3053 }
3054
3055 void HTMLMediaElement::removeVideoTrack(VideoTrack* track)
3056 {
3057     if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled())
3058         return;
3059
3060     m_videoTracks->remove(track);
3061 }
3062
3063 void HTMLMediaElement::removeAllInbandTracks()
3064 {
3065     while (m_audioTracks &&  m_audioTracks->length())
3066         removeAudioTrack(m_audioTracks->lastItem());
3067
3068     if (m_textTracks) {
3069         TrackDisplayUpdateScope scope(this);
3070         for (int i = m_textTracks->length() - 1; i >= 0; --i) {
3071             TextTrack* track = m_textTracks->item(i);
3072
3073             if (track->trackType() == TextTrack::InBand)
3074                 removeTextTrack(track);
3075         }
3076     }
3077
3078     while (m_videoTracks &&  m_videoTracks->length())
3079         removeVideoTrack(m_videoTracks->lastItem());
3080 }
3081
3082 PassRefPtr<TextTrack> HTMLMediaElement::addTextTrack(const String& kind, const String& label, const String& language, ExceptionCode& ec)
3083 {
3084     if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled())
3085         return 0;
3086
3087     // 4.8.10.12.4 Text track API
3088     // The addTextTrack(kind, label, language) method of media elements, when invoked, must run the following steps:
3089
3090     // 1. If kind is not one of the following strings, then throw a SyntaxError exception and abort these steps
3091     if (!TextTrack::isValidKindKeyword(kind)) {
3092         ec = SYNTAX_ERR;
3093         return 0;
3094     }
3095
3096     // 2. If the label argument was omitted, let label be the empty string.
3097     // 3. If the language argument was omitted, let language be the empty string.
3098     // 4. Create a new TextTrack object.
3099
3100     // 5. Create a new text track corresponding to the new object, and set its text track kind to kind, its text 
3101     // track label to label, its text track language to language...
3102     RefPtr<TextTrack> textTrack = TextTrack::create(ActiveDOMObject::scriptExecutionContext(), this, kind, label, language);
3103
3104     // Note, due to side effects when changing track parameters, we have to
3105     // first append the track to the text track list.
3106
3107     // 6. Add the new text track to the media element's list of text tracks.
3108     addTextTrack(textTrack);
3109
3110     // ... its text track readiness state to the text track loaded state ...
3111     textTrack->setReadinessState(TextTrack::Loaded);
3112
3113     // ... its text track mode to the text track hidden mode, and its text track list of cues to an empty list ...
3114     textTrack->setMode(TextTrack::hiddenKeyword());
3115
3116     return textTrack.release();
3117 }
3118
3119 AudioTrackList* HTMLMediaElement::audioTracks()
3120 {
3121     if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled())
3122         return 0;
3123
3124     if (!m_audioTracks)
3125         m_audioTracks = AudioTrackList::create(this, ActiveDOMObject::scriptExecutionContext());
3126
3127     return m_audioTracks.get();
3128 }
3129
3130 TextTrackList* HTMLMediaElement::textTracks() 
3131 {
3132     if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled())
3133         return 0;
3134
3135     if (!m_textTracks)
3136         m_textTracks = TextTrackList::create(this, ActiveDOMObject::scriptExecutionContext());
3137
3138     return m_textTracks.get();
3139 }
3140
3141 VideoTrackList* HTMLMediaElement::videoTracks()
3142 {
3143     if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled())
3144         return 0;
3145
3146     if (!m_videoTracks)
3147         m_videoTracks = VideoTrackList::create(this, ActiveDOMObject::scriptExecutionContext());
3148
3149     return m_videoTracks.get();
3150 }
3151
3152 void HTMLMediaElement::didAddTextTrack(HTMLTrackElement* trackElement)
3153 {
3154     ASSERT(trackElement->hasTagName(trackTag));
3155
3156     if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled())
3157         return;
3158
3159     // 4.8.10.12.3 Sourcing out-of-band text tracks
3160     // When a track element's parent element changes and the new parent is a media element, 
3161     // then the user agent must add the track element's corresponding text track to the 
3162     // media element's list of text tracks ... [continues in TextTrackList::append]
3163     RefPtr<TextTrack> textTrack = trackElement->track();
3164     if (!textTrack)
3165         return;
3166     
3167     addTextTrack(textTrack.release());
3168     
3169     // Do not schedule the track loading until parsing finishes so we don't start before all tracks
3170     // in the markup have been added.
3171     if (!m_parsingInProgress)
3172         scheduleDelayedAction(ConfigureTextTracks);
3173
3174     if (hasMediaControls())
3175         mediaControls()->closedCaptionTracksChanged();
3176 }
3177
3178 void HTMLMediaElement::didRemoveTextTrack(HTMLTrackElement* trackElement)
3179 {
3180     ASSERT(trackElement->hasTagName(trackTag));
3181
3182     if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled())
3183         return;
3184
3185 #if !LOG_DISABLED
3186     if (trackElement->hasTagName(trackTag)) {
3187         KURL url = trackElement->getNonEmptyURLAttribute(srcAttr);
3188         LOG(Media, "HTMLMediaElement::didRemoveTrack - 'src' is %s", urlForLoggingMedia(url).utf8().data());
3189     }
3190 #endif
3191
3192     RefPtr<TextTrack> textTrack = trackElement->track();
3193     if (!textTrack)
3194         return;
3195     
3196     textTrack->setHasBeenConfigured(false);
3197
3198     if (!m_textTracks)
3199         return;
3200     
3201     // 4.8.10.12.3 Sourcing out-of-band text tracks
3202     // When a track element's parent element changes and the old parent was a media element, 
3203     // then the user agent must remove the track element's corresponding text track from the 
3204     // media element's list of text tracks.
3205     removeTextTrack(textTrack.get());
3206
3207     size_t index = m_textTracksWhenResourceSelectionBegan.find(textTrack.get());
3208     if (index != notFound)
3209         m_textTracksWhenResourceSelectionBegan.remove(index);
3210 }
3211
3212 void HTMLMediaElement::configureTextTrackGroup(const TrackGroup& group)
3213 {
3214     ASSERT(group.tracks.size());
3215
3216     LOG(Media, "HTMLMediaElement::configureTextTrackGroup");
3217
3218     Page* page = document()->page();
3219     CaptionUserPreferences* captionPreferences = page? page->group().captionPreferences() : 0;
3220     CaptionUserPreferences::CaptionDisplayMode displayMode = captionPreferences ? captionPreferences->captionDisplayMode() : CaptionUserPreferences::Automatic;
3221
3222     // First, find the track in the group that should be enabled (if any).
3223     Vector<RefPtr<TextTrack> > currentlyEnabledTracks;
3224     RefPtr<TextTrack> trackToEnable;
3225     RefPtr<TextTrack> defaultTrack;
3226     RefPtr<TextTrack> fallbackTrack;
3227     RefPtr<TextTrack> forcedSubitleTrack;
3228     int highestTrackScore = 0;
3229     int highestForcedScore = 0;
3230     for (size_t i = 0; i < group.tracks.size(); ++i) {
3231         RefPtr<TextTrack> textTrack = group.tracks[i];
3232
3233         if (m_processingPreferenceChange && textTrack->mode() == TextTrack::showingKeyword())
3234             currentlyEnabledTracks.append(textTrack);
3235
3236         int trackScore = captionPreferences ? captionPreferences->textTrackSelectionScore(textTrack.get(), this) : 0;
3237         LOG(Media, "HTMLMediaElement::configureTextTrackGroup -  '%s' track with language '%s' has score %i", textTrack->kind().string().utf8().data(), textTrack->language().string().utf8().data(), trackScore);
3238
3239         if (trackScore) {
3240
3241             // * If the text track kind is { [subtitles or captions] [descriptions] } and the user has indicated an interest in having a
3242             // track with this text track kind, text track language, and text track label enabled, and there is no
3243             // other text track in the media element's list of text tracks with a text track kind of either subtitles
3244             // or captions whose text track mode is showing
3245             // ...
3246             // * If the text track kind is chapters and the text track language is one that the user agent has reason
3247             // to believe is appropriate for the user, and there is no other text track in the media element's list of
3248             // text tracks with a text track kind of chapters whose text track mode is showing
3249             //    Let the text track mode be showing.
3250             if (trackScore > highestTrackScore) {
3251                 highestTrackScore = trackScore;
3252                 trackToEnable = textTrack;
3253             }
3254
3255             if (!defaultTrack && textTrack->isDefault())
3256                 defaultTrack = textTrack;
3257             if (!defaultTrack && !fallbackTrack)
3258                 fallbackTrack = textTrack;
3259             if (textTrack->containsOnlyForcedSubtitles() && trackScore > highestForcedScore) {
3260                 forcedSubitleTrack = textTrack;
3261                 highestForcedScore = trackScore;
3262             }
3263         } else if (!group.visibleTrack && !defaultTrack && textTrack->isDefault()) {
3264             // * If the track element has a default attribute specified, and there is no other text track in the media
3265             // element's list of text tracks whose text track mode is showing or showing by default
3266             //    Let the text track mode be showing by default.
3267             if (group.kind != TrackGroup::CaptionsAndSubtitles || displayMode != CaptionUserPreferences::ForcedOnly)
3268                 defaultTrack = textTrack;
3269         }
3270     }
3271
3272     if (!trackToEnable && defaultTrack)
3273         trackToEnable = defaultTrack;
3274     
3275     // If no track matches the user's preferred language, none was marked as 'default', and there is a forced subtitle track
3276     // in the same language as the language of the primary audio track, enable it.
3277     if (!trackToEnable && forcedSubitleTrack)
3278         trackToEnable = forcedSubitleTrack;
3279
3280     // If no track matches, don't disable an already visible track unless preferences say they all should be off.
3281     if (group.kind != TrackGroup::CaptionsAndSubtitles || displayMode != CaptionUserPreferences::ForcedOnly) {
3282         if (!trackToEnable && !defaultTrack && group.visibleTrack)
3283             trackToEnable = group.visibleTrack;
3284     }
3285     
3286     // If no track matches the user's preferred language and non was marked 'default', enable the first track
3287     // because the user has explicitly stated a preference for this kind of track.
3288     if (!trackToEnable && fallbackTrack)
3289         trackToEnable = fallbackTrack;
3290
3291     if (trackToEnable)
3292         m_subtitleTrackLanguage = trackToEnable->language();
3293     else
3294         m_subtitleTrackLanguage = emptyString();
3295     
3296     if (currentlyEnabledTracks.size()) {
3297         for (size_t i = 0; i < currentlyEnabledTracks.size(); ++i) {
3298             RefPtr<TextTrack> textTrack = currentlyEnabledTracks[i];
3299             if (textTrack != trackToEnable)
3300                 textTrack->setMode(TextTrack::disabledKeyword());
3301         }
3302     }
3303
3304     if (trackToEnable)
3305         trackToEnable->setMode(TextTrack::showingKeyword());
3306
3307     m_processingPreferenceChange = false;
3308 }
3309
3310 void HTMLMediaElement::setSelectedTextTrack(TextTrack* trackToSelect)
3311 {
3312     TextTrackList* trackList = textTracks();
3313     if (!trackList || !trackList->length())
3314         return;
3315
3316     if (trackToSelect != TextTrack::captionMenuOffItem() && trackToSelect != TextTrack::captionMenuAutomaticItem()) {
3317         if (!trackList->contains(trackToSelect))
3318             return;
3319         
3320         for (int i = 0, length = trackList->length(); i < length; ++i) {
3321             TextTrack* track = trackList->item(i);
3322             if (!trackToSelect || track != trackToSelect)
3323                 track->setMode(TextTrack::disabledKeyword());
3324             else
3325                 track->setMode(TextTrack::showingKeyword());
3326         }
3327     }
3328
3329     CaptionUserPreferences* captionPreferences = document()->page() ? document()->page()->group().captionPreferences() : 0;
3330     if (!captionPreferences)
3331         return;
3332
3333     CaptionUserPreferences::CaptionDisplayMode displayMode = captionPreferences->captionDisplayMode();
3334     if (trackToSelect == TextTrack::captionMenuOffItem())
3335         displayMode = CaptionUserPreferences::ForcedOnly;
3336     else if (trackToSelect == TextTrack::captionMenuAutomaticItem())
3337         displayMode = CaptionUserPreferences::Automatic;
3338     else {
3339         displayMode = CaptionUserPreferences::AlwaysOn;
3340         if (trackToSelect->language().length())
3341             captionPreferences->setPreferredLanguage(trackToSelect->language());
3342         
3343         // Set m_captionDisplayMode here so we don't reconfigure again when the preference changed notification comes through.
3344         m_captionDisplayMode = displayMode;
3345     }
3346
3347     captionPreferences->setCaptionDisplayMode(displayMode);
3348 }
3349
3350 void HTMLMediaElement::configureTextTracks()
3351 {
3352     TrackGroup captionAndSubtitleTracks(TrackGroup::CaptionsAndSubtitles);
3353     TrackGroup descriptionTracks(TrackGroup::Description);
3354     TrackGroup chapterTracks(TrackGroup::Chapter);
3355     TrackGroup metadataTracks(TrackGroup::Metadata);
3356     TrackGroup otherTracks(TrackGroup::Other);
3357
3358     if (!m_textTracks)
3359         return;
3360
3361     for (size_t i = 0; i < m_textTracks->length(); ++i) {
3362         RefPtr<TextTrack> textTrack = m_textTracks->item(i);
3363         if (!textTrack)
3364             continue;
3365
3366         String kind = textTrack->kind();
3367         TrackGroup* currentGroup;
3368         if (kind == TextTrack::subtitlesKeyword() || kind == TextTrack::captionsKeyword() || kind == TextTrack::forcedKeyword())
3369             currentGroup = &captionAndSubtitleTracks;
3370         else if (kind == TextTrack::descriptionsKeyword())
3371             currentGroup = &descriptionTracks;
3372         else if (kind == TextTrack::chaptersKeyword())
3373             currentGroup = &chapterTracks;
3374         else if (kind == TextTrack::metadataKeyword())
3375             currentGroup = &metadataTracks;
3376         else
3377             currentGroup = &otherTracks;
3378
3379         if (!currentGroup->visibleTrack && textTrack->mode() == TextTrack::showingKeyword())
3380             currentGroup->visibleTrack = textTrack;
3381         if (!currentGroup->defaultTrack && textTrack->isDefault())
3382             currentGroup->defaultTrack = textTrack;
3383
3384         // Do not add this track to the group if it has already been automatically configured
3385         // as we only want to call configureTextTrack once per track so that adding another 
3386         // track after the initial configuration doesn't reconfigure every track - only those 
3387         // that should be changed by the new addition. For example all metadata tracks are 
3388         // disabled by default, and we don't want a track that has been enabled by script 
3389         // to be disabled automatically when a new metadata track is added later.
3390         if (textTrack->hasBeenConfigured())
3391             continue;
3392         
3393         if (textTrack->language().length())
3394             currentGroup->hasSrcLang = true;
3395         currentGroup->tracks.append(textTrack);
3396     }
3397     
3398     if (captionAndSubtitleTracks.tracks.size())
3399         configureTextTrackGroup(captionAndSubtitleTracks);
3400     if (descriptionTracks.tracks.size())
3401         configureTextTrackGroup(descriptionTracks);
3402     if (chapterTracks.tracks.size())
3403         configureTextTrackGroup(chapterTracks);
3404     if (metadataTracks.tracks.size())
3405         configureTextTrackGroup(metadataTracks);
3406     if (otherTracks.tracks.size())
3407         configureTextTrackGroup(otherTracks);
3408
3409     configureTextTrackDisplay();
3410     if (hasMediaControls())
3411         mediaControls()->closedCaptionTracksChanged();
3412 }
3413 #endif
3414
3415 bool HTMLMediaElement::havePotentialSourceChild()
3416 {
3417     // Stash the current <source> node and next nodes so we can restore them after checking
3418     // to see there is another potential.
3419     RefPtr<HTMLSourceElement> currentSourceNode = m_currentSourceNode;
3420     RefPtr<Node> nextNode = m_nextChildNodeToConsider;
3421
3422     KURL nextURL = selectNextSourceChild(0, 0, DoNothing);
3423
3424     m_currentSourceNode = currentSourceNode;
3425     m_nextChildNodeToConsider = nextNode;
3426
3427     return nextURL.isValid();
3428 }
3429
3430 KURL HTMLMediaElement::selectNextSourceChild(ContentType* contentType, String* keySystem, InvalidURLAction actionIfInvalid)
3431 {
3432 #if !LOG_DISABLED
3433     // Don't log if this was just called to find out if there are any valid <source> elements.
3434     bool shouldLog = actionIfInvalid != DoNothing;
3435     if (shouldLog)
3436         LOG(Media, "HTMLMediaElement::selectNextSourceChild");
3437 #endif
3438
3439     if (!m_nextChildNodeToConsider) {
3440 #if !LOG_DISABLED
3441         if (shouldLog)
3442             LOG(Media, "HTMLMediaElement::selectNextSourceChild -> 0x0000, \"\"");
3443 #endif
3444         return KURL();
3445     }
3446
3447     KURL mediaURL;
3448     Node* node;
3449     HTMLSourceElement* source = 0;
3450     String type;
3451     String system;
3452     bool lookingForStartNode = m_nextChildNodeToConsider;
3453     bool canUseSourceElement = false;
3454     bool okToLoadSourceURL;
3455
3456     NodeVector potentialSourceNodes;
3457     getChildNodes(this, potentialSourceNodes);
3458
3459     for (unsigned i = 0; !canUseSourceElement && i < potentialSourceNodes.size(); ++i) {
3460         node = potentialSourceNodes[i].get();
3461         if (lookingForStartNode && m_nextChildNodeToConsider != node)
3462             continue;
3463         lookingForStartNode = false;
3464
3465         if (!node->hasTagName(sourceTag))
3466             continue;
3467         if (node->parentNode() != this)
3468             continue;
3469
3470         source = static_cast<HTMLSourceElement*>(node);
3471
3472         // 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
3473         mediaURL = source->getNonEmptyURLAttribute(srcAttr);
3474 #if !LOG_DISABLED
3475         if (shouldLog)
3476             LOG(Media, "HTMLMediaElement::selectNextSourceChild - 'src' is %s", urlForLoggingMedia(mediaURL).utf8().data());
3477 #endif
3478         if (mediaURL.isEmpty())
3479             goto check_again;
3480         
3481         if (source->fastHasAttribute(mediaAttr)) {
3482             MediaQueryEvaluator screenEval("screen", document()->frame(), renderer() ? renderer()->style() : 0);
3483             RefPtr<MediaQuerySet> media = MediaQuerySet::createAllowingDescriptionSyntax(source->media());
3484 #if !LOG_DISABLED
3485             if (shouldLog)
3486                 LOG(Media, "HTMLMediaElement::selectNextSourceChild - 'media' is %s", source->media().utf8().data());
3487 #endif
3488             if (!screenEval.eval(media.get())) 
3489                 goto check_again;
3490         }
3491
3492         type = source->type();
3493         // FIXME(82965): Add support for keySystem in <source> and set system from source.
3494         if (type.isEmpty() && mediaURL.protocolIsData())
3495             type = mimeTypeFromDataURL(mediaURL);
3496         if (!type.isEmpty() || !system.isEmpty()) {
3497 #if !LOG_DISABLED
3498             if (shouldLog)
3499                 LOG(Media, "HTMLMediaElement::selectNextSourceChild - 'type' is '%s' - key system is '%s'", type.utf8().data(), system.utf8().data());
3500 #endif
3501             if (!MediaPlayer::supportsType(ContentType(type), system, mediaURL, this))
3502                 goto check_again;
3503         }
3504
3505         // Is it safe to load this url?
3506         okToLoadSourceURL = isSafeToLoadURL(mediaURL, actionIfInvalid) && dispatchBeforeLoadEvent(mediaURL.string());
3507
3508         // A 'beforeload' event handler can mutate the DOM, so check to see if the source element is still a child node.
3509         if (node->parentNode() != this) {
3510             LOG(Media, "HTMLMediaElement::selectNextSourceChild : 'beforeload' removed current element");
3511             source = 0;
3512             goto check_again;
3513         }
3514
3515         if (!okToLoadSourceURL)
3516             goto check_again;
3517
3518         // Making it this far means the <source> looks reasonable.
3519         canUseSourceElement = true;
3520
3521 check_again:
3522         if (!canUseSourceElement && actionIfInvalid == Complain && source)
3523             source->scheduleErrorEvent();
3524     }
3525
3526     if (canUseSourceElement) {
3527         if (contentType)
3528             *contentType = ContentType(type);
3529         if (keySystem)
3530             *keySystem = system;
3531         m_currentSourceNode = source;
3532         m_nextChildNodeToConsider = source->nextSibling();
3533     } else {
3534         m_currentSourceNode = 0;
3535         m_nextChildNodeToConsider = 0;
3536     }
3537
3538 #if !LOG_DISABLED
3539     if (shouldLog)
3540         LOG(Media, "HTMLMediaElement::selectNextSourceChild -> %p, %s", m_currentSourceNode.get(), canUseSourceElement ? urlForLoggingMedia(mediaURL).utf8().data() : "");
3541 #endif
3542     return canUseSourceElement ? mediaURL : KURL();
3543 }
3544
3545 void HTMLMediaElement::sourceWasAdded(HTMLSourceElement* source)
3546 {
3547     LOG(Media, "HTMLMediaElement::sourceWasAdded(%p)", source);
3548
3549 #if !LOG_DISABLED
3550     if (source->hasTagName(sourceTag)) {
3551         KURL url = source->getNonEmptyURLAttribute(srcAttr);
3552         LOG(Media, "HTMLMediaElement::sourceWasAdded - 'src' is %s", urlForLoggingMedia(url).utf8().data());
3553     }
3554 #endif
3555     
3556     // We should only consider a <source> element when there is not src attribute at all.
3557     if (fastHasAttribute(srcAttr))
3558         return;
3559
3560     // 4.8.8 - If a source element is inserted as a child of a media element that has no src 
3561     // attribute and whose networkState has the value NETWORK_EMPTY, the user agent must invoke 
3562     // the media element's resource selection algorithm.
3563     if (networkState() == HTMLMediaElement::NETWORK_EMPTY) {
3564         scheduleDelayedAction(LoadMediaResource);
3565         m_nextChildNodeToConsider = source;
3566         return;
3567     }
3568
3569     if (m_currentSourceNode && source == m_currentSourceNode->nextSibling()) {
3570         LOG(Media, "HTMLMediaElement::sourceWasAdded - <source> inserted immediately after current source");
3571         m_nextChildNodeToConsider = source;
3572         return;
3573     }
3574
3575     if (m_nextChildNodeToConsider)
3576         return;
3577     
3578     // 4.8.9.5, resource selection algorithm, source elements section:
3579     // 21. Wait until the node after pointer is a node other than the end of the list. (This step might wait forever.)
3580     // 22. Asynchronously await a stable state...
3581     // 23. Set the element's delaying-the-load-event flag back to true (this delays the load event again, in case 
3582     // it hasn't been fired yet).
3583     setShouldDelayLoadEvent(true);
3584
3585     // 24. Set the networkState back to NETWORK_LOADING.
3586     m_networkState = NETWORK_LOADING;
3587     
3588     // 25. Jump back to the find next candidate step above.
3589     m_nextChildNodeToConsider = source;
3590     scheduleNextSourceChild();
3591 }
3592
3593 void HTMLMediaElement::sourceWasRemoved(HTMLSourceElement* source)
3594 {
3595     LOG(Media, "HTMLMediaElement::sourceWasRemoved(%p)", source);
3596
3597 #if !LOG_DISABLED
3598     if (source->hasTagName(sourceTag)) {
3599         KURL url = source->getNonEmptyURLAttribute(srcAttr);
3600         LOG(Media, "HTMLMediaElement::sourceWasRemoved - 'src' is %s", urlForLoggingMedia(url).utf8().data());
3601     }
3602 #endif
3603
3604     if (source != m_currentSourceNode && source != m_nextChildNodeToConsider)
3605         return;
3606
3607     if (source == m_nextChildNodeToConsider) {
3608         if (m_currentSourceNode)
3609             m_nextChildNodeToConsider = m_currentSourceNode->nextSibling();
3610         LOG(Media, "HTMLMediaElement::sourceRemoved - m_nextChildNodeToConsider set to %p", m_nextChildNodeToConsider.get());
3611     } else if (source == m_currentSourceNode) {
3612         // Clear the current source node pointer, but don't change the movie as the spec says:
3613         // 4.8.8 - Dynamically modifying a source element and its attribute when the element is already 
3614         // inserted in a video or audio element will have no effect.
3615         m_currentSourceNode = 0;
3616         LOG(Media, "HTMLMediaElement::sourceRemoved - m_currentSourceNode set to 0");
3617     }
3618 }
3619
3620 void HTMLMediaElement::mediaPlayerTimeChanged(MediaPlayer*)
3621 {
3622     LOG(Media, "HTMLMediaElement::mediaPlayerTimeChanged");
3623
3624 #if ENABLE(VIDEO_TRACK)
3625     if (RuntimeEnabledFeatures::webkitVideoTrackEnabled())
3626         updateActiveTextTrackCues(currentTime());
3627 #endif
3628
3629     beginProcessingMediaPlayerCallback();
3630
3631     invalidateCachedTime();
3632
3633     // 4.8.10.9 step 14 & 15.  Needed if no ReadyState change is associated with the seek.
3634     if (m_seeking && m_readyState >= HAVE_CURRENT_DATA && !m_player->seeking())
3635         finishSeek();
3636     
3637     // Always call scheduleTimeupdateEvent when the media engine reports a time discontinuity, 
3638     // it will only queue a 'timeupdate' event if we haven't already posted one at the current
3639     // movie time.
3640     scheduleTimeupdateEvent(false);
3641
3642     double now = currentTime();
3643     double dur = duration();
3644     
3645     // When the current playback position reaches the end of the media resource when the direction of
3646     // playback is forwards, then the user agent must follow these steps:
3647     if (!std::isnan(dur) && dur && now >= dur && m_playbackRate > 0) {
3648         // If the media element has a loop attribute specified and does not have a current media controller,
3649         if (loop() && !m_mediaController) {
3650             m_sentEndEvent = false;
3651             //  then seek to the earliest possible position of the media resource and abort these steps.
3652             seek(startTime(), IGNORE_EXCEPTION);
3653         } else {
3654             // If the media element does not have a current media controller, and the media element
3655             // has still ended playback, and the direction of playback is still forwards, and paused
3656             // is false,
3657             if (!m_mediaController && !m_paused) {
3658                 // changes paused to true and fires a simple event named pause at the media element.
3659                 m_paused = true;
3660                 scheduleEvent(eventNames().pauseEvent);
3661             }
3662             // Queue a task to fire a simple event named ended at the media element.
3663             if (!m_sentEndEvent) {
3664                 m_sentEndEvent = true;
3665                 scheduleEvent(eventNames().endedEvent);
3666             }
3667             // If the media element has a current media controller, then report the controller state
3668             // for the media element's current media controller.
3669             updateMediaController();
3670         }
3671     }
3672     else
3673         m_sentEndEvent = false;
3674
3675     updatePlayState();
3676     endProcessingMediaPlayerCallback();
3677 }
3678
3679 void HTMLMediaElement::mediaPlayerVolumeChanged(MediaPlayer*)
3680 {
3681     LOG(Media, "HTMLMediaElement::mediaPlayerVolumeChanged");
3682
3683     beginProcessingMediaPlayerCallback();
3684     if (m_player) {
3685         double vol = m_player->volume();
3686         if (vol != m_volume) {
3687             m_volume = vol;
3688             updateVolume();
3689             scheduleEvent(eventNames().volumechangeEvent);
3690         }
3691     }
3692     endProcessingMediaPlayerCallback();
3693 }
3694
3695 void HTMLMediaElement::mediaPlayerMuteChanged(MediaPlayer*)
3696 {
3697     LOG(Media, "HTMLMediaElement::mediaPlayerMuteChanged");
3698
3699     beginProcessingMediaPlayerCallback();
3700     if (m_player)
3701         setMuted(m_player->muted());
3702     endProcessingMediaPlayerCallback();
3703 }
3704
3705 void HTMLMediaElement::mediaPlayerDurationChanged(MediaPlayer* player)
3706 {
3707     LOG(Media, "HTMLMediaElement::mediaPlayerDurationChanged");
3708
3709     beginProcessingMediaPlayerCallback();
3710
3711     scheduleEvent(eventNames().durationchangeEvent);
3712     mediaPlayerCharacteristicChanged(player);
3713
3714     double now = currentTime();
3715     double dur = duration();
3716     if (now > dur)
3717         seek(dur, IGNORE_EXCEPTION);
3718
3719     endProcessingMediaPlayerCallback();
3720 }
3721
3722 void HTMLMediaElement::mediaPlayerRateChanged(MediaPlayer*)
3723 {
3724     LOG(Media, "HTMLMediaElement::mediaPlayerRateChanged");
3725
3726     beginProcessingMediaPlayerCallback();
3727
3728     // Stash the rate in case the one we tried to set isn't what the engine is
3729     // using (eg. it can't handle the rate we set)
3730     m_playbackRate = m_player->rate();
3731     if (m_playing)
3732         invalidateCachedTime();
3733
3734 #if PLATFORM(MAC)
3735     updateDisableSleep();
3736 #endif
3737
3738     endProcessingMediaPlayerCallback();
3739 }
3740
3741 void HTMLMediaElement::mediaPlayerPlaybackStateChanged(MediaPlayer*)
3742 {
3743     LOG(Media, "HTMLMediaElement::mediaPlayerPlaybackStateChanged");
3744
3745     if (!m_player || m_pausedInternal)
3746         return;
3747
3748     beginProcessingMediaPlayerCallback();
3749     if (m_player->paused())
3750         pauseInternal();
3751     else
3752         playInternal();
3753     endProcessingMediaPlayerCallback();
3754 }
3755
3756 void HTMLMediaElement::mediaPlayerSawUnsupportedTracks(MediaPlayer*)
3757 {
3758     LOG(Media, "HTMLMediaElement::mediaPlayerSawUnsupportedTracks");
3759
3760     // The MediaPlayer came across content it cannot completely handle.
3761     // This is normally acceptable except when we are in a standalone
3762     // MediaDocument. If so, tell the document what has happened.
3763     if (document()->isMediaDocument()) {
3764         MediaDocument* mediaDocument = toMediaDocument(document());
3765         mediaDocument->mediaElementSawUnsupportedTracks();
3766     }
3767 }
3768
3769 void HTMLMediaElement::mediaPlayerResourceNotSupported(MediaPlayer*)
3770 {
3771     LOG(Media, "HTMLMediaElement::mediaPlayerResourceNotSupported");
3772
3773     // The MediaPlayer came across content which no installed engine supports.
3774     mediaLoadingFailed(MediaPlayer::FormatError);
3775 }
3776
3777 // MediaPlayerPresentation methods
3778 void HTMLMediaElement::mediaPlayerRepaint(MediaPlayer*)
3779 {
3780     beginProcessingMediaPlayerCallback();
3781     updateDisplayState();
3782     if (renderer())
3783         renderer()->repaint();
3784     endProcessingMediaPlayerCallback();
3785 }
3786
3787 void HTMLMediaElement::mediaPlayerSizeChanged(MediaPlayer*)
3788 {
3789     LOG(Media, "HTMLMediaElement::mediaPlayerSizeChanged");
3790
3791     beginProcessingMediaPlayerCallback();
3792     if (renderer())
3793         renderer()->updateFromElement();
3794     endProcessingMediaPlayerCallback();
3795 }
3796
3797 #if USE(ACCELERATED_COMPOSITING)
3798 bool HTMLMediaElement::mediaPlayerRenderingCanBeAccelerated(MediaPlayer*)
3799 {
3800     if (renderer() && renderer()->isVideo())
3801         return renderer()->view().compositor().canAccelerateVideoRendering(toRenderVideo(renderer()));
3802     return false;
3803 }
3804
3805 void HTMLMediaElement::mediaPlayerRenderingModeChanged(MediaPlayer*)
3806 {
3807     LOG(Media, "HTMLMediaElement::mediaPlayerRenderingModeChanged");
3808
3809     // Kick off a fake recalcStyle that will update the compositing tree.
3810     setNeedsStyleRecalc(SyntheticStyleChange);
3811 }
3812 #endif
3813
3814 #if PLATFORM(WIN) && USE(AVFOUNDATION)
3815 GraphicsDeviceAdapter* HTMLMediaElement::mediaPlayerGraphicsDeviceAdapter(const MediaPlayer*) const
3816 {
3817     if (!document() || !document()->page())
3818         return 0;
3819
3820     return document()->page()->chrome().client().graphicsDeviceAdapter();
3821 }
3822 #endif
3823
3824 void HTMLMediaElement::mediaPlayerEngineUpdated(MediaPlayer*)
3825 {
3826     LOG(Media, "HTMLMediaElement::mediaPlayerEngineUpdated");
3827     beginProcessingMediaPlayerCallback();
3828     if (renderer())
3829         renderer()->updateFromElement();
3830     endProcessingMediaPlayerCallback();
3831 }
3832
3833 void HTMLMediaElement::mediaPlayerFirstVideoFrameAvailable(MediaPlayer*)
3834 {
3835     LOG(Media, "HTMLMediaElement::mediaPlayerFirstVideoFrameAvailable");
3836     beginProcessingMediaPlayerCallback();
3837     if (displayMode() == PosterWaitingForVideo) {
3838         setDisplayMode(Video);
3839 #if USE(ACCELERATED_COMPOSITING)
3840         mediaPlayerRenderingModeChanged(m_player.get());
3841 #endif
3842     }
3843     endProcessingMediaPlayerCallback();
3844 }
3845
3846 void HTMLMediaElement::mediaPlayerCharacteristicChanged(MediaPlayer*)
3847 {
3848     LOG(Media, "HTMLMediaElement::mediaPlayerCharacteristicChanged");
3849     
3850     beginProcessingMediaPlayerCallback();
3851
3852 #if ENABLE(VIDEO_TRACK)
3853     if (m_captionDisplayMode == CaptionUserPreferences::Automatic && m_subtitleTrackLanguage != m_player->languageOfPrimaryAudioTrack())
3854         markCaptionAndSubtitleTracksAsUnconfigured(AfterDelay);
3855 #endif
3856
3857     if (hasMediaControls())
3858         mediaControls()->reset();
3859     if (renderer())
3860         renderer()->updateFromElement();
3861     endProcessingMediaPlayerCallback();
3862 }
3863
3864 PassRefPtr<TimeRanges> HTMLMediaElement::buffered() const
3865 {
3866     if (!m_player)
3867         return TimeRanges::create();
3868     return m_player->buffered();
3869 }
3870
3871 PassRefPtr<TimeRanges> HTMLMediaElement::played()
3872 {
3873     if (m_playing) {
3874         double time = currentTime();
3875         if (time > m_lastSeekTime)
3876             addPlayedRange(m_lastSeekTime, time);
3877     }
3878
3879     if (!m_playedTimeRanges)
3880         m_playedTimeRanges = TimeRanges::create();
3881
3882     return m_playedTimeRanges->copy();
3883 }
3884
3885 PassRefPtr<TimeRanges> HTMLMediaElement::seekable() const
3886 {
3887     return m_player ? m_player->seekable() : TimeRanges::create();
3888 }
3889
3890 bool HTMLMediaElement::potentiallyPlaying() const
3891 {
3892     // "pausedToBuffer" means the media engine's rate is 0, but only because it had to stop playing
3893     // when it ran out of buffered data. A movie is this state is "potentially playing", modulo the
3894     // checks in couldPlayIfEnoughData().
3895     bool pausedToBuffer = m_readyStateMaximum >= HAVE_FUTURE_DATA && m_readyState < HAVE_FUTURE_DATA;
3896     return (pausedToBuffer || m_readyState >= HAVE_FUTURE_DATA) && couldPlayIfEnoughData() && !isBlockedOnMediaController();
3897 }
3898
3899 bool HTMLMediaElement::couldPlayIfEnoughData() const
3900 {
3901     return !paused() && !endedPlayback() && !stoppedDueToErrors() && !pausedForUserInteraction();
3902 }
3903
3904 bool HTMLMediaElement::endedPlayback() const
3905 {
3906     double dur = duration();
3907     if (!m_player || std::isnan(dur))
3908         return false;
3909
3910     // 4.8.10.8 Playing the media resource
3911
3912     // A media element is said to have ended playback when the element's 
3913     // readyState attribute is HAVE_METADATA or greater, 
3914     if (m_readyState < HAVE_METADATA)
3915         return false;
3916
3917     // and the current playback position is the end of the media resource and the direction
3918     // of playback is forwards, Either the media element does not have a loop attribute specified,
3919     // or the media element has a current media controller.
3920     double now = currentTime();
3921     if (m_playbackRate > 0)
3922         return dur > 0 && now >= dur && (!loop() || m_mediaController);
3923
3924     // or the current playback position is the earliest possible position and the direction 
3925     // of playback is backwards
3926     if (m_playbackRate < 0)
3927         return now <= 0;
3928
3929     return false;
3930 }
3931
3932 bool HTMLMediaElement::stoppedDueToErrors() const
3933 {
3934     if (m_readyState >= HAVE_METADATA && m_error) {
3935         RefPtr<TimeRanges> seekableRanges = seekable();
3936         if (!seekableRanges->contain(currentTime()))
3937             return true;
3938     }
3939     
3940     return false;
3941 }
3942
3943 bool HTMLMediaElement::pausedForUserInteraction() const
3944 {
3945 //    return !paused() && m_readyState >= HAVE_FUTURE_DATA && [UA requires a decitions from the user]
3946     return false;
3947 }
3948
3949 double HTMLMediaElement::minTimeSeekable() const
3950 {
3951     return m_player ? m_player->minTimeSeekable() : 0;
3952 }
3953
3954 double HTMLMediaElement::maxTimeSeekable() const
3955 {
3956     return m_player ? m_player->maxTimeSeekable() : 0;
3957 }
3958     
3959 void HTMLMediaElement::updateVolume()
3960 {
3961     if (!m_player)
3962         return;
3963
3964     // Avoid recursion when the player reports volume changes.
3965     if (!processingMediaPlayerCallback()) {
3966         Page* page = document()->page();
3967         double volumeMultiplier = page ? page->mediaVolume() : 1;
3968         bool shouldMute = m_muted;
3969
3970         if (m_mediaController) {
3971             volumeMultiplier *= m_mediaController->volume();
3972             shouldMute = m_mediaController->muted();
3973         }
3974
3975         m_player->setMuted(shouldMute);
3976         m_player->setVolume(m_volume * volumeMultiplier);
3977     }
3978
3979     if (hasMediaControls())
3980         mediaControls()->changedVolume();
3981 }
3982
3983 void HTMLMediaElement::updatePlayState()
3984 {
3985     if (!m_player)
3986         return;
3987
3988     if (m_pausedInternal) {
3989         if (!m_player->paused())
3990             m_player->pause();
3991         refreshCachedTime();
3992         m_playbackProgressTimer.stop();
3993         if (hasMediaControls())
3994             mediaControls()->playbackStopped();
3995         m_activityToken = nullptr;
3996         return;
3997     }
3998     
3999     bool shouldBePlaying = potentiallyPlaying();
4000     bool playerPaused = m_player->paused();
4001
4002     LOG(Media, "HTMLMediaElement::updatePlayState - shouldBePlaying = %s, playerPaused = %s", 
4003         boolString(shouldBePlaying), boolString(playerPaused));
4004
4005     if (shouldBePlaying) {
4006         setDisplayMode(Video);
4007         invalidateCachedTime();
4008
4009         if (playerPaused) {
4010             if (!m_isFullscreen && isVideo() && document() && document()->page() && document()->page()->chrome().requiresFullscreenForVideoPlayback())
4011                 enterFullscreen();
4012
4013             // Set rate, muted before calling play in case they were set before the media engine was setup.
4014             // The media engine should just stash the rate and muted values since it isn't already playing.
4015             m_player->setRate(m_playbackRate);
4016             m_player->setMuted(m_muted);
4017
4018             m_player->play();
4019         }
4020
4021         if (hasMediaControls())
4022             mediaControls()->playbackStarted();
4023         if (document()->page())
4024             m_activityToken = document()->page()->createActivityToken();
4025
4026         startPlaybackProgressTimer();
4027         m_playing = true;
4028
4029     } else { // Should not be playing right now
4030         if (!playerPaused)
4031             m_player->pause();
4032         refreshCachedTime();
4033
4034         m_playbackProgressTimer.stop();
4035         m_playing = false;
4036         double time = currentTime();
4037         if (time > m_lastSeekTime)
4038             addPlayedRange(m_lastSeekTime, time);
4039
4040         if (couldPlayIfEnoughData())
4041             prepareToPlay();
4042
4043         if (hasMediaControls())
4044             mediaControls()->playbackStopped();
4045         m_activityToken = nullptr;
4046     }
4047
4048     updateMediaController();
4049
4050     if (renderer())
4051         renderer()->updateFromElement();
4052 }
4053
4054 void HTMLMediaElement::setPausedInternal(bool b)
4055 {
4056     m_pausedInternal = b;
4057     updatePlayState();
4058 }
4059
4060 void HTMLMediaElement::stopPeriodicTimers()
4061 {
4062     m_progressEventTimer.stop();
4063     m_playbackProgressTimer.stop();
4064 }
4065
4066 void HTMLMediaElement::userCancelledLoad()
4067 {
4068     LOG(Media, "HTMLMediaElement::userCancelledLoad");
4069
4070     if (m_networkState == NETWORK_EMPTY || m_completelyLoaded)
4071         return;
4072
4073     // If the media data fetching process is aborted by the user:
4074
4075     // 1 - The user agent should cancel the fetching process.
4076     clearMediaPlayer(-1);
4077
4078     // 2 - Set the error attribute to a new MediaError object whose code attribute is set to MEDIA_ERR_ABORTED.
4079     m_error = MediaError::create(MediaError::MEDIA_ERR_ABORTED);
4080
4081     // 3 - Queue a task to fire a simple event named error at the media element.
4082     scheduleEvent(eventNames().abortEvent);
4083
4084 #if ENABLE(MEDIA_SOURCE)
4085     setSourceState(MediaSource::closedKeyword());