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