1 /*
2 * Copyright (C) 2007, 2008, 2009, 2010 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 "Chrome.h"
32 #include "ChromeClient.h"
33 #include "ClientRect.h"
34 #include "ClientRectList.h"
35 #include "CSSHelper.h"
36 #include "CSSPropertyNames.h"
37 #include "CSSValueKeywords.h"
38 #include "ContentType.h"
39 #include "DocLoader.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 "MIMETypeRegistry.h"
52 #include "MappedAttribute.h"
53 #include "MediaDocument.h"
54 #include "MediaError.h"
55 #include "MediaList.h"
56 #include "MediaPlayer.h"
57 #include "MediaQueryEvaluator.h"
58 #include "Page.h"
59 #include "RenderVideo.h"
60 #include "RenderView.h"
61 #include "ScriptEventListener.h"
62 #include "TimeRanges.h"
63 #include <limits>
64 #include <wtf/CurrentTime.h>
65 #include <wtf/MathExtras.h>
66
67 #if USE(ACCELERATED_COMPOSITING)
68 #include "RenderView.h"
69 #include "RenderLayerCompositor.h"
70 #endif
71
72 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
73 #include "RenderPartObject.h"
74 #include "Widget.h"
75 #endif
76
77 using namespace std;
78
79 namespace WebCore {
80
81 using namespace HTMLNames;
82
HTMLMediaElement(const QualifiedName & tagName,Document * doc)83 HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document* doc)
84 : HTMLElement(tagName, doc)
85 , m_loadTimer(this, &HTMLMediaElement::loadTimerFired)
86 , m_asyncEventTimer(this, &HTMLMediaElement::asyncEventTimerFired)
87 , m_progressEventTimer(this, &HTMLMediaElement::progressEventTimerFired)
88 , m_playbackProgressTimer(this, &HTMLMediaElement::playbackProgressTimerFired)
89 , m_playedTimeRanges()
90 , m_playbackRate(1.0f)
91 , m_defaultPlaybackRate(1.0f)
92 , m_webkitPreservesPitch(true)
93 , m_networkState(NETWORK_EMPTY)
94 , m_readyState(HAVE_NOTHING)
95 , m_volume(1.0f)
96 , m_lastSeekTime(0)
97 , m_previousProgress(0)
98 , m_previousProgressTime(numeric_limits<double>::max())
99 , m_lastTimeUpdateEventWallTime(0)
100 , m_lastTimeUpdateEventMovieTime(numeric_limits<float>::max())
101 , m_loadState(WaitingForSource)
102 , m_currentSourceNode(0)
103 , m_player(0)
104 , m_restrictions(NoRestrictions)
105 , m_playing(false)
106 , m_processingMediaPlayerCallback(0)
107 , m_processingLoad(false)
108 , m_delayingTheLoadEvent(false)
109 , m_haveFiredLoadedData(false)
110 , m_inActiveDocument(true)
111 , m_autoplaying(true)
112 , m_muted(false)
113 , m_paused(true)
114 , m_seeking(false)
115 , m_sentStalledEvent(false)
116 , m_sentEndEvent(false)
117 , m_pausedInternal(false)
118 , m_sendProgressEvents(true)
119 , m_isFullscreen(false)
120 , m_closedCaptionsVisible(false)
121 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
122 , m_needWidgetUpdate(false)
123 #endif
124 {
125 document()->registerForDocumentActivationCallbacks(this);
126 document()->registerForMediaVolumeCallbacks(this);
127 }
128
~HTMLMediaElement()129 HTMLMediaElement::~HTMLMediaElement()
130 {
131 document()->unregisterForDocumentActivationCallbacks(this);
132 document()->unregisterForMediaVolumeCallbacks(this);
133 }
134
willMoveToNewOwnerDocument()135 void HTMLMediaElement::willMoveToNewOwnerDocument()
136 {
137 document()->unregisterForDocumentActivationCallbacks(this);
138 document()->unregisterForMediaVolumeCallbacks(this);
139 HTMLElement::willMoveToNewOwnerDocument();
140 }
141
didMoveToNewOwnerDocument()142 void HTMLMediaElement::didMoveToNewOwnerDocument()
143 {
144 document()->registerForDocumentActivationCallbacks(this);
145 document()->registerForMediaVolumeCallbacks(this);
146 HTMLElement::didMoveToNewOwnerDocument();
147 }
148
149
checkDTD(const Node * newChild)150 bool HTMLMediaElement::checkDTD(const Node* newChild)
151 {
152 return newChild->hasTagName(sourceTag) || HTMLElement::checkDTD(newChild);
153 }
154
attributeChanged(Attribute * attr,bool preserveDecls)155 void HTMLMediaElement::attributeChanged(Attribute* attr, bool preserveDecls)
156 {
157 HTMLElement::attributeChanged(attr, preserveDecls);
158
159 const QualifiedName& attrName = attr->name();
160 if (attrName == srcAttr) {
161 // don't have a src or any <source> children, trigger load
162 if (inDocument() && m_loadState == WaitingForSource)
163 scheduleLoad();
164 }
165 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
166 else if (attrName == controlsAttr) {
167 if (!isVideo() && attached() && (controls() != (renderer() != 0))) {
168 detach();
169 attach();
170 }
171 if (renderer())
172 renderer()->updateFromElement();
173 }
174 #endif
175 }
176
parseMappedAttribute(MappedAttribute * attr)177 void HTMLMediaElement::parseMappedAttribute(MappedAttribute* attr)
178 {
179 const QualifiedName& attrName = attr->name();
180
181 if (attrName == autobufferAttr) {
182 if (m_player)
183 m_player->setAutobuffer(!attr->isNull());
184 } else if (attrName == onabortAttr)
185 setAttributeEventListener(eventNames().abortEvent, createAttributeEventListener(this, attr));
186 else if (attrName == onbeforeloadAttr)
187 setAttributeEventListener(eventNames().beforeloadEvent, createAttributeEventListener(this, attr));
188 else if (attrName == oncanplayAttr)
189 setAttributeEventListener(eventNames().canplayEvent, createAttributeEventListener(this, attr));
190 else if (attrName == oncanplaythroughAttr)
191 setAttributeEventListener(eventNames().canplaythroughEvent, createAttributeEventListener(this, attr));
192 else if (attrName == ondurationchangeAttr)
193 setAttributeEventListener(eventNames().durationchangeEvent, createAttributeEventListener(this, attr));
194 else if (attrName == onemptiedAttr)
195 setAttributeEventListener(eventNames().emptiedEvent, createAttributeEventListener(this, attr));
196 else if (attrName == onendedAttr)
197 setAttributeEventListener(eventNames().endedEvent, createAttributeEventListener(this, attr));
198 else if (attrName == onerrorAttr)
199 setAttributeEventListener(eventNames().errorEvent, createAttributeEventListener(this, attr));
200 else if (attrName == onloadAttr)
201 setAttributeEventListener(eventNames().loadEvent, createAttributeEventListener(this, attr));
202 else if (attrName == onloadeddataAttr)
203 setAttributeEventListener(eventNames().loadeddataEvent, createAttributeEventListener(this, attr));
204 else if (attrName == onloadedmetadataAttr)
205 setAttributeEventListener(eventNames().loadedmetadataEvent, createAttributeEventListener(this, attr));
206 else if (attrName == onloadstartAttr)
207 setAttributeEventListener(eventNames().loadstartEvent, createAttributeEventListener(this, attr));
208 else if (attrName == onpauseAttr)
209 setAttributeEventListener(eventNames().pauseEvent, createAttributeEventListener(this, attr));
210 else if (attrName == onplayAttr)
211 setAttributeEventListener(eventNames().playEvent, createAttributeEventListener(this, attr));
212 else if (attrName == onplayingAttr)
213 setAttributeEventListener(eventNames().playingEvent, createAttributeEventListener(this, attr));
214 else if (attrName == onprogressAttr)
215 setAttributeEventListener(eventNames().progressEvent, createAttributeEventListener(this, attr));
216 else if (attrName == onratechangeAttr)
217 setAttributeEventListener(eventNames().ratechangeEvent, createAttributeEventListener(this, attr));
218 else if (attrName == onseekedAttr)
219 setAttributeEventListener(eventNames().seekedEvent, createAttributeEventListener(this, attr));
220 else if (attrName == onseekingAttr)
221 setAttributeEventListener(eventNames().seekingEvent, createAttributeEventListener(this, attr));
222 else if (attrName == onstalledAttr)
223 setAttributeEventListener(eventNames().stalledEvent, createAttributeEventListener(this, attr));
224 else if (attrName == onsuspendAttr)
225 setAttributeEventListener(eventNames().suspendEvent, createAttributeEventListener(this, attr));
226 else if (attrName == ontimeupdateAttr)
227 setAttributeEventListener(eventNames().timeupdateEvent, createAttributeEventListener(this, attr));
228 else if (attrName == onvolumechangeAttr)
229 setAttributeEventListener(eventNames().volumechangeEvent, createAttributeEventListener(this, attr));
230 else if (attrName == onwaitingAttr)
231 setAttributeEventListener(eventNames().waitingEvent, createAttributeEventListener(this, attr));
232 else if (attrName == onwebkitbeginfullscreenAttr)
233 setAttributeEventListener(eventNames().webkitbeginfullscreenEvent, createAttributeEventListener(this, attr));
234 else if (attrName == onwebkitendfullscreenAttr)
235 setAttributeEventListener(eventNames().webkitendfullscreenEvent, createAttributeEventListener(this, attr));
236 else
237 HTMLElement::parseMappedAttribute(attr);
238 }
239
rendererIsNeeded(RenderStyle * style)240 bool HTMLMediaElement::rendererIsNeeded(RenderStyle* style)
241 {
242 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
243 UNUSED_PARAM(style);
244 Frame* frame = document()->frame();
245 if (!frame)
246 return false;
247
248 return true;
249 #else
250 return controls() ? HTMLElement::rendererIsNeeded(style) : false;
251 #endif
252 }
253
createRenderer(RenderArena * arena,RenderStyle *)254 RenderObject* HTMLMediaElement::createRenderer(RenderArena* arena, RenderStyle*)
255 {
256 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
257 return new (arena) RenderEmbeddedObject(this);
258 #else
259 return new (arena) RenderMedia(this);
260 #endif
261 }
262
insertedIntoDocument()263 void HTMLMediaElement::insertedIntoDocument()
264 {
265 HTMLElement::insertedIntoDocument();
266 if (!src().isEmpty() && m_networkState == NETWORK_EMPTY)
267 scheduleLoad();
268 }
269
removedFromDocument()270 void HTMLMediaElement::removedFromDocument()
271 {
272 if (m_networkState > NETWORK_EMPTY)
273 pause(processingUserGesture());
274 if (m_isFullscreen)
275 exitFullscreen();
276 HTMLElement::removedFromDocument();
277 }
278
attach()279 void HTMLMediaElement::attach()
280 {
281 ASSERT(!attached());
282
283 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
284 m_needWidgetUpdate = true;
285 #endif
286
287 HTMLElement::attach();
288
289 if (renderer())
290 renderer()->updateFromElement();
291 }
292
recalcStyle(StyleChange change)293 void HTMLMediaElement::recalcStyle(StyleChange change)
294 {
295 HTMLElement::recalcStyle(change);
296
297 if (renderer())
298 renderer()->updateFromElement();
299 }
300
scheduleLoad()301 void HTMLMediaElement::scheduleLoad()
302 {
303 if (m_loadTimer.isActive())
304 return;
305 prepareForLoad();
306 m_loadTimer.startOneShot(0);
307 }
308
scheduleNextSourceChild()309 void HTMLMediaElement::scheduleNextSourceChild()
310 {
311 // Schedule the timer to try the next <source> element WITHOUT resetting state ala prepareForLoad.
312 m_loadTimer.startOneShot(0);
313 }
314
scheduleEvent(const AtomicString & eventName)315 void HTMLMediaElement::scheduleEvent(const AtomicString& eventName)
316 {
317 m_pendingEvents.append(Event::create(eventName, false, true));
318 if (!m_asyncEventTimer.isActive())
319 m_asyncEventTimer.startOneShot(0);
320 }
321
asyncEventTimerFired(Timer<HTMLMediaElement> *)322 void HTMLMediaElement::asyncEventTimerFired(Timer<HTMLMediaElement>*)
323 {
324 Vector<RefPtr<Event> > pendingEvents;
325 ExceptionCode ec = 0;
326
327 m_pendingEvents.swap(pendingEvents);
328 unsigned count = pendingEvents.size();
329 for (unsigned ndx = 0; ndx < count; ++ndx)
330 dispatchEvent(pendingEvents[ndx].release(), ec);
331 }
332
loadTimerFired(Timer<HTMLMediaElement> *)333 void HTMLMediaElement::loadTimerFired(Timer<HTMLMediaElement>*)
334 {
335 if (m_loadState == LoadingFromSourceElement)
336 loadNextSourceChild();
337 else
338 loadInternal();
339 }
340
serializeTimeOffset(float time)341 static String serializeTimeOffset(float time)
342 {
343 String timeString = String::number(time);
344 // FIXME serialize time offset values properly (format not specified yet)
345 timeString.append("s");
346 return timeString;
347 }
348
parseTimeOffset(const String & timeString,bool * ok=0)349 static float parseTimeOffset(const String& timeString, bool* ok = 0)
350 {
351 const UChar* characters = timeString.characters();
352 unsigned length = timeString.length();
353
354 if (length && characters[length - 1] == 's')
355 length--;
356
357 // FIXME parse time offset values (format not specified yet)
358 float val = charactersToFloat(characters, length, ok);
359 return val;
360 }
361
getTimeOffsetAttribute(const QualifiedName & name,float valueOnError) const362 float HTMLMediaElement::getTimeOffsetAttribute(const QualifiedName& name, float valueOnError) const
363 {
364 bool ok;
365 String timeString = getAttribute(name);
366 float result = parseTimeOffset(timeString, &ok);
367 if (ok)
368 return result;
369 return valueOnError;
370 }
371
setTimeOffsetAttribute(const QualifiedName & name,float value)372 void HTMLMediaElement::setTimeOffsetAttribute(const QualifiedName& name, float value)
373 {
374 setAttribute(name, serializeTimeOffset(value));
375 }
376
error() const377 PassRefPtr<MediaError> HTMLMediaElement::error() const
378 {
379 return m_error;
380 }
381
src() const382 KURL HTMLMediaElement::src() const
383 {
384 return document()->completeURL(getAttribute(srcAttr));
385 }
386
setSrc(const String & url)387 void HTMLMediaElement::setSrc(const String& url)
388 {
389 setAttribute(srcAttr, url);
390 }
391
currentSrc() const392 String HTMLMediaElement::currentSrc() const
393 {
394 return m_currentSrc;
395 }
396
networkState() const397 HTMLMediaElement::NetworkState HTMLMediaElement::networkState() const
398 {
399 return m_networkState;
400 }
401
canPlayType(const String & mimeType) const402 String HTMLMediaElement::canPlayType(const String& mimeType) const
403 {
404 MediaPlayer::SupportsType support = MediaPlayer::supportsType(ContentType(mimeType));
405 String canPlay;
406
407 // 4.8.10.3
408 switch (support)
409 {
410 case MediaPlayer::IsNotSupported:
411 canPlay = "";
412 break;
413 case MediaPlayer::MayBeSupported:
414 canPlay = "maybe";
415 break;
416 case MediaPlayer::IsSupported:
417 canPlay = "probably";
418 break;
419 }
420
421 return canPlay;
422 }
423
load(bool isUserGesture,ExceptionCode & ec)424 void HTMLMediaElement::load(bool isUserGesture, ExceptionCode& ec)
425 {
426 if (m_restrictions & RequireUserGestureForLoadRestriction && !isUserGesture)
427 ec = INVALID_STATE_ERR;
428 else {
429 prepareForLoad();
430 loadInternal();
431 }
432 }
433
prepareForLoad()434 void HTMLMediaElement::prepareForLoad()
435 {
436 // Perform the cleanup required for the resource load algorithm to run.
437 stopPeriodicTimers();
438 m_loadTimer.stop();
439 m_sentStalledEvent = false;
440 m_haveFiredLoadedData = false;
441
442 // 2 - Abort any already-running instance of the resource selection algorithm for this element.
443 m_currentSourceNode = 0;
444
445 // 3 - If there are any tasks from the media element's media element event task source in
446 // one of the task queues, then remove those tasks.
447 cancelPendingEventsAndCallbacks();
448 }
449
loadInternal()450 void HTMLMediaElement::loadInternal()
451 {
452 // If the load() method for this element is already being invoked, then abort these steps.
453 if (m_processingLoad)
454 return;
455 m_processingLoad = true;
456
457 // Steps 1 and 2 were done in prepareForLoad()
458
459 // 3 - If the media element's networkState is set to NETWORK_LOADING or NETWORK_IDLE, queue
460 // a task to fire a simple event named abort at the media element.
461 if (m_networkState == NETWORK_LOADING || m_networkState == NETWORK_IDLE)
462 scheduleEvent(eventNames().abortEvent);
463
464 // 4
465 if (m_networkState != NETWORK_EMPTY) {
466 m_networkState = NETWORK_EMPTY;
467 m_readyState = HAVE_NOTHING;
468 m_paused = true;
469 m_seeking = false;
470 if (m_player) {
471 m_player->pause();
472 m_playing = false;
473 m_player->seek(0);
474 }
475 scheduleEvent(eventNames().emptiedEvent);
476 }
477
478 // 5 - Set the playbackRate attribute to the value of the defaultPlaybackRate attribute.
479 setPlaybackRate(defaultPlaybackRate());
480
481 // 6 - Set the error attribute to null and the autoplaying flag to true.
482 m_error = 0;
483 m_autoplaying = true;
484
485 m_playedTimeRanges = TimeRanges::create();
486 m_lastSeekTime = 0;
487 m_closedCaptionsVisible = false;
488
489 // 7 - Invoke the media element's resource selection algorithm.
490 selectMediaResource();
491 m_processingLoad = false;
492 }
493
selectMediaResource()494 void HTMLMediaElement::selectMediaResource()
495 {
496 // 1 - Set the networkState to NETWORK_NO_SOURCE
497 m_networkState = NETWORK_NO_SOURCE;
498
499 // 2 - Asynchronously await a stable state.
500
501 // 3 - ... the media element has neither a src attribute ...
502 String mediaSrc = getAttribute(srcAttr);
503 if (!mediaSrc) {
504 // ... nor a source element child: ...
505 Node* node;
506 for (node = firstChild(); node; node = node->nextSibling()) {
507 if (node->hasTagName(sourceTag))
508 break;
509 }
510
511 if (!node) {
512 m_loadState = WaitingForSource;
513
514 // ... set the networkState to NETWORK_EMPTY, and abort these steps
515 m_networkState = NETWORK_EMPTY;
516 ASSERT(!m_delayingTheLoadEvent);
517 return;
518 }
519 }
520
521 // 4
522 m_delayingTheLoadEvent = true;
523 m_networkState = NETWORK_LOADING;
524
525 // 5
526 scheduleEvent(eventNames().loadstartEvent);
527
528 // 6 - If the media element has a src attribute, then run these substeps
529 ContentType contentType("");
530 if (!mediaSrc.isNull()) {
531 KURL mediaURL = document()->completeURL(mediaSrc);
532 if (isSafeToLoadURL(mediaURL, Complain) && dispatchBeforeLoadEvent(mediaURL.string())) {
533 m_loadState = LoadingFromSrcAttr;
534 loadResource(mediaURL, contentType);
535 } else
536 noneSupported();
537
538 return;
539 }
540
541 // Otherwise, the source elements will be used
542 m_currentSourceNode = 0;
543 loadNextSourceChild();
544 }
545
loadNextSourceChild()546 void HTMLMediaElement::loadNextSourceChild()
547 {
548 ContentType contentType("");
549 KURL mediaURL = selectNextSourceChild(&contentType, Complain);
550 if (!mediaURL.isValid()) {
551 waitForSourceChange();
552 return;
553 }
554
555 m_loadState = LoadingFromSourceElement;
556 loadResource(mediaURL, contentType);
557 }
558
loadResource(const KURL & initialURL,ContentType & contentType)559 void HTMLMediaElement::loadResource(const KURL& initialURL, ContentType& contentType)
560 {
561 ASSERT(isSafeToLoadURL(initialURL, Complain));
562
563 Frame* frame = document()->frame();
564 if (!frame)
565 return;
566 FrameLoader* loader = frame->loader();
567 if (!loader)
568 return;
569
570 KURL url(initialURL);
571 if (!loader->willLoadMediaElementURL(url))
572 return;
573
574 // The resource fetch algorithm
575 m_networkState = NETWORK_LOADING;
576
577 m_currentSrc = url;
578
579 if (m_sendProgressEvents)
580 startProgressEventTimer();
581
582 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
583 m_player = MediaPlayer::create(this);
584 #else
585 if (!m_player)
586 m_player = MediaPlayer::create(this);
587 #endif
588
589 m_player->setAutobuffer(autobuffer());
590 m_player->setPreservesPitch(m_webkitPreservesPitch);
591 updateVolume();
592
593 #if PLATFORM(ANDROID)
594 if (isVideo())
595 m_player->setMediaElementType(MediaPlayer::Video);
596 else
597 m_player->setMediaElementType(MediaPlayer::Audio);
598 #endif
599 m_player->load(m_currentSrc, contentType);
600
601 if (isVideo() && m_player->canLoadPoster()) {
602 KURL posterUrl = static_cast<HTMLVideoElement*>(this)->poster();
603 if (!posterUrl.isEmpty())
604 m_player->setPoster(posterUrl);
605 }
606
607 if (renderer())
608 renderer()->updateFromElement();
609 }
610
isSafeToLoadURL(const KURL & url,InvalidSourceAction actionIfInvalid)611 bool HTMLMediaElement::isSafeToLoadURL(const KURL& url, InvalidSourceAction actionIfInvalid)
612 {
613 Frame* frame = document()->frame();
614 FrameLoader* loader = frame ? frame->loader() : 0;
615
616 // don't allow remote to local urls, and check with the frame loader client.
617 if (!loader || !SecurityOrigin::canLoad(url, String(), document())) {
618 if (actionIfInvalid == Complain)
619 FrameLoader::reportLocalLoadFailed(frame, url.string());
620 return false;
621 }
622
623 return true;
624 }
625
startProgressEventTimer()626 void HTMLMediaElement::startProgressEventTimer()
627 {
628 if (m_progressEventTimer.isActive())
629 return;
630
631 m_previousProgressTime = WTF::currentTime();
632 m_previousProgress = 0;
633 // 350ms is not magic, it is in the spec!
634 m_progressEventTimer.startRepeating(0.350);
635 }
636
waitForSourceChange()637 void HTMLMediaElement::waitForSourceChange()
638 {
639 stopPeriodicTimers();
640 m_loadState = WaitingForSource;
641
642 // 6.17 - Waiting: Set the element's networkState attribute to the NETWORK_NO_SOURCE value
643 m_networkState = NETWORK_NO_SOURCE;
644
645 // 6.18 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
646 m_delayingTheLoadEvent = false;
647 }
648
noneSupported()649 void HTMLMediaElement::noneSupported()
650 {
651 stopPeriodicTimers();
652 m_loadState = WaitingForSource;
653 m_currentSourceNode = 0;
654
655 // 5 - Reaching this step indicates that either the URL failed to resolve, or the media
656 // resource failed to load. Set the error attribute to a new MediaError object whose
657 // code attribute is set to MEDIA_ERR_SRC_NOT_SUPPORTED.
658 m_error = MediaError::create(MediaError::MEDIA_ERR_SRC_NOT_SUPPORTED);
659
660 // 6 - Set the element's networkState attribute to the NETWORK_NO_SOURCE value.
661 m_networkState = NETWORK_NO_SOURCE;
662
663 // 7 - Queue a task to fire a progress event called error at the media element, in
664 // the context of the fetching process that was used to try to obtain the media
665 // resource in the resource fetch algorithm.
666 scheduleEvent(eventNames().errorEvent);
667
668 // 8 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
669 m_delayingTheLoadEvent = false;
670
671 // 9 -Abort these steps. Until the load() method is invoked, the element won't attempt to load another resource.
672
673 if (isVideo())
674 static_cast<HTMLVideoElement*>(this)->updatePosterImage();
675 if (renderer())
676 renderer()->updateFromElement();
677 }
678
mediaEngineError(PassRefPtr<MediaError> err)679 void HTMLMediaElement::mediaEngineError(PassRefPtr<MediaError> err)
680 {
681 // 1 - The user agent should cancel the fetching process.
682 stopPeriodicTimers();
683 m_loadState = WaitingForSource;
684
685 // 2 - Set the error attribute to a new MediaError object whose code attribute is
686 // set to MEDIA_ERR_NETWORK/MEDIA_ERR_DECODE.
687 m_error = err;
688
689 // 3 - Queue a task to fire a progress event called error at the media element, in
690 // the context of the fetching process started by this instance of this algorithm.
691 scheduleEvent(eventNames().errorEvent);
692
693 // 4 - Set the element's networkState attribute to the NETWORK_EMPTY value and queue a
694 // task to fire a simple event called emptied at the element.
695 m_networkState = NETWORK_EMPTY;
696 scheduleEvent(eventNames().emptiedEvent);
697
698 // 5 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
699 m_delayingTheLoadEvent = false;
700
701 // 6 - Abort the overall resource selection algorithm.
702 m_currentSourceNode = 0;
703 }
704
cancelPendingEventsAndCallbacks()705 void HTMLMediaElement::cancelPendingEventsAndCallbacks()
706 {
707 m_pendingEvents.clear();
708
709 for (Node* node = firstChild(); node; node = node->nextSibling()) {
710 if (node->hasTagName(sourceTag))
711 static_cast<HTMLSourceElement*>(node)->cancelPendingErrorEvent();
712 }
713 }
714
mediaPlayerNetworkStateChanged(MediaPlayer *)715 void HTMLMediaElement::mediaPlayerNetworkStateChanged(MediaPlayer*)
716 {
717 beginProcessingMediaPlayerCallback();
718 setNetworkState(m_player->networkState());
719 endProcessingMediaPlayerCallback();
720 }
721
setNetworkState(MediaPlayer::NetworkState state)722 void HTMLMediaElement::setNetworkState(MediaPlayer::NetworkState state)
723 {
724 if (state == MediaPlayer::Empty) {
725 // just update the cached state and leave, we can't do anything
726 m_networkState = NETWORK_EMPTY;
727 return;
728 }
729
730 if (state == MediaPlayer::FormatError || state == MediaPlayer::NetworkError || state == MediaPlayer::DecodeError) {
731 stopPeriodicTimers();
732
733 // If we failed while trying to load a <source> element, the movie was never parsed, and there are more
734 // <source> children, schedule the next one
735 if (m_readyState < HAVE_METADATA && m_loadState == LoadingFromSourceElement) {
736 m_currentSourceNode->scheduleErrorEvent();
737 if (havePotentialSourceChild())
738 scheduleNextSourceChild();
739 else
740 waitForSourceChange();
741
742 return;
743 }
744
745 if (state == MediaPlayer::NetworkError)
746 mediaEngineError(MediaError::create(MediaError::MEDIA_ERR_NETWORK));
747 else if (state == MediaPlayer::DecodeError)
748 mediaEngineError(MediaError::create(MediaError::MEDIA_ERR_DECODE));
749 else if (state == MediaPlayer::FormatError && m_loadState == LoadingFromSrcAttr)
750 noneSupported();
751
752 if (isVideo())
753 static_cast<HTMLVideoElement*>(this)->updatePosterImage();
754
755 return;
756 }
757
758 if (state == MediaPlayer::Idle) {
759 if (m_networkState > NETWORK_IDLE) {
760 stopPeriodicTimers();
761 scheduleEvent(eventNames().suspendEvent);
762 }
763 m_networkState = NETWORK_IDLE;
764 }
765
766 if (state == MediaPlayer::Loading) {
767 if (m_networkState < NETWORK_LOADING || m_networkState == NETWORK_NO_SOURCE)
768 startProgressEventTimer();
769 m_networkState = NETWORK_LOADING;
770 }
771
772 if (state == MediaPlayer::Loaded) {
773 NetworkState oldState = m_networkState;
774
775 m_networkState = NETWORK_LOADED;
776 if (oldState < NETWORK_LOADED || oldState == NETWORK_NO_SOURCE) {
777 m_progressEventTimer.stop();
778
779 // Schedule one last progress event so we guarantee that at least one is fired
780 // for files that load very quickly.
781 scheduleEvent(eventNames().progressEvent);
782
783 // Check to see if readyState changes need to be dealt with before sending the
784 // 'load' event so we report 'canplaythrough' first. This is necessary because a
785 // media engine reports readyState and networkState changes separately
786 MediaPlayer::ReadyState currentState = m_player->readyState();
787 if (static_cast<ReadyState>(currentState) != m_readyState)
788 setReadyState(currentState);
789
790 scheduleEvent(eventNames().loadEvent);
791 }
792 }
793 }
794
mediaPlayerReadyStateChanged(MediaPlayer *)795 void HTMLMediaElement::mediaPlayerReadyStateChanged(MediaPlayer*)
796 {
797 beginProcessingMediaPlayerCallback();
798
799 setReadyState(m_player->readyState());
800
801 endProcessingMediaPlayerCallback();
802 }
803
setReadyState(MediaPlayer::ReadyState state)804 void HTMLMediaElement::setReadyState(MediaPlayer::ReadyState state)
805 {
806 // Set "wasPotentiallyPlaying" BEFORE updating m_readyState, potentiallyPlaying() uses it
807 bool wasPotentiallyPlaying = potentiallyPlaying();
808
809 ReadyState oldState = m_readyState;
810 m_readyState = static_cast<ReadyState>(state);
811
812 if (m_readyState == oldState)
813 return;
814
815 if (m_networkState == NETWORK_EMPTY)
816 return;
817
818 if (m_seeking) {
819 // 4.8.10.10, step 8
820 if (wasPotentiallyPlaying && m_readyState < HAVE_FUTURE_DATA)
821 scheduleEvent(eventNames().waitingEvent);
822
823 // 4.8.10.10, step 9
824 if (m_readyState < HAVE_CURRENT_DATA) {
825 if (oldState >= HAVE_CURRENT_DATA)
826 scheduleEvent(eventNames().seekingEvent);
827 } else {
828 // 4.8.10.10 step 12 & 13.
829 finishSeek();
830 }
831
832 } else {
833 if (wasPotentiallyPlaying && m_readyState < HAVE_FUTURE_DATA) {
834 // 4.8.10.9
835 scheduleTimeupdateEvent(false);
836 scheduleEvent(eventNames().waitingEvent);
837 }
838 }
839
840 if (m_readyState >= HAVE_METADATA && oldState < HAVE_METADATA) {
841 scheduleEvent(eventNames().durationchangeEvent);
842 scheduleEvent(eventNames().loadedmetadataEvent);
843
844 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
845 if (renderer() && renderer()->isVideo()) {
846 toRenderVideo(renderer())->videoSizeChanged();
847 }
848 #endif
849 m_delayingTheLoadEvent = false;
850 m_player->seek(0);
851 }
852
853 bool shouldUpdatePosterImage = false;
854
855 // 4.8.10.7 says loadeddata is sent only when the new state *is* HAVE_CURRENT_DATA: "If the
856 // previous ready state was HAVE_METADATA and the new ready state is HAVE_CURRENT_DATA",
857 // but the event table at the end of the spec says it is sent when: "readyState newly
858 // increased to HAVE_CURRENT_DATA or greater for the first time"
859 // We go with the later because it seems useful to count on getting this event
860 if (m_readyState >= HAVE_CURRENT_DATA && oldState < HAVE_CURRENT_DATA && !m_haveFiredLoadedData) {
861 m_haveFiredLoadedData = true;
862 shouldUpdatePosterImage = true;
863 scheduleEvent(eventNames().loadeddataEvent);
864 }
865
866 bool isPotentiallyPlaying = potentiallyPlaying();
867 if (m_readyState == HAVE_FUTURE_DATA && oldState <= HAVE_CURRENT_DATA) {
868 scheduleEvent(eventNames().canplayEvent);
869 if (isPotentiallyPlaying)
870 scheduleEvent(eventNames().playingEvent);
871 shouldUpdatePosterImage = true;
872 }
873
874 if (m_readyState == HAVE_ENOUGH_DATA && oldState < HAVE_ENOUGH_DATA) {
875 if (oldState <= HAVE_CURRENT_DATA)
876 scheduleEvent(eventNames().canplayEvent);
877
878 scheduleEvent(eventNames().canplaythroughEvent);
879
880 if (isPotentiallyPlaying && oldState <= HAVE_CURRENT_DATA)
881 scheduleEvent(eventNames().playingEvent);
882
883 if (m_autoplaying && m_paused && autoplay()) {
884 m_paused = false;
885 scheduleEvent(eventNames().playEvent);
886 scheduleEvent(eventNames().playingEvent);
887 }
888
889 shouldUpdatePosterImage = true;
890 }
891
892 if (shouldUpdatePosterImage && isVideo())
893 static_cast<HTMLVideoElement*>(this)->updatePosterImage();
894
895 updatePlayState();
896 }
897
progressEventTimerFired(Timer<HTMLMediaElement> *)898 void HTMLMediaElement::progressEventTimerFired(Timer<HTMLMediaElement>*)
899 {
900 ASSERT(m_player);
901 if (m_networkState == NETWORK_EMPTY || m_networkState >= NETWORK_LOADED)
902 return;
903
904 unsigned progress = m_player->bytesLoaded();
905 double time = WTF::currentTime();
906 double timedelta = time - m_previousProgressTime;
907
908 if (progress == m_previousProgress) {
909 if (timedelta > 3.0 && !m_sentStalledEvent) {
910 scheduleEvent(eventNames().stalledEvent);
911 m_sentStalledEvent = true;
912 }
913 } else {
914 scheduleEvent(eventNames().progressEvent);
915 m_previousProgress = progress;
916 m_previousProgressTime = time;
917 m_sentStalledEvent = false;
918 if (renderer())
919 renderer()->updateFromElement();
920 }
921 }
922
rewind(float timeDelta)923 void HTMLMediaElement::rewind(float timeDelta)
924 {
925 ExceptionCode e;
926 setCurrentTime(max(currentTime() - timeDelta, minTimeSeekable()), e);
927 }
928
returnToRealtime()929 void HTMLMediaElement::returnToRealtime()
930 {
931 ExceptionCode e;
932 setCurrentTime(maxTimeSeekable(), e);
933 }
934
addPlayedRange(float start,float end)935 void HTMLMediaElement::addPlayedRange(float start, float end)
936 {
937 if (!m_playedTimeRanges)
938 m_playedTimeRanges = TimeRanges::create();
939 m_playedTimeRanges->add(start, end);
940 }
941
supportsSave() const942 bool HTMLMediaElement::supportsSave() const
943 {
944 return m_player ? m_player->supportsSave() : false;
945 }
946
seek(float time,ExceptionCode & ec)947 void HTMLMediaElement::seek(float time, ExceptionCode& ec)
948 {
949 // 4.8.10.10. Seeking
950 // 1
951 if (m_readyState == HAVE_NOTHING || !m_player) {
952 ec = INVALID_STATE_ERR;
953 return;
954 }
955
956 // 2
957 time = min(time, duration());
958
959 // 3
960 time = max(time, 0.0f);
961
962 // 4
963 RefPtr<TimeRanges> seekableRanges = seekable();
964 if (!seekableRanges->contain(time)) {
965 ec = INDEX_SIZE_ERR;
966 return;
967 }
968
969 // avoid generating events when the time won't actually change
970 float now = currentTime();
971 if (time == now)
972 return;
973
974 // 5
975 if (m_playing) {
976 if (m_lastSeekTime < now)
977 addPlayedRange(m_lastSeekTime, now);
978 }
979 m_lastSeekTime = time;
980
981 // 6 - set the seeking flag, it will be cleared when the engine tells is the time has actually changed
982 m_seeking = true;
983
984 // 7
985 scheduleTimeupdateEvent(false);
986
987 // 8 - this is covered, if necessary, when the engine signals a readystate change
988
989 // 10
990 m_player->seek(time);
991 m_sentEndEvent = false;
992 }
993
finishSeek()994 void HTMLMediaElement::finishSeek()
995 {
996 // 4.8.10.10 Seeking step 12
997 m_seeking = false;
998
999 // 4.8.10.10 Seeking step 13
1000 scheduleEvent(eventNames().seekedEvent);
1001 }
1002
readyState() const1003 HTMLMediaElement::ReadyState HTMLMediaElement::readyState() const
1004 {
1005 return m_readyState;
1006 }
1007
movieLoadType() const1008 MediaPlayer::MovieLoadType HTMLMediaElement::movieLoadType() const
1009 {
1010 return m_player ? m_player->movieLoadType() : MediaPlayer::Unknown;
1011 }
1012
hasAudio() const1013 bool HTMLMediaElement::hasAudio() const
1014 {
1015 return m_player ? m_player->hasAudio() : false;
1016 }
1017
seeking() const1018 bool HTMLMediaElement::seeking() const
1019 {
1020 return m_seeking;
1021 }
1022
1023 // playback state
currentTime() const1024 float HTMLMediaElement::currentTime() const
1025 {
1026 if (!m_player)
1027 return 0;
1028 if (m_seeking)
1029 return m_lastSeekTime;
1030 return m_player->currentTime();
1031 }
1032
setCurrentTime(float time,ExceptionCode & ec)1033 void HTMLMediaElement::setCurrentTime(float time, ExceptionCode& ec)
1034 {
1035 seek(time, ec);
1036 }
1037
startTime() const1038 float HTMLMediaElement::startTime() const
1039 {
1040 if (!m_player)
1041 return 0;
1042 return m_player->startTime();
1043 }
1044
duration() const1045 float HTMLMediaElement::duration() const
1046 {
1047 if (m_readyState >= HAVE_METADATA)
1048 return m_player->duration();
1049
1050 return numeric_limits<float>::quiet_NaN();
1051 }
1052
paused() const1053 bool HTMLMediaElement::paused() const
1054 {
1055 return m_paused;
1056 }
1057
defaultPlaybackRate() const1058 float HTMLMediaElement::defaultPlaybackRate() const
1059 {
1060 return m_defaultPlaybackRate;
1061 }
1062
setDefaultPlaybackRate(float rate)1063 void HTMLMediaElement::setDefaultPlaybackRate(float rate)
1064 {
1065 if (m_defaultPlaybackRate != rate) {
1066 m_defaultPlaybackRate = rate;
1067 scheduleEvent(eventNames().ratechangeEvent);
1068 }
1069 }
1070
playbackRate() const1071 float HTMLMediaElement::playbackRate() const
1072 {
1073 return m_player ? m_player->rate() : 0;
1074 }
1075
setPlaybackRate(float rate)1076 void HTMLMediaElement::setPlaybackRate(float rate)
1077 {
1078 if (m_playbackRate != rate) {
1079 m_playbackRate = rate;
1080 scheduleEvent(eventNames().ratechangeEvent);
1081 }
1082 if (m_player && potentiallyPlaying() && m_player->rate() != rate)
1083 m_player->setRate(rate);
1084 }
1085
webkitPreservesPitch() const1086 bool HTMLMediaElement::webkitPreservesPitch() const
1087 {
1088 return m_webkitPreservesPitch;
1089 }
1090
setWebkitPreservesPitch(bool preservesPitch)1091 void HTMLMediaElement::setWebkitPreservesPitch(bool preservesPitch)
1092 {
1093 m_webkitPreservesPitch = preservesPitch;
1094
1095 if (!m_player)
1096 return;
1097
1098 m_player->setPreservesPitch(preservesPitch);
1099 }
1100
ended() const1101 bool HTMLMediaElement::ended() const
1102 {
1103 // 4.8.10.8 Playing the media resource
1104 // The ended attribute must return true if the media element has ended
1105 // playback and the direction of playback is forwards, and false otherwise.
1106 return endedPlayback() && m_playbackRate > 0;
1107 }
1108
autoplay() const1109 bool HTMLMediaElement::autoplay() const
1110 {
1111 return hasAttribute(autoplayAttr);
1112 }
1113
setAutoplay(bool b)1114 void HTMLMediaElement::setAutoplay(bool b)
1115 {
1116 setBooleanAttribute(autoplayAttr, b);
1117 }
1118
autobuffer() const1119 bool HTMLMediaElement::autobuffer() const
1120 {
1121 return hasAttribute(autobufferAttr);
1122 }
1123
setAutobuffer(bool b)1124 void HTMLMediaElement::setAutobuffer(bool b)
1125 {
1126 setBooleanAttribute(autobufferAttr, b);
1127 }
1128
play(bool isUserGesture)1129 void HTMLMediaElement::play(bool isUserGesture)
1130 {
1131 if (m_restrictions & RequireUserGestureForRateChangeRestriction && !isUserGesture)
1132 return;
1133
1134 playInternal();
1135 }
1136
playInternal()1137 void HTMLMediaElement::playInternal()
1138 {
1139 // 4.8.10.9. Playing the media resource
1140 if (!m_player || m_networkState == NETWORK_EMPTY)
1141 scheduleLoad();
1142
1143 if (endedPlayback()) {
1144 ExceptionCode unused;
1145 seek(0, unused);
1146 }
1147
1148 setPlaybackRate(defaultPlaybackRate());
1149
1150 if (m_paused) {
1151 m_paused = false;
1152 scheduleEvent(eventNames().playEvent);
1153
1154 if (m_readyState <= HAVE_CURRENT_DATA)
1155 scheduleEvent(eventNames().waitingEvent);
1156 else if (m_readyState >= HAVE_FUTURE_DATA)
1157 scheduleEvent(eventNames().playingEvent);
1158 }
1159 m_autoplaying = false;
1160
1161 updatePlayState();
1162 }
1163
pause(bool isUserGesture)1164 void HTMLMediaElement::pause(bool isUserGesture)
1165 {
1166 if (m_restrictions & RequireUserGestureForRateChangeRestriction && !isUserGesture)
1167 return;
1168
1169 pauseInternal();
1170 }
1171
1172
pauseInternal()1173 void HTMLMediaElement::pauseInternal()
1174 {
1175 // 4.8.10.9. Playing the media resource
1176 if (!m_player || m_networkState == NETWORK_EMPTY)
1177 scheduleLoad();
1178
1179 m_autoplaying = false;
1180
1181 if (!m_paused) {
1182 m_paused = true;
1183 scheduleTimeupdateEvent(false);
1184 scheduleEvent(eventNames().pauseEvent);
1185 }
1186
1187 updatePlayState();
1188 }
1189
loop() const1190 bool HTMLMediaElement::loop() const
1191 {
1192 return hasAttribute(loopAttr);
1193 }
1194
setLoop(bool b)1195 void HTMLMediaElement::setLoop(bool b)
1196 {
1197 setBooleanAttribute(loopAttr, b);
1198 }
1199
controls() const1200 bool HTMLMediaElement::controls() const
1201 {
1202 Frame* frame = document()->frame();
1203
1204 // always show controls when scripting is disabled
1205 if (frame && !frame->script()->canExecuteScripts())
1206 return true;
1207
1208 return hasAttribute(controlsAttr);
1209 }
1210
setControls(bool b)1211 void HTMLMediaElement::setControls(bool b)
1212 {
1213 setBooleanAttribute(controlsAttr, b);
1214 }
1215
volume() const1216 float HTMLMediaElement::volume() const
1217 {
1218 return m_volume;
1219 }
1220
setVolume(float vol,ExceptionCode & ec)1221 void HTMLMediaElement::setVolume(float vol, ExceptionCode& ec)
1222 {
1223 if (vol < 0.0f || vol > 1.0f) {
1224 ec = INDEX_SIZE_ERR;
1225 return;
1226 }
1227
1228 if (m_volume != vol) {
1229 m_volume = vol;
1230 updateVolume();
1231 scheduleEvent(eventNames().volumechangeEvent);
1232 }
1233 }
1234
muted() const1235 bool HTMLMediaElement::muted() const
1236 {
1237 return m_muted;
1238 }
1239
setMuted(bool muted)1240 void HTMLMediaElement::setMuted(bool muted)
1241 {
1242 if (m_muted != muted) {
1243 m_muted = muted;
1244 // Avoid recursion when the player reports volume changes.
1245 if (!processingMediaPlayerCallback()) {
1246 if (m_player && m_player->supportsMuting()) {
1247 m_player->setMuted(m_muted);
1248 if (renderer())
1249 renderer()->updateFromElement();
1250 } else
1251 updateVolume();
1252 }
1253 scheduleEvent(eventNames().volumechangeEvent);
1254 }
1255 }
1256
togglePlayState()1257 void HTMLMediaElement::togglePlayState()
1258 {
1259 // We can safely call the internal play/pause methods, which don't check restrictions, because
1260 // this method is only called from the built-in media controller
1261 if (canPlay())
1262 playInternal();
1263 else
1264 pauseInternal();
1265 }
1266
beginScrubbing()1267 void HTMLMediaElement::beginScrubbing()
1268 {
1269 if (!paused()) {
1270 if (ended()) {
1271 // Because a media element stays in non-paused state when it reaches end, playback resumes
1272 // when the slider is dragged from the end to another position unless we pause first. Do
1273 // a "hard pause" so an event is generated, since we want to stay paused after scrubbing finishes.
1274 pause(processingUserGesture());
1275 } else {
1276 // Not at the end but we still want to pause playback so the media engine doesn't try to
1277 // continue playing during scrubbing. Pause without generating an event as we will
1278 // unpause after scrubbing finishes.
1279 setPausedInternal(true);
1280 }
1281 }
1282 }
1283
endScrubbing()1284 void HTMLMediaElement::endScrubbing()
1285 {
1286 if (m_pausedInternal)
1287 setPausedInternal(false);
1288 }
1289
1290 // The spec says to fire periodic timeupdate events (those sent while playing) every
1291 // "15 to 250ms", we choose the slowest frequency
1292 static const double maxTimeupdateEventFrequency = 0.25;
1293
startPlaybackProgressTimer()1294 void HTMLMediaElement::startPlaybackProgressTimer()
1295 {
1296 if (m_playbackProgressTimer.isActive())
1297 return;
1298
1299 m_previousProgressTime = WTF::currentTime();
1300 m_previousProgress = 0;
1301 m_playbackProgressTimer.startRepeating(maxTimeupdateEventFrequency);
1302 }
1303
playbackProgressTimerFired(Timer<HTMLMediaElement> *)1304 void HTMLMediaElement::playbackProgressTimerFired(Timer<HTMLMediaElement>*)
1305 {
1306 ASSERT(m_player);
1307 if (!m_playbackRate)
1308 return;
1309
1310 scheduleTimeupdateEvent(true);
1311
1312 // FIXME: deal with cue ranges here
1313 }
1314
scheduleTimeupdateEvent(bool periodicEvent)1315 void HTMLMediaElement::scheduleTimeupdateEvent(bool periodicEvent)
1316 {
1317 double now = WTF::currentTime();
1318 double timedelta = now - m_lastTimeUpdateEventWallTime;
1319
1320 // throttle the periodic events
1321 if (periodicEvent && timedelta < maxTimeupdateEventFrequency)
1322 return;
1323
1324 // Some media engines make multiple "time changed" callbacks at the same time, but we only want one
1325 // event at a given time so filter here
1326 float movieTime = m_player ? m_player->currentTime() : 0;
1327 if (movieTime != m_lastTimeUpdateEventMovieTime) {
1328 scheduleEvent(eventNames().timeupdateEvent);
1329 m_lastTimeUpdateEventWallTime = now;
1330 m_lastTimeUpdateEventMovieTime = movieTime;
1331 }
1332 }
1333
canPlay() const1334 bool HTMLMediaElement::canPlay() const
1335 {
1336 return paused() || ended() || m_readyState < HAVE_METADATA;
1337 }
1338
percentLoaded() const1339 float HTMLMediaElement::percentLoaded() const
1340 {
1341 if (!m_player)
1342 return 0;
1343 float duration = m_player->duration();
1344
1345 if (!duration || isinf(duration))
1346 return 0;
1347
1348 float buffered = 0;
1349 RefPtr<TimeRanges> timeRanges = m_player->buffered();
1350 for (unsigned i = 0; i < timeRanges->length(); ++i) {
1351 ExceptionCode ignoredException;
1352 float start = timeRanges->start(i, ignoredException);
1353 float end = timeRanges->end(i, ignoredException);
1354 buffered += end - start;
1355 }
1356 return buffered / duration;
1357 }
1358
havePotentialSourceChild()1359 bool HTMLMediaElement::havePotentialSourceChild()
1360 {
1361 // Stash the current <source> node so we can restore it after checking
1362 // to see there is another potential
1363 HTMLSourceElement* currentSourceNode = m_currentSourceNode;
1364 KURL nextURL = selectNextSourceChild(0, DoNothing);
1365 m_currentSourceNode = currentSourceNode;
1366
1367 return nextURL.isValid();
1368 }
1369
selectNextSourceChild(ContentType * contentType,InvalidSourceAction actionIfInvalid)1370 KURL HTMLMediaElement::selectNextSourceChild(ContentType *contentType, InvalidSourceAction actionIfInvalid)
1371 {
1372 KURL mediaURL;
1373 Node* node;
1374 bool lookingForPreviousNode = m_currentSourceNode;
1375 bool canUse = false;
1376
1377 for (node = firstChild(); !canUse && node; node = node->nextSibling()) {
1378 if (!node->hasTagName(sourceTag))
1379 continue;
1380
1381 if (lookingForPreviousNode) {
1382 if (m_currentSourceNode == static_cast<HTMLSourceElement*>(node))
1383 lookingForPreviousNode = false;
1384 continue;
1385 }
1386
1387 HTMLSourceElement* source = static_cast<HTMLSourceElement*>(node);
1388 if (!source->hasAttribute(srcAttr))
1389 goto check_again;
1390
1391 if (source->hasAttribute(mediaAttr)) {
1392 MediaQueryEvaluator screenEval("screen", document()->frame(), renderer() ? renderer()->style() : 0);
1393 RefPtr<MediaList> media = MediaList::createAllowingDescriptionSyntax(source->media());
1394 if (!screenEval.eval(media.get()))
1395 goto check_again;
1396 }
1397
1398 if (source->hasAttribute(typeAttr)) {
1399 if (!MediaPlayer::supportsType(ContentType(source->type())))
1400 goto check_again;
1401 }
1402
1403 // Is it safe to load this url?
1404 mediaURL = source->src();
1405 if (!mediaURL.isValid() || !isSafeToLoadURL(mediaURL, actionIfInvalid) || !dispatchBeforeLoadEvent(mediaURL.string()))
1406 goto check_again;
1407
1408 // Making it this far means the <source> looks reasonable
1409 canUse = true;
1410 if (contentType)
1411 *contentType = ContentType(source->type());
1412
1413 check_again:
1414 if (!canUse && actionIfInvalid == Complain)
1415 source->scheduleErrorEvent();
1416 m_currentSourceNode = static_cast<HTMLSourceElement*>(node);
1417 }
1418
1419 if (!canUse)
1420 m_currentSourceNode = 0;
1421 return canUse ? mediaURL : KURL();
1422 }
1423
mediaPlayerTimeChanged(MediaPlayer *)1424 void HTMLMediaElement::mediaPlayerTimeChanged(MediaPlayer*)
1425 {
1426 beginProcessingMediaPlayerCallback();
1427
1428 // Always call scheduleTimeupdateEvent when the media engine reports a time discontinuity,
1429 // it will only queue a 'timeupdate' event if we haven't already posted one at the current
1430 // movie time.
1431 scheduleTimeupdateEvent(false);
1432
1433 // 4.8.10.10 step 12 & 13. Needed if no ReadyState change is associated with the seek.
1434 if (m_readyState >= HAVE_CURRENT_DATA && m_seeking)
1435 finishSeek();
1436
1437 float now = currentTime();
1438 float dur = duration();
1439 if (!isnan(dur) && dur && now >= dur) {
1440 if (loop()) {
1441 ExceptionCode ignoredException;
1442 m_sentEndEvent = false;
1443 seek(0, ignoredException);
1444 } else {
1445 if (!m_sentEndEvent) {
1446 m_sentEndEvent = true;
1447 scheduleEvent(eventNames().endedEvent);
1448 }
1449 }
1450 }
1451 else
1452 m_sentEndEvent = false;
1453
1454 updatePlayState();
1455 endProcessingMediaPlayerCallback();
1456 }
1457
mediaPlayerVolumeChanged(MediaPlayer *)1458 void HTMLMediaElement::mediaPlayerVolumeChanged(MediaPlayer*)
1459 {
1460 beginProcessingMediaPlayerCallback();
1461 if (m_player)
1462 m_volume = m_player->volume();
1463 updateVolume();
1464 endProcessingMediaPlayerCallback();
1465 }
1466
mediaPlayerMuteChanged(MediaPlayer *)1467 void HTMLMediaElement::mediaPlayerMuteChanged(MediaPlayer*)
1468 {
1469 beginProcessingMediaPlayerCallback();
1470 if (m_player)
1471 setMuted(m_player->muted());
1472 endProcessingMediaPlayerCallback();
1473 }
1474
mediaPlayerDurationChanged(MediaPlayer *)1475 void HTMLMediaElement::mediaPlayerDurationChanged(MediaPlayer*)
1476 {
1477 beginProcessingMediaPlayerCallback();
1478 scheduleEvent(eventNames().durationchangeEvent);
1479 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
1480 if (renderer()) {
1481 renderer()->updateFromElement();
1482 if (renderer()->isVideo())
1483 toRenderVideo(renderer())->videoSizeChanged();
1484 }
1485 #endif
1486 endProcessingMediaPlayerCallback();
1487 }
1488
mediaPlayerRateChanged(MediaPlayer *)1489 void HTMLMediaElement::mediaPlayerRateChanged(MediaPlayer*)
1490 {
1491 beginProcessingMediaPlayerCallback();
1492 // Stash the rate in case the one we tried to set isn't what the engine is
1493 // using (eg. it can't handle the rate we set)
1494 m_playbackRate = m_player->rate();
1495 endProcessingMediaPlayerCallback();
1496 }
1497
mediaPlayerSawUnsupportedTracks(MediaPlayer *)1498 void HTMLMediaElement::mediaPlayerSawUnsupportedTracks(MediaPlayer*)
1499 {
1500 // The MediaPlayer came across content it cannot completely handle.
1501 // This is normally acceptable except when we are in a standalone
1502 // MediaDocument. If so, tell the document what has happened.
1503 if (ownerDocument()->isMediaDocument()) {
1504 MediaDocument* mediaDocument = static_cast<MediaDocument*>(ownerDocument());
1505 mediaDocument->mediaElementSawUnsupportedTracks();
1506 }
1507 }
1508
1509 // MediaPlayerPresentation methods
mediaPlayerRepaint(MediaPlayer *)1510 void HTMLMediaElement::mediaPlayerRepaint(MediaPlayer*)
1511 {
1512 beginProcessingMediaPlayerCallback();
1513 if (renderer())
1514 renderer()->repaint();
1515 endProcessingMediaPlayerCallback();
1516 }
1517
mediaPlayerSizeChanged(MediaPlayer *)1518 void HTMLMediaElement::mediaPlayerSizeChanged(MediaPlayer*)
1519 {
1520 beginProcessingMediaPlayerCallback();
1521 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
1522 if (renderer() && renderer()->isVideo())
1523 toRenderVideo(renderer())->videoSizeChanged();
1524 #endif
1525 endProcessingMediaPlayerCallback();
1526 }
1527
1528 #if USE(ACCELERATED_COMPOSITING)
mediaPlayerRenderingCanBeAccelerated(MediaPlayer *)1529 bool HTMLMediaElement::mediaPlayerRenderingCanBeAccelerated(MediaPlayer*)
1530 {
1531 if (renderer() && renderer()->isVideo()) {
1532 ASSERT(renderer()->view());
1533 return renderer()->view()->compositor()->canAccelerateVideoRendering(toRenderVideo(renderer()));
1534 }
1535 return false;
1536 }
1537
mediaPlayerGraphicsLayer(MediaPlayer *)1538 GraphicsLayer* HTMLMediaElement::mediaPlayerGraphicsLayer(MediaPlayer*)
1539 {
1540 if (renderer() && renderer()->isVideo())
1541 return toRenderVideo(renderer())->videoGraphicsLayer();
1542 return 0;
1543 }
1544 #endif
1545
buffered() const1546 PassRefPtr<TimeRanges> HTMLMediaElement::buffered() const
1547 {
1548 if (!m_player)
1549 return TimeRanges::create();
1550 return m_player->buffered();
1551 }
1552
played()1553 PassRefPtr<TimeRanges> HTMLMediaElement::played()
1554 {
1555 if (m_playing) {
1556 float time = currentTime();
1557 if (time > m_lastSeekTime)
1558 addPlayedRange(m_lastSeekTime, time);
1559 }
1560
1561 if (!m_playedTimeRanges)
1562 m_playedTimeRanges = TimeRanges::create();
1563
1564 return m_playedTimeRanges->copy();
1565 }
1566
seekable() const1567 PassRefPtr<TimeRanges> HTMLMediaElement::seekable() const
1568 {
1569 // FIXME real ranges support
1570 if (!maxTimeSeekable())
1571 return TimeRanges::create();
1572 return TimeRanges::create(minTimeSeekable(), maxTimeSeekable());
1573 }
1574
potentiallyPlaying() const1575 bool HTMLMediaElement::potentiallyPlaying() const
1576 {
1577 return m_readyState >= HAVE_FUTURE_DATA && couldPlayIfEnoughData();
1578 }
1579
couldPlayIfEnoughData() const1580 bool HTMLMediaElement::couldPlayIfEnoughData() const
1581 {
1582 return !paused() && !endedPlayback() && !stoppedDueToErrors() && !pausedForUserInteraction();
1583 }
1584
endedPlayback() const1585 bool HTMLMediaElement::endedPlayback() const
1586 {
1587 float dur = duration();
1588 if (!m_player || isnan(dur))
1589 return false;
1590
1591 // 4.8.10.8 Playing the media resource
1592
1593 // A media element is said to have ended playback when the element's
1594 // readyState attribute is HAVE_METADATA or greater,
1595 if (m_readyState < HAVE_METADATA)
1596 return false;
1597
1598 // and the current playback position is the end of the media resource and the direction
1599 // of playback is forwards and the media element does not have a loop attribute specified,
1600 float now = currentTime();
1601 if (m_playbackRate > 0)
1602 return now >= dur && !loop();
1603
1604 // or the current playback position is the earliest possible position and the direction
1605 // of playback is backwards
1606 if (m_playbackRate < 0)
1607 return now <= 0;
1608
1609 return false;
1610 }
1611
stoppedDueToErrors() const1612 bool HTMLMediaElement::stoppedDueToErrors() const
1613 {
1614 if (m_readyState >= HAVE_METADATA && m_error) {
1615 RefPtr<TimeRanges> seekableRanges = seekable();
1616 if (!seekableRanges->contain(currentTime()))
1617 return true;
1618 }
1619
1620 return false;
1621 }
1622
pausedForUserInteraction() const1623 bool HTMLMediaElement::pausedForUserInteraction() const
1624 {
1625 // return !paused() && m_readyState >= HAVE_FUTURE_DATA && [UA requires a decitions from the user]
1626 return false;
1627 }
1628
minTimeSeekable() const1629 float HTMLMediaElement::minTimeSeekable() const
1630 {
1631 return 0;
1632 }
1633
maxTimeSeekable() const1634 float HTMLMediaElement::maxTimeSeekable() const
1635 {
1636 return m_player ? m_player->maxTimeSeekable() : 0;
1637 }
1638
updateVolume()1639 void HTMLMediaElement::updateVolume()
1640 {
1641 if (!m_player)
1642 return;
1643
1644 // Avoid recursion when the player reports volume changes.
1645 if (!processingMediaPlayerCallback()) {
1646 Page* page = document()->page();
1647 float volumeMultiplier = page ? page->mediaVolume() : 1;
1648
1649 m_player->setVolume(m_muted ? 0 : m_volume * volumeMultiplier);
1650 }
1651
1652 if (renderer())
1653 renderer()->updateFromElement();
1654 }
1655
updatePlayState()1656 void HTMLMediaElement::updatePlayState()
1657 {
1658 if (!m_player)
1659 return;
1660
1661 if (m_pausedInternal) {
1662 if (!m_player->paused())
1663 m_player->pause();
1664 m_playbackProgressTimer.stop();
1665 return;
1666 }
1667
1668 bool shouldBePlaying = potentiallyPlaying();
1669 bool playerPaused = m_player->paused();
1670 if (shouldBePlaying && playerPaused) {
1671 // Set rate before calling play in case the rate was set before the media engine wasn't setup.
1672 // The media engine should just stash the rate since it isn't already playing.
1673 m_player->setRate(m_playbackRate);
1674 m_player->play();
1675 startPlaybackProgressTimer();
1676 m_playing = true;
1677 } else if (!shouldBePlaying && !playerPaused) {
1678 m_player->pause();
1679 m_playbackProgressTimer.stop();
1680 m_playing = false;
1681 float time = currentTime();
1682 if (time > m_lastSeekTime)
1683 addPlayedRange(m_lastSeekTime, time);
1684 } else if (couldPlayIfEnoughData() && playerPaused)
1685 m_player->prepareToPlay();
1686
1687 if (renderer())
1688 renderer()->updateFromElement();
1689 }
1690
setPausedInternal(bool b)1691 void HTMLMediaElement::setPausedInternal(bool b)
1692 {
1693 m_pausedInternal = b;
1694 updatePlayState();
1695 }
1696
stopPeriodicTimers()1697 void HTMLMediaElement::stopPeriodicTimers()
1698 {
1699 m_progressEventTimer.stop();
1700 m_playbackProgressTimer.stop();
1701 }
1702
userCancelledLoad()1703 void HTMLMediaElement::userCancelledLoad()
1704 {
1705 if (m_networkState == NETWORK_EMPTY || m_networkState >= NETWORK_LOADED)
1706 return;
1707
1708 // If the media data fetching process is aborted by the user:
1709
1710 // 1 - The user agent should cancel the fetching process.
1711 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
1712 m_player.clear();
1713 #endif
1714 stopPeriodicTimers();
1715
1716 // 2 - Set the error attribute to a new MediaError object whose code attribute is set to MEDIA_ERR_ABORTED.
1717 m_error = MediaError::create(MediaError::MEDIA_ERR_ABORTED);
1718
1719 // 3 - Queue a task to fire a progress event called abort at the media element, in the context
1720 // of the fetching process started by this instance of this algorithm.
1721 scheduleEvent(eventNames().abortEvent);
1722
1723 // 5 - If the media element's readyState attribute has a value equal to HAVE_NOTHING, set the
1724 // element's networkState attribute to the NETWORK_EMPTY value and queue a task to fire a
1725 // simple event called emptied at the element. Otherwise, set set the element's networkState
1726 // attribute to the NETWORK_IDLE value.
1727 if (m_readyState == HAVE_NOTHING) {
1728 m_networkState = NETWORK_EMPTY;
1729 scheduleEvent(eventNames().emptiedEvent);
1730 }
1731 else
1732 m_networkState = NETWORK_IDLE;
1733
1734 // 6 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
1735 m_delayingTheLoadEvent = false;
1736
1737 // 7 - Abort the overall resource selection algorithm.
1738 m_currentSourceNode = 0;
1739 }
1740
documentWillBecomeInactive()1741 void HTMLMediaElement::documentWillBecomeInactive()
1742 {
1743 if (m_isFullscreen)
1744 exitFullscreen();
1745
1746 m_inActiveDocument = false;
1747 userCancelledLoad();
1748
1749 // Stop the playback without generating events
1750 setPausedInternal(true);
1751
1752 if (renderer())
1753 renderer()->updateFromElement();
1754
1755 stopPeriodicTimers();
1756 cancelPendingEventsAndCallbacks();
1757 }
1758
documentDidBecomeActive()1759 void HTMLMediaElement::documentDidBecomeActive()
1760 {
1761 m_inActiveDocument = true;
1762 setPausedInternal(false);
1763
1764 if (m_error && m_error->code() == MediaError::MEDIA_ERR_ABORTED) {
1765 // Restart the load if it was aborted in the middle by moving the document to the page cache.
1766 // m_error is only left at MEDIA_ERR_ABORTED when the document becomes inactive (it is set to
1767 // MEDIA_ERR_ABORTED while the abortEvent is being sent, but cleared immediately afterwards).
1768 // This behavior is not specified but it seems like a sensible thing to do.
1769 ExceptionCode ec;
1770 load(processingUserGesture(), ec);
1771 }
1772
1773 if (renderer())
1774 renderer()->updateFromElement();
1775 }
1776
mediaVolumeDidChange()1777 void HTMLMediaElement::mediaVolumeDidChange()
1778 {
1779 updateVolume();
1780 }
1781
screenRect()1782 const IntRect HTMLMediaElement::screenRect()
1783 {
1784 IntRect elementRect;
1785 if (renderer())
1786 elementRect = renderer()->view()->frameView()->contentsToScreen(renderer()->absoluteBoundingBoxRect());
1787 return elementRect;
1788 }
1789
defaultEventHandler(Event * event)1790 void HTMLMediaElement::defaultEventHandler(Event* event)
1791 {
1792 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
1793 RenderObject* r = renderer();
1794 if (!r || !r->isWidget())
1795 return;
1796
1797 Widget* widget = toRenderWidget(r)->widget();
1798 if (widget)
1799 widget->handleEvent(event);
1800 #else
1801 if (renderer() && renderer()->isMedia())
1802 toRenderMedia(renderer())->forwardEvent(event);
1803 if (event->defaultHandled())
1804 return;
1805 HTMLElement::defaultEventHandler(event);
1806 #endif
1807 }
1808
processingUserGesture() const1809 bool HTMLMediaElement::processingUserGesture() const
1810 {
1811 Frame* frame = document()->frame();
1812 FrameLoader* loader = frame ? frame->loader() : 0;
1813
1814 // return 'true' for safety if we don't know the answer
1815 return loader ? loader->isProcessingUserGesture() : true;
1816 }
1817
1818 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
1819
deliverNotification(MediaPlayerProxyNotificationType notification)1820 void HTMLMediaElement::deliverNotification(MediaPlayerProxyNotificationType notification)
1821 {
1822 if (notification == MediaPlayerNotificationPlayPauseButtonPressed) {
1823 togglePlayState();
1824 return;
1825 }
1826
1827 if (m_player)
1828 m_player->deliverNotification(notification);
1829 }
1830
setMediaPlayerProxy(WebMediaPlayerProxy * proxy)1831 void HTMLMediaElement::setMediaPlayerProxy(WebMediaPlayerProxy* proxy)
1832 {
1833 if (m_player)
1834 m_player->setMediaPlayerProxy(proxy);
1835 }
1836
initialURL()1837 String HTMLMediaElement::initialURL()
1838 {
1839 KURL initialSrc = document()->completeURL(getAttribute(srcAttr));
1840
1841 if (!initialSrc.isValid())
1842 initialSrc = selectNextSourceChild(0, DoNothing);
1843
1844 m_currentSrc = initialSrc.string();
1845
1846 return initialSrc;
1847 }
1848
finishParsingChildren()1849 void HTMLMediaElement::finishParsingChildren()
1850 {
1851 HTMLElement::finishParsingChildren();
1852 if (!m_player)
1853 m_player = MediaPlayer::create(this);
1854
1855 document()->updateStyleIfNeeded();
1856 if (m_needWidgetUpdate && renderer())
1857 toRenderEmbeddedObject(renderer())->updateWidget(true);
1858 }
1859
1860 #endif
1861
enterFullscreen()1862 void HTMLMediaElement::enterFullscreen()
1863 {
1864 ASSERT(!m_isFullscreen);
1865 if (document() && document()->page()) {
1866 document()->page()->chrome()->client()->enterFullscreenForNode(this);
1867 scheduleEvent(eventNames().webkitbeginfullscreenEvent);
1868 m_isFullscreen = true;
1869 }
1870 }
1871
exitFullscreen()1872 void HTMLMediaElement::exitFullscreen()
1873 {
1874 ASSERT(m_isFullscreen);
1875 if (document() && document()->page()) {
1876 document()->page()->chrome()->client()->exitFullscreenForNode(this);
1877 scheduleEvent(eventNames().webkitendfullscreenEvent);
1878 }
1879 m_isFullscreen = false;
1880 }
1881
platformMedia() const1882 PlatformMedia HTMLMediaElement::platformMedia() const
1883 {
1884 return m_player ? m_player->platformMedia() : NoPlatformMedia;
1885 }
1886
hasClosedCaptions() const1887 bool HTMLMediaElement::hasClosedCaptions() const
1888 {
1889 return m_player && m_player->hasClosedCaptions();
1890 }
1891
closedCaptionsVisible() const1892 bool HTMLMediaElement::closedCaptionsVisible() const
1893 {
1894 return m_closedCaptionsVisible;
1895 }
1896
setClosedCaptionsVisible(bool closedCaptionVisible)1897 void HTMLMediaElement::setClosedCaptionsVisible(bool closedCaptionVisible)
1898 {
1899 if (!m_player ||!hasClosedCaptions())
1900 return;
1901
1902 m_closedCaptionsVisible = closedCaptionVisible;
1903 m_player->setClosedCaptionsVisible(closedCaptionVisible);
1904 if (renderer())
1905 renderer()->updateFromElement();
1906 }
1907
setWebkitClosedCaptionsVisible(bool visible)1908 void HTMLMediaElement::setWebkitClosedCaptionsVisible(bool visible)
1909 {
1910 setClosedCaptionsVisible(visible);
1911 }
1912
webkitClosedCaptionsVisible() const1913 bool HTMLMediaElement::webkitClosedCaptionsVisible() const
1914 {
1915 return closedCaptionsVisible();
1916 }
1917
1918
webkitHasClosedCaptions() const1919 bool HTMLMediaElement::webkitHasClosedCaptions() const
1920 {
1921 return hasClosedCaptions();
1922 }
1923
1924 }
1925
1926 #endif
1927