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