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