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