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