d0ba80e377c2a7392f1e196e46b2c4befc4f8f8e
[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/v8/ExceptionMessages.h"
30 #include "bindings/v8/ExceptionState.h"
31 #include "bindings/v8/ExceptionStatePlaceholder.h"
32 #include "core/dom/ExceptionCode.h"
33 #include "core/html/HTMLMediaElement.h"
34 #include "core/html/TimeRanges.h"
35 #include "platform/Clock.h"
36 #include "wtf/CurrentTime.h"
37 #include "wtf/StdLibExtras.h"
38 #include "wtf/text/AtomicString.h"
39
40 using namespace WebCore;
41 using namespace std;
42
43 PassRefPtr<MediaController> MediaController::create(ExecutionContext* context)
44 {
45     return adoptRef(new MediaController(context));
46 }
47
48 MediaController::MediaController(ExecutionContext* context)
49     : m_paused(false)
50     , m_defaultPlaybackRate(1)
51     , m_volume(1)
52     , m_position(MediaPlayer::invalidTime())
53     , m_muted(false)
54     , m_readyState(HTMLMediaElement::HAVE_NOTHING)
55     , m_playbackState(WAITING)
56     , m_asyncEventTimer(this, &MediaController::asyncEventTimerFired)
57     , m_clearPositionTimer(this, &MediaController::clearPositionTimerFired)
58     , m_clock(Clock::create())
59     , m_executionContext(context)
60     , m_timeupdateTimer(this, &MediaController::timeupdateTimerFired)
61     , m_previousTimeupdateTime(0)
62 {
63     ScriptWrappable::init(this);
64 }
65
66 MediaController::~MediaController()
67 {
68 }
69
70 void MediaController::addMediaElement(HTMLMediaElement* element)
71 {
72     ASSERT(element);
73     ASSERT(!m_mediaElements.contains(element));
74
75     m_mediaElements.append(element);
76     bringElementUpToSpeed(element);
77 }
78
79 void MediaController::removeMediaElement(HTMLMediaElement* element)
80 {
81     ASSERT(element);
82     ASSERT(m_mediaElements.contains(element));
83     m_mediaElements.remove(m_mediaElements.find(element));
84 }
85
86 bool MediaController::containsMediaElement(HTMLMediaElement* element) const
87 {
88     return m_mediaElements.contains(element);
89 }
90
91 PassRefPtr<TimeRanges> MediaController::buffered() const
92 {
93     if (m_mediaElements.isEmpty())
94         return TimeRanges::create();
95
96     // The buffered attribute must return a new static normalized TimeRanges object that represents
97     // the intersection of the ranges of the media resources of the slaved media elements that the
98     // user agent has buffered, at the time the attribute is evaluated.
99     RefPtr<TimeRanges> bufferedRanges = m_mediaElements.first()->buffered();
100     for (size_t index = 1; index < m_mediaElements.size(); ++index)
101         bufferedRanges->intersectWith(m_mediaElements[index]->buffered().get());
102     return bufferedRanges;
103 }
104
105 PassRefPtr<TimeRanges> MediaController::seekable() const
106 {
107     if (m_mediaElements.isEmpty())
108         return TimeRanges::create();
109
110     // The seekable attribute must return a new static normalized TimeRanges object that represents
111     // the intersection of the ranges of the media resources of the slaved media elements that the
112     // user agent is able to seek to, at the time the attribute is evaluated.
113     RefPtr<TimeRanges> seekableRanges = m_mediaElements.first()->seekable();
114     for (size_t index = 1; index < m_mediaElements.size(); ++index)
115         seekableRanges->intersectWith(m_mediaElements[index]->seekable().get());
116     return seekableRanges;
117 }
118
119 PassRefPtr<TimeRanges> MediaController::played()
120 {
121     if (m_mediaElements.isEmpty())
122         return TimeRanges::create();
123
124     // The played attribute must return a new static normalized TimeRanges object that represents
125     // the union of the ranges of the media resources of the slaved media elements that the
126     // user agent has so far rendered, at the time the attribute is evaluated.
127     RefPtr<TimeRanges> playedRanges = m_mediaElements.first()->played();
128     for (size_t index = 1; index < m_mediaElements.size(); ++index)
129         playedRanges->unionWith(m_mediaElements[index]->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 (size_t index = 0; index < m_mediaElements.size(); ++index) {
139         double duration = m_mediaElements[index]->duration();
140         if (std::isnan(duration))
141             continue;
142         maxDuration = 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 = max(0.0, 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 = 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 = min(time, duration());
171
172     // Set the media controller position to the new playback position.
173     m_clock->setCurrentTime(time);
174
175     // Seek each slaved media element to the new playback position relative to the media element timeline.
176     for (size_t index = 0; index < m_mediaElements.size(); ++index)
177         m_mediaElements[index]->seek(time, exceptionState);
178
179     scheduleTimeupdateEvent();
180 }
181
182 void MediaController::unpause()
183 {
184     // When the unpause() method is invoked, if the MediaController is a paused media controller,
185     if (!m_paused)
186         return;
187
188     // the user agent must change the MediaController into a playing media controller,
189     m_paused = false;
190     // queue a task to fire a simple event named play at the MediaController,
191     scheduleEvent(EventTypeNames::play);
192     // and then report the controller state of the MediaController.
193     reportControllerState();
194 }
195
196 void MediaController::play()
197 {
198     // When the play() method is invoked, the user agent must invoke the play method of each
199     // slaved media element in turn,
200     for (size_t index = 0; index < m_mediaElements.size(); ++index)
201         m_mediaElements[index]->play();
202
203     // and then invoke the unpause method of the MediaController.
204     unpause();
205 }
206
207 void MediaController::pause()
208 {
209     // When the pause() method is invoked, if the MediaController is a playing media controller,
210     if (m_paused)
211         return;
212
213     // then the user agent must change the MediaController into a paused media controller,
214     m_paused = true;
215     // queue a task to fire a simple event named pause at the MediaController,
216     scheduleEvent(EventTypeNames::pause);
217     // and then report the controller state of the MediaController.
218     reportControllerState();
219 }
220
221 void MediaController::setDefaultPlaybackRate(double rate)
222 {
223     if (m_defaultPlaybackRate == rate)
224         return;
225
226     // The defaultPlaybackRate attribute, on setting, must set the MediaController's media controller
227     // default playback rate to the new value,
228     m_defaultPlaybackRate = rate;
229
230     // then queue a task to fire a simple event named ratechange at the MediaController.
231     scheduleEvent(EventTypeNames::ratechange);
232 }
233
234 double MediaController::playbackRate() const
235 {
236     return m_clock->playRate();
237 }
238
239 void MediaController::setPlaybackRate(double rate)
240 {
241     if (m_clock->playRate() == rate)
242         return;
243
244     // The playbackRate attribute, on setting, must set the MediaController's media controller
245     // playback rate to the new value,
246     m_clock->setPlayRate(rate);
247
248     for (size_t index = 0; index < m_mediaElements.size(); ++index)
249         m_mediaElements[index]->updatePlaybackRate();
250
251     // then queue a task to fire a simple event named ratechange at the MediaController.
252     scheduleEvent(EventTypeNames::ratechange);
253 }
254
255 void MediaController::setVolume(double level, ExceptionState& exceptionState)
256 {
257     if (m_volume == level)
258         return;
259
260     // If the new value is outside the range 0.0 to 1.0 inclusive, then, on setting, an
261     // IndexSizeError exception must be raised instead.
262     if (level < 0 || level > 1) {
263         exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::indexOutsideRange("volume", level, 0.0, ExceptionMessages::InclusiveBound, 1.0, ExceptionMessages::InclusiveBound));
264         return;
265     }
266
267     // The volume attribute, on setting, if the new value is in the range 0.0 to 1.0 inclusive,
268     // must set the MediaController's media controller volume multiplier to the new value
269     m_volume = level;
270
271     // and queue a task to fire a simple event named volumechange at the MediaController.
272     scheduleEvent(EventTypeNames::volumechange);
273
274     for (size_t index = 0; index < m_mediaElements.size(); ++index)
275         m_mediaElements[index]->updateVolume();
276 }
277
278 void MediaController::setMuted(bool flag)
279 {
280     if (m_muted == flag)
281         return;
282
283     // The muted attribute, on setting, must set the MediaController's media controller mute override
284     // to the new value
285     m_muted = flag;
286
287     // and queue a task to fire a simple event named volumechange at the MediaController.
288     scheduleEvent(EventTypeNames::volumechange);
289
290     for (size_t index = 0; index < m_mediaElements.size(); ++index)
291         m_mediaElements[index]->updateVolume();
292 }
293
294 static const AtomicString& playbackStateWaiting()
295 {
296     DEFINE_STATIC_LOCAL(AtomicString, waiting, ("waiting", AtomicString::ConstructFromLiteral));
297     return waiting;
298 }
299
300 static const AtomicString& playbackStatePlaying()
301 {
302     DEFINE_STATIC_LOCAL(AtomicString, playing, ("playing", AtomicString::ConstructFromLiteral));
303     return playing;
304 }
305
306 static const AtomicString& playbackStateEnded()
307 {
308     DEFINE_STATIC_LOCAL(AtomicString, ended, ("ended", AtomicString::ConstructFromLiteral));
309     return ended;
310 }
311
312 const AtomicString& MediaController::playbackState() const
313 {
314     switch (m_playbackState) {
315     case WAITING:
316         return playbackStateWaiting();
317     case PLAYING:
318         return playbackStatePlaying();
319     case ENDED:
320         return playbackStateEnded();
321     default:
322         ASSERT_NOT_REACHED();
323         return nullAtom;
324     }
325 }
326
327 void MediaController::reportControllerState()
328 {
329     updateReadyState();
330     updatePlaybackState();
331 }
332
333 static const AtomicString& eventNameForReadyState(HTMLMediaElement::ReadyState state)
334 {
335     switch (state) {
336     case HTMLMediaElement::HAVE_NOTHING:
337         return EventTypeNames::emptied;
338     case HTMLMediaElement::HAVE_METADATA:
339         return EventTypeNames::loadedmetadata;
340     case HTMLMediaElement::HAVE_CURRENT_DATA:
341         return EventTypeNames::loadeddata;
342     case HTMLMediaElement::HAVE_FUTURE_DATA:
343         return EventTypeNames::canplay;
344     case HTMLMediaElement::HAVE_ENOUGH_DATA:
345         return EventTypeNames::canplaythrough;
346     default:
347         ASSERT_NOT_REACHED();
348         return nullAtom;
349     }
350 }
351
352 void MediaController::updateReadyState()
353 {
354     ReadyState oldReadyState = m_readyState;
355     ReadyState newReadyState;
356
357     if (m_mediaElements.isEmpty()) {
358         // If the MediaController has no slaved media elements, let new readiness state be 0.
359         newReadyState = HTMLMediaElement::HAVE_NOTHING;
360     } else {
361         // Otherwise, let it have the lowest value of the readyState IDL attributes of all of its
362         // slaved media elements.
363         newReadyState = m_mediaElements.first()->readyState();
364         for (size_t index = 1; index < m_mediaElements.size(); ++index)
365             newReadyState = min(newReadyState, m_mediaElements[index]->readyState());
366     }
367
368     if (newReadyState == oldReadyState)
369         return;
370
371     // If the MediaController's most recently reported readiness state is greater than new readiness
372     // state then queue a task to fire a simple event at the MediaController object, whose name is the
373     // event name corresponding to the value of new readiness state given in the table below. [omitted]
374     if (oldReadyState > newReadyState) {
375         scheduleEvent(eventNameForReadyState(newReadyState));
376         return;
377     }
378
379     // If the MediaController's most recently reported readiness state is less than the new readiness
380     // state, then run these substeps:
381     // 1. Let next state be the MediaController's most recently reported readiness state.
382     ReadyState nextState = oldReadyState;
383     do {
384         // 2. Loop: Increment next state by one.
385         nextState = static_cast<ReadyState>(nextState + 1);
386         // 3. Queue a task to fire a simple event at the MediaController object, whose name is the
387         // event name corresponding to the value of next state given in the table below. [omitted]
388         scheduleEvent(eventNameForReadyState(nextState));
389         // If next state is less than new readiness state, then return to the step labeled loop
390     } while (nextState < newReadyState);
391
392     // Let the MediaController's most recently reported readiness state be new readiness state.
393     m_readyState = newReadyState;
394 }
395
396 void MediaController::updatePlaybackState()
397 {
398     PlaybackState oldPlaybackState = m_playbackState;
399     PlaybackState newPlaybackState;
400
401     // Initialize new playback state by setting it to the state given for the first matching
402     // condition from the following list:
403     if (m_mediaElements.isEmpty()) {
404         // If the MediaController has no slaved media elements
405         // Let new playback state be waiting.
406         newPlaybackState = WAITING;
407     } else if (hasEnded()) {
408         // If all of the MediaController's slaved media elements have ended playback and the media
409         // controller playback rate is positive or zero
410         // Let new playback state be ended.
411         newPlaybackState = ENDED;
412     } else if (isBlocked()) {
413         // If the MediaController is a blocked media controller
414         // Let new playback state be waiting.
415         newPlaybackState = WAITING;
416     } else {
417         // Otherwise
418         // Let new playback state be playing.
419         newPlaybackState = PLAYING;
420     }
421
422     // If the MediaController's most recently reported playback state is not equal to new playback state
423     if (newPlaybackState == oldPlaybackState)
424         return;
425
426     // and the new playback state is ended,
427     if (newPlaybackState == ENDED) {
428         // then queue a task that, if the MediaController object is a playing media controller, and
429         // all of the MediaController's slaved media elements have still ended playback, and the
430         // media controller playback rate is still positive or zero,
431         if (!m_paused && hasEnded()) {
432             // changes the MediaController object to a paused media controller
433             m_paused = true;
434
435             // and then fires a simple event named pause at the MediaController object.
436             scheduleEvent(EventTypeNames::pause);
437         }
438     }
439
440     // If the MediaController's most recently reported playback state is not equal to new playback state
441     // then queue a task to fire a simple event at the MediaController object, whose name is playing
442     // if new playback state is playing, ended if new playback state is ended, and waiting otherwise.
443     AtomicString eventName;
444     switch (newPlaybackState) {
445     case WAITING:
446         eventName = EventTypeNames::waiting;
447         m_clock->stop();
448         m_timeupdateTimer.stop();
449         break;
450     case ENDED:
451         eventName = EventTypeNames::ended;
452         m_clock->stop();
453         m_timeupdateTimer.stop();
454         break;
455     case PLAYING:
456         eventName = EventTypeNames::playing;
457         m_clock->start();
458         startTimeupdateTimer();
459         break;
460     default:
461         ASSERT_NOT_REACHED();
462     }
463     scheduleEvent(eventName);
464
465     // Let the MediaController's most recently reported playback state be new playback state.
466     m_playbackState = newPlaybackState;
467
468     updateMediaElements();
469 }
470
471 void MediaController::updateMediaElements()
472 {
473     for (size_t index = 0; index < m_mediaElements.size(); ++index)
474         m_mediaElements[index]->updatePlayState();
475 }
476
477 void MediaController::bringElementUpToSpeed(HTMLMediaElement* element)
478 {
479     ASSERT(element);
480     ASSERT(m_mediaElements.contains(element));
481
482     // When the user agent is to bring a media element up to speed with its new media controller,
483     // it must seek that media element to the MediaController's media controller position relative
484     // to the media element's timeline.
485     element->seek(currentTime(), IGNORE_EXCEPTION);
486 }
487
488 bool MediaController::isRestrained() const
489 {
490     ASSERT(!m_mediaElements.isEmpty());
491
492     // A MediaController is a restrained media controller if the MediaController is a playing media
493     // controller,
494     if (m_paused)
495         return false;
496
497     bool anyAutoplayingAndPaused = false;
498     bool allPaused = true;
499     for (size_t index = 0; index < m_mediaElements.size(); ++index) {
500         HTMLMediaElement* element = m_mediaElements[index];
501
502         // and none of its slaved media elements are blocked media elements,
503         if (element->isBlocked())
504             return false;
505
506         if (element->isAutoplaying() && element->paused())
507             anyAutoplayingAndPaused = true;
508
509         if (!element->paused())
510             allPaused = false;
511     }
512
513     // but either at least one of its slaved media elements whose autoplaying flag is true still has
514     // its paused attribute set to true, or, all of its slaved media elements have their paused
515     // attribute set to true.
516     return anyAutoplayingAndPaused || allPaused;
517 }
518
519 bool MediaController::isBlocked() const
520 {
521     ASSERT(!m_mediaElements.isEmpty());
522
523     // A MediaController is a blocked media controller if the MediaController is a paused media
524     // controller,
525     if (m_paused)
526         return true;
527
528     bool allPaused = true;
529     for (size_t index = 0; index < m_mediaElements.size(); ++index) {
530         HTMLMediaElement* element = m_mediaElements[index];
531
532         // or if any of its slaved media elements are blocked media elements,
533         if (element->isBlocked())
534             return true;
535
536         // or if any of its slaved media elements whose autoplaying flag is true still have their
537         // paused attribute set to true,
538         if (element->isAutoplaying() && element->paused())
539             return true;
540
541         if (!element->paused())
542             allPaused = false;
543     }
544
545     // or if all of its slaved media elements have their paused attribute set to true.
546     return allPaused;
547 }
548
549 bool MediaController::hasEnded() const
550 {
551     // If the ... media controller playback rate is positive or zero
552     if (m_clock->playRate() < 0)
553         return false;
554
555     // [and] all of the MediaController's slaved media elements have ended playback ... let new
556     // playback state be ended.
557     if (m_mediaElements.isEmpty())
558         return false;
559
560     bool allHaveEnded = true;
561     for (size_t index = 0; index < m_mediaElements.size(); ++index) {
562         if (!m_mediaElements[index]->ended())
563             allHaveEnded = false;
564     }
565     return allHaveEnded;
566 }
567
568 void MediaController::scheduleEvent(const AtomicString& eventName)
569 {
570     m_pendingEvents.append(Event::createCancelable(eventName));
571     if (!m_asyncEventTimer.isActive())
572         m_asyncEventTimer.startOneShot(0, FROM_HERE);
573 }
574
575 void MediaController::asyncEventTimerFired(Timer<MediaController>*)
576 {
577     Vector<RefPtr<Event> > pendingEvents;
578
579     m_pendingEvents.swap(pendingEvents);
580     size_t count = pendingEvents.size();
581     for (size_t index = 0; index < count; ++index)
582         dispatchEvent(pendingEvents[index].release(), IGNORE_EXCEPTION);
583 }
584
585 void MediaController::clearPositionTimerFired(Timer<MediaController>*)
586 {
587     m_position = MediaPlayer::invalidTime();
588 }
589
590 bool MediaController::hasAudio() const
591 {
592     for (size_t index = 0; index < m_mediaElements.size(); ++index) {
593         if (m_mediaElements[index]->hasAudio())
594             return true;
595     }
596     return false;
597 }
598
599 const AtomicString& MediaController::interfaceName() const
600 {
601     return EventTargetNames::MediaController;
602 }
603
604 // The spec says to fire periodic timeupdate events (those sent while playing) every
605 // "15 to 250ms", we choose the slowest frequency
606 static const double maxTimeupdateEventFrequency = 0.25;
607
608 void MediaController::startTimeupdateTimer()
609 {
610     if (m_timeupdateTimer.isActive())
611         return;
612
613     m_timeupdateTimer.startRepeating(maxTimeupdateEventFrequency, FROM_HERE);
614 }
615
616 void MediaController::timeupdateTimerFired(Timer<MediaController>*)
617 {
618     scheduleTimeupdateEvent();
619 }
620
621 void MediaController::scheduleTimeupdateEvent()
622 {
623     double now = WTF::currentTime();
624     double timedelta = now - m_previousTimeupdateTime;
625
626     if (timedelta < maxTimeupdateEventFrequency)
627         return;
628
629     scheduleEvent(EventTypeNames::timeupdate);
630     m_previousTimeupdateTime = now;
631 }