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