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