65bd7db7f5bf0b675477152e2930cc728371d4b6
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / html / MediaController.cpp
1 /*
2  * Copyright (C) 2011 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "core/html/MediaController.h"
28
29 #include "bindings/core/v8/ExceptionMessages.h"
30 #include "bindings/core/v8/ExceptionState.h"
31 #include "bindings/core/v8/ExceptionStatePlaceholder.h"
32 #include "core/dom/ExceptionCode.h"
33 #include "core/dom/ExecutionContext.h"
34 #include "core/events/Event.h"
35 #include "core/events/GenericEventQueue.h"
36 #include "core/html/HTMLMediaElement.h"
37 #include "core/html/TimeRanges.h"
38 #include "platform/Clock.h"
39 #include "wtf/CurrentTime.h"
40 #include "wtf/StdLibExtras.h"
41 #include "wtf/text/AtomicString.h"
42
43 namespace blink {
44
45 PassRefPtrWillBeRawPtr<MediaController> MediaController::create(ExecutionContext* context)
46 {
47     return adoptRefWillBeRefCountedGarbageCollected(new MediaController(context));
48 }
49
50 MediaController::MediaController(ExecutionContext* context)
51     : m_paused(false)
52     , m_defaultPlaybackRate(1)
53     , m_volume(1)
54     , m_position(MediaPlayer::invalidTime())
55     , m_muted(false)
56     , m_readyState(HTMLMediaElement::HAVE_NOTHING)
57     , m_playbackState(WAITING)
58     , m_pendingEventsQueue(GenericEventQueue::create(this))
59     , m_clearPositionTimer(this, &MediaController::clearPositionTimerFired)
60     , m_clock(Clock::create())
61     , m_executionContext(context)
62     , m_timeupdateTimer(this, &MediaController::timeupdateTimerFired)
63     , m_previousTimeupdateTime(0)
64 {
65     ScriptWrappable::init(this);
66 }
67
68 MediaController::~MediaController()
69 {
70 }
71
72 void MediaController::addMediaElement(HTMLMediaElement* element)
73 {
74     ASSERT(element);
75     ASSERT(!m_mediaElements.contains(element));
76
77     m_mediaElements.add(element);
78     bringElementUpToSpeed(element);
79 }
80
81 void MediaController::removeMediaElement(HTMLMediaElement* element)
82 {
83     ASSERT(element);
84     ASSERT(m_mediaElements.contains(element));
85     m_mediaElements.remove(m_mediaElements.find(element));
86 }
87
88 PassRefPtrWillBeRawPtr<TimeRanges> MediaController::buffered() const
89 {
90     if (m_mediaElements.isEmpty())
91         return TimeRanges::create();
92
93     // The buffered attribute must return a new static normalized TimeRanges object that represents
94     // the intersection of the ranges of the media resources of the slaved media elements that the
95     // user agent has buffered, at the time the attribute is evaluated.
96     MediaElementSequence::const_iterator it = m_mediaElements.begin();
97     RefPtrWillBeRawPtr<TimeRanges> bufferedRanges = (*it)->buffered();
98     for (++it; it != m_mediaElements.end(); ++it)
99         bufferedRanges->intersectWith((*it)->buffered().get());
100     return bufferedRanges;
101 }
102
103 PassRefPtrWillBeRawPtr<TimeRanges> MediaController::seekable() const
104 {
105     if (m_mediaElements.isEmpty())
106         return TimeRanges::create();
107
108     // The seekable attribute must return a new static normalized TimeRanges object that represents
109     // the intersection of the ranges of the media resources of the slaved media elements that the
110     // user agent is able to seek to, at the time the attribute is evaluated.
111     MediaElementSequence::const_iterator it = m_mediaElements.begin();
112     RefPtrWillBeRawPtr<TimeRanges> seekableRanges = (*it)->seekable();
113     for (++it; it != m_mediaElements.end(); ++it)
114         seekableRanges->intersectWith((*it)->seekable().get());
115     return seekableRanges;
116 }
117
118 PassRefPtrWillBeRawPtr<TimeRanges> MediaController::played()
119 {
120     if (m_mediaElements.isEmpty())
121         return TimeRanges::create();
122
123     // The played attribute must return a new static normalized TimeRanges object that represents
124     // the union of the ranges of the media resources of the slaved media elements that the
125     // user agent has so far rendered, at the time the attribute is evaluated.
126     MediaElementSequence::const_iterator it = m_mediaElements.begin();
127     RefPtrWillBeRawPtr<TimeRanges> playedRanges = (*it)->played();
128     for (++it; it != m_mediaElements.end(); ++it)
129         playedRanges->unionWith((*it)->played().get());
130     return playedRanges;
131 }
132
133 double MediaController::duration() const
134 {
135     // FIXME: Investigate caching the maximum duration and only updating the cached value
136     // when the slaved media elements' durations change.
137     double maxDuration = 0;
138     for (MediaElementSequence::const_iterator it = m_mediaElements.begin(); it != m_mediaElements.end(); ++it) {
139         double duration = (*it)->duration();
140         if (std::isnan(duration))
141             continue;
142         maxDuration = std::max(maxDuration, duration);
143     }
144     return maxDuration;
145 }
146
147 double MediaController::currentTime() const
148 {
149     if (m_mediaElements.isEmpty())
150         return 0;
151
152     if (m_position == MediaPlayer::invalidTime()) {
153         // Some clocks may return times outside the range of [0..duration].
154         m_position = std::max(0.0, std::min(duration(), m_clock->currentTime()));
155         m_clearPositionTimer.startOneShot(0, FROM_HERE);
156     }
157
158     return m_position;
159 }
160
161 void MediaController::setCurrentTime(double time, ExceptionState& exceptionState)
162 {
163     // When the user agent is to seek the media controller to a particular new playback position,
164     // it must follow these steps:
165     // If the new playback position is less than zero, then set it to zero.
166     time = std::max(0.0, time);
167
168     // If the new playback position is greater than the media controller duration, then set it
169     // to the media controller duration.
170     time = std::min(time, duration());
171
172     // Set the media controller position to the new playback position.
173     m_position = time;
174     m_clock->setCurrentTime(time);
175
176     // Seek each slaved media element to the new playback position relative to the media element timeline.
177     for (MediaElementSequence::const_iterator it = m_mediaElements.begin(); it != m_mediaElements.end(); ++it)
178         (*it)->seek(time, exceptionState);
179
180     scheduleTimeupdateEvent();
181 }
182
183 void MediaController::unpause()
184 {
185     // When the unpause() method is invoked, if the MediaController is a paused media controller,
186     if (!m_paused)
187         return;
188
189     // the user agent must change the MediaController into a playing media controller,
190     m_paused = false;
191     // queue a task to fire a simple event named play at the MediaController,
192     scheduleEvent(EventTypeNames::play);
193     // and then report the controller state of the MediaController.
194     reportControllerState();
195 }
196
197 void MediaController::play()
198 {
199     // When the play() method is invoked, the user agent must invoke the play method of each
200     // slaved media element in turn,
201     for (MediaElementSequence::const_iterator it = m_mediaElements.begin(); it != m_mediaElements.end(); ++it)
202         (*it)->play();
203
204     // and then invoke the unpause method of the MediaController.
205     unpause();
206 }
207
208 void MediaController::pause()
209 {
210     // When the pause() method is invoked, if the MediaController is a playing media controller,
211     if (m_paused)
212         return;
213
214     // then the user agent must change the MediaController into a paused media controller,
215     m_paused = true;
216     // queue a task to fire a simple event named pause at the MediaController,
217     scheduleEvent(EventTypeNames::pause);
218     // and then report the controller state of the MediaController.
219     reportControllerState();
220 }
221
222 void MediaController::setDefaultPlaybackRate(double rate)
223 {
224     if (m_defaultPlaybackRate == rate)
225         return;
226
227     // The defaultPlaybackRate attribute, on setting, must set the MediaController's media controller
228     // default playback rate to the new value,
229     m_defaultPlaybackRate = rate;
230
231     // then queue a task to fire a simple event named ratechange at the MediaController.
232     scheduleEvent(EventTypeNames::ratechange);
233 }
234
235 double MediaController::playbackRate() const
236 {
237     return m_clock->playRate();
238 }
239
240 void MediaController::setPlaybackRate(double rate)
241 {
242     if (m_clock->playRate() == rate)
243         return;
244
245     // The playbackRate attribute, on setting, must set the MediaController's media controller
246     // playback rate to the new value,
247     m_clock->setPlayRate(rate);
248
249     for (MediaElementSequence::const_iterator it = m_mediaElements.begin(); it != m_mediaElements.end(); ++it)
250         (*it)->updatePlaybackRate();
251
252     // then queue a task to fire a simple event named ratechange at the MediaController.
253     scheduleEvent(EventTypeNames::ratechange);
254 }
255
256 void MediaController::setVolume(double level, ExceptionState& exceptionState)
257 {
258     if (m_volume == level)
259         return;
260
261     // If the new value is outside the range 0.0 to 1.0 inclusive, then, on setting, an
262     // IndexSizeError exception must be raised instead.
263     if (level < 0 || level > 1) {
264         exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::indexOutsideRange("volume", level, 0.0, ExceptionMessages::InclusiveBound, 1.0, ExceptionMessages::InclusiveBound));
265         return;
266     }
267
268     // The volume attribute, on setting, if the new value is in the range 0.0 to 1.0 inclusive,
269     // must set the MediaController's media controller volume multiplier to the new value
270     m_volume = level;
271
272     // and queue a task to fire a simple event named volumechange at the MediaController.
273     scheduleEvent(EventTypeNames::volumechange);
274
275     for (MediaElementSequence::const_iterator it = m_mediaElements.begin(); it != m_mediaElements.end(); ++it)
276         (*it)->updateVolume();
277 }
278
279 void MediaController::setMuted(bool flag)
280 {
281     if (m_muted == flag)
282         return;
283
284     // The muted attribute, on setting, must set the MediaController's media controller mute override
285     // to the new value
286     m_muted = flag;
287
288     // and queue a task to fire a simple event named volumechange at the MediaController.
289     scheduleEvent(EventTypeNames::volumechange);
290
291     for (MediaElementSequence::const_iterator it = m_mediaElements.begin(); it != m_mediaElements.end(); ++it)
292         (*it)->updateVolume();
293 }
294
295 static const AtomicString& playbackStateWaiting()
296 {
297     DEFINE_STATIC_LOCAL(AtomicString, waiting, ("waiting", AtomicString::ConstructFromLiteral));
298     return waiting;
299 }
300
301 static const AtomicString& playbackStatePlaying()
302 {
303     DEFINE_STATIC_LOCAL(AtomicString, playing, ("playing", AtomicString::ConstructFromLiteral));
304     return playing;
305 }
306
307 static const AtomicString& playbackStateEnded()
308 {
309     DEFINE_STATIC_LOCAL(AtomicString, ended, ("ended", AtomicString::ConstructFromLiteral));
310     return ended;
311 }
312
313 const AtomicString& MediaController::playbackState() const
314 {
315     switch (m_playbackState) {
316     case WAITING:
317         return playbackStateWaiting();
318     case PLAYING:
319         return playbackStatePlaying();
320     case ENDED:
321         return playbackStateEnded();
322     default:
323         ASSERT_NOT_REACHED();
324         return nullAtom;
325     }
326 }
327
328 void MediaController::reportControllerState()
329 {
330     updateReadyState();
331     updatePlaybackState();
332 }
333
334 static const AtomicString& eventNameForReadyState(HTMLMediaElement::ReadyState state)
335 {
336     switch (state) {
337     case HTMLMediaElement::HAVE_NOTHING:
338         return EventTypeNames::emptied;
339     case HTMLMediaElement::HAVE_METADATA:
340         return EventTypeNames::loadedmetadata;
341     case HTMLMediaElement::HAVE_CURRENT_DATA:
342         return EventTypeNames::loadeddata;
343     case HTMLMediaElement::HAVE_FUTURE_DATA:
344         return EventTypeNames::canplay;
345     case HTMLMediaElement::HAVE_ENOUGH_DATA:
346         return EventTypeNames::canplaythrough;
347     default:
348         ASSERT_NOT_REACHED();
349         return nullAtom;
350     }
351 }
352
353 void MediaController::updateReadyState()
354 {
355     ReadyState oldReadyState = m_readyState;
356     ReadyState newReadyState;
357
358     if (m_mediaElements.isEmpty()) {
359         // If the MediaController has no slaved media elements, let new readiness state be 0.
360         newReadyState = HTMLMediaElement::HAVE_NOTHING;
361     } else {
362         // Otherwise, let it have the lowest value of the readyState IDL attributes of all of its
363         // slaved media elements.
364         MediaElementSequence::const_iterator it = m_mediaElements.begin();
365         newReadyState = (*it)->readyState();
366         for (++it; it != m_mediaElements.end(); ++it)
367             newReadyState = std::min(newReadyState, (*it)->readyState());
368     }
369
370     if (newReadyState == oldReadyState)
371         return;
372
373     // If the MediaController's most recently reported readiness state is greater than new readiness
374     // state then queue a task to fire a simple event at the MediaController object, whose name is the
375     // event name corresponding to the value of new readiness state given in the table below. [omitted]
376     if (oldReadyState > newReadyState) {
377         scheduleEvent(eventNameForReadyState(newReadyState));
378         return;
379     }
380
381     // If the MediaController's most recently reported readiness state is less than the new readiness
382     // state, then run these substeps:
383     // 1. Let next state be the MediaController's most recently reported readiness state.
384     ReadyState nextState = oldReadyState;
385     do {
386         // 2. Loop: Increment next state by one.
387         nextState = static_cast<ReadyState>(nextState + 1);
388         // 3. Queue a task to fire a simple event at the MediaController object, whose name is the
389         // event name corresponding to the value of next state given in the table below. [omitted]
390         scheduleEvent(eventNameForReadyState(nextState));
391         // If next state is less than new readiness state, then return to the step labeled loop
392     } while (nextState < newReadyState);
393
394     // Let the MediaController's most recently reported readiness state be new readiness state.
395     m_readyState = newReadyState;
396 }
397
398 void MediaController::updatePlaybackState()
399 {
400     PlaybackState oldPlaybackState = m_playbackState;
401     PlaybackState newPlaybackState;
402
403     // Initialize new playback state by setting it to the state given for the first matching
404     // condition from the following list:
405     if (m_mediaElements.isEmpty()) {
406         // If the MediaController has no slaved media elements
407         // Let new playback state be waiting.
408         newPlaybackState = WAITING;
409     } else if (hasEnded()) {
410         // If all of the MediaController's slaved media elements have ended playback and the media
411         // controller playback rate is positive or zero
412         // Let new playback state be ended.
413         newPlaybackState = ENDED;
414     } else if (isBlocked()) {
415         // If the MediaController is a blocked media controller
416         // Let new playback state be waiting.
417         newPlaybackState = WAITING;
418     } else {
419         // Otherwise
420         // Let new playback state be playing.
421         newPlaybackState = PLAYING;
422     }
423
424     // If the MediaController's most recently reported playback state is not equal to new playback state
425     if (newPlaybackState == oldPlaybackState)
426         return;
427
428     // and the new playback state is ended,
429     if (newPlaybackState == ENDED) {
430         // then queue a task that, if the MediaController object is a playing media controller, and
431         // all of the MediaController's slaved media elements have still ended playback, and the
432         // media controller playback rate is still positive or zero,
433         if (!m_paused && hasEnded()) {
434             // changes the MediaController object to a paused media controller
435             m_paused = true;
436
437             // and then fires a simple event named pause at the MediaController object.
438             scheduleEvent(EventTypeNames::pause);
439         }
440     }
441
442     // If the MediaController's most recently reported playback state is not equal to new playback state
443     // then queue a task to fire a simple event at the MediaController object, whose name is playing
444     // if new playback state is playing, ended if new playback state is ended, and waiting otherwise.
445     AtomicString eventName;
446     switch (newPlaybackState) {
447     case WAITING:
448         eventName = EventTypeNames::waiting;
449         m_clock->stop();
450         m_timeupdateTimer.stop();
451         break;
452     case ENDED:
453         eventName = EventTypeNames::ended;
454         m_clock->stop();
455         m_timeupdateTimer.stop();
456         break;
457     case PLAYING:
458         eventName = EventTypeNames::playing;
459         m_clock->start();
460         startTimeupdateTimer();
461         break;
462     default:
463         ASSERT_NOT_REACHED();
464     }
465     scheduleEvent(eventName);
466
467     // Let the MediaController's most recently reported playback state be new playback state.
468     m_playbackState = newPlaybackState;
469
470     updateMediaElements();
471 }
472
473 void MediaController::updateMediaElements()
474 {
475     for (MediaElementSequence::const_iterator it = m_mediaElements.begin(); it != m_mediaElements.end(); ++it)
476         (*it)->updatePlayState();
477 }
478
479 void MediaController::bringElementUpToSpeed(HTMLMediaElement* element)
480 {
481     ASSERT(element);
482     ASSERT(m_mediaElements.contains(element));
483
484     // When the user agent is to bring a media element up to speed with its new media controller,
485     // it must seek that media element to the MediaController's media controller position relative
486     // to the media element's timeline.
487     element->seek(currentTime(), IGNORE_EXCEPTION);
488
489     // Update volume to take controller volume and mute into account.
490     element->updateVolume();
491 }
492
493 bool MediaController::isRestrained() const
494 {
495     ASSERT(!m_mediaElements.isEmpty());
496
497     // A MediaController is a restrained media controller if the MediaController is a playing media
498     // controller,
499     if (m_paused)
500         return false;
501
502     bool anyAutoplayingAndPaused = false;
503     bool allPaused = true;
504     for (MediaElementSequence::const_iterator it = m_mediaElements.begin(); it != m_mediaElements.end(); ++it) {
505         HTMLMediaElement* element = *it;
506
507         // and none of its slaved media elements are blocked media elements,
508         if (element->isBlocked())
509             return false;
510
511         if (element->isAutoplaying() && element->paused())
512             anyAutoplayingAndPaused = true;
513
514         if (!element->paused())
515             allPaused = false;
516     }
517
518     // but either at least one of its slaved media elements whose autoplaying flag is true still has
519     // its paused attribute set to true, or, all of its slaved media elements have their paused
520     // attribute set to true.
521     return anyAutoplayingAndPaused || allPaused;
522 }
523
524 bool MediaController::isBlocked() const
525 {
526     ASSERT(!m_mediaElements.isEmpty());
527
528     // A MediaController is a blocked media controller if the MediaController is a paused media
529     // controller,
530     if (m_paused)
531         return true;
532
533     bool allPaused = true;
534     for (MediaElementSequence::const_iterator it = m_mediaElements.begin(); it != m_mediaElements.end(); ++it) {
535         HTMLMediaElement* element = *it;
536
537         // or if any of its slaved media elements are blocked media elements,
538         if (element->isBlocked())
539             return true;
540
541         // or if any of its slaved media elements whose autoplaying flag is true still have their
542         // paused attribute set to true,
543         if (element->isAutoplaying() && element->paused())
544             return true;
545
546         if (!element->paused())
547             allPaused = false;
548     }
549
550     // or if all of its slaved media elements have their paused attribute set to true.
551     return allPaused;
552 }
553
554 bool MediaController::hasEnded() const
555 {
556     // If the ... media controller playback rate is positive or zero
557     if (m_clock->playRate() < 0)
558         return false;
559
560     // [and] all of the MediaController's slaved media elements have ended playback ... let new
561     // playback state be ended.
562     if (m_mediaElements.isEmpty())
563         return false;
564
565     bool allHaveEnded = true;
566     for (MediaElementSequence::const_iterator it = m_mediaElements.begin(); it != m_mediaElements.end(); ++it) {
567         if (!(*it)->ended())
568             allHaveEnded = false;
569     }
570     return allHaveEnded;
571 }
572
573 void MediaController::scheduleEvent(const AtomicString& eventName)
574 {
575     m_pendingEventsQueue->enqueueEvent(Event::createCancelable(eventName));
576 }
577
578 void MediaController::clearPositionTimerFired(Timer<MediaController>*)
579 {
580     m_position = MediaPlayer::invalidTime();
581 }
582
583 const AtomicString& MediaController::interfaceName() const
584 {
585     return EventTargetNames::MediaController;
586 }
587
588 // The spec says to fire periodic timeupdate events (those sent while playing) every
589 // "15 to 250ms", we choose the slowest frequency
590 static const double maxTimeupdateEventFrequency = 0.25;
591
592 void MediaController::startTimeupdateTimer()
593 {
594     if (m_timeupdateTimer.isActive())
595         return;
596
597     m_timeupdateTimer.startRepeating(maxTimeupdateEventFrequency, FROM_HERE);
598 }
599
600 void MediaController::timeupdateTimerFired(Timer<MediaController>*)
601 {
602     scheduleTimeupdateEvent();
603 }
604
605 void MediaController::scheduleTimeupdateEvent()
606 {
607     double now = WTF::currentTime();
608     double timedelta = now - m_previousTimeupdateTime;
609
610     if (timedelta < maxTimeupdateEventFrequency)
611         return;
612
613     scheduleEvent(EventTypeNames::timeupdate);
614     m_previousTimeupdateTime = now;
615 }
616
617 void MediaController::trace(Visitor* visitor)
618 {
619 #if ENABLE(OILPAN)
620     visitor->trace(m_mediaElements);
621     visitor->trace(m_pendingEventsQueue);
622     visitor->trace(m_executionContext);
623 #endif
624     EventTargetWithInlineData::trace(visitor);
625 }
626
627 }