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