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