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