1 /*
2 * Copyright (C) 2007, 2008 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 "CSSHelper.h"
32 #include "CSSPropertyNames.h"
33 #include "CSSValueKeywords.h"
34 #include "Event.h"
35 #include "EventNames.h"
36 #include "ExceptionCode.h"
37 #include "HTMLDocument.h"
38 #include "HTMLNames.h"
39 #include "HTMLSourceElement.h"
40 #include "HTMLVideoElement.h"
41 #include <limits>
42 #include "MediaError.h"
43 #include "MediaList.h"
44 #include "MediaQueryEvaluator.h"
45 #include "MIMETypeRegistry.h"
46 #include "MediaPlayer.h"
47 #include "Page.h"
48 #include "RenderVideo.h"
49 #include "TimeRanges.h"
50 #include <wtf/CurrentTime.h>
51 #include <wtf/MathExtras.h>
52
53 using namespace std;
54
55 namespace WebCore {
56
57 using namespace HTMLNames;
58
HTMLMediaElement(const QualifiedName & tagName,Document * doc)59 HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document* doc)
60 : HTMLElement(tagName, doc)
61 , m_loadTimer(this, &HTMLMediaElement::loadTimerFired)
62 , m_asyncEventTimer(this, &HTMLMediaElement::asyncEventTimerFired)
63 , m_progressEventTimer(this, &HTMLMediaElement::progressEventTimerFired)
64 , m_defaultPlaybackRate(1.0f)
65 , m_networkState(EMPTY)
66 , m_readyState(DATA_UNAVAILABLE)
67 , m_begun(false)
68 , m_loadedFirstFrame(false)
69 , m_autoplaying(true)
70 , m_currentLoop(0)
71 , m_volume(1.0f)
72 , m_muted(false)
73 , m_paused(true)
74 , m_seeking(false)
75 , m_currentTimeDuringSeek(0)
76 , m_previousProgress(0)
77 , m_previousProgressTime(numeric_limits<double>::max())
78 , m_sentStalledEvent(false)
79 , m_bufferingRate(0)
80 , m_loadNestingLevel(0)
81 , m_terminateLoadBelowNestingLevel(0)
82 , m_pausedInternal(false)
83 , m_inActiveDocument(true)
84 , m_player(0)
85 {
86 document()->registerForDocumentActivationCallbacks(this);
87 document()->registerForMediaVolumeCallbacks(this);
88 }
89
~HTMLMediaElement()90 HTMLMediaElement::~HTMLMediaElement()
91 {
92 document()->unregisterForDocumentActivationCallbacks(this);
93 document()->unregisterForMediaVolumeCallbacks(this);
94 }
95
checkDTD(const Node * newChild)96 bool HTMLMediaElement::checkDTD(const Node* newChild)
97 {
98 return newChild->hasTagName(sourceTag) || HTMLElement::checkDTD(newChild);
99 }
100
attributeChanged(Attribute * attr,bool preserveDecls)101 void HTMLMediaElement::attributeChanged(Attribute* attr, bool preserveDecls)
102 {
103 HTMLElement::attributeChanged(attr, preserveDecls);
104
105 const QualifiedName& attrName = attr->name();
106 if (attrName == srcAttr) {
107 // 3.14.9.2.
108 // change to src attribute triggers load()
109 if (inDocument() && m_networkState == EMPTY)
110 scheduleLoad();
111 } if (attrName == controlsAttr) {
112 if (!isVideo() && attached() && (controls() != (renderer() != 0))) {
113 detach();
114 attach();
115 }
116 if (renderer())
117 renderer()->updateFromElement();
118 }
119 }
120
rendererIsNeeded(RenderStyle * style)121 bool HTMLMediaElement::rendererIsNeeded(RenderStyle* style)
122 {
123 return controls() ? HTMLElement::rendererIsNeeded(style) : false;
124 }
125
createRenderer(RenderArena * arena,RenderStyle *)126 RenderObject* HTMLMediaElement::createRenderer(RenderArena* arena, RenderStyle*)
127 {
128 return new (arena) RenderMedia(this);
129 }
130
insertedIntoDocument()131 void HTMLMediaElement::insertedIntoDocument()
132 {
133 HTMLElement::insertedIntoDocument();
134 if (!src().isEmpty())
135 scheduleLoad();
136 }
137
removedFromDocument()138 void HTMLMediaElement::removedFromDocument()
139 {
140 // FIXME: pause() may invoke load() which seem like a strange thing to do as a side effect
141 // of removing an element. This might need to be fixed in the spec.
142 ExceptionCode ec;
143 pause(ec);
144 HTMLElement::removedFromDocument();
145 }
146
attach()147 void HTMLMediaElement::attach()
148 {
149 ASSERT(!attached());
150
151 HTMLElement::attach();
152
153 if (renderer())
154 renderer()->updateFromElement();
155 }
156
recalcStyle(StyleChange change)157 void HTMLMediaElement::recalcStyle(StyleChange change)
158 {
159 HTMLElement::recalcStyle(change);
160
161 if (renderer())
162 renderer()->updateFromElement();
163 }
164
scheduleLoad()165 void HTMLMediaElement::scheduleLoad()
166 {
167 m_loadTimer.startOneShot(0);
168 }
169
initAndDispatchProgressEvent(const AtomicString & eventName)170 void HTMLMediaElement::initAndDispatchProgressEvent(const AtomicString& eventName)
171 {
172 bool totalKnown = m_player && m_player->totalBytesKnown();
173 unsigned loaded = m_player ? m_player->bytesLoaded() : 0;
174 unsigned total = m_player ? m_player->totalBytes() : 0;
175 dispatchProgressEvent(eventName, totalKnown, loaded, total);
176 if (renderer())
177 renderer()->updateFromElement();
178 }
179
dispatchEventAsync(const AtomicString & eventName)180 void HTMLMediaElement::dispatchEventAsync(const AtomicString& eventName)
181 {
182 m_asyncEventsToDispatch.append(eventName);
183 if (!m_asyncEventTimer.isActive())
184 m_asyncEventTimer.startOneShot(0);
185 }
186
loadTimerFired(Timer<HTMLMediaElement> *)187 void HTMLMediaElement::loadTimerFired(Timer<HTMLMediaElement>*)
188 {
189 ExceptionCode ec;
190 load(ec);
191 }
192
asyncEventTimerFired(Timer<HTMLMediaElement> *)193 void HTMLMediaElement::asyncEventTimerFired(Timer<HTMLMediaElement>*)
194 {
195 Vector<AtomicString> asyncEventsToDispatch;
196 m_asyncEventsToDispatch.swap(asyncEventsToDispatch);
197 unsigned count = asyncEventsToDispatch.size();
198 for (unsigned n = 0; n < count; ++n)
199 dispatchEventForType(asyncEventsToDispatch[n], false, true);
200 }
201
serializeTimeOffset(float time)202 static String serializeTimeOffset(float time)
203 {
204 String timeString = String::number(time);
205 // FIXME serialize time offset values properly (format not specified yet)
206 timeString.append("s");
207 return timeString;
208 }
209
parseTimeOffset(const String & timeString,bool * ok=0)210 static float parseTimeOffset(const String& timeString, bool* ok = 0)
211 {
212 const UChar* characters = timeString.characters();
213 unsigned length = timeString.length();
214
215 if (length && characters[length - 1] == 's')
216 length--;
217
218 // FIXME parse time offset values (format not specified yet)
219 float val = charactersToFloat(characters, length, ok);
220 return val;
221 }
222
getTimeOffsetAttribute(const QualifiedName & name,float valueOnError) const223 float HTMLMediaElement::getTimeOffsetAttribute(const QualifiedName& name, float valueOnError) const
224 {
225 bool ok;
226 String timeString = getAttribute(name);
227 float result = parseTimeOffset(timeString, &ok);
228 if (ok)
229 return result;
230 return valueOnError;
231 }
232
setTimeOffsetAttribute(const QualifiedName & name,float value)233 void HTMLMediaElement::setTimeOffsetAttribute(const QualifiedName& name, float value)
234 {
235 setAttribute(name, serializeTimeOffset(value));
236 }
237
error() const238 PassRefPtr<MediaError> HTMLMediaElement::error() const
239 {
240 return m_error;
241 }
242
src() const243 KURL HTMLMediaElement::src() const
244 {
245 return document()->completeURL(getAttribute(srcAttr));
246 }
247
setSrc(const String & url)248 void HTMLMediaElement::setSrc(const String& url)
249 {
250 setAttribute(srcAttr, url);
251 }
252
currentSrc() const253 String HTMLMediaElement::currentSrc() const
254 {
255 return m_currentSrc;
256 }
257
networkState() const258 HTMLMediaElement::NetworkState HTMLMediaElement::networkState() const
259 {
260 return m_networkState;
261 }
262
bufferingRate()263 float HTMLMediaElement::bufferingRate()
264 {
265 if (!m_player)
266 return 0;
267 return m_bufferingRate;
268 //return m_player->dataRate();
269 }
270
load(ExceptionCode & ec)271 void HTMLMediaElement::load(ExceptionCode& ec)
272 {
273 String mediaSrc;
274
275 // 3.14.9.4. Loading the media resource
276 // 1
277 // if an event generated during load() ends up re-entering load(), terminate previous instances
278 m_loadNestingLevel++;
279 m_terminateLoadBelowNestingLevel = m_loadNestingLevel;
280
281 m_progressEventTimer.stop();
282 m_sentStalledEvent = false;
283 m_bufferingRate = 0;
284
285 m_loadTimer.stop();
286
287 // 2
288 if (m_begun) {
289 m_begun = false;
290 m_error = MediaError::create(MediaError::MEDIA_ERR_ABORTED);
291 initAndDispatchProgressEvent(eventNames().abortEvent);
292 if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel)
293 goto end;
294 }
295
296 // 3
297 m_error = 0;
298 m_loadedFirstFrame = false;
299 m_autoplaying = true;
300
301 // 4
302 setPlaybackRate(defaultPlaybackRate(), ec);
303
304 // 5
305 if (networkState() != EMPTY) {
306 m_networkState = EMPTY;
307 m_readyState = DATA_UNAVAILABLE;
308 m_paused = true;
309 m_seeking = false;
310 if (m_player) {
311 m_player->pause();
312 m_player->seek(0);
313 }
314 m_currentLoop = 0;
315 dispatchEventForType(eventNames().emptiedEvent, false, true);
316 if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel)
317 goto end;
318 }
319
320 // 6
321 mediaSrc = pickMedia();
322 if (mediaSrc.isEmpty()) {
323 ec = INVALID_STATE_ERR;
324 goto end;
325 }
326
327 // 7
328 m_networkState = LOADING;
329
330 // 8
331 m_currentSrc = mediaSrc;
332
333 // 9
334 m_begun = true;
335 dispatchProgressEvent(eventNames().loadstartEvent, false, 0, 0);
336 if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel)
337 goto end;
338
339 // 10, 11, 12, 13
340 m_player.clear();
341 m_player.set(new MediaPlayer(this));
342 updateVolume();
343 m_player->load(m_currentSrc);
344 if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel)
345 goto end;
346
347 if (renderer())
348 renderer()->updateFromElement();
349
350 // 14
351 m_previousProgressTime = WTF::currentTime();
352 m_previousProgress = 0;
353 if (m_begun)
354 // 350ms is not magic, it is in the spec!
355 m_progressEventTimer.startRepeating(0.350);
356 end:
357 ASSERT(m_loadNestingLevel);
358 m_loadNestingLevel--;
359 }
360
mediaPlayerNetworkStateChanged(MediaPlayer *)361 void HTMLMediaElement::mediaPlayerNetworkStateChanged(MediaPlayer*)
362 {
363 if (!m_begun || m_networkState == EMPTY)
364 return;
365
366 m_terminateLoadBelowNestingLevel = m_loadNestingLevel;
367
368 MediaPlayer::NetworkState state = m_player->networkState();
369
370 // 3.14.9.4. Loading the media resource
371 // 14
372 if (state == MediaPlayer::LoadFailed) {
373 //delete m_player;
374 //m_player = 0;
375 // FIXME better error handling
376 m_error = MediaError::create(MediaError::MEDIA_ERR_NETWORK);
377 m_begun = false;
378 m_progressEventTimer.stop();
379 m_bufferingRate = 0;
380
381 initAndDispatchProgressEvent(eventNames().errorEvent);
382 if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel)
383 return;
384
385 m_networkState = EMPTY;
386
387 if (isVideo())
388 static_cast<HTMLVideoElement*>(this)->updatePosterImage();
389
390 dispatchEventForType(eventNames().emptiedEvent, false, true);
391 return;
392 }
393
394 if (state >= MediaPlayer::Loading && m_networkState < LOADING)
395 m_networkState = LOADING;
396
397 if (state >= MediaPlayer::LoadedMetaData && m_networkState < LOADED_METADATA) {
398 m_player->seek(effectiveStart());
399 m_networkState = LOADED_METADATA;
400
401 dispatchEventForType(eventNames().durationchangeEvent, false, true);
402 if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel)
403 return;
404
405 dispatchEventForType(eventNames().loadedmetadataEvent, false, true);
406 if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel)
407 return;
408 }
409
410 if (state >= MediaPlayer::LoadedFirstFrame && m_networkState < LOADED_FIRST_FRAME) {
411 m_networkState = LOADED_FIRST_FRAME;
412
413 setReadyState(CAN_SHOW_CURRENT_FRAME);
414
415 if (isVideo())
416 static_cast<HTMLVideoElement*>(this)->updatePosterImage();
417
418 if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel)
419 return;
420
421 m_loadedFirstFrame = true;
422 if (renderer()) {
423 ASSERT(!renderer()->isImage());
424 static_cast<RenderVideo*>(renderer())->videoSizeChanged();
425 }
426
427 dispatchEventForType(eventNames().loadedfirstframeEvent, false, true);
428 if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel)
429 return;
430
431 dispatchEventForType(eventNames().canshowcurrentframeEvent, false, true);
432 if (m_loadNestingLevel < m_terminateLoadBelowNestingLevel)
433 return;
434 }
435
436 // 15
437 if (state == MediaPlayer::Loaded && m_networkState < LOADED) {
438 m_begun = false;
439 m_networkState = LOADED;
440 m_progressEventTimer.stop();
441 m_bufferingRate = 0;
442 initAndDispatchProgressEvent(eventNames().loadEvent);
443 }
444 }
445
mediaPlayerReadyStateChanged(MediaPlayer *)446 void HTMLMediaElement::mediaPlayerReadyStateChanged(MediaPlayer*)
447 {
448 MediaPlayer::ReadyState state = m_player->readyState();
449 setReadyState((ReadyState)state);
450 }
451
setReadyState(ReadyState state)452 void HTMLMediaElement::setReadyState(ReadyState state)
453 {
454 // 3.14.9.6. The ready states
455 if (m_readyState == state)
456 return;
457
458 bool wasActivelyPlaying = activelyPlaying();
459 m_readyState = state;
460
461 if (state >= CAN_PLAY)
462 m_seeking = false;
463
464 if (networkState() == EMPTY)
465 return;
466
467 if (state == DATA_UNAVAILABLE) {
468 dispatchEventForType(eventNames().dataunavailableEvent, false, true);
469 if (wasActivelyPlaying) {
470 dispatchEventForType(eventNames().timeupdateEvent, false, true);
471 dispatchEventForType(eventNames().waitingEvent, false, true);
472 }
473 } else if (state == CAN_SHOW_CURRENT_FRAME) {
474 if (m_loadedFirstFrame)
475 dispatchEventForType(eventNames().canshowcurrentframeEvent, false, true);
476 if (wasActivelyPlaying) {
477 dispatchEventForType(eventNames().timeupdateEvent, false, true);
478 dispatchEventForType(eventNames().waitingEvent, false, true);
479 }
480 } else if (state == CAN_PLAY) {
481 dispatchEventForType(eventNames().canplayEvent, false, true);
482 } else if (state == CAN_PLAY_THROUGH) {
483 dispatchEventForType(eventNames().canplaythroughEvent, false, true);
484 if (m_autoplaying && m_paused && autoplay()) {
485 m_paused = false;
486 dispatchEventForType(eventNames().playEvent, false, true);
487 }
488 }
489 updatePlayState();
490 }
491
progressEventTimerFired(Timer<HTMLMediaElement> *)492 void HTMLMediaElement::progressEventTimerFired(Timer<HTMLMediaElement>*)
493 {
494 ASSERT(m_player);
495 unsigned progress = m_player->bytesLoaded();
496 double time = WTF::currentTime();
497 double timedelta = time - m_previousProgressTime;
498 if (timedelta)
499 m_bufferingRate = (float)(0.8 * m_bufferingRate + 0.2 * ((float)(progress - m_previousProgress)) / timedelta);
500
501 if (progress == m_previousProgress) {
502 if (timedelta > 3.0 && !m_sentStalledEvent) {
503 m_bufferingRate = 0;
504 initAndDispatchProgressEvent(eventNames().stalledEvent);
505 m_sentStalledEvent = true;
506 }
507 } else {
508 initAndDispatchProgressEvent(eventNames().progressEvent);
509 m_previousProgress = progress;
510 m_previousProgressTime = time;
511 m_sentStalledEvent = false;
512 }
513 }
514
seek(float time,ExceptionCode & ec)515 void HTMLMediaElement::seek(float time, ExceptionCode& ec)
516 {
517 // 3.14.9.8. Seeking
518 // 1
519 if (networkState() < LOADED_METADATA) {
520 ec = INVALID_STATE_ERR;
521 return;
522 }
523
524 // 2
525 float minTime;
526 if (currentLoop() == 0)
527 minTime = effectiveStart();
528 else
529 minTime = effectiveLoopStart();
530
531 // 3
532 float maxTime = currentLoop() == playCount() - 1 ? effectiveEnd() : effectiveLoopEnd();
533
534 // 4
535 time = min(time, maxTime);
536
537 // 5
538 time = max(time, minTime);
539
540 // 6
541 RefPtr<TimeRanges> seekableRanges = seekable();
542 if (!seekableRanges->contain(time)) {
543 ec = INDEX_SIZE_ERR;
544 return;
545 }
546
547 // 7
548 m_currentTimeDuringSeek = time;
549
550 // 8
551 m_seeking = true;
552
553 // 9
554 dispatchEventForType(eventNames().timeupdateEvent, false, true);
555
556 // 10
557 // As soon as the user agent has established whether or not the media data for the new playback position is available,
558 // and, if it is, decoded enough data to play back that position, the seeking DOM attribute must be set to false.
559 if (m_player) {
560 m_player->setEndTime(maxTime);
561 m_player->seek(time);
562 }
563 }
564
readyState() const565 HTMLMediaElement::ReadyState HTMLMediaElement::readyState() const
566 {
567 return m_readyState;
568 }
569
seeking() const570 bool HTMLMediaElement::seeking() const
571 {
572 return m_seeking;
573 }
574
575 // playback state
currentTime() const576 float HTMLMediaElement::currentTime() const
577 {
578 if (!m_player)
579 return 0;
580 if (m_seeking)
581 return m_currentTimeDuringSeek;
582 return m_player->currentTime();
583 }
584
setCurrentTime(float time,ExceptionCode & ec)585 void HTMLMediaElement::setCurrentTime(float time, ExceptionCode& ec)
586 {
587 seek(time, ec);
588 }
589
duration() const590 float HTMLMediaElement::duration() const
591 {
592 return m_player ? m_player->duration() : 0;
593 }
594
paused() const595 bool HTMLMediaElement::paused() const
596 {
597 return m_paused;
598 }
599
defaultPlaybackRate() const600 float HTMLMediaElement::defaultPlaybackRate() const
601 {
602 return m_defaultPlaybackRate;
603 }
604
setDefaultPlaybackRate(float rate,ExceptionCode & ec)605 void HTMLMediaElement::setDefaultPlaybackRate(float rate, ExceptionCode& ec)
606 {
607 if (rate == 0.0f) {
608 ec = NOT_SUPPORTED_ERR;
609 return;
610 }
611 if (m_defaultPlaybackRate != rate) {
612 m_defaultPlaybackRate = rate;
613 dispatchEventAsync(eventNames().ratechangeEvent);
614 }
615 }
616
playbackRate() const617 float HTMLMediaElement::playbackRate() const
618 {
619 return m_player ? m_player->rate() : 0;
620 }
621
setPlaybackRate(float rate,ExceptionCode & ec)622 void HTMLMediaElement::setPlaybackRate(float rate, ExceptionCode& ec)
623 {
624 if (rate == 0.0f) {
625 ec = NOT_SUPPORTED_ERR;
626 return;
627 }
628 if (m_player && m_player->rate() != rate) {
629 m_player->setRate(rate);
630 dispatchEventAsync(eventNames().ratechangeEvent);
631 }
632 }
633
ended() const634 bool HTMLMediaElement::ended() const
635 {
636 return endedPlayback();
637 }
638
autoplay() const639 bool HTMLMediaElement::autoplay() const
640 {
641 return hasAttribute(autoplayAttr);
642 }
643
setAutoplay(bool b)644 void HTMLMediaElement::setAutoplay(bool b)
645 {
646 setBooleanAttribute(autoplayAttr, b);
647 }
648
play(ExceptionCode & ec)649 void HTMLMediaElement::play(ExceptionCode& ec)
650 {
651 // 3.14.9.7. Playing the media resource
652 if (!m_player || networkState() == EMPTY) {
653 ec = 0;
654 load(ec);
655 if (ec)
656 return;
657 }
658 ExceptionCode unused;
659 if (endedPlayback()) {
660 m_currentLoop = 0;
661 seek(effectiveStart(), unused);
662 }
663 setPlaybackRate(defaultPlaybackRate(), unused);
664
665 if (m_paused) {
666 m_paused = false;
667 dispatchEventAsync(eventNames().playEvent);
668 }
669
670 m_autoplaying = false;
671
672 updatePlayState();
673 }
674
pause(ExceptionCode & ec)675 void HTMLMediaElement::pause(ExceptionCode& ec)
676 {
677 // 3.14.9.7. Playing the media resource
678 if (!m_player || networkState() == EMPTY) {
679 ec = 0;
680 load(ec);
681 if (ec)
682 return;
683 }
684
685 if (!m_paused) {
686 m_paused = true;
687 dispatchEventAsync(eventNames().timeupdateEvent);
688 dispatchEventAsync(eventNames().pauseEvent);
689 }
690
691 m_autoplaying = false;
692
693 updatePlayState();
694 }
695
playCount() const696 unsigned HTMLMediaElement::playCount() const
697 {
698 bool ok;
699 unsigned count = getAttribute(playcountAttr).string().toUInt(&ok);
700 return (count > 0 && ok) ? count : 1;
701 }
702
setPlayCount(unsigned count,ExceptionCode & ec)703 void HTMLMediaElement::setPlayCount(unsigned count, ExceptionCode& ec)
704 {
705 if (!count) {
706 ec = INDEX_SIZE_ERR;
707 return;
708 }
709 setAttribute(playcountAttr, String::number(count));
710 checkIfSeekNeeded();
711 }
712
start() const713 float HTMLMediaElement::start() const
714 {
715 return getTimeOffsetAttribute(startAttr, 0);
716 }
717
setStart(float time)718 void HTMLMediaElement::setStart(float time)
719 {
720 setTimeOffsetAttribute(startAttr, time);
721 checkIfSeekNeeded();
722 }
723
end() const724 float HTMLMediaElement::end() const
725 {
726 return getTimeOffsetAttribute(endAttr, std::numeric_limits<float>::infinity());
727 }
728
setEnd(float time)729 void HTMLMediaElement::setEnd(float time)
730 {
731 setTimeOffsetAttribute(endAttr, time);
732 checkIfSeekNeeded();
733 }
734
loopStart() const735 float HTMLMediaElement::loopStart() const
736 {
737 return getTimeOffsetAttribute(loopstartAttr, start());
738 }
739
setLoopStart(float time)740 void HTMLMediaElement::setLoopStart(float time)
741 {
742 setTimeOffsetAttribute(loopstartAttr, time);
743 checkIfSeekNeeded();
744 }
745
loopEnd() const746 float HTMLMediaElement::loopEnd() const
747 {
748 return getTimeOffsetAttribute(loopendAttr, end());
749 }
750
setLoopEnd(float time)751 void HTMLMediaElement::setLoopEnd(float time)
752 {
753 setTimeOffsetAttribute(loopendAttr, time);
754 checkIfSeekNeeded();
755 }
756
currentLoop() const757 unsigned HTMLMediaElement::currentLoop() const
758 {
759 return m_currentLoop;
760 }
761
setCurrentLoop(unsigned currentLoop)762 void HTMLMediaElement::setCurrentLoop(unsigned currentLoop)
763 {
764 m_currentLoop = currentLoop;
765 }
766
controls() const767 bool HTMLMediaElement::controls() const
768 {
769 return hasAttribute(controlsAttr);
770 }
771
setControls(bool b)772 void HTMLMediaElement::setControls(bool b)
773 {
774 setBooleanAttribute(controlsAttr, b);
775 }
776
volume() const777 float HTMLMediaElement::volume() const
778 {
779 return m_volume;
780 }
781
setVolume(float vol,ExceptionCode & ec)782 void HTMLMediaElement::setVolume(float vol, ExceptionCode& ec)
783 {
784 if (vol < 0.0f || vol > 1.0f) {
785 ec = INDEX_SIZE_ERR;
786 return;
787 }
788
789 if (m_volume != vol) {
790 m_volume = vol;
791 updateVolume();
792 dispatchEventAsync(eventNames().volumechangeEvent);
793 }
794 }
795
muted() const796 bool HTMLMediaElement::muted() const
797 {
798 return m_muted;
799 }
800
setMuted(bool muted)801 void HTMLMediaElement::setMuted(bool muted)
802 {
803 if (m_muted != muted) {
804 m_muted = muted;
805 updateVolume();
806 dispatchEventAsync(eventNames().volumechangeEvent);
807 }
808 }
809
canPlay() const810 bool HTMLMediaElement::canPlay() const
811 {
812 return paused() || ended() || networkState() < LOADED_METADATA;
813 }
814
pickMedia()815 String HTMLMediaElement::pickMedia()
816 {
817 // 3.14.9.2. Location of the media resource
818 String mediaSrc = getAttribute(srcAttr);
819 if (mediaSrc.isEmpty()) {
820 for (Node* n = firstChild(); n; n = n->nextSibling()) {
821 if (n->hasTagName(sourceTag)) {
822 HTMLSourceElement* source = static_cast<HTMLSourceElement*>(n);
823 if (!source->hasAttribute(srcAttr))
824 continue;
825 if (source->hasAttribute(mediaAttr)) {
826 MediaQueryEvaluator screenEval("screen", document()->frame(), renderer() ? renderer()->style() : 0);
827 RefPtr<MediaList> media = MediaList::createAllowingDescriptionSyntax(source->media());
828 if (!screenEval.eval(media.get()))
829 continue;
830 }
831 if (source->hasAttribute(typeAttr)) {
832 String type = source->type().stripWhiteSpace();
833
834 // "type" can have parameters after a semi-colon, strip them before checking with the type registry
835 int semi = type.find(';');
836 if (semi != -1)
837 type = type.left(semi).stripWhiteSpace();
838
839 if (!MIMETypeRegistry::isSupportedMediaMIMEType(type))
840 continue;
841 }
842 mediaSrc = source->src().string();
843 break;
844 }
845 }
846 }
847 if (!mediaSrc.isEmpty())
848 mediaSrc = document()->completeURL(mediaSrc).string();
849 return mediaSrc;
850 }
851
checkIfSeekNeeded()852 void HTMLMediaElement::checkIfSeekNeeded()
853 {
854 // 3.14.9.5. Offsets into the media resource
855 // 1
856 if (playCount() <= m_currentLoop)
857 m_currentLoop = playCount() - 1;
858
859 // 2
860 if (networkState() <= LOADING)
861 return;
862
863 // 3
864 ExceptionCode ec;
865 float time = currentTime();
866 if (!m_currentLoop && time < effectiveStart())
867 seek(effectiveStart(), ec);
868
869 // 4
870 if (m_currentLoop && time < effectiveLoopStart())
871 seek(effectiveLoopStart(), ec);
872
873 // 5
874 if (m_currentLoop < playCount() - 1 && time > effectiveLoopEnd()) {
875 seek(effectiveLoopStart(), ec);
876 m_currentLoop++;
877 }
878
879 // 6
880 if (m_currentLoop == playCount() - 1 && time > effectiveEnd())
881 seek(effectiveEnd(), ec);
882
883 updatePlayState();
884 }
885
mediaPlayerTimeChanged(MediaPlayer *)886 void HTMLMediaElement::mediaPlayerTimeChanged(MediaPlayer*)
887 {
888 if (readyState() >= CAN_PLAY)
889 m_seeking = false;
890
891 if (m_currentLoop < playCount() - 1 && currentTime() >= effectiveLoopEnd()) {
892 ExceptionCode ec;
893 seek(effectiveLoopStart(), ec);
894 m_currentLoop++;
895 dispatchEventForType(eventNames().timeupdateEvent, false, true);
896 }
897
898 if (m_currentLoop == playCount() - 1 && currentTime() >= effectiveEnd()) {
899 dispatchEventForType(eventNames().timeupdateEvent, false, true);
900 dispatchEventForType(eventNames().endedEvent, false, true);
901 }
902
903 updatePlayState();
904 }
905
mediaPlayerRepaint(MediaPlayer *)906 void HTMLMediaElement::mediaPlayerRepaint(MediaPlayer*)
907 {
908 if (renderer())
909 renderer()->repaint();
910 }
911
buffered() const912 PassRefPtr<TimeRanges> HTMLMediaElement::buffered() const
913 {
914 // FIXME real ranges support
915 if (!m_player || !m_player->maxTimeBuffered())
916 return TimeRanges::create();
917 return TimeRanges::create(0, m_player->maxTimeBuffered());
918 }
919
played() const920 PassRefPtr<TimeRanges> HTMLMediaElement::played() const
921 {
922 // FIXME track played
923 return TimeRanges::create();
924 }
925
seekable() const926 PassRefPtr<TimeRanges> HTMLMediaElement::seekable() const
927 {
928 // FIXME real ranges support
929 if (!m_player || !m_player->maxTimeSeekable())
930 return TimeRanges::create();
931 return TimeRanges::create(0, m_player->maxTimeSeekable());
932 }
933
effectiveStart() const934 float HTMLMediaElement::effectiveStart() const
935 {
936 if (!m_player)
937 return 0;
938 return min(start(), m_player->duration());
939 }
940
effectiveEnd() const941 float HTMLMediaElement::effectiveEnd() const
942 {
943 if (!m_player)
944 return 0;
945 return min(max(end(), max(start(), loopStart())), m_player->duration());
946 }
947
effectiveLoopStart() const948 float HTMLMediaElement::effectiveLoopStart() const
949 {
950 if (!m_player)
951 return 0;
952 return min(loopStart(), m_player->duration());
953 }
954
effectiveLoopEnd() const955 float HTMLMediaElement::effectiveLoopEnd() const
956 {
957 if (!m_player)
958 return 0;
959 return min(max(start(), max(loopStart(), loopEnd())), m_player->duration());
960 }
961
activelyPlaying() const962 bool HTMLMediaElement::activelyPlaying() const
963 {
964 return !paused() && readyState() >= CAN_PLAY && !endedPlayback(); // && !stoppedDueToErrors() && !pausedForUserInteraction();
965 }
966
endedPlayback() const967 bool HTMLMediaElement::endedPlayback() const
968 {
969 return networkState() >= LOADED_METADATA && currentTime() >= effectiveEnd() && currentLoop() == playCount() - 1;
970 }
971
updateVolume()972 void HTMLMediaElement::updateVolume()
973 {
974 if (!m_player)
975 return;
976
977 Page* page = document()->page();
978 float volumeMultiplier = page ? page->mediaVolume() : 1;
979
980 m_player->setVolume(m_muted ? 0 : m_volume * volumeMultiplier);
981
982 if (renderer())
983 renderer()->updateFromElement();
984 }
985
updatePlayState()986 void HTMLMediaElement::updatePlayState()
987 {
988 if (!m_player)
989 return;
990
991 if (m_pausedInternal) {
992 if (!m_player->paused())
993 m_player->pause();
994 return;
995 }
996
997 m_player->setEndTime(currentLoop() == playCount() - 1 ? effectiveEnd() : effectiveLoopEnd());
998
999 bool shouldBePlaying = activelyPlaying() && currentTime() < effectiveEnd();
1000 if (shouldBePlaying && m_player->paused())
1001 m_player->play();
1002 else if (!shouldBePlaying && !m_player->paused())
1003 m_player->pause();
1004
1005 if (renderer())
1006 renderer()->updateFromElement();
1007 }
1008
setPausedInternal(bool b)1009 void HTMLMediaElement::setPausedInternal(bool b)
1010 {
1011 m_pausedInternal = b;
1012 updatePlayState();
1013 }
1014
documentWillBecomeInactive()1015 void HTMLMediaElement::documentWillBecomeInactive()
1016 {
1017 // 3.14.9.4. Loading the media resource
1018 // 14
1019 if (m_begun) {
1020 // For simplicity cancel the incomplete load by deleting the player
1021 m_player.clear();
1022 m_progressEventTimer.stop();
1023
1024 m_error = MediaError::create(MediaError::MEDIA_ERR_ABORTED);
1025 m_begun = false;
1026 initAndDispatchProgressEvent(eventNames().abortEvent);
1027 if (m_networkState >= LOADING) {
1028 m_networkState = EMPTY;
1029 m_readyState = DATA_UNAVAILABLE;
1030 dispatchEventForType(eventNames().emptiedEvent, false, true);
1031 }
1032 }
1033 m_inActiveDocument = false;
1034 // Stop the playback without generating events
1035 setPausedInternal(true);
1036
1037 if (renderer())
1038 renderer()->updateFromElement();
1039 }
1040
documentDidBecomeActive()1041 void HTMLMediaElement::documentDidBecomeActive()
1042 {
1043 m_inActiveDocument = true;
1044 setPausedInternal(false);
1045
1046 if (m_error && m_error->code() == MediaError::MEDIA_ERR_ABORTED) {
1047 // Restart the load if it was aborted in the middle by moving the document to the page cache.
1048 // This behavior is not specified but it seems like a sensible thing to do.
1049 ExceptionCode ec;
1050 load(ec);
1051 }
1052
1053 if (renderer())
1054 renderer()->updateFromElement();
1055 }
1056
mediaVolumeDidChange()1057 void HTMLMediaElement::mediaVolumeDidChange()
1058 {
1059 updateVolume();
1060 }
1061
defaultEventHandler(Event * event)1062 void HTMLMediaElement::defaultEventHandler(Event* event)
1063 {
1064 if (renderer() && renderer()->isMedia())
1065 static_cast<RenderMedia*>(renderer())->forwardEvent(event);
1066 if (event->defaultHandled())
1067 return;
1068 HTMLElement::defaultEventHandler(event);
1069 }
1070
1071 }
1072
1073 #endif
1074