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