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