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