1 /*
2  * Copyright (C) 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #include "config.h"
27 
28 #if ENABLE(VIDEO)
29 #include "HTMLMediaElement.h"
30 
31 #include "Attribute.h"
32 #include "Chrome.h"
33 #include "ChromeClient.h"
34 #include "ClientRect.h"
35 #include "ClientRectList.h"
36 #include "ContentSecurityPolicy.h"
37 #include "ContentType.h"
38 #include "CSSPropertyNames.h"
39 #include "CSSValueKeywords.h"
40 #include "Event.h"
41 #include "EventNames.h"
42 #include "ExceptionCode.h"
43 #include "Frame.h"
44 #include "FrameLoader.h"
45 #include "FrameLoaderClient.h"
46 #include "FrameView.h"
47 #include "HTMLDocument.h"
48 #include "HTMLNames.h"
49 #include "HTMLSourceElement.h"
50 #include "HTMLVideoElement.h"
51 #include "Logging.h"
52 #include "MediaControls.h"
53 #include "MediaDocument.h"
54 #include "MediaError.h"
55 #include "MediaList.h"
56 #include "MediaPlayer.h"
57 #include "MediaQueryEvaluator.h"
58 #include "MouseEvent.h"
59 #include "MIMETypeRegistry.h"
60 #include "Page.h"
61 #include "RenderVideo.h"
62 #include "RenderView.h"
63 #include "ScriptEventListener.h"
64 #include "SecurityOrigin.h"
65 #include "Settings.h"
66 #include "ShadowRoot.h"
67 #include "TimeRanges.h"
68 #include <limits>
69 #include <wtf/CurrentTime.h>
70 #include <wtf/MathExtras.h>
71 #include <wtf/text/CString.h>
72 
73 #if USE(ACCELERATED_COMPOSITING)
74 #include "RenderView.h"
75 #include "RenderLayerCompositor.h"
76 #endif
77 
78 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
79 #include "RenderEmbeddedObject.h"
80 #include "Widget.h"
81 #endif
82 
83 using namespace std;
84 
85 namespace WebCore {
86 
87 #if !LOG_DISABLED
urlForLogging(const String & url)88 static String urlForLogging(const String& url)
89 {
90     static const unsigned maximumURLLengthForLogging = 128;
91 
92     if (url.length() < maximumURLLengthForLogging)
93         return url;
94     return url.substring(0, maximumURLLengthForLogging) + "...";
95 }
96 
boolString(bool val)97 static const char *boolString(bool val)
98 {
99     return val ? "true" : "false";
100 }
101 #endif
102 
103 #ifndef LOG_MEDIA_EVENTS
104 // Default to not logging events because so many are generated they can overwhelm the rest of
105 // the logging.
106 #define LOG_MEDIA_EVENTS 0
107 #endif
108 
109 #ifndef LOG_CACHED_TIME_WARNINGS
110 // Default to not logging warnings about excessive drift in the cached media time because it adds a
111 // fair amount of overhead and logging.
112 #define LOG_CACHED_TIME_WARNINGS 0
113 #endif
114 
115 static const float invalidMediaTime = -1;
116 
117 using namespace HTMLNames;
118 
HTMLMediaElement(const QualifiedName & tagName,Document * document)119 HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document* document)
120     : HTMLElement(tagName, document)
121     , ActiveDOMObject(document, this)
122     , m_loadTimer(this, &HTMLMediaElement::loadTimerFired)
123     , m_asyncEventTimer(this, &HTMLMediaElement::asyncEventTimerFired)
124     , m_progressEventTimer(this, &HTMLMediaElement::progressEventTimerFired)
125     , m_playbackProgressTimer(this, &HTMLMediaElement::playbackProgressTimerFired)
126     , m_playedTimeRanges()
127     , m_playbackRate(1.0f)
128     , m_defaultPlaybackRate(1.0f)
129     , m_webkitPreservesPitch(true)
130     , m_networkState(NETWORK_EMPTY)
131     , m_readyState(HAVE_NOTHING)
132     , m_readyStateMaximum(HAVE_NOTHING)
133     , m_volume(1.0f)
134     , m_lastSeekTime(0)
135     , m_previousProgress(0)
136     , m_previousProgressTime(numeric_limits<double>::max())
137     , m_lastTimeUpdateEventWallTime(0)
138     , m_lastTimeUpdateEventMovieTime(numeric_limits<float>::max())
139     , m_loadState(WaitingForSource)
140     , m_currentSourceNode(0)
141     , m_nextChildNodeToConsider(0)
142 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
143     , m_proxyWidget(0)
144 #endif
145     , m_restrictions(RequireUserGestureForFullScreenRestriction)
146     , m_preload(MediaPlayer::Auto)
147     , m_displayMode(Unknown)
148     , m_processingMediaPlayerCallback(0)
149     , m_cachedTime(invalidMediaTime)
150     , m_cachedTimeWallClockUpdateTime(0)
151     , m_minimumWallClockTimeToCacheMediaTime(0)
152     , m_playing(false)
153     , m_isWaitingUntilMediaCanStart(false)
154     , m_shouldDelayLoadEvent(false)
155     , m_haveFiredLoadedData(false)
156     , m_inActiveDocument(true)
157     , m_autoplaying(true)
158     , m_muted(false)
159     , m_paused(true)
160     , m_seeking(false)
161     , m_sentStalledEvent(false)
162     , m_sentEndEvent(false)
163     , m_pausedInternal(false)
164     , m_sendProgressEvents(true)
165     , m_isFullscreen(false)
166     , m_closedCaptionsVisible(false)
167 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
168     , m_needWidgetUpdate(false)
169 #endif
170     , m_dispatchingCanPlayEvent(false)
171     , m_loadInitiatedByUserGesture(false)
172     , m_completelyLoaded(false)
173     , m_havePreparedToPlay(false)
174 {
175     LOG(Media, "HTMLMediaElement::HTMLMediaElement");
176     document->registerForDocumentActivationCallbacks(this);
177     document->registerForMediaVolumeCallbacks(this);
178     document->registerForPrivateBrowsingStateChangedCallbacks(this);
179 }
180 
~HTMLMediaElement()181 HTMLMediaElement::~HTMLMediaElement()
182 {
183     LOG(Media, "HTMLMediaElement::~HTMLMediaElement");
184     if (m_isWaitingUntilMediaCanStart)
185         document()->removeMediaCanStartListener(this);
186     setShouldDelayLoadEvent(false);
187     document()->unregisterForDocumentActivationCallbacks(this);
188     document()->unregisterForMediaVolumeCallbacks(this);
189     document()->unregisterForPrivateBrowsingStateChangedCallbacks(this);
190 }
191 
willMoveToNewOwnerDocument()192 void HTMLMediaElement::willMoveToNewOwnerDocument()
193 {
194     if (m_isWaitingUntilMediaCanStart)
195         document()->removeMediaCanStartListener(this);
196     setShouldDelayLoadEvent(false);
197     document()->unregisterForDocumentActivationCallbacks(this);
198     document()->unregisterForMediaVolumeCallbacks(this);
199     HTMLElement::willMoveToNewOwnerDocument();
200 }
201 
didMoveToNewOwnerDocument()202 void HTMLMediaElement::didMoveToNewOwnerDocument()
203 {
204     if (m_isWaitingUntilMediaCanStart)
205         document()->addMediaCanStartListener(this);
206     if (m_readyState < HAVE_CURRENT_DATA)
207         setShouldDelayLoadEvent(true);
208     document()->registerForDocumentActivationCallbacks(this);
209     document()->registerForMediaVolumeCallbacks(this);
210     HTMLElement::didMoveToNewOwnerDocument();
211 }
212 
attributeChanged(Attribute * attr,bool preserveDecls)213 void HTMLMediaElement::attributeChanged(Attribute* attr, bool preserveDecls)
214 {
215     HTMLElement::attributeChanged(attr, preserveDecls);
216 
217     const QualifiedName& attrName = attr->name();
218     if (attrName == srcAttr) {
219         // Trigger a reload, as long as the 'src' attribute is present.
220         if (!getAttribute(srcAttr).isEmpty())
221             scheduleLoad();
222     }
223     else if (attrName == controlsAttr) {
224 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
225         if (controls()) {
226             if (!hasMediaControls()) {
227                 ensureMediaControls();
228                 mediaControls()->reset();
229             }
230             mediaControls()->show();
231         } else if (hasMediaControls())
232             mediaControls()->hide();
233 #else
234         if (m_player)
235             m_player->setControls(controls());
236 #endif
237     }
238 }
239 
parseMappedAttribute(Attribute * attr)240 void HTMLMediaElement::parseMappedAttribute(Attribute* attr)
241 {
242     const QualifiedName& attrName = attr->name();
243 
244     if (attrName == preloadAttr) {
245         String value = attr->value();
246 
247         if (equalIgnoringCase(value, "none"))
248             m_preload = MediaPlayer::None;
249         else if (equalIgnoringCase(value, "metadata"))
250             m_preload = MediaPlayer::MetaData;
251         else {
252             // The spec does not define an "invalid value default" but "auto" is suggested as the
253             // "missing value default", so use it for everything except "none" and "metadata"
254             m_preload = MediaPlayer::Auto;
255         }
256 
257         // The attribute must be ignored if the autoplay attribute is present
258         if (!autoplay() && m_player)
259             m_player->setPreload(m_preload);
260 
261     } else if (attrName == onabortAttr)
262         setAttributeEventListener(eventNames().abortEvent, createAttributeEventListener(this, attr));
263     else if (attrName == onbeforeloadAttr)
264         setAttributeEventListener(eventNames().beforeloadEvent, createAttributeEventListener(this, attr));
265     else if (attrName == oncanplayAttr)
266         setAttributeEventListener(eventNames().canplayEvent, createAttributeEventListener(this, attr));
267     else if (attrName == oncanplaythroughAttr)
268         setAttributeEventListener(eventNames().canplaythroughEvent, createAttributeEventListener(this, attr));
269     else if (attrName == ondurationchangeAttr)
270         setAttributeEventListener(eventNames().durationchangeEvent, createAttributeEventListener(this, attr));
271     else if (attrName == onemptiedAttr)
272         setAttributeEventListener(eventNames().emptiedEvent, createAttributeEventListener(this, attr));
273     else if (attrName == onendedAttr)
274         setAttributeEventListener(eventNames().endedEvent, createAttributeEventListener(this, attr));
275     else if (attrName == onerrorAttr)
276         setAttributeEventListener(eventNames().errorEvent, createAttributeEventListener(this, attr));
277     else if (attrName == onloadeddataAttr)
278         setAttributeEventListener(eventNames().loadeddataEvent, createAttributeEventListener(this, attr));
279     else if (attrName == onloadedmetadataAttr)
280         setAttributeEventListener(eventNames().loadedmetadataEvent, createAttributeEventListener(this, attr));
281     else if (attrName == onloadstartAttr)
282         setAttributeEventListener(eventNames().loadstartEvent, createAttributeEventListener(this, attr));
283     else if (attrName == onpauseAttr)
284         setAttributeEventListener(eventNames().pauseEvent, createAttributeEventListener(this, attr));
285     else if (attrName == onplayAttr)
286         setAttributeEventListener(eventNames().playEvent, createAttributeEventListener(this, attr));
287     else if (attrName == onplayingAttr)
288         setAttributeEventListener(eventNames().playingEvent, createAttributeEventListener(this, attr));
289     else if (attrName == onprogressAttr)
290         setAttributeEventListener(eventNames().progressEvent, createAttributeEventListener(this, attr));
291     else if (attrName == onratechangeAttr)
292         setAttributeEventListener(eventNames().ratechangeEvent, createAttributeEventListener(this, attr));
293     else if (attrName == onseekedAttr)
294         setAttributeEventListener(eventNames().seekedEvent, createAttributeEventListener(this, attr));
295     else if (attrName == onseekingAttr)
296         setAttributeEventListener(eventNames().seekingEvent, createAttributeEventListener(this, attr));
297     else if (attrName == onstalledAttr)
298         setAttributeEventListener(eventNames().stalledEvent, createAttributeEventListener(this, attr));
299     else if (attrName == onsuspendAttr)
300         setAttributeEventListener(eventNames().suspendEvent, createAttributeEventListener(this, attr));
301     else if (attrName == ontimeupdateAttr)
302         setAttributeEventListener(eventNames().timeupdateEvent, createAttributeEventListener(this, attr));
303     else if (attrName == onvolumechangeAttr)
304         setAttributeEventListener(eventNames().volumechangeEvent, createAttributeEventListener(this, attr));
305     else if (attrName == onwaitingAttr)
306         setAttributeEventListener(eventNames().waitingEvent, createAttributeEventListener(this, attr));
307     else if (attrName == onwebkitbeginfullscreenAttr)
308         setAttributeEventListener(eventNames().webkitbeginfullscreenEvent, createAttributeEventListener(this, attr));
309     else if (attrName == onwebkitendfullscreenAttr)
310         setAttributeEventListener(eventNames().webkitendfullscreenEvent, createAttributeEventListener(this, attr));
311     else
312         HTMLElement::parseMappedAttribute(attr);
313 }
314 
rendererIsNeeded(RenderStyle * style)315 bool HTMLMediaElement::rendererIsNeeded(RenderStyle* style)
316 {
317 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
318     UNUSED_PARAM(style);
319     Frame* frame = document()->frame();
320     if (!frame)
321         return false;
322 
323     return true;
324 #else
325     return controls() ? HTMLElement::rendererIsNeeded(style) : false;
326 #endif
327 }
328 
createRenderer(RenderArena * arena,RenderStyle *)329 RenderObject* HTMLMediaElement::createRenderer(RenderArena* arena, RenderStyle*)
330 {
331 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
332     // Setup the renderer if we already have a proxy widget.
333     RenderEmbeddedObject* mediaRenderer = new (arena) RenderEmbeddedObject(this);
334     if (m_proxyWidget) {
335         mediaRenderer->setWidget(m_proxyWidget);
336 
337         Frame* frame = document()->frame();
338         FrameLoader* loader = frame ? frame->loader() : 0;
339         if (loader)
340             loader->showMediaPlayerProxyPlugin(m_proxyWidget.get());
341     }
342     return mediaRenderer;
343 #else
344     return new (arena) RenderMedia(this);
345 #endif
346 }
347 
insertedIntoDocument()348 void HTMLMediaElement::insertedIntoDocument()
349 {
350     LOG(Media, "HTMLMediaElement::removedFromDocument");
351     HTMLElement::insertedIntoDocument();
352     if (!getAttribute(srcAttr).isEmpty() && m_networkState == NETWORK_EMPTY)
353         scheduleLoad();
354 }
355 
removedFromDocument()356 void HTMLMediaElement::removedFromDocument()
357 {
358     LOG(Media, "HTMLMediaElement::removedFromDocument");
359     if (m_networkState > NETWORK_EMPTY)
360         pause(processingUserGesture());
361     if (m_isFullscreen)
362         exitFullscreen();
363     HTMLElement::removedFromDocument();
364 }
365 
attach()366 void HTMLMediaElement::attach()
367 {
368     ASSERT(!attached());
369 
370 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
371     m_needWidgetUpdate = true;
372 #endif
373 
374     HTMLElement::attach();
375 
376     if (renderer())
377         renderer()->updateFromElement();
378 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
379     else if (m_proxyWidget) {
380         Frame* frame = document()->frame();
381         FrameLoader* loader = frame ? frame->loader() : 0;
382         if (loader)
383             loader->hideMediaPlayerProxyPlugin(m_proxyWidget.get());
384     }
385 #endif
386 }
387 
recalcStyle(StyleChange change)388 void HTMLMediaElement::recalcStyle(StyleChange change)
389 {
390     HTMLElement::recalcStyle(change);
391 
392     if (renderer())
393         renderer()->updateFromElement();
394 }
395 
scheduleLoad()396 void HTMLMediaElement::scheduleLoad()
397 {
398     LOG(Media, "HTMLMediaElement::scheduleLoad");
399 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
400     createMediaPlayerProxy();
401 #endif
402 
403     if (m_loadTimer.isActive())
404         return;
405     prepareForLoad();
406     m_loadTimer.startOneShot(0);
407 }
408 
scheduleNextSourceChild()409 void HTMLMediaElement::scheduleNextSourceChild()
410 {
411     // Schedule the timer to try the next <source> element WITHOUT resetting state ala prepareForLoad.
412     m_loadTimer.startOneShot(0);
413 }
414 
scheduleEvent(const AtomicString & eventName)415 void HTMLMediaElement::scheduleEvent(const AtomicString& eventName)
416 {
417 #if LOG_MEDIA_EVENTS
418     LOG(Media, "HTMLMediaElement::scheduleEvent - scheduling '%s'", eventName.string().ascii().data());
419 #endif
420     m_pendingEvents.append(Event::create(eventName, false, true));
421     if (!m_asyncEventTimer.isActive())
422         m_asyncEventTimer.startOneShot(0);
423 }
424 
asyncEventTimerFired(Timer<HTMLMediaElement> *)425 void HTMLMediaElement::asyncEventTimerFired(Timer<HTMLMediaElement>*)
426 {
427     Vector<RefPtr<Event> > pendingEvents;
428     ExceptionCode ec = 0;
429 
430     m_pendingEvents.swap(pendingEvents);
431     unsigned count = pendingEvents.size();
432     for (unsigned ndx = 0; ndx < count; ++ndx) {
433 #if LOG_MEDIA_EVENTS
434         LOG(Media, "HTMLMediaElement::asyncEventTimerFired - dispatching '%s'", pendingEvents[ndx]->type().string().ascii().data());
435 #endif
436         if (pendingEvents[ndx]->type() == eventNames().canplayEvent) {
437             m_dispatchingCanPlayEvent = true;
438             dispatchEvent(pendingEvents[ndx].release(), ec);
439             m_dispatchingCanPlayEvent = false;
440         } else
441             dispatchEvent(pendingEvents[ndx].release(), ec);
442     }
443 }
444 
loadTimerFired(Timer<HTMLMediaElement> *)445 void HTMLMediaElement::loadTimerFired(Timer<HTMLMediaElement>*)
446 {
447     if (m_loadState == LoadingFromSourceElement)
448         loadNextSourceChild();
449     else
450         loadInternal();
451 }
452 
error() const453 PassRefPtr<MediaError> HTMLMediaElement::error() const
454 {
455     return m_error;
456 }
457 
setSrc(const String & url)458 void HTMLMediaElement::setSrc(const String& url)
459 {
460     setAttribute(srcAttr, url);
461 }
462 
currentSrc() const463 String HTMLMediaElement::currentSrc() const
464 {
465     return m_currentSrc;
466 }
467 
networkState() const468 HTMLMediaElement::NetworkState HTMLMediaElement::networkState() const
469 {
470     return m_networkState;
471 }
472 
canPlayType(const String & mimeType) const473 String HTMLMediaElement::canPlayType(const String& mimeType) const
474 {
475     MediaPlayer::SupportsType support = MediaPlayer::supportsType(ContentType(mimeType));
476     String canPlay;
477 
478     // 4.8.10.3
479     switch (support)
480     {
481         case MediaPlayer::IsNotSupported:
482             canPlay = "";
483             break;
484         case MediaPlayer::MayBeSupported:
485             canPlay = "maybe";
486             break;
487         case MediaPlayer::IsSupported:
488             canPlay = "probably";
489             break;
490     }
491 
492     LOG(Media, "HTMLMediaElement::canPlayType(%s) -> %s", mimeType.utf8().data(), canPlay.utf8().data());
493 
494     return canPlay;
495 }
496 
load(bool isUserGesture,ExceptionCode & ec)497 void HTMLMediaElement::load(bool isUserGesture, ExceptionCode& ec)
498 {
499     LOG(Media, "HTMLMediaElement::load(isUserGesture : %s)", boolString(isUserGesture));
500 
501     if (m_restrictions & RequireUserGestureForLoadRestriction && !isUserGesture)
502         ec = INVALID_STATE_ERR;
503     else {
504         m_loadInitiatedByUserGesture = isUserGesture;
505         prepareForLoad();
506         loadInternal();
507     }
508 }
509 
prepareForLoad()510 void HTMLMediaElement::prepareForLoad()
511 {
512     LOG(Media, "HTMLMediaElement::prepareForLoad");
513 
514     // Perform the cleanup required for the resource load algorithm to run.
515     stopPeriodicTimers();
516     m_loadTimer.stop();
517     m_sentEndEvent = false;
518     m_sentStalledEvent = false;
519     m_haveFiredLoadedData = false;
520     m_completelyLoaded = false;
521     m_havePreparedToPlay = false;
522     m_displayMode = Unknown;
523 
524     // 1 - Abort any already-running instance of the resource selection algorithm for this element.
525     m_loadState = WaitingForSource;
526     m_currentSourceNode = 0;
527 
528     // 2 - If there are any tasks from the media element's media element event task source in
529     // one of the task queues, then remove those tasks.
530     cancelPendingEventsAndCallbacks();
531 
532     // 3 - If the media element's networkState is set to NETWORK_LOADING or NETWORK_IDLE, queue
533     // a task to fire a simple event named abort at the media element.
534     if (m_networkState == NETWORK_LOADING || m_networkState == NETWORK_IDLE)
535         scheduleEvent(eventNames().abortEvent);
536 
537 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
538     m_player = MediaPlayer::create(this);
539 #else
540     if (m_player)
541         m_player->cancelLoad();
542     else
543         createMediaPlayerProxy();
544 #endif
545 
546     // 4 - If the media element's networkState is not set to NETWORK_EMPTY, then run these substeps
547     if (m_networkState != NETWORK_EMPTY) {
548         m_networkState = NETWORK_EMPTY;
549         m_readyState = HAVE_NOTHING;
550         m_readyStateMaximum = HAVE_NOTHING;
551         refreshCachedTime();
552         m_paused = true;
553         m_seeking = false;
554         invalidateCachedTime();
555         scheduleEvent(eventNames().emptiedEvent);
556     }
557 
558     // 5 - Set the playbackRate attribute to the value of the defaultPlaybackRate attribute.
559     setPlaybackRate(defaultPlaybackRate());
560 
561     // 6 - Set the error attribute to null and the autoplaying flag to true.
562     m_error = 0;
563     m_autoplaying = true;
564 
565     // 7 - Invoke the media element's resource selection algorithm.
566 
567     // 8 - Note: Playback of any previously playing media resource for this element stops.
568 
569     // The resource selection algorithm
570     // 1 - Set the networkState to NETWORK_NO_SOURCE
571     m_networkState = NETWORK_NO_SOURCE;
572 
573     // 2 - Asynchronously await a stable state.
574 
575     m_playedTimeRanges = TimeRanges::create();
576     m_lastSeekTime = 0;
577     m_closedCaptionsVisible = false;
578 
579     // The spec doesn't say to block the load event until we actually run the asynchronous section
580     // algorithm, but do it now because we won't start that until after the timer fires and the
581     // event may have already fired by then.
582     setShouldDelayLoadEvent(true);
583 }
584 
loadInternal()585 void HTMLMediaElement::loadInternal()
586 {
587     // If we can't start a load right away, start it later.
588     Page* page = document()->page();
589     if (page && !page->canStartMedia()) {
590         if (m_isWaitingUntilMediaCanStart)
591             return;
592         document()->addMediaCanStartListener(this);
593         m_isWaitingUntilMediaCanStart = true;
594         return;
595     }
596 
597     selectMediaResource();
598 }
599 
selectMediaResource()600 void HTMLMediaElement::selectMediaResource()
601 {
602     LOG(Media, "HTMLMediaElement::selectMediaResource");
603 
604     enum Mode { attribute, children };
605     Mode mode = attribute;
606 
607     // 3 - ... the media element has neither a src attribute ...
608     if (!hasAttribute(srcAttr)) {
609         // ... nor a source element child: ...
610         Node* node;
611         for (node = firstChild(); node; node = node->nextSibling()) {
612             if (node->hasTagName(sourceTag))
613                 break;
614         }
615 
616         if (!node) {
617             m_loadState = WaitingForSource;
618             setShouldDelayLoadEvent(false);
619 
620             // ... set the networkState to NETWORK_EMPTY, and abort these steps
621             m_networkState = NETWORK_EMPTY;
622 
623             LOG(Media, "HTMLMediaElement::selectMediaResource, nothing to load");
624             return;
625         }
626 
627         mode = children;
628     }
629 
630     // 4 - Set the media element's delaying-the-load-event flag to true (this delays the load event),
631     // and set its networkState to NETWORK_LOADING.
632     setShouldDelayLoadEvent(true);
633     m_networkState = NETWORK_LOADING;
634 
635     // 5
636     scheduleEvent(eventNames().loadstartEvent);
637 
638     // 6 - If mode is attribute, then run these substeps
639     if (mode == attribute) {
640         // If the src attribute's value is the empty string ... jump down to the failed step below
641         KURL mediaURL = getNonEmptyURLAttribute(srcAttr);
642         if (mediaURL.isEmpty()) {
643             noneSupported();
644             LOG(Media, "HTMLMediaElement::selectMediaResource, empty 'src'");
645             return;
646         }
647 
648         if (isSafeToLoadURL(mediaURL, Complain) && dispatchBeforeLoadEvent(mediaURL.string())) {
649             ContentType contentType("");
650             m_loadState = LoadingFromSrcAttr;
651             loadResource(mediaURL, contentType);
652         } else
653             noneSupported();
654 
655         LOG(Media, "HTMLMediaElement::selectMediaResource, 'src' not used");
656         return;
657     }
658 
659     // Otherwise, the source elements will be used
660     m_currentSourceNode = 0;
661     loadNextSourceChild();
662 }
663 
loadNextSourceChild()664 void HTMLMediaElement::loadNextSourceChild()
665 {
666     ContentType contentType("");
667     KURL mediaURL = selectNextSourceChild(&contentType, Complain);
668     if (!mediaURL.isValid()) {
669         waitForSourceChange();
670         return;
671     }
672 
673 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
674     // Recreate the media player for the new url
675     m_player = MediaPlayer::create(this);
676 #endif
677 
678     m_loadState = LoadingFromSourceElement;
679     loadResource(mediaURL, contentType);
680 }
681 
loadResource(const KURL & initialURL,ContentType & contentType)682 void HTMLMediaElement::loadResource(const KURL& initialURL, ContentType& contentType)
683 {
684     ASSERT(isSafeToLoadURL(initialURL, Complain));
685 
686     LOG(Media, "HTMLMediaElement::loadResource(%s, %s)", urlForLogging(initialURL.string()).utf8().data(), contentType.raw().utf8().data());
687 
688     Frame* frame = document()->frame();
689     if (!frame)
690         return;
691     FrameLoader* loader = frame->loader();
692     if (!loader)
693         return;
694 
695     KURL url(initialURL);
696     if (!loader->willLoadMediaElementURL(url))
697         return;
698 
699     // The resource fetch algorithm
700     m_networkState = NETWORK_LOADING;
701 
702     m_currentSrc = url;
703 
704     LOG(Media, "HTMLMediaElement::loadResource - m_currentSrc -> %s", urlForLogging(m_currentSrc).utf8().data());
705 
706     if (m_sendProgressEvents)
707         startProgressEventTimer();
708 
709     Settings* settings = document()->settings();
710     bool privateMode = !settings || settings->privateBrowsingEnabled();
711     m_player->setPrivateBrowsingMode(privateMode);
712 
713     // Reset display mode to force a recalculation of what to show because we are resetting the player.
714     setDisplayMode(Unknown);
715 
716     if (!autoplay())
717         m_player->setPreload(m_preload);
718     m_player->setPreservesPitch(m_webkitPreservesPitch);
719     updateVolume();
720 
721     m_player->load(m_currentSrc, contentType);
722 
723     // If there is no poster to display, allow the media engine to render video frames as soon as
724     // they are available.
725     updateDisplayState();
726 
727     if (renderer())
728         renderer()->updateFromElement();
729 }
730 
isSafeToLoadURL(const KURL & url,InvalidSourceAction actionIfInvalid)731 bool HTMLMediaElement::isSafeToLoadURL(const KURL& url, InvalidSourceAction actionIfInvalid)
732 {
733     if (!url.isValid()) {
734         LOG(Media, "HTMLMediaElement::isSafeToLoadURL(%s) -> FALSE because url is invalid", urlForLogging(url.string()).utf8().data());
735         return false;
736     }
737 
738     Frame* frame = document()->frame();
739     if (!frame || !document()->securityOrigin()->canDisplay(url)) {
740         if (actionIfInvalid == Complain)
741             FrameLoader::reportLocalLoadFailed(frame, url.string());
742         LOG(Media, "HTMLMediaElement::isSafeToLoadURL(%s) -> FALSE rejected by SecurityOrigin", urlForLogging(url.string()).utf8().data());
743         return false;
744     }
745 
746     if (!document()->contentSecurityPolicy()->allowMediaFromSource(url))
747         return false;
748 
749     return true;
750 }
751 
startProgressEventTimer()752 void HTMLMediaElement::startProgressEventTimer()
753 {
754     if (m_progressEventTimer.isActive())
755         return;
756 
757     m_previousProgressTime = WTF::currentTime();
758     m_previousProgress = 0;
759     // 350ms is not magic, it is in the spec!
760     m_progressEventTimer.startRepeating(0.350);
761 }
762 
waitForSourceChange()763 void HTMLMediaElement::waitForSourceChange()
764 {
765     LOG(Media, "HTMLMediaElement::waitForSourceChange");
766 
767     stopPeriodicTimers();
768     m_loadState = WaitingForSource;
769 
770     // 6.17 - Waiting: Set the element's networkState attribute to the NETWORK_NO_SOURCE value
771     m_networkState = NETWORK_NO_SOURCE;
772 
773     // 6.18 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
774     setShouldDelayLoadEvent(false);
775 
776     updateDisplayState();
777 
778     if (renderer())
779         renderer()->updateFromElement();
780 }
781 
noneSupported()782 void HTMLMediaElement::noneSupported()
783 {
784     LOG(Media, "HTMLMediaElement::noneSupported");
785 
786     stopPeriodicTimers();
787     m_loadState = WaitingForSource;
788     m_currentSourceNode = 0;
789 
790     // 5 - Reaching this step indicates that either the URL failed to resolve, or the media
791     // resource failed to load. Set the error attribute to a new MediaError object whose
792     // code attribute is set to MEDIA_ERR_SRC_NOT_SUPPORTED.
793     m_error = MediaError::create(MediaError::MEDIA_ERR_SRC_NOT_SUPPORTED);
794 
795     // 6 - Set the element's networkState attribute to the NETWORK_NO_SOURCE value.
796     m_networkState = NETWORK_NO_SOURCE;
797 
798     // 7 - Queue a task to fire a progress event called error at the media element, in
799     // the context of the fetching process that was used to try to obtain the media
800     // resource in the resource fetch algorithm.
801     scheduleEvent(eventNames().errorEvent);
802 
803     // 8 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
804     setShouldDelayLoadEvent(false);
805 
806     // 9 -Abort these steps. Until the load() method is invoked, the element won't attempt to load another resource.
807 
808     updateDisplayState();
809 
810     if (renderer())
811         renderer()->updateFromElement();
812 }
813 
mediaEngineError(PassRefPtr<MediaError> err)814 void HTMLMediaElement::mediaEngineError(PassRefPtr<MediaError> err)
815 {
816     LOG(Media, "HTMLMediaElement::mediaEngineError(%d)", static_cast<int>(err->code()));
817 
818     // 1 - The user agent should cancel the fetching process.
819     stopPeriodicTimers();
820     m_loadState = WaitingForSource;
821 
822     // 2 - Set the error attribute to a new MediaError object whose code attribute is
823     // set to MEDIA_ERR_NETWORK/MEDIA_ERR_DECODE.
824     m_error = err;
825 
826     // 3 - Queue a task to fire a simple event named error at the media element.
827     scheduleEvent(eventNames().errorEvent);
828 
829     // 4 - Set the element's networkState attribute to the NETWORK_EMPTY value and queue a
830     // task to fire a simple event called emptied at the element.
831     m_networkState = NETWORK_EMPTY;
832     scheduleEvent(eventNames().emptiedEvent);
833 
834     // 5 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
835     setShouldDelayLoadEvent(false);
836 
837     // 6 - Abort the overall resource selection algorithm.
838     m_currentSourceNode = 0;
839 }
840 
cancelPendingEventsAndCallbacks()841 void HTMLMediaElement::cancelPendingEventsAndCallbacks()
842 {
843     LOG(Media, "HTMLMediaElement::cancelPendingEventsAndCallbacks");
844 
845     m_pendingEvents.clear();
846 
847     for (Node* node = firstChild(); node; node = node->nextSibling()) {
848         if (node->hasTagName(sourceTag))
849             static_cast<HTMLSourceElement*>(node)->cancelPendingErrorEvent();
850     }
851 }
852 
mediaPlayerOwningDocument()853 Document* HTMLMediaElement::mediaPlayerOwningDocument()
854 {
855     Document* d = document();
856 
857     if (!d)
858         d = ownerDocument();
859 
860     return d;
861 }
862 
mediaPlayerNetworkStateChanged(MediaPlayer *)863 void HTMLMediaElement::mediaPlayerNetworkStateChanged(MediaPlayer*)
864 {
865     beginProcessingMediaPlayerCallback();
866     setNetworkState(m_player->networkState());
867     endProcessingMediaPlayerCallback();
868 }
869 
setNetworkState(MediaPlayer::NetworkState state)870 void HTMLMediaElement::setNetworkState(MediaPlayer::NetworkState state)
871 {
872     LOG(Media, "HTMLMediaElement::setNetworkState(%d) - current state is %d", static_cast<int>(state), static_cast<int>(m_networkState));
873 
874     if (state == MediaPlayer::Empty) {
875         // Just update the cached state and leave, we can't do anything.
876         m_networkState = NETWORK_EMPTY;
877         return;
878     }
879 
880     if (state == MediaPlayer::FormatError || state == MediaPlayer::NetworkError || state == MediaPlayer::DecodeError) {
881         stopPeriodicTimers();
882 
883         // If we failed while trying to load a <source> element, the movie was never parsed, and there are more
884         // <source> children, schedule the next one
885         if (m_readyState < HAVE_METADATA && m_loadState == LoadingFromSourceElement) {
886 
887             if (m_currentSourceNode)
888                 m_currentSourceNode->scheduleErrorEvent();
889             else
890                 LOG(Media, "HTMLMediaElement::setNetworkState - error event not sent, <source> was removed");
891 
892             if (havePotentialSourceChild()) {
893                 LOG(Media, "HTMLMediaElement::setNetworkState - scheduling next <source>");
894                 scheduleNextSourceChild();
895             } else {
896                 LOG(Media, "HTMLMediaElement::setNetworkState - no more <source> elements, waiting");
897                 waitForSourceChange();
898             }
899 
900             return;
901         }
902 
903         if (state == MediaPlayer::NetworkError)
904             mediaEngineError(MediaError::create(MediaError::MEDIA_ERR_NETWORK));
905         else if (state == MediaPlayer::DecodeError)
906             mediaEngineError(MediaError::create(MediaError::MEDIA_ERR_DECODE));
907         else if (state == MediaPlayer::FormatError && m_loadState == LoadingFromSrcAttr)
908             noneSupported();
909 
910         updateDisplayState();
911         if (hasMediaControls())
912             mediaControls()->reportedError();
913         return;
914     }
915 
916     if (state == MediaPlayer::Idle) {
917         if (m_networkState > NETWORK_IDLE) {
918             m_progressEventTimer.stop();
919             scheduleEvent(eventNames().suspendEvent);
920             setShouldDelayLoadEvent(false);
921         }
922         m_networkState = NETWORK_IDLE;
923     }
924 
925     if (state == MediaPlayer::Loading) {
926         if (m_networkState < NETWORK_LOADING || m_networkState == NETWORK_NO_SOURCE)
927             startProgressEventTimer();
928         m_networkState = NETWORK_LOADING;
929     }
930 
931     if (state == MediaPlayer::Loaded) {
932         if (m_networkState != NETWORK_IDLE) {
933             m_progressEventTimer.stop();
934 
935             // Schedule one last progress event so we guarantee that at least one is fired
936             // for files that load very quickly.
937             scheduleEvent(eventNames().progressEvent);
938         }
939         m_networkState = NETWORK_IDLE;
940         m_completelyLoaded = true;
941     }
942 
943     if (hasMediaControls())
944         mediaControls()->updateStatusDisplay();
945 }
946 
mediaPlayerReadyStateChanged(MediaPlayer *)947 void HTMLMediaElement::mediaPlayerReadyStateChanged(MediaPlayer*)
948 {
949     beginProcessingMediaPlayerCallback();
950 
951     setReadyState(m_player->readyState());
952 
953     endProcessingMediaPlayerCallback();
954 }
955 
setReadyState(MediaPlayer::ReadyState state)956 void HTMLMediaElement::setReadyState(MediaPlayer::ReadyState state)
957 {
958     LOG(Media, "HTMLMediaElement::setReadyState(%d) - current state is %d,", static_cast<int>(state), static_cast<int>(m_readyState));
959 
960     // Set "wasPotentiallyPlaying" BEFORE updating m_readyState, potentiallyPlaying() uses it
961     bool wasPotentiallyPlaying = potentiallyPlaying();
962 
963     ReadyState oldState = m_readyState;
964     m_readyState = static_cast<ReadyState>(state);
965 
966     if (m_readyState == oldState)
967         return;
968 
969     if (oldState > m_readyStateMaximum)
970         m_readyStateMaximum = oldState;
971 
972     if (m_networkState == NETWORK_EMPTY)
973         return;
974 
975     if (m_seeking) {
976         // 4.8.10.9, step 11
977         if (wasPotentiallyPlaying && m_readyState < HAVE_FUTURE_DATA)
978             scheduleEvent(eventNames().waitingEvent);
979 
980         // 4.8.10.10 step 14 & 15.
981         if (m_readyState >= HAVE_CURRENT_DATA)
982             finishSeek();
983     } else {
984         if (wasPotentiallyPlaying && m_readyState < HAVE_FUTURE_DATA) {
985             // 4.8.10.8
986             scheduleTimeupdateEvent(false);
987             scheduleEvent(eventNames().waitingEvent);
988         }
989     }
990 
991     if (m_readyState >= HAVE_METADATA && oldState < HAVE_METADATA) {
992         scheduleEvent(eventNames().durationchangeEvent);
993         scheduleEvent(eventNames().loadedmetadataEvent);
994         if (hasMediaControls())
995             mediaControls()->loadedMetadata();
996         if (renderer())
997             renderer()->updateFromElement();
998     }
999 
1000     bool shouldUpdateDisplayState = false;
1001 
1002     if (m_readyState >= HAVE_CURRENT_DATA && oldState < HAVE_CURRENT_DATA && !m_haveFiredLoadedData) {
1003         m_haveFiredLoadedData = true;
1004         shouldUpdateDisplayState = true;
1005         scheduleEvent(eventNames().loadeddataEvent);
1006         setShouldDelayLoadEvent(false);
1007     }
1008 
1009     bool isPotentiallyPlaying = potentiallyPlaying();
1010     if (m_readyState == HAVE_FUTURE_DATA && oldState <= HAVE_CURRENT_DATA) {
1011         scheduleEvent(eventNames().canplayEvent);
1012         if (isPotentiallyPlaying)
1013             scheduleEvent(eventNames().playingEvent);
1014         shouldUpdateDisplayState = true;
1015     }
1016 
1017     if (m_readyState == HAVE_ENOUGH_DATA && oldState < HAVE_ENOUGH_DATA) {
1018         if (oldState <= HAVE_CURRENT_DATA)
1019             scheduleEvent(eventNames().canplayEvent);
1020 
1021         scheduleEvent(eventNames().canplaythroughEvent);
1022 
1023         if (isPotentiallyPlaying && oldState <= HAVE_CURRENT_DATA)
1024             scheduleEvent(eventNames().playingEvent);
1025 
1026         if (m_autoplaying && m_paused && autoplay()) {
1027             m_paused = false;
1028             invalidateCachedTime();
1029             scheduleEvent(eventNames().playEvent);
1030             scheduleEvent(eventNames().playingEvent);
1031         }
1032 
1033         shouldUpdateDisplayState = true;
1034     }
1035 
1036     if (shouldUpdateDisplayState) {
1037         updateDisplayState();
1038         if (hasMediaControls())
1039             mediaControls()->updateStatusDisplay();
1040     }
1041 
1042     updatePlayState();
1043 }
1044 
progressEventTimerFired(Timer<HTMLMediaElement> *)1045 void HTMLMediaElement::progressEventTimerFired(Timer<HTMLMediaElement>*)
1046 {
1047     ASSERT(m_player);
1048     if (m_networkState != NETWORK_LOADING)
1049         return;
1050 
1051     unsigned progress = m_player->bytesLoaded();
1052     double time = WTF::currentTime();
1053     double timedelta = time - m_previousProgressTime;
1054 
1055     if (progress == m_previousProgress) {
1056         if (timedelta > 3.0 && !m_sentStalledEvent) {
1057             scheduleEvent(eventNames().stalledEvent);
1058             m_sentStalledEvent = true;
1059             setShouldDelayLoadEvent(false);
1060         }
1061     } else {
1062         scheduleEvent(eventNames().progressEvent);
1063         m_previousProgress = progress;
1064         m_previousProgressTime = time;
1065         m_sentStalledEvent = false;
1066         if (renderer())
1067             renderer()->updateFromElement();
1068     }
1069 }
1070 
rewind(float timeDelta)1071 void HTMLMediaElement::rewind(float timeDelta)
1072 {
1073     LOG(Media, "HTMLMediaElement::rewind(%f)", timeDelta);
1074 
1075     ExceptionCode e;
1076     setCurrentTime(max(currentTime() - timeDelta, minTimeSeekable()), e);
1077 }
1078 
returnToRealtime()1079 void HTMLMediaElement::returnToRealtime()
1080 {
1081     LOG(Media, "HTMLMediaElement::returnToRealtime");
1082     ExceptionCode e;
1083     setCurrentTime(maxTimeSeekable(), e);
1084 }
1085 
addPlayedRange(float start,float end)1086 void HTMLMediaElement::addPlayedRange(float start, float end)
1087 {
1088     LOG(Media, "HTMLMediaElement::addPlayedRange(%f, %f)", start, end);
1089     if (!m_playedTimeRanges)
1090         m_playedTimeRanges = TimeRanges::create();
1091     m_playedTimeRanges->add(start, end);
1092 }
1093 
supportsSave() const1094 bool HTMLMediaElement::supportsSave() const
1095 {
1096     return m_player ? m_player->supportsSave() : false;
1097 }
1098 
prepareToPlay()1099 void HTMLMediaElement::prepareToPlay()
1100 {
1101     if (m_havePreparedToPlay)
1102         return;
1103     m_havePreparedToPlay = true;
1104     m_player->prepareToPlay();
1105 }
1106 
seek(float time,ExceptionCode & ec)1107 void HTMLMediaElement::seek(float time, ExceptionCode& ec)
1108 {
1109     LOG(Media, "HTMLMediaElement::seek(%f)", time);
1110 
1111     // 4.8.9.9 Seeking
1112 
1113     // 1 - If the media element's readyState is HAVE_NOTHING, then raise an INVALID_STATE_ERR exception.
1114     if (m_readyState == HAVE_NOTHING || !m_player) {
1115         ec = INVALID_STATE_ERR;
1116         return;
1117     }
1118 
1119     // If the media engine has been told to postpone loading data, let it go ahead now.
1120     if (m_preload < MediaPlayer::Auto && m_readyState < HAVE_FUTURE_DATA)
1121         prepareToPlay();
1122 
1123     // Get the current time before setting m_seeking, m_lastSeekTime is returned once it is set.
1124     refreshCachedTime();
1125     float now = currentTime();
1126 
1127     // 2 - If the element's seeking IDL attribute is true, then another instance of this algorithm is
1128     // already running. Abort that other instance of the algorithm without waiting for the step that
1129     // it is running to complete.
1130     // Nothing specific to be done here.
1131 
1132     // 3 - Set the seeking IDL attribute to true.
1133     // The flag will be cleared when the engine tells us the time has actually changed.
1134     m_seeking = true;
1135 
1136     // 5 - If the new playback position is later than the end of the media resource, then let it be the end
1137     // of the media resource instead.
1138     time = min(time, duration());
1139 
1140     // 6 - If the new playback position is less than the earliest possible position, let it be that position instead.
1141     float earliestTime = m_player->startTime();
1142     time = max(time, earliestTime);
1143 
1144     // Ask the media engine for the time value in the movie's time scale before comparing with current time. This
1145     // is necessary because if the seek time is not equal to currentTime but the delta is less than the movie's
1146     // time scale, we will ask the media engine to "seek" to the current movie time, which may be a noop and
1147     // not generate a timechanged callback. This means m_seeking will never be cleared and we will never
1148     // fire a 'seeked' event.
1149 #if !LOG_DISABLED
1150     float mediaTime = m_player->mediaTimeForTimeValue(time);
1151     if (time != mediaTime)
1152         LOG(Media, "HTMLMediaElement::seek(%f) - media timeline equivalent is %f", time, mediaTime);
1153 #endif
1154     time = m_player->mediaTimeForTimeValue(time);
1155 
1156     // 7 - If the (possibly now changed) new playback position is not in one of the ranges given in the
1157     // seekable attribute, then let it be the position in one of the ranges given in the seekable attribute
1158     // that is the nearest to the new playback position. ... If there are no ranges given in the seekable
1159     // attribute then set the seeking IDL attribute to false and abort these steps.
1160     RefPtr<TimeRanges> seekableRanges = seekable();
1161 
1162     // Short circuit seeking to the current time by just firing the events if no seek is required.
1163     // Don't skip calling the media engine if we are in poster mode because a seek should always
1164     // cancel poster display.
1165     bool noSeekRequired = !seekableRanges->length() || (time == now && displayMode() != Poster);
1166     if (noSeekRequired) {
1167         if (time == now) {
1168             scheduleEvent(eventNames().seekingEvent);
1169             scheduleTimeupdateEvent(false);
1170             scheduleEvent(eventNames().seekedEvent);
1171         }
1172         m_seeking = false;
1173         return;
1174     }
1175     time = seekableRanges->nearest(time);
1176 
1177     if (m_playing) {
1178         if (m_lastSeekTime < now)
1179             addPlayedRange(m_lastSeekTime, now);
1180     }
1181     m_lastSeekTime = time;
1182     m_sentEndEvent = false;
1183 
1184     // 8 - Set the current playback position to the given new playback position
1185     m_player->seek(time);
1186 
1187     // 9 - Queue a task to fire a simple event named seeking at the element.
1188     scheduleEvent(eventNames().seekingEvent);
1189 
1190     // 10 - Queue a task to fire a simple event named timeupdate at the element.
1191     scheduleTimeupdateEvent(false);
1192 
1193     // 11-15 are handled, if necessary, when the engine signals a readystate change.
1194 }
1195 
finishSeek()1196 void HTMLMediaElement::finishSeek()
1197 {
1198     LOG(Media, "HTMLMediaElement::finishSeek");
1199 
1200     // 4.8.10.9 Seeking step 14
1201     m_seeking = false;
1202 
1203     // 4.8.10.9 Seeking step 15
1204     scheduleEvent(eventNames().seekedEvent);
1205 
1206     setDisplayMode(Video);
1207 }
1208 
readyState() const1209 HTMLMediaElement::ReadyState HTMLMediaElement::readyState() const
1210 {
1211     return m_readyState;
1212 }
1213 
movieLoadType() const1214 MediaPlayer::MovieLoadType HTMLMediaElement::movieLoadType() const
1215 {
1216     return m_player ? m_player->movieLoadType() : MediaPlayer::Unknown;
1217 }
1218 
hasAudio() const1219 bool HTMLMediaElement::hasAudio() const
1220 {
1221     return m_player ? m_player->hasAudio() : false;
1222 }
1223 
seeking() const1224 bool HTMLMediaElement::seeking() const
1225 {
1226     return m_seeking;
1227 }
1228 
refreshCachedTime() const1229 void HTMLMediaElement::refreshCachedTime() const
1230 {
1231     m_cachedTime = m_player->currentTime();
1232     m_cachedTimeWallClockUpdateTime = WTF::currentTime();
1233 }
1234 
invalidateCachedTime()1235 void HTMLMediaElement::invalidateCachedTime()
1236 {
1237     LOG(Media, "HTMLMediaElement::invalidateCachedTime");
1238 
1239     // Don't try to cache movie time when playback first starts as the time reported by the engine
1240     // sometimes fluctuates for a short amount of time, so the cached time will be off if we take it
1241     // too early.
1242     static const double minimumTimePlayingBeforeCacheSnapshot = 0.5;
1243 
1244     m_minimumWallClockTimeToCacheMediaTime = WTF::currentTime() + minimumTimePlayingBeforeCacheSnapshot;
1245     m_cachedTime = invalidMediaTime;
1246 }
1247 
1248 // playback state
currentTime() const1249 float HTMLMediaElement::currentTime() const
1250 {
1251 #if LOG_CACHED_TIME_WARNINGS
1252     static const double minCachedDeltaForWarning = 0.01;
1253 #endif
1254 
1255     if (!m_player)
1256         return 0;
1257 
1258     if (m_seeking) {
1259         LOG(Media, "HTMLMediaElement::currentTime - seeking, returning %f", m_lastSeekTime);
1260         return m_lastSeekTime;
1261     }
1262 
1263     if (m_cachedTime != invalidMediaTime && m_paused) {
1264 #if LOG_CACHED_TIME_WARNINGS
1265         float delta = m_cachedTime - m_player->currentTime();
1266         if (delta > minCachedDeltaForWarning)
1267             LOG(Media, "HTMLMediaElement::currentTime - WARNING, cached time is %f seconds off of media time when paused", delta);
1268 #endif
1269         return m_cachedTime;
1270     }
1271 
1272     // Is it too soon use a cached time?
1273     double now = WTF::currentTime();
1274     double maximumDurationToCacheMediaTime = m_player->maximumDurationToCacheMediaTime();
1275 
1276     if (maximumDurationToCacheMediaTime && m_cachedTime != invalidMediaTime && !m_paused && now > m_minimumWallClockTimeToCacheMediaTime) {
1277         double wallClockDelta = now - m_cachedTimeWallClockUpdateTime;
1278 
1279         // Not too soon, use the cached time only if it hasn't expired.
1280         if (wallClockDelta < maximumDurationToCacheMediaTime) {
1281             float adjustedCacheTime = static_cast<float>(m_cachedTime + (m_playbackRate * wallClockDelta));
1282 
1283 #if LOG_CACHED_TIME_WARNINGS
1284             float delta = adjustedCacheTime - m_player->currentTime();
1285             if (delta > minCachedDeltaForWarning)
1286                 LOG(Media, "HTMLMediaElement::currentTime - WARNING, cached time is %f seconds off of media time when playing", delta);
1287 #endif
1288             return adjustedCacheTime;
1289         }
1290     }
1291 
1292 #if LOG_CACHED_TIME_WARNINGS
1293     if (maximumDurationToCacheMediaTime && now > m_minimumWallClockTimeToCacheMediaTime && m_cachedTime != invalidMediaTime) {
1294         double wallClockDelta = now - m_cachedTimeWallClockUpdateTime;
1295         float delta = m_cachedTime + (m_playbackRate * wallClockDelta) - m_player->currentTime();
1296         LOG(Media, "HTMLMediaElement::currentTime - cached time was %f seconds off of media time when it expired", delta);
1297     }
1298 #endif
1299 
1300     refreshCachedTime();
1301 
1302     return m_cachedTime;
1303 }
1304 
setCurrentTime(float time,ExceptionCode & ec)1305 void HTMLMediaElement::setCurrentTime(float time, ExceptionCode& ec)
1306 {
1307     seek(time, ec);
1308 }
1309 
startTime() const1310 float HTMLMediaElement::startTime() const
1311 {
1312     if (!m_player)
1313         return 0;
1314     return m_player->startTime();
1315 }
1316 
duration() const1317 float HTMLMediaElement::duration() const
1318 {
1319     if (m_player && m_readyState >= HAVE_METADATA)
1320         return m_player->duration();
1321 
1322     return numeric_limits<float>::quiet_NaN();
1323 }
1324 
paused() const1325 bool HTMLMediaElement::paused() const
1326 {
1327     return m_paused;
1328 }
1329 
defaultPlaybackRate() const1330 float HTMLMediaElement::defaultPlaybackRate() const
1331 {
1332     return m_defaultPlaybackRate;
1333 }
1334 
setDefaultPlaybackRate(float rate)1335 void HTMLMediaElement::setDefaultPlaybackRate(float rate)
1336 {
1337     if (m_defaultPlaybackRate != rate) {
1338         m_defaultPlaybackRate = rate;
1339         scheduleEvent(eventNames().ratechangeEvent);
1340     }
1341 }
1342 
playbackRate() const1343 float HTMLMediaElement::playbackRate() const
1344 {
1345     return m_playbackRate;
1346 }
1347 
setPlaybackRate(float rate)1348 void HTMLMediaElement::setPlaybackRate(float rate)
1349 {
1350     LOG(Media, "HTMLMediaElement::setPlaybackRate(%f)", rate);
1351 
1352     if (m_playbackRate != rate) {
1353         m_playbackRate = rate;
1354         invalidateCachedTime();
1355         scheduleEvent(eventNames().ratechangeEvent);
1356     }
1357     if (m_player && potentiallyPlaying() && m_player->rate() != rate)
1358         m_player->setRate(rate);
1359 }
1360 
webkitPreservesPitch() const1361 bool HTMLMediaElement::webkitPreservesPitch() const
1362 {
1363     return m_webkitPreservesPitch;
1364 }
1365 
setWebkitPreservesPitch(bool preservesPitch)1366 void HTMLMediaElement::setWebkitPreservesPitch(bool preservesPitch)
1367 {
1368     LOG(Media, "HTMLMediaElement::setWebkitPreservesPitch(%s)", boolString(preservesPitch));
1369 
1370     m_webkitPreservesPitch = preservesPitch;
1371 
1372     if (!m_player)
1373         return;
1374 
1375     m_player->setPreservesPitch(preservesPitch);
1376 }
1377 
ended() const1378 bool HTMLMediaElement::ended() const
1379 {
1380     // 4.8.10.8 Playing the media resource
1381     // The ended attribute must return true if the media element has ended
1382     // playback and the direction of playback is forwards, and false otherwise.
1383     return endedPlayback() && m_playbackRate > 0;
1384 }
1385 
autoplay() const1386 bool HTMLMediaElement::autoplay() const
1387 {
1388     return hasAttribute(autoplayAttr);
1389 }
1390 
setAutoplay(bool b)1391 void HTMLMediaElement::setAutoplay(bool b)
1392 {
1393     LOG(Media, "HTMLMediaElement::setAutoplay(%s)", boolString(b));
1394     setBooleanAttribute(autoplayAttr, b);
1395 }
1396 
preload() const1397 String HTMLMediaElement::preload() const
1398 {
1399     switch (m_preload) {
1400     case MediaPlayer::None:
1401         return "none";
1402         break;
1403     case MediaPlayer::MetaData:
1404         return "metadata";
1405         break;
1406     case MediaPlayer::Auto:
1407         return "auto";
1408         break;
1409     }
1410 
1411     ASSERT_NOT_REACHED();
1412     return String();
1413 }
1414 
setPreload(const String & preload)1415 void HTMLMediaElement::setPreload(const String& preload)
1416 {
1417     LOG(Media, "HTMLMediaElement::setPreload(%s)", preload.utf8().data());
1418     setAttribute(preloadAttr, preload);
1419 }
1420 
play(bool isUserGesture)1421 void HTMLMediaElement::play(bool isUserGesture)
1422 {
1423     LOG(Media, "HTMLMediaElement::play(isUserGesture : %s)", boolString(isUserGesture));
1424 
1425     if (m_restrictions & RequireUserGestureForRateChangeRestriction && !isUserGesture)
1426         return;
1427 
1428     Document* doc = document();
1429     Settings* settings = doc->settings();
1430     if (settings && settings->needsSiteSpecificQuirks() && m_dispatchingCanPlayEvent && !m_loadInitiatedByUserGesture) {
1431         // It should be impossible to be processing the canplay event while handling a user gesture
1432         // since it is dispatched asynchronously.
1433         ASSERT(!isUserGesture);
1434         String host = doc->baseURL().host();
1435         if (host.endsWith(".npr.org", false) || equalIgnoringCase(host, "npr.org"))
1436             return;
1437     }
1438 
1439     playInternal();
1440 }
1441 
playInternal()1442 void HTMLMediaElement::playInternal()
1443 {
1444     LOG(Media, "HTMLMediaElement::playInternal");
1445 
1446     // 4.8.10.9. Playing the media resource
1447     if (!m_player || m_networkState == NETWORK_EMPTY)
1448         scheduleLoad();
1449 
1450     if (endedPlayback()) {
1451         ExceptionCode unused;
1452         seek(0, unused);
1453     }
1454 
1455     if (m_paused) {
1456         m_paused = false;
1457         invalidateCachedTime();
1458         scheduleEvent(eventNames().playEvent);
1459 
1460         if (m_readyState <= HAVE_CURRENT_DATA)
1461             scheduleEvent(eventNames().waitingEvent);
1462         else if (m_readyState >= HAVE_FUTURE_DATA)
1463             scheduleEvent(eventNames().playingEvent);
1464     }
1465     m_autoplaying = false;
1466 
1467     updatePlayState();
1468 }
1469 
pause(bool isUserGesture)1470 void HTMLMediaElement::pause(bool isUserGesture)
1471 {
1472     LOG(Media, "HTMLMediaElement::pause(isUserGesture : %s)", boolString(isUserGesture));
1473 
1474     if (m_restrictions & RequireUserGestureForRateChangeRestriction && !isUserGesture)
1475         return;
1476 
1477     pauseInternal();
1478 }
1479 
1480 
pauseInternal()1481 void HTMLMediaElement::pauseInternal()
1482 {
1483     LOG(Media, "HTMLMediaElement::pauseInternal");
1484 
1485     // 4.8.10.9. Playing the media resource
1486     if (!m_player || m_networkState == NETWORK_EMPTY)
1487         scheduleLoad();
1488 
1489     m_autoplaying = false;
1490 
1491     if (!m_paused) {
1492         m_paused = true;
1493         scheduleTimeupdateEvent(false);
1494         scheduleEvent(eventNames().pauseEvent);
1495     }
1496 
1497     updatePlayState();
1498 }
1499 
loop() const1500 bool HTMLMediaElement::loop() const
1501 {
1502     return hasAttribute(loopAttr);
1503 }
1504 
setLoop(bool b)1505 void HTMLMediaElement::setLoop(bool b)
1506 {
1507     LOG(Media, "HTMLMediaElement::setLoop(%s)", boolString(b));
1508     setBooleanAttribute(loopAttr, b);
1509 }
1510 
controls() const1511 bool HTMLMediaElement::controls() const
1512 {
1513     Frame* frame = document()->frame();
1514 
1515     // always show controls when scripting is disabled
1516     if (frame && !frame->script()->canExecuteScripts(NotAboutToExecuteScript))
1517         return true;
1518 
1519     // always show controls for video when fullscreen playback is required.
1520     if (isVideo() && document()->page() && document()->page()->chrome()->requiresFullscreenForVideoPlayback())
1521         return true;
1522 
1523     // Always show controls when in full screen mode.
1524     if (isFullscreen())
1525         return true;
1526 
1527     return hasAttribute(controlsAttr);
1528 }
1529 
setControls(bool b)1530 void HTMLMediaElement::setControls(bool b)
1531 {
1532     LOG(Media, "HTMLMediaElement::setControls(%s)", boolString(b));
1533     setBooleanAttribute(controlsAttr, b);
1534 }
1535 
volume() const1536 float HTMLMediaElement::volume() const
1537 {
1538     return m_volume;
1539 }
1540 
setVolume(float vol,ExceptionCode & ec)1541 void HTMLMediaElement::setVolume(float vol, ExceptionCode& ec)
1542 {
1543     LOG(Media, "HTMLMediaElement::setVolume(%f)", vol);
1544 
1545     if (vol < 0.0f || vol > 1.0f) {
1546         ec = INDEX_SIZE_ERR;
1547         return;
1548     }
1549 
1550     if (m_volume != vol) {
1551         m_volume = vol;
1552         updateVolume();
1553         scheduleEvent(eventNames().volumechangeEvent);
1554     }
1555 }
1556 
muted() const1557 bool HTMLMediaElement::muted() const
1558 {
1559     return m_muted;
1560 }
1561 
setMuted(bool muted)1562 void HTMLMediaElement::setMuted(bool muted)
1563 {
1564     LOG(Media, "HTMLMediaElement::setMuted(%s)", boolString(muted));
1565 
1566     if (m_muted != muted) {
1567         m_muted = muted;
1568         // Avoid recursion when the player reports volume changes.
1569         if (!processingMediaPlayerCallback()) {
1570             if (m_player) {
1571                 m_player->setMuted(m_muted);
1572                 if (hasMediaControls())
1573                     mediaControls()->changedMute();
1574             }
1575         }
1576         scheduleEvent(eventNames().volumechangeEvent);
1577     }
1578 }
1579 
togglePlayState()1580 void HTMLMediaElement::togglePlayState()
1581 {
1582     LOG(Media, "HTMLMediaElement::togglePlayState - canPlay() is %s", boolString(canPlay()));
1583 
1584     // We can safely call the internal play/pause methods, which don't check restrictions, because
1585     // this method is only called from the built-in media controller
1586     if (canPlay()) {
1587         setPlaybackRate(defaultPlaybackRate());
1588         playInternal();
1589     } else
1590         pauseInternal();
1591 }
1592 
beginScrubbing()1593 void HTMLMediaElement::beginScrubbing()
1594 {
1595     LOG(Media, "HTMLMediaElement::beginScrubbing - paused() is %s", boolString(paused()));
1596 
1597     if (!paused()) {
1598         if (ended()) {
1599             // Because a media element stays in non-paused state when it reaches end, playback resumes
1600             // when the slider is dragged from the end to another position unless we pause first. Do
1601             // a "hard pause" so an event is generated, since we want to stay paused after scrubbing finishes.
1602             pause(processingUserGesture());
1603         } else {
1604             // Not at the end but we still want to pause playback so the media engine doesn't try to
1605             // continue playing during scrubbing. Pause without generating an event as we will
1606             // unpause after scrubbing finishes.
1607             setPausedInternal(true);
1608         }
1609     }
1610 }
1611 
endScrubbing()1612 void HTMLMediaElement::endScrubbing()
1613 {
1614     LOG(Media, "HTMLMediaElement::endScrubbing - m_pausedInternal is %s", boolString(m_pausedInternal));
1615 
1616     if (m_pausedInternal)
1617         setPausedInternal(false);
1618 }
1619 
1620 // The spec says to fire periodic timeupdate events (those sent while playing) every
1621 // "15 to 250ms", we choose the slowest frequency
1622 static const double maxTimeupdateEventFrequency = 0.25;
1623 
1624 static const double timeWithoutMouseMovementBeforeHidingControls = 3;
1625 
startPlaybackProgressTimer()1626 void HTMLMediaElement::startPlaybackProgressTimer()
1627 {
1628     if (m_playbackProgressTimer.isActive())
1629         return;
1630 
1631     m_previousProgressTime = WTF::currentTime();
1632     m_previousProgress = 0;
1633     m_playbackProgressTimer.startRepeating(maxTimeupdateEventFrequency);
1634 }
1635 
playbackProgressTimerFired(Timer<HTMLMediaElement> *)1636 void HTMLMediaElement::playbackProgressTimerFired(Timer<HTMLMediaElement>*)
1637 {
1638     ASSERT(m_player);
1639     if (!m_playbackRate)
1640         return;
1641 
1642     scheduleTimeupdateEvent(true);
1643     if (hasMediaControls())
1644         mediaControls()->playbackProgressed();
1645     // FIXME: deal with cue ranges here
1646 }
1647 
scheduleTimeupdateEvent(bool periodicEvent)1648 void HTMLMediaElement::scheduleTimeupdateEvent(bool periodicEvent)
1649 {
1650     double now = WTF::currentTime();
1651     double timedelta = now - m_lastTimeUpdateEventWallTime;
1652 
1653     // throttle the periodic events
1654     if (periodicEvent && timedelta < maxTimeupdateEventFrequency)
1655         return;
1656 
1657     // Some media engines make multiple "time changed" callbacks at the same time, but we only want one
1658     // event at a given time so filter here
1659     float movieTime = currentTime();
1660     if (movieTime != m_lastTimeUpdateEventMovieTime) {
1661         scheduleEvent(eventNames().timeupdateEvent);
1662         m_lastTimeUpdateEventWallTime = now;
1663         m_lastTimeUpdateEventMovieTime = movieTime;
1664     }
1665 }
1666 
canPlay() const1667 bool HTMLMediaElement::canPlay() const
1668 {
1669     return paused() || ended() || m_readyState < HAVE_METADATA;
1670 }
1671 
percentLoaded() const1672 float HTMLMediaElement::percentLoaded() const
1673 {
1674     if (!m_player)
1675         return 0;
1676     float duration = m_player->duration();
1677 
1678     if (!duration || isinf(duration))
1679         return 0;
1680 
1681     float buffered = 0;
1682     RefPtr<TimeRanges> timeRanges = m_player->buffered();
1683     for (unsigned i = 0; i < timeRanges->length(); ++i) {
1684         ExceptionCode ignoredException;
1685         float start = timeRanges->start(i, ignoredException);
1686         float end = timeRanges->end(i, ignoredException);
1687         buffered += end - start;
1688     }
1689     return buffered / duration;
1690 }
1691 
havePotentialSourceChild()1692 bool HTMLMediaElement::havePotentialSourceChild()
1693 {
1694     // Stash the current <source> node and next nodes so we can restore them after checking
1695     // to see there is another potential.
1696     HTMLSourceElement* currentSourceNode = m_currentSourceNode;
1697     Node* nextNode = m_nextChildNodeToConsider;
1698 
1699     KURL nextURL = selectNextSourceChild(0, DoNothing);
1700 
1701     m_currentSourceNode = currentSourceNode;
1702     m_nextChildNodeToConsider = nextNode;
1703 
1704     return nextURL.isValid();
1705 }
1706 
selectNextSourceChild(ContentType * contentType,InvalidSourceAction actionIfInvalid)1707 KURL HTMLMediaElement::selectNextSourceChild(ContentType *contentType, InvalidSourceAction actionIfInvalid)
1708 {
1709 #if !LOG_DISABLED
1710     // Don't log if this was just called to find out if there are any valid <source> elements.
1711     bool shouldLog = actionIfInvalid != DoNothing;
1712     if (shouldLog)
1713         LOG(Media, "HTMLMediaElement::selectNextSourceChild(contentType : \"%s\")", contentType ? contentType->raw().utf8().data() : "");
1714 #endif
1715 
1716     if (m_nextChildNodeToConsider == sourceChildEndOfListValue()) {
1717 #if !LOG_DISABLED
1718         if (shouldLog)
1719             LOG(Media, "HTMLMediaElement::selectNextSourceChild -> 0x0000, \"\"");
1720 #endif
1721         return KURL();
1722     }
1723 
1724     KURL mediaURL;
1725     Node* node;
1726     HTMLSourceElement* source = 0;
1727     bool lookingForStartNode = m_nextChildNodeToConsider;
1728     bool canUse = false;
1729 
1730     for (node = firstChild(); !canUse && node; node = node->nextSibling()) {
1731         if (lookingForStartNode && m_nextChildNodeToConsider != node)
1732             continue;
1733         lookingForStartNode = false;
1734 
1735         if (!node->hasTagName(sourceTag))
1736             continue;
1737 
1738         source = static_cast<HTMLSourceElement*>(node);
1739 
1740         // If candidate does not have a src attribute, or if its src attribute's value is the empty string ... jump down to the failed step below
1741         mediaURL = source->getNonEmptyURLAttribute(srcAttr);
1742 #if !LOG_DISABLED
1743         if (shouldLog)
1744             LOG(Media, "HTMLMediaElement::selectNextSourceChild - 'src' is %s", urlForLogging(mediaURL).utf8().data());
1745 #endif
1746         if (mediaURL.isEmpty())
1747             goto check_again;
1748 
1749         if (source->hasAttribute(mediaAttr)) {
1750             MediaQueryEvaluator screenEval("screen", document()->frame(), renderer() ? renderer()->style() : 0);
1751             RefPtr<MediaList> media = MediaList::createAllowingDescriptionSyntax(source->media());
1752 #if !LOG_DISABLED
1753             if (shouldLog)
1754                 LOG(Media, "HTMLMediaElement::selectNextSourceChild - 'media' is %s", source->media().utf8().data());
1755 #endif
1756             if (!screenEval.eval(media.get()))
1757                 goto check_again;
1758         }
1759 
1760         if (source->hasAttribute(typeAttr)) {
1761 #if !LOG_DISABLED
1762             if (shouldLog)
1763                 LOG(Media, "HTMLMediaElement::selectNextSourceChild - 'type' is %s", source->type().utf8().data());
1764 #endif
1765             if (!MediaPlayer::supportsType(ContentType(source->type())))
1766                 goto check_again;
1767         }
1768 
1769         // Is it safe to load this url?
1770         if (!isSafeToLoadURL(mediaURL, actionIfInvalid) || !dispatchBeforeLoadEvent(mediaURL.string()))
1771             goto check_again;
1772 
1773         // Making it this far means the <source> looks reasonable.
1774         canUse = true;
1775 
1776 check_again:
1777         if (!canUse && actionIfInvalid == Complain)
1778             source->scheduleErrorEvent();
1779     }
1780 
1781     if (canUse) {
1782         if (contentType)
1783             *contentType = ContentType(source->type());
1784         m_currentSourceNode = source;
1785         m_nextChildNodeToConsider = source->nextSibling();
1786         if (!m_nextChildNodeToConsider)
1787             m_nextChildNodeToConsider = sourceChildEndOfListValue();
1788     } else {
1789         m_currentSourceNode = 0;
1790         m_nextChildNodeToConsider = sourceChildEndOfListValue();
1791     }
1792 
1793 #if !LOG_DISABLED
1794     if (shouldLog)
1795         LOG(Media, "HTMLMediaElement::selectNextSourceChild -> %p, %s", m_currentSourceNode, canUse ? urlForLogging(mediaURL.string()).utf8().data() : "");
1796 #endif
1797     return canUse ? mediaURL : KURL();
1798 }
1799 
sourceWasAdded(HTMLSourceElement * source)1800 void HTMLMediaElement::sourceWasAdded(HTMLSourceElement* source)
1801 {
1802     LOG(Media, "HTMLMediaElement::sourceWasAdded(%p)", source);
1803 
1804 #if !LOG_DISABLED
1805     if (source->hasTagName(sourceTag)) {
1806         KURL url = source->getNonEmptyURLAttribute(srcAttr);
1807         LOG(Media, "HTMLMediaElement::sourceWasAdded - 'src' is %s", urlForLogging(url).utf8().data());
1808     }
1809 #endif
1810 
1811     // We should only consider a <source> element when there is not src attribute at all.
1812     if (hasAttribute(srcAttr))
1813         return;
1814 
1815     // 4.8.8 - If a source element is inserted as a child of a media element that has no src
1816     // attribute and whose networkState has the value NETWORK_EMPTY, the user agent must invoke
1817     // the media element's resource selection algorithm.
1818     if (networkState() == HTMLMediaElement::NETWORK_EMPTY) {
1819         scheduleLoad();
1820         return;
1821     }
1822 
1823     if (m_currentSourceNode && source == m_currentSourceNode->nextSibling()) {
1824         LOG(Media, "HTMLMediaElement::sourceWasAdded - <source> inserted immediately after current source");
1825         m_nextChildNodeToConsider = source;
1826         return;
1827     }
1828 
1829     if (m_nextChildNodeToConsider != sourceChildEndOfListValue())
1830         return;
1831 
1832     // 4.8.9.5, resource selection algorithm, source elements section:
1833     // 20 - Wait until the node after pointer is a node other than the end of the list. (This step might wait forever.)
1834     // 21 - Asynchronously await a stable state...
1835     // 22 - Set the element's delaying-the-load-event flag back to true (this delays the load event again, in case
1836     // it hasn't been fired yet).
1837     setShouldDelayLoadEvent(true);
1838 
1839     // 23 - Set the networkState back to NETWORK_LOADING.
1840     m_networkState = NETWORK_LOADING;
1841 
1842     // 24 - Jump back to the find next candidate step above.
1843     m_nextChildNodeToConsider = source;
1844     scheduleNextSourceChild();
1845 }
1846 
sourceWillBeRemoved(HTMLSourceElement * source)1847 void HTMLMediaElement::sourceWillBeRemoved(HTMLSourceElement* source)
1848 {
1849     LOG(Media, "HTMLMediaElement::sourceWillBeRemoved(%p)", source);
1850 
1851 #if !LOG_DISABLED
1852     if (source->hasTagName(sourceTag)) {
1853         KURL url = source->getNonEmptyURLAttribute(srcAttr);
1854         LOG(Media, "HTMLMediaElement::sourceWillBeRemoved - 'src' is %s", urlForLogging(url).utf8().data());
1855     }
1856 #endif
1857 
1858     if (source != m_currentSourceNode && source != m_nextChildNodeToConsider)
1859         return;
1860 
1861     if (source == m_nextChildNodeToConsider) {
1862         m_nextChildNodeToConsider = m_nextChildNodeToConsider->nextSibling();
1863         if (!m_nextChildNodeToConsider)
1864             m_nextChildNodeToConsider = sourceChildEndOfListValue();
1865         LOG(Media, "HTMLMediaElement::sourceRemoved - m_nextChildNodeToConsider set to %p", m_nextChildNodeToConsider);
1866     } else if (source == m_currentSourceNode) {
1867         // Clear the current source node pointer, but don't change the movie as the spec says:
1868         // 4.8.8 - Dynamically modifying a source element and its attribute when the element is already
1869         // inserted in a video or audio element will have no effect.
1870         m_currentSourceNode = 0;
1871         LOG(Media, "HTMLMediaElement::sourceRemoved - m_currentSourceNode set to 0");
1872     }
1873 }
1874 
mediaPlayerTimeChanged(MediaPlayer *)1875 void HTMLMediaElement::mediaPlayerTimeChanged(MediaPlayer*)
1876 {
1877     LOG(Media, "HTMLMediaElement::mediaPlayerTimeChanged");
1878 
1879     beginProcessingMediaPlayerCallback();
1880 
1881     invalidateCachedTime();
1882 
1883     // 4.8.10.9 step 14 & 15.  Needed if no ReadyState change is associated with the seek.
1884     if (m_seeking && m_readyState >= HAVE_CURRENT_DATA)
1885         finishSeek();
1886 
1887     // Always call scheduleTimeupdateEvent when the media engine reports a time discontinuity,
1888     // it will only queue a 'timeupdate' event if we haven't already posted one at the current
1889     // movie time.
1890     scheduleTimeupdateEvent(false);
1891 
1892     float now = currentTime();
1893     float dur = duration();
1894     if (!isnan(dur) && dur && now >= dur) {
1895         if (loop()) {
1896             ExceptionCode ignoredException;
1897             m_sentEndEvent = false;
1898             seek(0, ignoredException);
1899         } else {
1900             if (!m_sentEndEvent) {
1901                 m_sentEndEvent = true;
1902                 scheduleEvent(eventNames().endedEvent);
1903             }
1904         }
1905     }
1906     else
1907         m_sentEndEvent = false;
1908 
1909     updatePlayState();
1910     endProcessingMediaPlayerCallback();
1911 }
1912 
mediaPlayerVolumeChanged(MediaPlayer *)1913 void HTMLMediaElement::mediaPlayerVolumeChanged(MediaPlayer*)
1914 {
1915     LOG(Media, "HTMLMediaElement::mediaPlayerVolumeChanged");
1916 
1917     beginProcessingMediaPlayerCallback();
1918     if (m_player) {
1919         float vol = m_player->volume();
1920         if (vol != m_volume) {
1921             m_volume = vol;
1922             updateVolume();
1923             scheduleEvent(eventNames().volumechangeEvent);
1924         }
1925     }
1926     endProcessingMediaPlayerCallback();
1927 }
1928 
mediaPlayerMuteChanged(MediaPlayer *)1929 void HTMLMediaElement::mediaPlayerMuteChanged(MediaPlayer*)
1930 {
1931     LOG(Media, "HTMLMediaElement::mediaPlayerMuteChanged");
1932 
1933     beginProcessingMediaPlayerCallback();
1934     if (m_player)
1935         setMuted(m_player->muted());
1936     endProcessingMediaPlayerCallback();
1937 }
1938 
mediaPlayerDurationChanged(MediaPlayer *)1939 void HTMLMediaElement::mediaPlayerDurationChanged(MediaPlayer*)
1940 {
1941     LOG(Media, "HTMLMediaElement::mediaPlayerDurationChanged");
1942 
1943     beginProcessingMediaPlayerCallback();
1944     scheduleEvent(eventNames().durationchangeEvent);
1945     if (renderer())
1946         renderer()->updateFromElement();
1947     endProcessingMediaPlayerCallback();
1948 }
1949 
mediaPlayerRateChanged(MediaPlayer *)1950 void HTMLMediaElement::mediaPlayerRateChanged(MediaPlayer*)
1951 {
1952     LOG(Media, "HTMLMediaElement::mediaPlayerRateChanged");
1953 
1954     beginProcessingMediaPlayerCallback();
1955 
1956     invalidateCachedTime();
1957 
1958     // Stash the rate in case the one we tried to set isn't what the engine is
1959     // using (eg. it can't handle the rate we set)
1960     m_playbackRate = m_player->rate();
1961     invalidateCachedTime();
1962     endProcessingMediaPlayerCallback();
1963 }
1964 
mediaPlayerPlaybackStateChanged(MediaPlayer *)1965 void HTMLMediaElement::mediaPlayerPlaybackStateChanged(MediaPlayer*)
1966 {
1967     LOG(Media, "HTMLMediaElement::mediaPlayerPlaybackStateChanged");
1968 
1969     if (!m_player || m_pausedInternal)
1970         return;
1971 
1972     beginProcessingMediaPlayerCallback();
1973     if (m_player->paused())
1974         pauseInternal();
1975     else
1976         playInternal();
1977     endProcessingMediaPlayerCallback();
1978 }
1979 
mediaPlayerSawUnsupportedTracks(MediaPlayer *)1980 void HTMLMediaElement::mediaPlayerSawUnsupportedTracks(MediaPlayer*)
1981 {
1982     LOG(Media, "HTMLMediaElement::mediaPlayerSawUnsupportedTracks");
1983 
1984     // The MediaPlayer came across content it cannot completely handle.
1985     // This is normally acceptable except when we are in a standalone
1986     // MediaDocument. If so, tell the document what has happened.
1987     if (ownerDocument()->isMediaDocument()) {
1988         MediaDocument* mediaDocument = static_cast<MediaDocument*>(ownerDocument());
1989         mediaDocument->mediaElementSawUnsupportedTracks();
1990     }
1991 }
1992 
1993 // MediaPlayerPresentation methods
mediaPlayerRepaint(MediaPlayer *)1994 void HTMLMediaElement::mediaPlayerRepaint(MediaPlayer*)
1995 {
1996     beginProcessingMediaPlayerCallback();
1997     updateDisplayState();
1998     if (renderer())
1999         renderer()->repaint();
2000     endProcessingMediaPlayerCallback();
2001 }
2002 
mediaPlayerSizeChanged(MediaPlayer *)2003 void HTMLMediaElement::mediaPlayerSizeChanged(MediaPlayer*)
2004 {
2005     LOG(Media, "HTMLMediaElement::mediaPlayerSizeChanged");
2006 
2007     beginProcessingMediaPlayerCallback();
2008     if (renderer())
2009         renderer()->updateFromElement();
2010     endProcessingMediaPlayerCallback();
2011 }
2012 
2013 #if USE(ACCELERATED_COMPOSITING)
mediaPlayerRenderingCanBeAccelerated(MediaPlayer *)2014 bool HTMLMediaElement::mediaPlayerRenderingCanBeAccelerated(MediaPlayer*)
2015 {
2016     if (renderer() && renderer()->isVideo()) {
2017         ASSERT(renderer()->view());
2018         return renderer()->view()->compositor()->canAccelerateVideoRendering(toRenderVideo(renderer()));
2019     }
2020     return false;
2021 }
2022 
mediaPlayerRenderingModeChanged(MediaPlayer *)2023 void HTMLMediaElement::mediaPlayerRenderingModeChanged(MediaPlayer*)
2024 {
2025     LOG(Media, "HTMLMediaElement::mediaPlayerRenderingModeChanged");
2026 
2027     // Kick off a fake recalcStyle that will update the compositing tree.
2028     setNeedsStyleRecalc(SyntheticStyleChange);
2029 }
2030 #endif
2031 
mediaPlayerEngineUpdated(MediaPlayer *)2032 void HTMLMediaElement::mediaPlayerEngineUpdated(MediaPlayer*)
2033 {
2034     LOG(Media, "HTMLMediaElement::mediaPlayerEngineUpdated");
2035     beginProcessingMediaPlayerCallback();
2036     if (renderer())
2037         renderer()->updateFromElement();
2038     endProcessingMediaPlayerCallback();
2039 }
2040 
mediaPlayerFirstVideoFrameAvailable(MediaPlayer *)2041 void HTMLMediaElement::mediaPlayerFirstVideoFrameAvailable(MediaPlayer*)
2042 {
2043     LOG(Media, "HTMLMediaElement::mediaPlayerFirstVideoFrameAvailable");
2044     beginProcessingMediaPlayerCallback();
2045     if (displayMode() == PosterWaitingForVideo) {
2046         setDisplayMode(Video);
2047 #if USE(ACCELERATED_COMPOSITING)
2048         mediaPlayerRenderingModeChanged(m_player.get());
2049 #endif
2050     }
2051     endProcessingMediaPlayerCallback();
2052 }
2053 
buffered() const2054 PassRefPtr<TimeRanges> HTMLMediaElement::buffered() const
2055 {
2056     if (!m_player)
2057         return TimeRanges::create();
2058     return m_player->buffered();
2059 }
2060 
played()2061 PassRefPtr<TimeRanges> HTMLMediaElement::played()
2062 {
2063     if (m_playing) {
2064         float time = currentTime();
2065         if (time > m_lastSeekTime)
2066             addPlayedRange(m_lastSeekTime, time);
2067     }
2068 
2069     if (!m_playedTimeRanges)
2070         m_playedTimeRanges = TimeRanges::create();
2071 
2072     return m_playedTimeRanges->copy();
2073 }
2074 
seekable() const2075 PassRefPtr<TimeRanges> HTMLMediaElement::seekable() const
2076 {
2077     // FIXME real ranges support
2078     if (!maxTimeSeekable())
2079         return TimeRanges::create();
2080     return TimeRanges::create(minTimeSeekable(), maxTimeSeekable());
2081 }
2082 
potentiallyPlaying() const2083 bool HTMLMediaElement::potentiallyPlaying() const
2084 {
2085     // "pausedToBuffer" means the media engine's rate is 0, but only because it had to stop playing
2086     // when it ran out of buffered data. A movie is this state is "potentially playing", modulo the
2087     // checks in couldPlayIfEnoughData().
2088     bool pausedToBuffer = m_readyStateMaximum >= HAVE_FUTURE_DATA && m_readyState < HAVE_FUTURE_DATA;
2089     return (pausedToBuffer || m_readyState >= HAVE_FUTURE_DATA) && couldPlayIfEnoughData();
2090 }
2091 
couldPlayIfEnoughData() const2092 bool HTMLMediaElement::couldPlayIfEnoughData() const
2093 {
2094     return !paused() && !endedPlayback() && !stoppedDueToErrors() && !pausedForUserInteraction();
2095 }
2096 
endedPlayback() const2097 bool HTMLMediaElement::endedPlayback() const
2098 {
2099     float dur = duration();
2100     if (!m_player || isnan(dur))
2101         return false;
2102 
2103     // 4.8.10.8 Playing the media resource
2104 
2105     // A media element is said to have ended playback when the element's
2106     // readyState attribute is HAVE_METADATA or greater,
2107     if (m_readyState < HAVE_METADATA)
2108         return false;
2109 
2110     // and the current playback position is the end of the media resource and the direction
2111     // of playback is forwards and the media element does not have a loop attribute specified,
2112     float now = currentTime();
2113     if (m_playbackRate > 0)
2114         return dur > 0 && now >= dur && !loop();
2115 
2116     // or the current playback position is the earliest possible position and the direction
2117     // of playback is backwards
2118     if (m_playbackRate < 0)
2119         return now <= 0;
2120 
2121     return false;
2122 }
2123 
stoppedDueToErrors() const2124 bool HTMLMediaElement::stoppedDueToErrors() const
2125 {
2126     if (m_readyState >= HAVE_METADATA && m_error) {
2127         RefPtr<TimeRanges> seekableRanges = seekable();
2128         if (!seekableRanges->contain(currentTime()))
2129             return true;
2130     }
2131 
2132     return false;
2133 }
2134 
pausedForUserInteraction() const2135 bool HTMLMediaElement::pausedForUserInteraction() const
2136 {
2137 //    return !paused() && m_readyState >= HAVE_FUTURE_DATA && [UA requires a decitions from the user]
2138     return false;
2139 }
2140 
minTimeSeekable() const2141 float HTMLMediaElement::minTimeSeekable() const
2142 {
2143     return 0;
2144 }
2145 
maxTimeSeekable() const2146 float HTMLMediaElement::maxTimeSeekable() const
2147 {
2148     return m_player ? m_player->maxTimeSeekable() : 0;
2149 }
2150 
updateVolume()2151 void HTMLMediaElement::updateVolume()
2152 {
2153     if (!m_player)
2154         return;
2155 
2156     // Avoid recursion when the player reports volume changes.
2157     if (!processingMediaPlayerCallback()) {
2158         Page* page = document()->page();
2159         float volumeMultiplier = page ? page->mediaVolume() : 1;
2160 
2161         m_player->setMuted(m_muted);
2162         m_player->setVolume(m_volume * volumeMultiplier);
2163     }
2164 
2165     if (hasMediaControls())
2166         mediaControls()->changedVolume();
2167 }
2168 
updatePlayState()2169 void HTMLMediaElement::updatePlayState()
2170 {
2171     if (!m_player)
2172         return;
2173 
2174     if (m_pausedInternal) {
2175         if (!m_player->paused())
2176             m_player->pause();
2177         refreshCachedTime();
2178         m_playbackProgressTimer.stop();
2179         if (hasMediaControls())
2180             mediaControls()->playbackStopped();
2181         return;
2182     }
2183 
2184     bool shouldBePlaying = potentiallyPlaying();
2185     bool playerPaused = m_player->paused();
2186 
2187     LOG(Media, "HTMLMediaElement::updatePlayState - shouldBePlaying = %s, playerPaused = %s",
2188         boolString(shouldBePlaying), boolString(playerPaused));
2189 
2190     if (shouldBePlaying) {
2191         setDisplayMode(Video);
2192         invalidateCachedTime();
2193 
2194         if (playerPaused) {
2195             if (!m_isFullscreen && isVideo() && document() && document()->page() && document()->page()->chrome()->requiresFullscreenForVideoPlayback())
2196                 enterFullscreen();
2197 
2198             // Set rate, muted before calling play in case they were set before the media engine was setup.
2199             // The media engine should just stash the rate and muted values since it isn't already playing.
2200             m_player->setRate(m_playbackRate);
2201             m_player->setMuted(m_muted);
2202 
2203             m_player->play();
2204         }
2205 
2206         if (hasMediaControls())
2207             mediaControls()->playbackStarted();
2208         startPlaybackProgressTimer();
2209         m_playing = true;
2210 
2211     } else { // Should not be playing right now
2212         if (!playerPaused)
2213             m_player->pause();
2214         refreshCachedTime();
2215 
2216         m_playbackProgressTimer.stop();
2217         m_playing = false;
2218         float time = currentTime();
2219         if (time > m_lastSeekTime)
2220             addPlayedRange(m_lastSeekTime, time);
2221 
2222         if (couldPlayIfEnoughData())
2223             prepareToPlay();
2224 
2225         if (hasMediaControls())
2226             mediaControls()->playbackStopped();
2227     }
2228 
2229     if (renderer())
2230         renderer()->updateFromElement();
2231 }
2232 
setPausedInternal(bool b)2233 void HTMLMediaElement::setPausedInternal(bool b)
2234 {
2235     m_pausedInternal = b;
2236     updatePlayState();
2237 }
2238 
stopPeriodicTimers()2239 void HTMLMediaElement::stopPeriodicTimers()
2240 {
2241     m_progressEventTimer.stop();
2242     m_playbackProgressTimer.stop();
2243 }
2244 
userCancelledLoad()2245 void HTMLMediaElement::userCancelledLoad()
2246 {
2247     LOG(Media, "HTMLMediaElement::userCancelledLoad");
2248 
2249     if (m_networkState == NETWORK_EMPTY || m_completelyLoaded)
2250         return;
2251 
2252     // If the media data fetching process is aborted by the user:
2253 
2254     // 1 - The user agent should cancel the fetching process.
2255 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
2256     m_player.clear();
2257 #endif
2258     stopPeriodicTimers();
2259     m_loadTimer.stop();
2260     m_loadState = WaitingForSource;
2261 
2262     // 2 - Set the error attribute to a new MediaError object whose code attribute is set to MEDIA_ERR_ABORTED.
2263     m_error = MediaError::create(MediaError::MEDIA_ERR_ABORTED);
2264 
2265     // 3 - Queue a task to fire a simple event named error at the media element.
2266     scheduleEvent(eventNames().abortEvent);
2267 
2268     // 4 - If the media element's readyState attribute has a value equal to HAVE_NOTHING, set the
2269     // element's networkState attribute to the NETWORK_EMPTY value and queue a task to fire a
2270     // simple event named emptied at the element. Otherwise, set the element's networkState
2271     // attribute to the NETWORK_IDLE value.
2272     if (m_readyState == HAVE_NOTHING) {
2273         m_networkState = NETWORK_EMPTY;
2274         scheduleEvent(eventNames().emptiedEvent);
2275     }
2276     else
2277         m_networkState = NETWORK_IDLE;
2278 
2279     // 5 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
2280     setShouldDelayLoadEvent(false);
2281 
2282     // 6 - Abort the overall resource selection algorithm.
2283     m_currentSourceNode = 0;
2284 
2285     // Reset m_readyState since m_player is gone.
2286     m_readyState = HAVE_NOTHING;
2287 }
2288 
canSuspend() const2289 bool HTMLMediaElement::canSuspend() const
2290 {
2291     return true;
2292 }
2293 
stop()2294 void HTMLMediaElement::stop()
2295 {
2296     LOG(Media, "HTMLMediaElement::stop");
2297     if (m_isFullscreen)
2298         exitFullscreen();
2299 
2300     m_inActiveDocument = false;
2301     userCancelledLoad();
2302 
2303     // Stop the playback without generating events
2304     setPausedInternal(true);
2305 
2306     if (renderer())
2307         renderer()->updateFromElement();
2308 
2309     stopPeriodicTimers();
2310     cancelPendingEventsAndCallbacks();
2311 }
2312 
suspend(ReasonForSuspension why)2313 void HTMLMediaElement::suspend(ReasonForSuspension why)
2314 {
2315     LOG(Media, "HTMLMediaElement::suspend");
2316 
2317     switch (why)
2318     {
2319         case DocumentWillBecomeInactive:
2320             stop();
2321             break;
2322         case JavaScriptDebuggerPaused:
2323         case WillShowDialog:
2324             // Do nothing, we don't pause media playback in these cases.
2325             break;
2326     }
2327 }
2328 
resume()2329 void HTMLMediaElement::resume()
2330 {
2331     LOG(Media, "HTMLMediaElement::resume");
2332 
2333     m_inActiveDocument = true;
2334     setPausedInternal(false);
2335 
2336     if (m_error && m_error->code() == MediaError::MEDIA_ERR_ABORTED) {
2337         // Restart the load if it was aborted in the middle by moving the document to the page cache.
2338         // m_error is only left at MEDIA_ERR_ABORTED when the document becomes inactive (it is set to
2339         //  MEDIA_ERR_ABORTED while the abortEvent is being sent, but cleared immediately afterwards).
2340         // This behavior is not specified but it seems like a sensible thing to do.
2341         ExceptionCode ec;
2342         load(processingUserGesture(), ec);
2343     }
2344 
2345     if (renderer())
2346         renderer()->updateFromElement();
2347 }
2348 
hasPendingActivity() const2349 bool HTMLMediaElement::hasPendingActivity() const
2350 {
2351     // Return true when we have pending events so we can't fire events after the JS
2352     // object gets collected.
2353     bool pending = m_pendingEvents.size();
2354     LOG(Media, "HTMLMediaElement::hasPendingActivity -> %s", boolString(pending));
2355     return pending;
2356 }
2357 
mediaVolumeDidChange()2358 void HTMLMediaElement::mediaVolumeDidChange()
2359 {
2360     LOG(Media, "HTMLMediaElement::mediaVolumeDidChange");
2361     updateVolume();
2362 }
2363 
defaultEventHandler(Event * event)2364 void HTMLMediaElement::defaultEventHandler(Event* event)
2365 {
2366 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
2367     RenderObject* r = renderer();
2368     if (!r || !r->isWidget())
2369         return;
2370 
2371     Widget* widget = toRenderWidget(r)->widget();
2372     if (widget)
2373         widget->handleEvent(event);
2374 #else
2375     HTMLElement::defaultEventHandler(event);
2376 #endif
2377 }
2378 
processingUserGesture() const2379 bool HTMLMediaElement::processingUserGesture() const
2380 {
2381     Frame* frame = document()->frame();
2382     FrameLoader* loader = frame ? frame->loader() : 0;
2383 
2384     // return 'true' for safety if we don't know the answer
2385     return loader ? loader->isProcessingUserGesture() : true;
2386 }
2387 
2388 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
2389 
ensureMediaPlayer()2390 void HTMLMediaElement::ensureMediaPlayer()
2391 {
2392     if (!m_player)
2393         m_player = MediaPlayer::create(this);
2394 }
2395 
deliverNotification(MediaPlayerProxyNotificationType notification)2396 void HTMLMediaElement::deliverNotification(MediaPlayerProxyNotificationType notification)
2397 {
2398     if (notification == MediaPlayerNotificationPlayPauseButtonPressed) {
2399         togglePlayState();
2400         return;
2401     }
2402 
2403     if (m_player)
2404         m_player->deliverNotification(notification);
2405 }
2406 
setMediaPlayerProxy(WebMediaPlayerProxy * proxy)2407 void HTMLMediaElement::setMediaPlayerProxy(WebMediaPlayerProxy* proxy)
2408 {
2409     ensureMediaPlayer();
2410     m_player->setMediaPlayerProxy(proxy);
2411 }
2412 
getPluginProxyParams(KURL & url,Vector<String> & names,Vector<String> & values)2413 void HTMLMediaElement::getPluginProxyParams(KURL& url, Vector<String>& names, Vector<String>& values)
2414 {
2415     Frame* frame = document()->frame();
2416     FrameLoader* loader = frame ? frame->loader() : 0;
2417 
2418     if (isVideo()) {
2419         KURL posterURL = getNonEmptyURLAttribute(posterAttr);
2420         if (!posterURL.isEmpty() && loader && loader->willLoadMediaElementURL(posterURL)) {
2421             names.append("_media_element_poster_");
2422             values.append(posterURL.string());
2423         }
2424     }
2425 
2426     if (controls()) {
2427         names.append("_media_element_controls_");
2428         values.append("true");
2429     }
2430 
2431     url = src();
2432     if (!isSafeToLoadURL(url, Complain))
2433         url = selectNextSourceChild(0, DoNothing);
2434 
2435     m_currentSrc = url.string();
2436     if (url.isValid() && loader && loader->willLoadMediaElementURL(url)) {
2437         names.append("_media_element_src_");
2438         values.append(m_currentSrc);
2439     }
2440 }
2441 
finishParsingChildren()2442 void HTMLMediaElement::finishParsingChildren()
2443 {
2444     HTMLElement::finishParsingChildren();
2445     document()->updateStyleIfNeeded();
2446     createMediaPlayerProxy();
2447 }
2448 
createMediaPlayerProxy()2449 void HTMLMediaElement::createMediaPlayerProxy()
2450 {
2451     ensureMediaPlayer();
2452 
2453     if (m_proxyWidget || (inDocument() && !m_needWidgetUpdate))
2454         return;
2455 
2456     Frame* frame = document()->frame();
2457     FrameLoader* loader = frame ? frame->loader() : 0;
2458     if (!loader)
2459         return;
2460 
2461     LOG(Media, "HTMLMediaElement::createMediaPlayerProxy");
2462 
2463     KURL url;
2464     Vector<String> paramNames;
2465     Vector<String> paramValues;
2466 
2467     getPluginProxyParams(url, paramNames, paramValues);
2468 
2469     // Hang onto the proxy widget so it won't be destroyed if the plug-in is set to
2470     // display:none
2471     m_proxyWidget = loader->subframeLoader()->loadMediaPlayerProxyPlugin(this, url, paramNames, paramValues);
2472     if (m_proxyWidget)
2473         m_needWidgetUpdate = false;
2474 }
2475 
updateWidget(PluginCreationOption)2476 void HTMLMediaElement::updateWidget(PluginCreationOption)
2477 {
2478     mediaElement->setNeedWidgetUpdate(false);
2479 
2480     Vector<String> paramNames;
2481     Vector<String> paramValues;
2482     KURL kurl;
2483 
2484     mediaElement->getPluginProxyParams(kurl, paramNames, paramValues);
2485     SubframeLoader* loader = document()->frame()->loader()->subframeLoader();
2486     loader->loadMediaPlayerProxyPlugin(mediaElement, kurl, paramNames, paramValues);
2487 }
2488 
2489 #endif // ENABLE(PLUGIN_PROXY_FOR_VIDEO)
2490 
isFullscreen() const2491 bool HTMLMediaElement::isFullscreen() const
2492 {
2493     if (m_isFullscreen)
2494         return true;
2495 
2496 #if ENABLE(FULLSCREEN_API)
2497     if (document()->webkitIsFullScreen() && document()->webkitCurrentFullScreenElement() == this)
2498         return true;
2499 #endif
2500 
2501     return false;
2502 }
2503 
enterFullscreen()2504 void HTMLMediaElement::enterFullscreen()
2505 {
2506     LOG(Media, "HTMLMediaElement::enterFullscreen");
2507 
2508 #if ENABLE(FULLSCREEN_API)
2509     if (document() && document()->settings() && document()->settings()->fullScreenEnabled()) {
2510         document()->requestFullScreenForElement(this, 0, Document::ExemptIFrameAllowFulScreenRequirement);
2511         return;
2512     }
2513 #endif
2514     ASSERT(!m_isFullscreen);
2515     m_isFullscreen = true;
2516     if (hasMediaControls())
2517         mediaControls()->enteredFullscreen();
2518     if (document() && document()->page()) {
2519         document()->page()->chrome()->client()->enterFullscreenForNode(this);
2520         scheduleEvent(eventNames().webkitbeginfullscreenEvent);
2521     }
2522 }
2523 
exitFullscreen()2524 void HTMLMediaElement::exitFullscreen()
2525 {
2526     LOG(Media, "HTMLMediaElement::exitFullscreen");
2527 
2528 #if ENABLE(FULLSCREEN_API)
2529     if (document() && document()->settings() && document()->settings()->fullScreenEnabled()) {
2530         if (document()->webkitIsFullScreen() && document()->webkitCurrentFullScreenElement() == this)
2531             document()->webkitCancelFullScreen();
2532         return;
2533     }
2534 #endif
2535     ASSERT(m_isFullscreen);
2536     m_isFullscreen = false;
2537     if (hasMediaControls())
2538         mediaControls()->exitedFullscreen();
2539     if (document() && document()->page()) {
2540         if (document()->page()->chrome()->requiresFullscreenForVideoPlayback())
2541             pauseInternal();
2542         document()->page()->chrome()->client()->exitFullscreenForNode(this);
2543         scheduleEvent(eventNames().webkitendfullscreenEvent);
2544     }
2545 }
2546 
platformMedia() const2547 PlatformMedia HTMLMediaElement::platformMedia() const
2548 {
2549     return m_player ? m_player->platformMedia() : NoPlatformMedia;
2550 }
2551 
2552 #if USE(ACCELERATED_COMPOSITING)
platformLayer() const2553 PlatformLayer* HTMLMediaElement::platformLayer() const
2554 {
2555     return m_player ? m_player->platformLayer() : 0;
2556 }
2557 #endif
2558 
hasClosedCaptions() const2559 bool HTMLMediaElement::hasClosedCaptions() const
2560 {
2561     return m_player && m_player->hasClosedCaptions();
2562 }
2563 
closedCaptionsVisible() const2564 bool HTMLMediaElement::closedCaptionsVisible() const
2565 {
2566     return m_closedCaptionsVisible;
2567 }
2568 
setClosedCaptionsVisible(bool closedCaptionVisible)2569 void HTMLMediaElement::setClosedCaptionsVisible(bool closedCaptionVisible)
2570 {
2571     LOG(Media, "HTMLMediaElement::setClosedCaptionsVisible(%s)", boolString(closedCaptionVisible));
2572 
2573     if (!m_player ||!hasClosedCaptions())
2574         return;
2575 
2576     m_closedCaptionsVisible = closedCaptionVisible;
2577     m_player->setClosedCaptionsVisible(closedCaptionVisible);
2578     if (hasMediaControls())
2579         mediaControls()->changedClosedCaptionsVisibility();
2580 }
2581 
setWebkitClosedCaptionsVisible(bool visible)2582 void HTMLMediaElement::setWebkitClosedCaptionsVisible(bool visible)
2583 {
2584     setClosedCaptionsVisible(visible);
2585 }
2586 
webkitClosedCaptionsVisible() const2587 bool HTMLMediaElement::webkitClosedCaptionsVisible() const
2588 {
2589     return closedCaptionsVisible();
2590 }
2591 
2592 
webkitHasClosedCaptions() const2593 bool HTMLMediaElement::webkitHasClosedCaptions() const
2594 {
2595     return hasClosedCaptions();
2596 }
2597 
2598 #if ENABLE(MEDIA_STATISTICS)
webkitAudioDecodedByteCount() const2599 unsigned HTMLMediaElement::webkitAudioDecodedByteCount() const
2600 {
2601     if (!m_player)
2602         return 0;
2603     return m_player->audioDecodedByteCount();
2604 }
2605 
webkitVideoDecodedByteCount() const2606 unsigned HTMLMediaElement::webkitVideoDecodedByteCount() const
2607 {
2608     if (!m_player)
2609         return 0;
2610     return m_player->videoDecodedByteCount();
2611 }
2612 #endif
2613 
mediaCanStart()2614 void HTMLMediaElement::mediaCanStart()
2615 {
2616     LOG(Media, "HTMLMediaElement::mediaCanStart");
2617 
2618     ASSERT(m_isWaitingUntilMediaCanStart);
2619     m_isWaitingUntilMediaCanStart = false;
2620     loadInternal();
2621 }
2622 
isURLAttribute(Attribute * attribute) const2623 bool HTMLMediaElement::isURLAttribute(Attribute* attribute) const
2624 {
2625     return attribute->name() == srcAttr;
2626 }
2627 
setShouldDelayLoadEvent(bool shouldDelay)2628 void HTMLMediaElement::setShouldDelayLoadEvent(bool shouldDelay)
2629 {
2630     if (m_shouldDelayLoadEvent == shouldDelay)
2631         return;
2632 
2633     LOG(Media, "HTMLMediaElement::setShouldDelayLoadEvent(%s)", boolString(shouldDelay));
2634 
2635     m_shouldDelayLoadEvent = shouldDelay;
2636     if (shouldDelay)
2637         document()->incrementLoadEventDelayCount();
2638     else
2639         document()->decrementLoadEventDelayCount();
2640 }
2641 
2642 
getSitesInMediaCache(Vector<String> & sites)2643 void HTMLMediaElement::getSitesInMediaCache(Vector<String>& sites)
2644 {
2645     MediaPlayer::getSitesInMediaCache(sites);
2646 }
2647 
clearMediaCache()2648 void HTMLMediaElement::clearMediaCache()
2649 {
2650     MediaPlayer::clearMediaCache();
2651 }
2652 
clearMediaCacheForSite(const String & site)2653 void HTMLMediaElement::clearMediaCacheForSite(const String& site)
2654 {
2655     MediaPlayer::clearMediaCacheForSite(site);
2656 }
2657 
privateBrowsingStateDidChange()2658 void HTMLMediaElement::privateBrowsingStateDidChange()
2659 {
2660     if (!m_player)
2661         return;
2662 
2663     Settings* settings = document()->settings();
2664     bool privateMode = !settings || settings->privateBrowsingEnabled();
2665     LOG(Media, "HTMLMediaElement::privateBrowsingStateDidChange(%s)", boolString(privateMode));
2666     m_player->setPrivateBrowsingMode(privateMode);
2667 }
2668 
mediaControls()2669 MediaControls* HTMLMediaElement::mediaControls()
2670 {
2671     return toMediaControls(shadowRoot()->firstChild());
2672 }
2673 
hasMediaControls()2674 bool HTMLMediaElement::hasMediaControls()
2675 {
2676     if (!shadowRoot())
2677         return false;
2678 
2679     Node* node = shadowRoot()->firstChild();
2680     return node && node->isMediaControls();
2681 }
2682 
ensureMediaControls()2683 void HTMLMediaElement::ensureMediaControls()
2684 {
2685     if (hasMediaControls())
2686         return;
2687 
2688     ExceptionCode ec;
2689     ensureShadowRoot()->appendChild(MediaControls::create(this), ec);
2690 }
2691 
preDispatchEventHandler(Event * event)2692 void* HTMLMediaElement::preDispatchEventHandler(Event* event)
2693 {
2694     if (event && event->type() == eventNames().webkitfullscreenchangeEvent) {
2695         if (controls()) {
2696             if (!hasMediaControls()) {
2697                 ensureMediaControls();
2698                 mediaControls()->reset();
2699             }
2700             mediaControls()->show();
2701         } else if (hasMediaControls())
2702             mediaControls()->hide();
2703     }
2704     return 0;
2705 }
2706 
2707 
2708 }
2709 
2710 #endif
2711