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