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