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