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