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